1 /*! blanket - v1.1.5 */
5 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
6 Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
7 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
8 Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
9 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
10 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
11 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions are met:
16 * Redistributions of source code must retain the above copyright
17 notice, this list of conditions and the following disclaimer.
18 * Redistributions in binary form must reproduce the above copyright
19 notice, this list of conditions and the following disclaimer in the
20 documentation and/or other materials provided with the distribution.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
26 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 /*jslint bitwise:true plusplus:true */
35 /*global esprima:true, define:true, exports:true, window: true,
36 throwError: true, createLiteral: true, generateStatement: true,
37 parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
38 parseFunctionDeclaration: true, parseFunctionExpression: true,
39 parseFunctionSourceElements: true, parseVariableIdentifier: true,
40 parseLeftHandSideExpression: true,
41 parseStatement: true, parseSourceElement: true */
43 (function (root, factory) {
46 // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
47 // Rhino, and plain browser loading.
48 if (typeof define === 'function' && define.amd) {
49 define(['exports'], factory);
50 } else if (typeof exports !== 'undefined') {
53 factory((root.esprima = {}));
55 }(this, function (exports) {
86 TokenName[Token.BooleanLiteral] = 'Boolean';
87 TokenName[Token.EOF] = '<end>';
88 TokenName[Token.Identifier] = 'Identifier';
89 TokenName[Token.Keyword] = 'Keyword';
90 TokenName[Token.NullLiteral] = 'Null';
91 TokenName[Token.NumericLiteral] = 'Numeric';
92 TokenName[Token.Punctuator] = 'Punctuator';
93 TokenName[Token.StringLiteral] = 'String';
96 AssignmentExpression: 'AssignmentExpression',
97 ArrayExpression: 'ArrayExpression',
98 BlockStatement: 'BlockStatement',
99 BinaryExpression: 'BinaryExpression',
100 BreakStatement: 'BreakStatement',
101 CallExpression: 'CallExpression',
102 CatchClause: 'CatchClause',
103 ConditionalExpression: 'ConditionalExpression',
104 ContinueStatement: 'ContinueStatement',
105 DoWhileStatement: 'DoWhileStatement',
106 DebuggerStatement: 'DebuggerStatement',
107 EmptyStatement: 'EmptyStatement',
108 ExpressionStatement: 'ExpressionStatement',
109 ForStatement: 'ForStatement',
110 ForInStatement: 'ForInStatement',
111 FunctionDeclaration: 'FunctionDeclaration',
112 FunctionExpression: 'FunctionExpression',
113 Identifier: 'Identifier',
114 IfStatement: 'IfStatement',
116 LabeledStatement: 'LabeledStatement',
117 LogicalExpression: 'LogicalExpression',
118 MemberExpression: 'MemberExpression',
119 NewExpression: 'NewExpression',
120 ObjectExpression: 'ObjectExpression',
122 Property: 'Property',
123 ReturnStatement: 'ReturnStatement',
124 SequenceExpression: 'SequenceExpression',
125 SwitchStatement: 'SwitchStatement',
126 SwitchCase: 'SwitchCase',
127 ThisExpression: 'ThisExpression',
128 ThrowStatement: 'ThrowStatement',
129 TryStatement: 'TryStatement',
130 UnaryExpression: 'UnaryExpression',
131 UpdateExpression: 'UpdateExpression',
132 VariableDeclaration: 'VariableDeclaration',
133 VariableDeclarator: 'VariableDeclarator',
134 WhileStatement: 'WhileStatement',
135 WithStatement: 'WithStatement'
144 // Error messages should be identical to V8.
146 UnexpectedToken: 'Unexpected token %0',
147 UnexpectedNumber: 'Unexpected number',
148 UnexpectedString: 'Unexpected string',
149 UnexpectedIdentifier: 'Unexpected identifier',
150 UnexpectedReserved: 'Unexpected reserved word',
151 UnexpectedEOS: 'Unexpected end of input',
152 NewlineAfterThrow: 'Illegal newline after throw',
153 InvalidRegExp: 'Invalid regular expression',
154 UnterminatedRegExp: 'Invalid regular expression: missing /',
155 InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
156 InvalidLHSInForIn: 'Invalid left-hand side in for-in',
157 MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
158 NoCatchOrFinally: 'Missing catch or finally after try',
159 UnknownLabel: 'Undefined label \'%0\'',
160 Redeclaration: '%0 \'%1\' has already been declared',
161 IllegalContinue: 'Illegal continue statement',
162 IllegalBreak: 'Illegal break statement',
163 IllegalReturn: 'Illegal return statement',
164 StrictModeWith: 'Strict mode code may not include a with statement',
165 StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
166 StrictVarName: 'Variable name may not be eval or arguments in strict mode',
167 StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
168 StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
169 StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
170 StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
171 StrictDelete: 'Delete of an unqualified identifier in strict mode.',
172 StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
173 AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
174 AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
175 StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
176 StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
177 StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
178 StrictReservedWord: 'Use of future reserved word in strict mode'
181 // See also tools/generate-unicode-regex.py.
183 NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'),
184 NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]')
187 // Ensure the condition is true, otherwise throw an error.
188 // This is only to have a better contract semantic, i.e. another safety net
189 // to catch a logic error. The condition shall be fulfilled in normal case.
190 // Do NOT use this to enforce a certain condition on any user input.
192 function assert(condition, message) {
194 throw new Error('ASSERT: ' + message);
198 function sliceSource(from, to) {
199 return source.slice(from, to);
202 if (typeof 'esprima'[0] === 'undefined') {
203 sliceSource = function sliceArraySource(from, to) {
204 return source.slice(from, to).join('');
208 function isDecimalDigit(ch) {
209 return '0123456789'.indexOf(ch) >= 0;
212 function isHexDigit(ch) {
213 return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
216 function isOctalDigit(ch) {
217 return '01234567'.indexOf(ch) >= 0;
223 function isWhiteSpace(ch) {
224 return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
225 (ch === '\u000C') || (ch === '\u00A0') ||
226 (ch.charCodeAt(0) >= 0x1680 &&
227 '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
230 // 7.3 Line Terminators
232 function isLineTerminator(ch) {
233 return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029');
236 // 7.6 Identifier Names and Identifiers
238 function isIdentifierStart(ch) {
239 return (ch === '$') || (ch === '_') || (ch === '\\') ||
240 (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
241 ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
244 function isIdentifierPart(ch) {
245 return (ch === '$') || (ch === '_') || (ch === '\\') ||
246 (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
247 ((ch >= '0') && (ch <= '9')) ||
248 ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
251 // 7.6.1.2 Future Reserved Words
253 function isFutureReservedWord(id) {
256 // Future reserved words.
269 function isStrictModeReservedWord(id) {
272 // Strict Mode reserved words.
288 function isRestrictedWord(id) {
289 return id === 'eval' || id === 'arguments';
294 function isKeyword(id) {
298 keyword = (id === 'if') || (id === 'in') || (id === 'do');
301 keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
304 keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with');
307 keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw');
310 keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch');
313 keyword = (id === 'default') || (id === 'finally');
316 keyword = (id === 'function') || (id === 'continue') || (id === 'debugger');
319 keyword = (id === 'instanceof');
328 // Future reserved words.
329 // 'const' is specialized as Keyword in V8.
333 // For compatiblity to SpiderMonkey and ES.next
339 if (strict && isStrictModeReservedWord(id)) {
343 return isFutureReservedWord(id);
348 function skipComment() {
349 var ch, blockComment, lineComment;
351 blockComment = false;
354 while (index < length) {
358 ch = source[index++];
359 if (isLineTerminator(ch)) {
361 if (ch === '\r' && source[index] === '\n') {
367 } else if (blockComment) {
368 if (isLineTerminator(ch)) {
369 if (ch === '\r' && source[index + 1] === '\n') {
375 if (index >= length) {
376 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
379 ch = source[index++];
380 if (index >= length) {
381 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
387 blockComment = false;
391 } else if (ch === '/') {
392 ch = source[index + 1];
396 } else if (ch === '*') {
399 if (index >= length) {
400 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
405 } else if (isWhiteSpace(ch)) {
407 } else if (isLineTerminator(ch)) {
409 if (ch === '\r' && source[index] === '\n') {
420 function scanHexEscape(prefix) {
421 var i, len, ch, code = 0;
423 len = (prefix === 'u') ? 4 : 2;
424 for (i = 0; i < len; ++i) {
425 if (index < length && isHexDigit(source[index])) {
426 ch = source[index++];
427 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
432 return String.fromCharCode(code);
435 function scanIdentifier() {
436 var ch, start, id, restore;
439 if (!isIdentifierStart(ch)) {
446 if (source[index] !== 'u') {
451 ch = scanHexEscape('u');
453 if (ch === '\\' || !isIdentifierStart(ch)) {
462 id = source[index++];
465 while (index < length) {
467 if (!isIdentifierPart(ch)) {
472 if (source[index] !== 'u') {
477 ch = scanHexEscape('u');
479 if (ch === '\\' || !isIdentifierPart(ch)) {
488 id += source[index++];
492 // There is no keyword or literal with only one character.
493 // Thus, it must be an identifier.
494 if (id.length === 1) {
496 type: Token.Identifier,
498 lineNumber: lineNumber,
499 lineStart: lineStart,
500 range: [start, index]
508 lineNumber: lineNumber,
509 lineStart: lineStart,
510 range: [start, index]
514 // 7.8.1 Null Literals
518 type: Token.NullLiteral,
520 lineNumber: lineNumber,
521 lineStart: lineStart,
522 range: [start, index]
526 // 7.8.2 Boolean Literals
528 if (id === 'true' || id === 'false') {
530 type: Token.BooleanLiteral,
532 lineNumber: lineNumber,
533 lineStart: lineStart,
534 range: [start, index]
539 type: Token.Identifier,
541 lineNumber: lineNumber,
542 lineStart: lineStart,
543 range: [start, index]
549 function scanPunctuator() {
556 // Check for most common single-character punctuators.
558 if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
561 type: Token.Punctuator,
563 lineNumber: lineNumber,
564 lineStart: lineStart,
565 range: [start, index]
569 if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
572 type: Token.Punctuator,
574 lineNumber: lineNumber,
575 lineStart: lineStart,
576 range: [start, index]
580 // Dot (.) can also start a floating-point number, hence the need
581 // to check the next character.
583 ch2 = source[index + 1];
584 if (ch1 === '.' && !isDecimalDigit(ch2)) {
586 type: Token.Punctuator,
587 value: source[index++],
588 lineNumber: lineNumber,
589 lineStart: lineStart,
590 range: [start, index]
594 // Peek more characters.
596 ch3 = source[index + 2];
597 ch4 = source[index + 3];
599 // 4-character punctuator: >>>=
601 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
605 type: Token.Punctuator,
607 lineNumber: lineNumber,
608 lineStart: lineStart,
609 range: [start, index]
614 // 3-character punctuators: === !== >>> <<= >>=
616 if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
619 type: Token.Punctuator,
621 lineNumber: lineNumber,
622 lineStart: lineStart,
623 range: [start, index]
627 if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
630 type: Token.Punctuator,
632 lineNumber: lineNumber,
633 lineStart: lineStart,
634 range: [start, index]
638 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
641 type: Token.Punctuator,
643 lineNumber: lineNumber,
644 lineStart: lineStart,
645 range: [start, index]
649 if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
652 type: Token.Punctuator,
654 lineNumber: lineNumber,
655 lineStart: lineStart,
656 range: [start, index]
660 if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
663 type: Token.Punctuator,
665 lineNumber: lineNumber,
666 lineStart: lineStart,
667 range: [start, index]
671 // 2-character punctuators: <= >= == != ++ -- << >> && ||
672 // += -= *= %= &= |= ^= /=
675 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
678 type: Token.Punctuator,
680 lineNumber: lineNumber,
681 lineStart: lineStart,
682 range: [start, index]
687 if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
688 if ('+-<>&|'.indexOf(ch2) >= 0) {
691 type: Token.Punctuator,
693 lineNumber: lineNumber,
694 lineStart: lineStart,
695 range: [start, index]
700 // The remaining 1-character punctuators.
702 if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
704 type: Token.Punctuator,
705 value: source[index++],
706 lineNumber: lineNumber,
707 lineStart: lineStart,
708 range: [start, index]
713 // 7.8.3 Numeric Literals
715 function scanNumericLiteral() {
716 var number, start, ch;
719 assert(isDecimalDigit(ch) || (ch === '.'),
720 'Numeric literal must start with a decimal digit or a decimal point');
725 number = source[index++];
728 // Hex number starts with '0x'.
729 // Octal number starts with '0'.
730 if (number === '0') {
731 if (ch === 'x' || ch === 'X') {
732 number += source[index++];
733 while (index < length) {
735 if (!isHexDigit(ch)) {
738 number += source[index++];
741 if (number.length <= 2) {
743 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
746 if (index < length) {
748 if (isIdentifierStart(ch)) {
749 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
753 type: Token.NumericLiteral,
754 value: parseInt(number, 16),
755 lineNumber: lineNumber,
756 lineStart: lineStart,
757 range: [start, index]
759 } else if (isOctalDigit(ch)) {
760 number += source[index++];
761 while (index < length) {
763 if (!isOctalDigit(ch)) {
766 number += source[index++];
769 if (index < length) {
771 if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
772 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
776 type: Token.NumericLiteral,
777 value: parseInt(number, 8),
779 lineNumber: lineNumber,
780 lineStart: lineStart,
781 range: [start, index]
785 // decimal number starts with '0' such as '09' is illegal.
786 if (isDecimalDigit(ch)) {
787 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
791 while (index < length) {
793 if (!isDecimalDigit(ch)) {
796 number += source[index++];
801 number += source[index++];
802 while (index < length) {
804 if (!isDecimalDigit(ch)) {
807 number += source[index++];
811 if (ch === 'e' || ch === 'E') {
812 number += source[index++];
815 if (ch === '+' || ch === '-') {
816 number += source[index++];
820 if (isDecimalDigit(ch)) {
821 number += source[index++];
822 while (index < length) {
824 if (!isDecimalDigit(ch)) {
827 number += source[index++];
830 ch = 'character ' + ch;
831 if (index >= length) {
834 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
838 if (index < length) {
840 if (isIdentifierStart(ch)) {
841 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
846 type: Token.NumericLiteral,
847 value: parseFloat(number),
848 lineNumber: lineNumber,
849 lineStart: lineStart,
850 range: [start, index]
854 // 7.8.4 String Literals
856 function scanStringLiteral() {
857 var str = '', quote, start, ch, code, unescaped, restore, octal = false;
859 quote = source[index];
860 assert((quote === '\'' || quote === '"'),
861 'String literal must starts with a quote');
866 while (index < length) {
867 ch = source[index++];
872 } else if (ch === '\\') {
873 ch = source[index++];
874 if (!isLineTerminator(ch)) {
888 unescaped = scanHexEscape(ch);
907 if (isOctalDigit(ch)) {
908 code = '01234567'.indexOf(ch);
910 // \0 is not octal escape sequence
915 if (index < length && isOctalDigit(source[index])) {
917 code = code * 8 + '01234567'.indexOf(source[index++]);
919 // 3 digits are only allowed when string starts
921 if ('0123'.indexOf(ch) >= 0 &&
923 isOctalDigit(source[index])) {
924 code = code * 8 + '01234567'.indexOf(source[index++]);
927 str += String.fromCharCode(code);
935 if (ch === '\r' && source[index] === '\n') {
939 } else if (isLineTerminator(ch)) {
947 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
951 type: Token.StringLiteral,
954 lineNumber: lineNumber,
955 lineStart: lineStart,
956 range: [start, index]
960 function scanRegExp() {
961 var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false;
968 assert(ch === '/', 'Regular expression literal must start with a slash');
969 str = source[index++];
971 while (index < length) {
972 ch = source[index++];
980 ch = source[index++];
982 if (isLineTerminator(ch)) {
983 throwError({}, Messages.UnterminatedRegExp);
986 } else if (ch === '/') {
989 } else if (ch === '[') {
991 } else if (isLineTerminator(ch)) {
992 throwError({}, Messages.UnterminatedRegExp);
998 throwError({}, Messages.UnterminatedRegExp);
1001 // Exclude leading and trailing slash.
1002 pattern = str.substr(1, str.length - 2);
1005 while (index < length) {
1007 if (!isIdentifierPart(ch)) {
1012 if (ch === '\\' && index < length) {
1017 ch = scanHexEscape('u');
1021 for (; restore < index; ++restore) {
1022 str += source[restore];
1039 value = new RegExp(pattern, flags);
1041 throwError({}, Messages.InvalidRegExp);
1047 range: [start, index]
1051 function isIdentifierName(token) {
1052 return token.type === Token.Identifier ||
1053 token.type === Token.Keyword ||
1054 token.type === Token.BooleanLiteral ||
1055 token.type === Token.NullLiteral;
1058 function advance() {
1063 if (index >= length) {
1066 lineNumber: lineNumber,
1067 lineStart: lineStart,
1068 range: [index, index]
1072 token = scanPunctuator();
1073 if (typeof token !== 'undefined') {
1079 if (ch === '\'' || ch === '"') {
1080 return scanStringLiteral();
1083 if (ch === '.' || isDecimalDigit(ch)) {
1084 return scanNumericLiteral();
1087 token = scanIdentifier();
1088 if (typeof token !== 'undefined') {
1092 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
1099 index = buffer.range[1];
1100 lineNumber = buffer.lineNumber;
1101 lineStart = buffer.lineStart;
1111 function lookahead() {
1112 var pos, line, start;
1114 if (buffer !== null) {
1129 // Return true if there is a line terminator before the next token.
1131 function peekLineTerminator() {
1132 var pos, line, start, found;
1138 found = lineNumber !== line;
1146 // Throw an exception
1148 function throwError(token, messageFormat) {
1150 args = Array.prototype.slice.call(arguments, 2),
1151 msg = messageFormat.replace(
1153 function (whole, index) {
1154 return args[index] || '';
1158 if (typeof token.lineNumber === 'number') {
1159 error = new Error('Line ' + token.lineNumber + ': ' + msg);
1160 error.index = token.range[0];
1161 error.lineNumber = token.lineNumber;
1162 error.column = token.range[0] - lineStart + 1;
1164 error = new Error('Line ' + lineNumber + ': ' + msg);
1165 error.index = index;
1166 error.lineNumber = lineNumber;
1167 error.column = index - lineStart + 1;
1173 function throwErrorTolerant() {
1175 throwError.apply(null, arguments);
1178 extra.errors.push(e);
1186 // Throw an exception because of the token.
1188 function throwUnexpected(token) {
1189 if (token.type === Token.EOF) {
1190 throwError(token, Messages.UnexpectedEOS);
1193 if (token.type === Token.NumericLiteral) {
1194 throwError(token, Messages.UnexpectedNumber);
1197 if (token.type === Token.StringLiteral) {
1198 throwError(token, Messages.UnexpectedString);
1201 if (token.type === Token.Identifier) {
1202 throwError(token, Messages.UnexpectedIdentifier);
1205 if (token.type === Token.Keyword) {
1206 if (isFutureReservedWord(token.value)) {
1207 throwError(token, Messages.UnexpectedReserved);
1208 } else if (strict && isStrictModeReservedWord(token.value)) {
1209 throwErrorTolerant(token, Messages.StrictReservedWord);
1212 throwError(token, Messages.UnexpectedToken, token.value);
1215 // BooleanLiteral, NullLiteral, or Punctuator.
1216 throwError(token, Messages.UnexpectedToken, token.value);
1219 // Expect the next token to match the specified punctuator.
1220 // If not, an exception will be thrown.
1222 function expect(value) {
1224 if (token.type !== Token.Punctuator || token.value !== value) {
1225 throwUnexpected(token);
1229 // Expect the next token to match the specified keyword.
1230 // If not, an exception will be thrown.
1232 function expectKeyword(keyword) {
1234 if (token.type !== Token.Keyword || token.value !== keyword) {
1235 throwUnexpected(token);
1239 // Return true if the next token matches the specified punctuator.
1241 function match(value) {
1242 var token = lookahead();
1243 return token.type === Token.Punctuator && token.value === value;
1246 // Return true if the next token matches the specified keyword
1248 function matchKeyword(keyword) {
1249 var token = lookahead();
1250 return token.type === Token.Keyword && token.value === keyword;
1253 // Return true if the next token is an assignment operator
1255 function matchAssign() {
1256 var token = lookahead(),
1259 if (token.type !== Token.Punctuator) {
1262 return op === '=' ||
1276 function consumeSemicolon() {
1279 // Catch the very common case first.
1280 if (source[index] === ';') {
1287 if (lineNumber !== line) {
1296 token = lookahead();
1297 if (token.type !== Token.EOF && !match('}')) {
1298 throwUnexpected(token);
1302 // Return true if provided expression is LeftHandSideExpression
1304 function isLeftHandSide(expr) {
1305 return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
1308 // 11.1.4 Array Initialiser
1310 function parseArrayInitialiser() {
1315 while (!match(']')) {
1318 elements.push(null);
1320 elements.push(parseAssignmentExpression());
1331 type: Syntax.ArrayExpression,
1336 // 11.1.5 Object Initialiser
1338 function parsePropertyFunction(param, first) {
1339 var previousStrict, body;
1341 previousStrict = strict;
1342 body = parseFunctionSourceElements();
1343 if (first && strict && isRestrictedWord(param[0].name)) {
1344 throwErrorTolerant(first, Messages.StrictParamName);
1346 strict = previousStrict;
1349 type: Syntax.FunctionExpression,
1360 function parseObjectPropertyKey() {
1363 // Note: This function is called only from parseObjectProperty(), where
1364 // EOF and Punctuator tokens are already filtered out.
1366 if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
1367 if (strict && token.octal) {
1368 throwErrorTolerant(token, Messages.StrictOctalLiteral);
1370 return createLiteral(token);
1374 type: Syntax.Identifier,
1379 function parseObjectProperty() {
1380 var token, key, id, param;
1382 token = lookahead();
1384 if (token.type === Token.Identifier) {
1386 id = parseObjectPropertyKey();
1388 // Property Assignment: Getter and Setter.
1390 if (token.value === 'get' && !match(':')) {
1391 key = parseObjectPropertyKey();
1395 type: Syntax.Property,
1397 value: parsePropertyFunction([]),
1400 } else if (token.value === 'set' && !match(':')) {
1401 key = parseObjectPropertyKey();
1403 token = lookahead();
1404 if (token.type !== Token.Identifier) {
1405 throwUnexpected(lex());
1407 param = [ parseVariableIdentifier() ];
1410 type: Syntax.Property,
1412 value: parsePropertyFunction(param, token),
1418 type: Syntax.Property,
1420 value: parseAssignmentExpression(),
1424 } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
1425 throwUnexpected(token);
1427 key = parseObjectPropertyKey();
1430 type: Syntax.Property,
1432 value: parseAssignmentExpression(),
1438 function parseObjectInitialiser() {
1439 var properties = [], property, name, kind, map = {}, toString = String;
1443 while (!match('}')) {
1444 property = parseObjectProperty();
1446 if (property.key.type === Syntax.Identifier) {
1447 name = property.key.name;
1449 name = toString(property.key.value);
1451 kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
1452 if (Object.prototype.hasOwnProperty.call(map, name)) {
1453 if (map[name] === PropertyKind.Data) {
1454 if (strict && kind === PropertyKind.Data) {
1455 throwErrorTolerant({}, Messages.StrictDuplicateProperty);
1456 } else if (kind !== PropertyKind.Data) {
1457 throwErrorTolerant({}, Messages.AccessorDataProperty);
1460 if (kind === PropertyKind.Data) {
1461 throwErrorTolerant({}, Messages.AccessorDataProperty);
1462 } else if (map[name] & kind) {
1463 throwErrorTolerant({}, Messages.AccessorGetSet);
1471 properties.push(property);
1481 type: Syntax.ObjectExpression,
1482 properties: properties
1486 // 11.1.6 The Grouping Operator
1488 function parseGroupExpression() {
1493 expr = parseExpression();
1501 // 11.1 Primary Expressions
1503 function parsePrimaryExpression() {
1504 var token = lookahead(),
1507 if (type === Token.Identifier) {
1509 type: Syntax.Identifier,
1514 if (type === Token.StringLiteral || type === Token.NumericLiteral) {
1515 if (strict && token.octal) {
1516 throwErrorTolerant(token, Messages.StrictOctalLiteral);
1518 return createLiteral(lex());
1521 if (type === Token.Keyword) {
1522 if (matchKeyword('this')) {
1525 type: Syntax.ThisExpression
1529 if (matchKeyword('function')) {
1530 return parseFunctionExpression();
1534 if (type === Token.BooleanLiteral) {
1536 token.value = (token.value === 'true');
1537 return createLiteral(token);
1540 if (type === Token.NullLiteral) {
1543 return createLiteral(token);
1547 return parseArrayInitialiser();
1551 return parseObjectInitialiser();
1555 return parseGroupExpression();
1558 if (match('/') || match('/=')) {
1559 return createLiteral(scanRegExp());
1562 return throwUnexpected(lex());
1565 // 11.2 Left-Hand-Side Expressions
1567 function parseArguments() {
1573 while (index < length) {
1574 args.push(parseAssignmentExpression());
1587 function parseNonComputedProperty() {
1590 if (!isIdentifierName(token)) {
1591 throwUnexpected(token);
1595 type: Syntax.Identifier,
1600 function parseNonComputedMember() {
1603 return parseNonComputedProperty();
1606 function parseComputedMember() {
1611 expr = parseExpression();
1618 function parseNewExpression() {
1621 expectKeyword('new');
1624 type: Syntax.NewExpression,
1625 callee: parseLeftHandSideExpression(),
1630 expr['arguments'] = parseArguments();
1636 function parseLeftHandSideExpressionAllowCall() {
1639 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
1641 while (match('.') || match('[') || match('(')) {
1644 type: Syntax.CallExpression,
1646 'arguments': parseArguments()
1648 } else if (match('[')) {
1650 type: Syntax.MemberExpression,
1653 property: parseComputedMember()
1657 type: Syntax.MemberExpression,
1660 property: parseNonComputedMember()
1669 function parseLeftHandSideExpression() {
1672 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
1674 while (match('.') || match('[')) {
1677 type: Syntax.MemberExpression,
1680 property: parseComputedMember()
1684 type: Syntax.MemberExpression,
1687 property: parseNonComputedMember()
1695 // 11.3 Postfix Expressions
1697 function parsePostfixExpression() {
1698 var expr = parseLeftHandSideExpressionAllowCall(), token;
1700 token = lookahead();
1701 if (token.type !== Token.Punctuator) {
1705 if ((match('++') || match('--')) && !peekLineTerminator()) {
1707 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
1708 throwErrorTolerant({}, Messages.StrictLHSPostfix);
1711 if (!isLeftHandSide(expr)) {
1712 throwError({}, Messages.InvalidLHSInAssignment);
1716 type: Syntax.UpdateExpression,
1717 operator: lex().value,
1726 // 11.4 Unary Operators
1728 function parseUnaryExpression() {
1731 token = lookahead();
1732 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
1733 return parsePostfixExpression();
1736 if (match('++') || match('--')) {
1738 expr = parseUnaryExpression();
1740 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
1741 throwErrorTolerant({}, Messages.StrictLHSPrefix);
1744 if (!isLeftHandSide(expr)) {
1745 throwError({}, Messages.InvalidLHSInAssignment);
1749 type: Syntax.UpdateExpression,
1750 operator: token.value,
1757 if (match('+') || match('-') || match('~') || match('!')) {
1759 type: Syntax.UnaryExpression,
1760 operator: lex().value,
1761 argument: parseUnaryExpression()
1766 if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
1768 type: Syntax.UnaryExpression,
1769 operator: lex().value,
1770 argument: parseUnaryExpression()
1772 if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
1773 throwErrorTolerant({}, Messages.StrictDelete);
1778 return parsePostfixExpression();
1781 // 11.5 Multiplicative Operators
1783 function parseMultiplicativeExpression() {
1784 var expr = parseUnaryExpression();
1786 while (match('*') || match('/') || match('%')) {
1788 type: Syntax.BinaryExpression,
1789 operator: lex().value,
1791 right: parseUnaryExpression()
1798 // 11.6 Additive Operators
1800 function parseAdditiveExpression() {
1801 var expr = parseMultiplicativeExpression();
1803 while (match('+') || match('-')) {
1805 type: Syntax.BinaryExpression,
1806 operator: lex().value,
1808 right: parseMultiplicativeExpression()
1815 // 11.7 Bitwise Shift Operators
1817 function parseShiftExpression() {
1818 var expr = parseAdditiveExpression();
1820 while (match('<<') || match('>>') || match('>>>')) {
1822 type: Syntax.BinaryExpression,
1823 operator: lex().value,
1825 right: parseAdditiveExpression()
1831 // 11.8 Relational Operators
1833 function parseRelationalExpression() {
1834 var expr, previousAllowIn;
1836 previousAllowIn = state.allowIn;
1837 state.allowIn = true;
1839 expr = parseShiftExpression();
1841 while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
1843 type: Syntax.BinaryExpression,
1844 operator: lex().value,
1846 right: parseShiftExpression()
1850 state.allowIn = previousAllowIn;
1854 // 11.9 Equality Operators
1856 function parseEqualityExpression() {
1857 var expr = parseRelationalExpression();
1859 while (match('==') || match('!=') || match('===') || match('!==')) {
1861 type: Syntax.BinaryExpression,
1862 operator: lex().value,
1864 right: parseRelationalExpression()
1871 // 11.10 Binary Bitwise Operators
1873 function parseBitwiseANDExpression() {
1874 var expr = parseEqualityExpression();
1876 while (match('&')) {
1879 type: Syntax.BinaryExpression,
1882 right: parseEqualityExpression()
1889 function parseBitwiseXORExpression() {
1890 var expr = parseBitwiseANDExpression();
1892 while (match('^')) {
1895 type: Syntax.BinaryExpression,
1898 right: parseBitwiseANDExpression()
1905 function parseBitwiseORExpression() {
1906 var expr = parseBitwiseXORExpression();
1908 while (match('|')) {
1911 type: Syntax.BinaryExpression,
1914 right: parseBitwiseXORExpression()
1921 // 11.11 Binary Logical Operators
1923 function parseLogicalANDExpression() {
1924 var expr = parseBitwiseORExpression();
1926 while (match('&&')) {
1929 type: Syntax.LogicalExpression,
1932 right: parseBitwiseORExpression()
1939 function parseLogicalORExpression() {
1940 var expr = parseLogicalANDExpression();
1942 while (match('||')) {
1945 type: Syntax.LogicalExpression,
1948 right: parseLogicalANDExpression()
1955 // 11.12 Conditional Operator
1957 function parseConditionalExpression() {
1958 var expr, previousAllowIn, consequent;
1960 expr = parseLogicalORExpression();
1964 previousAllowIn = state.allowIn;
1965 state.allowIn = true;
1966 consequent = parseAssignmentExpression();
1967 state.allowIn = previousAllowIn;
1971 type: Syntax.ConditionalExpression,
1973 consequent: consequent,
1974 alternate: parseAssignmentExpression()
1981 // 11.13 Assignment Operators
1983 function parseAssignmentExpression() {
1986 token = lookahead();
1987 expr = parseConditionalExpression();
1989 if (matchAssign()) {
1990 // LeftHandSideExpression
1991 if (!isLeftHandSide(expr)) {
1992 throwError({}, Messages.InvalidLHSInAssignment);
1996 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
1997 throwErrorTolerant(token, Messages.StrictLHSAssignment);
2001 type: Syntax.AssignmentExpression,
2002 operator: lex().value,
2004 right: parseAssignmentExpression()
2011 // 11.14 Comma Operator
2013 function parseExpression() {
2014 var expr = parseAssignmentExpression();
2018 type: Syntax.SequenceExpression,
2019 expressions: [ expr ]
2022 while (index < length) {
2027 expr.expressions.push(parseAssignmentExpression());
2036 function parseStatementList() {
2040 while (index < length) {
2044 statement = parseSourceElement();
2045 if (typeof statement === 'undefined') {
2048 list.push(statement);
2054 function parseBlock() {
2059 block = parseStatementList();
2064 type: Syntax.BlockStatement,
2069 // 12.2 Variable Statement
2071 function parseVariableIdentifier() {
2074 if (token.type !== Token.Identifier) {
2075 throwUnexpected(token);
2079 type: Syntax.Identifier,
2084 function parseVariableDeclaration(kind) {
2085 var id = parseVariableIdentifier(),
2089 if (strict && isRestrictedWord(id.name)) {
2090 throwErrorTolerant({}, Messages.StrictVarName);
2093 if (kind === 'const') {
2095 init = parseAssignmentExpression();
2096 } else if (match('=')) {
2098 init = parseAssignmentExpression();
2102 type: Syntax.VariableDeclarator,
2108 function parseVariableDeclarationList(kind) {
2111 while (index < length) {
2112 list.push(parseVariableDeclaration(kind));
2122 function parseVariableStatement() {
2125 expectKeyword('var');
2127 declarations = parseVariableDeclarationList();
2132 type: Syntax.VariableDeclaration,
2133 declarations: declarations,
2138 // kind may be `const` or `let`
2139 // Both are experimental and not in the specification yet.
2140 // see http://wiki.ecmascript.org/doku.php?id=harmony:const
2141 // and http://wiki.ecmascript.org/doku.php?id=harmony:let
2142 function parseConstLetDeclaration(kind) {
2145 expectKeyword(kind);
2147 declarations = parseVariableDeclarationList(kind);
2152 type: Syntax.VariableDeclaration,
2153 declarations: declarations,
2158 // 12.3 Empty Statement
2160 function parseEmptyStatement() {
2164 type: Syntax.EmptyStatement
2168 // 12.4 Expression Statement
2170 function parseExpressionStatement() {
2171 var expr = parseExpression();
2176 type: Syntax.ExpressionStatement,
2181 // 12.5 If statement
2183 function parseIfStatement() {
2184 var test, consequent, alternate;
2186 expectKeyword('if');
2190 test = parseExpression();
2194 consequent = parseStatement();
2196 if (matchKeyword('else')) {
2198 alternate = parseStatement();
2204 type: Syntax.IfStatement,
2206 consequent: consequent,
2207 alternate: alternate
2211 // 12.6 Iteration Statements
2213 function parseDoWhileStatement() {
2214 var body, test, oldInIteration;
2216 expectKeyword('do');
2218 oldInIteration = state.inIteration;
2219 state.inIteration = true;
2221 body = parseStatement();
2223 state.inIteration = oldInIteration;
2225 expectKeyword('while');
2229 test = parseExpression();
2238 type: Syntax.DoWhileStatement,
2244 function parseWhileStatement() {
2245 var test, body, oldInIteration;
2247 expectKeyword('while');
2251 test = parseExpression();
2255 oldInIteration = state.inIteration;
2256 state.inIteration = true;
2258 body = parseStatement();
2260 state.inIteration = oldInIteration;
2263 type: Syntax.WhileStatement,
2269 function parseForVariableDeclaration() {
2273 type: Syntax.VariableDeclaration,
2274 declarations: parseVariableDeclarationList(),
2279 function parseForStatement() {
2280 var init, test, update, left, right, body, oldInIteration;
2282 init = test = update = null;
2284 expectKeyword('for');
2291 if (matchKeyword('var') || matchKeyword('let')) {
2292 state.allowIn = false;
2293 init = parseForVariableDeclaration();
2294 state.allowIn = true;
2296 if (init.declarations.length === 1 && matchKeyword('in')) {
2299 right = parseExpression();
2303 state.allowIn = false;
2304 init = parseExpression();
2305 state.allowIn = true;
2307 if (matchKeyword('in')) {
2308 // LeftHandSideExpression
2309 if (!isLeftHandSide(init)) {
2310 throwError({}, Messages.InvalidLHSInForIn);
2315 right = parseExpression();
2320 if (typeof left === 'undefined') {
2325 if (typeof left === 'undefined') {
2328 test = parseExpression();
2333 update = parseExpression();
2339 oldInIteration = state.inIteration;
2340 state.inIteration = true;
2342 body = parseStatement();
2344 state.inIteration = oldInIteration;
2346 if (typeof left === 'undefined') {
2348 type: Syntax.ForStatement,
2357 type: Syntax.ForInStatement,
2365 // 12.7 The continue statement
2367 function parseContinueStatement() {
2368 var token, label = null;
2370 expectKeyword('continue');
2372 // Optimize the most common form: 'continue;'.
2373 if (source[index] === ';') {
2376 if (!state.inIteration) {
2377 throwError({}, Messages.IllegalContinue);
2381 type: Syntax.ContinueStatement,
2386 if (peekLineTerminator()) {
2387 if (!state.inIteration) {
2388 throwError({}, Messages.IllegalContinue);
2392 type: Syntax.ContinueStatement,
2397 token = lookahead();
2398 if (token.type === Token.Identifier) {
2399 label = parseVariableIdentifier();
2401 if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
2402 throwError({}, Messages.UnknownLabel, label.name);
2408 if (label === null && !state.inIteration) {
2409 throwError({}, Messages.IllegalContinue);
2413 type: Syntax.ContinueStatement,
2418 // 12.8 The break statement
2420 function parseBreakStatement() {
2421 var token, label = null;
2423 expectKeyword('break');
2425 // Optimize the most common form: 'break;'.
2426 if (source[index] === ';') {
2429 if (!(state.inIteration || state.inSwitch)) {
2430 throwError({}, Messages.IllegalBreak);
2434 type: Syntax.BreakStatement,
2439 if (peekLineTerminator()) {
2440 if (!(state.inIteration || state.inSwitch)) {
2441 throwError({}, Messages.IllegalBreak);
2445 type: Syntax.BreakStatement,
2450 token = lookahead();
2451 if (token.type === Token.Identifier) {
2452 label = parseVariableIdentifier();
2454 if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
2455 throwError({}, Messages.UnknownLabel, label.name);
2461 if (label === null && !(state.inIteration || state.inSwitch)) {
2462 throwError({}, Messages.IllegalBreak);
2466 type: Syntax.BreakStatement,
2471 // 12.9 The return statement
2473 function parseReturnStatement() {
2474 var token, argument = null;
2476 expectKeyword('return');
2478 if (!state.inFunctionBody) {
2479 throwErrorTolerant({}, Messages.IllegalReturn);
2482 // 'return' followed by a space and an identifier is very common.
2483 if (source[index] === ' ') {
2484 if (isIdentifierStart(source[index + 1])) {
2485 argument = parseExpression();
2488 type: Syntax.ReturnStatement,
2494 if (peekLineTerminator()) {
2496 type: Syntax.ReturnStatement,
2502 token = lookahead();
2503 if (!match('}') && token.type !== Token.EOF) {
2504 argument = parseExpression();
2511 type: Syntax.ReturnStatement,
2516 // 12.10 The with statement
2518 function parseWithStatement() {
2522 throwErrorTolerant({}, Messages.StrictModeWith);
2525 expectKeyword('with');
2529 object = parseExpression();
2533 body = parseStatement();
2536 type: Syntax.WithStatement,
2542 // 12.10 The swith statement
2544 function parseSwitchCase() {
2549 if (matchKeyword('default')) {
2553 expectKeyword('case');
2554 test = parseExpression();
2558 while (index < length) {
2559 if (match('}') || matchKeyword('default') || matchKeyword('case')) {
2562 statement = parseStatement();
2563 if (typeof statement === 'undefined') {
2566 consequent.push(statement);
2570 type: Syntax.SwitchCase,
2572 consequent: consequent
2576 function parseSwitchStatement() {
2577 var discriminant, cases, clause, oldInSwitch, defaultFound;
2579 expectKeyword('switch');
2583 discriminant = parseExpression();
2592 type: Syntax.SwitchStatement,
2593 discriminant: discriminant
2599 oldInSwitch = state.inSwitch;
2600 state.inSwitch = true;
2601 defaultFound = false;
2603 while (index < length) {
2607 clause = parseSwitchCase();
2608 if (clause.test === null) {
2610 throwError({}, Messages.MultipleDefaultsInSwitch);
2612 defaultFound = true;
2617 state.inSwitch = oldInSwitch;
2622 type: Syntax.SwitchStatement,
2623 discriminant: discriminant,
2628 // 12.13 The throw statement
2630 function parseThrowStatement() {
2633 expectKeyword('throw');
2635 if (peekLineTerminator()) {
2636 throwError({}, Messages.NewlineAfterThrow);
2639 argument = parseExpression();
2644 type: Syntax.ThrowStatement,
2649 // 12.14 The try statement
2651 function parseCatchClause() {
2654 expectKeyword('catch');
2658 param = parseExpression();
2660 if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) {
2661 throwErrorTolerant({}, Messages.StrictCatchVariable);
2667 type: Syntax.CatchClause,
2673 function parseTryStatement() {
2674 var block, handlers = [], finalizer = null;
2676 expectKeyword('try');
2678 block = parseBlock();
2680 if (matchKeyword('catch')) {
2681 handlers.push(parseCatchClause());
2684 if (matchKeyword('finally')) {
2686 finalizer = parseBlock();
2689 if (handlers.length === 0 && !finalizer) {
2690 throwError({}, Messages.NoCatchOrFinally);
2694 type: Syntax.TryStatement,
2696 guardedHandlers: [],
2698 finalizer: finalizer
2702 // 12.15 The debugger statement
2704 function parseDebuggerStatement() {
2705 expectKeyword('debugger');
2710 type: Syntax.DebuggerStatement
2716 function parseStatement() {
2717 var token = lookahead(),
2721 if (token.type === Token.EOF) {
2722 throwUnexpected(token);
2725 if (token.type === Token.Punctuator) {
2726 switch (token.value) {
2728 return parseEmptyStatement();
2730 return parseBlock();
2732 return parseExpressionStatement();
2738 if (token.type === Token.Keyword) {
2739 switch (token.value) {
2741 return parseBreakStatement();
2743 return parseContinueStatement();
2745 return parseDebuggerStatement();
2747 return parseDoWhileStatement();
2749 return parseForStatement();
2751 return parseFunctionDeclaration();
2753 return parseIfStatement();
2755 return parseReturnStatement();
2757 return parseSwitchStatement();
2759 return parseThrowStatement();
2761 return parseTryStatement();
2763 return parseVariableStatement();
2765 return parseWhileStatement();
2767 return parseWithStatement();
2773 expr = parseExpression();
2775 // 12.12 Labelled Statements
2776 if ((expr.type === Syntax.Identifier) && match(':')) {
2779 if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) {
2780 throwError({}, Messages.Redeclaration, 'Label', expr.name);
2783 state.labelSet[expr.name] = true;
2784 labeledBody = parseStatement();
2785 delete state.labelSet[expr.name];
2788 type: Syntax.LabeledStatement,
2797 type: Syntax.ExpressionStatement,
2802 // 13 Function Definition
2804 function parseFunctionSourceElements() {
2805 var sourceElement, sourceElements = [], token, directive, firstRestricted,
2806 oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
2810 while (index < length) {
2811 token = lookahead();
2812 if (token.type !== Token.StringLiteral) {
2816 sourceElement = parseSourceElement();
2817 sourceElements.push(sourceElement);
2818 if (sourceElement.expression.type !== Syntax.Literal) {
2819 // this is not directive
2822 directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
2823 if (directive === 'use strict') {
2825 if (firstRestricted) {
2826 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
2829 if (!firstRestricted && token.octal) {
2830 firstRestricted = token;
2835 oldLabelSet = state.labelSet;
2836 oldInIteration = state.inIteration;
2837 oldInSwitch = state.inSwitch;
2838 oldInFunctionBody = state.inFunctionBody;
2840 state.labelSet = {};
2841 state.inIteration = false;
2842 state.inSwitch = false;
2843 state.inFunctionBody = true;
2845 while (index < length) {
2849 sourceElement = parseSourceElement();
2850 if (typeof sourceElement === 'undefined') {
2853 sourceElements.push(sourceElement);
2858 state.labelSet = oldLabelSet;
2859 state.inIteration = oldInIteration;
2860 state.inSwitch = oldInSwitch;
2861 state.inFunctionBody = oldInFunctionBody;
2864 type: Syntax.BlockStatement,
2865 body: sourceElements
2869 function parseFunctionDeclaration() {
2870 var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet;
2872 expectKeyword('function');
2873 token = lookahead();
2874 id = parseVariableIdentifier();
2876 if (isRestrictedWord(token.value)) {
2877 throwErrorTolerant(token, Messages.StrictFunctionName);
2880 if (isRestrictedWord(token.value)) {
2881 firstRestricted = token;
2882 message = Messages.StrictFunctionName;
2883 } else if (isStrictModeReservedWord(token.value)) {
2884 firstRestricted = token;
2885 message = Messages.StrictReservedWord;
2893 while (index < length) {
2894 token = lookahead();
2895 param = parseVariableIdentifier();
2897 if (isRestrictedWord(token.value)) {
2899 message = Messages.StrictParamName;
2901 if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
2903 message = Messages.StrictParamDupe;
2905 } else if (!firstRestricted) {
2906 if (isRestrictedWord(token.value)) {
2907 firstRestricted = token;
2908 message = Messages.StrictParamName;
2909 } else if (isStrictModeReservedWord(token.value)) {
2910 firstRestricted = token;
2911 message = Messages.StrictReservedWord;
2912 } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
2913 firstRestricted = token;
2914 message = Messages.StrictParamDupe;
2918 paramSet[param.name] = true;
2928 previousStrict = strict;
2929 body = parseFunctionSourceElements();
2930 if (strict && firstRestricted) {
2931 throwError(firstRestricted, message);
2933 if (strict && stricted) {
2934 throwErrorTolerant(stricted, message);
2936 strict = previousStrict;
2939 type: Syntax.FunctionDeclaration,
2950 function parseFunctionExpression() {
2951 var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
2953 expectKeyword('function');
2956 token = lookahead();
2957 id = parseVariableIdentifier();
2959 if (isRestrictedWord(token.value)) {
2960 throwErrorTolerant(token, Messages.StrictFunctionName);
2963 if (isRestrictedWord(token.value)) {
2964 firstRestricted = token;
2965 message = Messages.StrictFunctionName;
2966 } else if (isStrictModeReservedWord(token.value)) {
2967 firstRestricted = token;
2968 message = Messages.StrictReservedWord;
2977 while (index < length) {
2978 token = lookahead();
2979 param = parseVariableIdentifier();
2981 if (isRestrictedWord(token.value)) {
2983 message = Messages.StrictParamName;
2985 if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
2987 message = Messages.StrictParamDupe;
2989 } else if (!firstRestricted) {
2990 if (isRestrictedWord(token.value)) {
2991 firstRestricted = token;
2992 message = Messages.StrictParamName;
2993 } else if (isStrictModeReservedWord(token.value)) {
2994 firstRestricted = token;
2995 message = Messages.StrictReservedWord;
2996 } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
2997 firstRestricted = token;
2998 message = Messages.StrictParamDupe;
3002 paramSet[param.name] = true;
3012 previousStrict = strict;
3013 body = parseFunctionSourceElements();
3014 if (strict && firstRestricted) {
3015 throwError(firstRestricted, message);
3017 if (strict && stricted) {
3018 throwErrorTolerant(stricted, message);
3020 strict = previousStrict;
3023 type: Syntax.FunctionExpression,
3036 function parseSourceElement() {
3037 var token = lookahead();
3039 if (token.type === Token.Keyword) {
3040 switch (token.value) {
3043 return parseConstLetDeclaration(token.value);
3045 return parseFunctionDeclaration();
3047 return parseStatement();
3051 if (token.type !== Token.EOF) {
3052 return parseStatement();
3056 function parseSourceElements() {
3057 var sourceElement, sourceElements = [], token, directive, firstRestricted;
3059 while (index < length) {
3060 token = lookahead();
3061 if (token.type !== Token.StringLiteral) {
3065 sourceElement = parseSourceElement();
3066 sourceElements.push(sourceElement);
3067 if (sourceElement.expression.type !== Syntax.Literal) {
3068 // this is not directive
3071 directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
3072 if (directive === 'use strict') {
3074 if (firstRestricted) {
3075 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
3078 if (!firstRestricted && token.octal) {
3079 firstRestricted = token;
3084 while (index < length) {
3085 sourceElement = parseSourceElement();
3086 if (typeof sourceElement === 'undefined') {
3089 sourceElements.push(sourceElement);
3091 return sourceElements;
3094 function parseProgram() {
3098 type: Syntax.Program,
3099 body: parseSourceElements()
3104 // The following functions are needed only when the option to preserve
3105 // the comments is active.
3107 function addComment(type, value, start, end, loc) {
3108 assert(typeof start === 'number', 'Comment must have valid position');
3110 // Because the way the actual token is scanned, often the comments
3111 // (if any) are skipped twice during the lexical analysis.
3112 // Thus, we need to skip adding a comment if the comment array already
3114 if (extra.comments.length > 0) {
3115 if (extra.comments[extra.comments.length - 1].range[1] > start) {
3120 extra.comments.push({
3123 range: [start, end],
3128 function scanComment() {
3129 var comment, ch, loc, start, blockComment, lineComment;
3132 blockComment = false;
3133 lineComment = false;
3135 while (index < length) {
3139 ch = source[index++];
3140 if (isLineTerminator(ch)) {
3143 column: index - lineStart - 1
3145 lineComment = false;
3146 addComment('Line', comment, start, index - 1, loc);
3147 if (ch === '\r' && source[index] === '\n') {
3153 } else if (index >= length) {
3154 lineComment = false;
3158 column: length - lineStart
3160 addComment('Line', comment, start, length, loc);
3164 } else if (blockComment) {
3165 if (isLineTerminator(ch)) {
3166 if (ch === '\r' && source[index + 1] === '\n') {
3175 if (index >= length) {
3176 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
3179 ch = source[index++];
3180 if (index >= length) {
3181 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
3187 comment = comment.substr(0, comment.length - 1);
3188 blockComment = false;
3192 column: index - lineStart
3194 addComment('Block', comment, start, index, loc);
3199 } else if (ch === '/') {
3200 ch = source[index + 1];
3205 column: index - lineStart
3211 if (index >= length) {
3214 column: index - lineStart
3216 lineComment = false;
3217 addComment('Line', comment, start, index, loc);
3219 } else if (ch === '*') {
3222 blockComment = true;
3226 column: index - lineStart - 2
3229 if (index >= length) {
3230 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
3235 } else if (isWhiteSpace(ch)) {
3237 } else if (isLineTerminator(ch)) {
3239 if (ch === '\r' && source[index] === '\n') {
3250 function filterCommentLocation() {
3251 var i, entry, comment, comments = [];
3253 for (i = 0; i < extra.comments.length; ++i) {
3254 entry = extra.comments[i];
3260 comment.range = entry.range;
3263 comment.loc = entry.loc;
3265 comments.push(comment);
3268 extra.comments = comments;
3271 function collectToken() {
3272 var start, loc, token, range, value;
3279 column: index - lineStart
3283 token = extra.advance();
3286 column: index - lineStart
3289 if (token.type !== Token.EOF) {
3290 range = [token.range[0], token.range[1]];
3291 value = sliceSource(token.range[0], token.range[1]);
3293 type: TokenName[token.type],
3303 function collectRegex() {
3304 var pos, loc, regex, token;
3312 column: index - lineStart
3316 regex = extra.scanRegExp();
3319 column: index - lineStart
3322 // Pop the previous token, which is likely '/' or '/='
3323 if (extra.tokens.length > 0) {
3324 token = extra.tokens[extra.tokens.length - 1];
3325 if (token.range[0] === pos && token.type === 'Punctuator') {
3326 if (token.value === '/' || token.value === '/=') {
3333 type: 'RegularExpression',
3334 value: regex.literal,
3335 range: [pos, index],
3342 function filterTokenLocation() {
3343 var i, entry, token, tokens = [];
3345 for (i = 0; i < extra.tokens.length; ++i) {
3346 entry = extra.tokens[i];
3352 token.range = entry.range;
3355 token.loc = entry.loc;
3360 extra.tokens = tokens;
3363 function createLiteral(token) {
3365 type: Syntax.Literal,
3370 function createRawLiteral(token) {
3372 type: Syntax.Literal,
3374 raw: sliceSource(token.range[0], token.range[1])
3378 function createLocationMarker() {
3381 marker.range = [index, index];
3385 column: index - lineStart
3389 column: index - lineStart
3393 marker.end = function () {
3394 this.range[1] = index;
3395 this.loc.end.line = lineNumber;
3396 this.loc.end.column = index - lineStart;
3399 marker.applyGroup = function (node) {
3401 node.groupRange = [this.range[0], this.range[1]];
3406 line: this.loc.start.line,
3407 column: this.loc.start.column
3410 line: this.loc.end.line,
3411 column: this.loc.end.column
3417 marker.apply = function (node) {
3419 node.range = [this.range[0], this.range[1]];
3424 line: this.loc.start.line,
3425 column: this.loc.start.column
3428 line: this.loc.end.line,
3429 column: this.loc.end.column
3438 function trackGroupExpression() {
3442 marker = createLocationMarker();
3445 expr = parseExpression();
3450 marker.applyGroup(expr);
3455 function trackLeftHandSideExpression() {
3459 marker = createLocationMarker();
3461 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
3463 while (match('.') || match('[')) {
3466 type: Syntax.MemberExpression,
3469 property: parseComputedMember()
3475 type: Syntax.MemberExpression,
3478 property: parseNonComputedMember()
3488 function trackLeftHandSideExpressionAllowCall() {
3492 marker = createLocationMarker();
3494 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
3496 while (match('.') || match('[') || match('(')) {
3499 type: Syntax.CallExpression,
3501 'arguments': parseArguments()
3505 } else if (match('[')) {
3507 type: Syntax.MemberExpression,
3510 property: parseComputedMember()
3516 type: Syntax.MemberExpression,
3519 property: parseNonComputedMember()
3529 function filterGroup(node) {
3532 n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {};
3534 if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') {
3536 if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) {
3539 n[i] = filterGroup(entry);
3546 function wrapTrackingFunction(range, loc) {
3548 return function (parseFunction) {
3550 function isBinary(node) {
3551 return node.type === Syntax.LogicalExpression ||
3552 node.type === Syntax.BinaryExpression;
3555 function visit(node) {
3558 if (isBinary(node.left)) {
3561 if (isBinary(node.right)) {
3566 if (node.left.groupRange || node.right.groupRange) {
3567 start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0];
3568 end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1];
3569 node.range = [start, end];
3570 } else if (typeof node.range === 'undefined') {
3571 start = node.left.range[0];
3572 end = node.right.range[1];
3573 node.range = [start, end];
3577 if (node.left.groupLoc || node.right.groupLoc) {
3578 start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start;
3579 end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end;
3584 } else if (typeof node.loc === 'undefined') {
3586 start: node.left.loc.start,
3587 end: node.right.loc.end
3593 return function () {
3598 marker = createLocationMarker();
3599 node = parseFunction.apply(null, arguments);
3602 if (range && typeof node.range === 'undefined') {
3606 if (loc && typeof node.loc === 'undefined') {
3610 if (isBinary(node)) {
3623 if (extra.comments) {
3624 extra.skipComment = skipComment;
3625 skipComment = scanComment;
3629 extra.createLiteral = createLiteral;
3630 createLiteral = createRawLiteral;
3633 if (extra.range || extra.loc) {
3635 extra.parseGroupExpression = parseGroupExpression;
3636 extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
3637 extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
3638 parseGroupExpression = trackGroupExpression;
3639 parseLeftHandSideExpression = trackLeftHandSideExpression;
3640 parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall;
3642 wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
3644 extra.parseAdditiveExpression = parseAdditiveExpression;
3645 extra.parseAssignmentExpression = parseAssignmentExpression;
3646 extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
3647 extra.parseBitwiseORExpression = parseBitwiseORExpression;
3648 extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
3649 extra.parseBlock = parseBlock;
3650 extra.parseFunctionSourceElements = parseFunctionSourceElements;
3651 extra.parseCatchClause = parseCatchClause;
3652 extra.parseComputedMember = parseComputedMember;
3653 extra.parseConditionalExpression = parseConditionalExpression;
3654 extra.parseConstLetDeclaration = parseConstLetDeclaration;
3655 extra.parseEqualityExpression = parseEqualityExpression;
3656 extra.parseExpression = parseExpression;
3657 extra.parseForVariableDeclaration = parseForVariableDeclaration;
3658 extra.parseFunctionDeclaration = parseFunctionDeclaration;
3659 extra.parseFunctionExpression = parseFunctionExpression;
3660 extra.parseLogicalANDExpression = parseLogicalANDExpression;
3661 extra.parseLogicalORExpression = parseLogicalORExpression;
3662 extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
3663 extra.parseNewExpression = parseNewExpression;
3664 extra.parseNonComputedProperty = parseNonComputedProperty;
3665 extra.parseObjectProperty = parseObjectProperty;
3666 extra.parseObjectPropertyKey = parseObjectPropertyKey;
3667 extra.parsePostfixExpression = parsePostfixExpression;
3668 extra.parsePrimaryExpression = parsePrimaryExpression;
3669 extra.parseProgram = parseProgram;
3670 extra.parsePropertyFunction = parsePropertyFunction;
3671 extra.parseRelationalExpression = parseRelationalExpression;
3672 extra.parseStatement = parseStatement;
3673 extra.parseShiftExpression = parseShiftExpression;
3674 extra.parseSwitchCase = parseSwitchCase;
3675 extra.parseUnaryExpression = parseUnaryExpression;
3676 extra.parseVariableDeclaration = parseVariableDeclaration;
3677 extra.parseVariableIdentifier = parseVariableIdentifier;
3679 parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression);
3680 parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression);
3681 parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression);
3682 parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression);
3683 parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression);
3684 parseBlock = wrapTracking(extra.parseBlock);
3685 parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements);
3686 parseCatchClause = wrapTracking(extra.parseCatchClause);
3687 parseComputedMember = wrapTracking(extra.parseComputedMember);
3688 parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
3689 parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
3690 parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
3691 parseExpression = wrapTracking(extra.parseExpression);
3692 parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
3693 parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
3694 parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
3695 parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression);
3696 parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
3697 parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
3698 parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
3699 parseNewExpression = wrapTracking(extra.parseNewExpression);
3700 parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
3701 parseObjectProperty = wrapTracking(extra.parseObjectProperty);
3702 parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
3703 parsePostfixExpression = wrapTracking(extra.parsePostfixExpression);
3704 parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression);
3705 parseProgram = wrapTracking(extra.parseProgram);
3706 parsePropertyFunction = wrapTracking(extra.parsePropertyFunction);
3707 parseRelationalExpression = wrapTracking(extra.parseRelationalExpression);
3708 parseStatement = wrapTracking(extra.parseStatement);
3709 parseShiftExpression = wrapTracking(extra.parseShiftExpression);
3710 parseSwitchCase = wrapTracking(extra.parseSwitchCase);
3711 parseUnaryExpression = wrapTracking(extra.parseUnaryExpression);
3712 parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration);
3713 parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier);
3716 if (typeof extra.tokens !== 'undefined') {
3717 extra.advance = advance;
3718 extra.scanRegExp = scanRegExp;
3720 advance = collectToken;
3721 scanRegExp = collectRegex;
3725 function unpatch() {
3726 if (typeof extra.skipComment === 'function') {
3727 skipComment = extra.skipComment;
3731 createLiteral = extra.createLiteral;
3734 if (extra.range || extra.loc) {
3735 parseAdditiveExpression = extra.parseAdditiveExpression;
3736 parseAssignmentExpression = extra.parseAssignmentExpression;
3737 parseBitwiseANDExpression = extra.parseBitwiseANDExpression;
3738 parseBitwiseORExpression = extra.parseBitwiseORExpression;
3739 parseBitwiseXORExpression = extra.parseBitwiseXORExpression;
3740 parseBlock = extra.parseBlock;
3741 parseFunctionSourceElements = extra.parseFunctionSourceElements;
3742 parseCatchClause = extra.parseCatchClause;
3743 parseComputedMember = extra.parseComputedMember;
3744 parseConditionalExpression = extra.parseConditionalExpression;
3745 parseConstLetDeclaration = extra.parseConstLetDeclaration;
3746 parseEqualityExpression = extra.parseEqualityExpression;
3747 parseExpression = extra.parseExpression;
3748 parseForVariableDeclaration = extra.parseForVariableDeclaration;
3749 parseFunctionDeclaration = extra.parseFunctionDeclaration;
3750 parseFunctionExpression = extra.parseFunctionExpression;
3751 parseGroupExpression = extra.parseGroupExpression;
3752 parseLeftHandSideExpression = extra.parseLeftHandSideExpression;
3753 parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall;
3754 parseLogicalANDExpression = extra.parseLogicalANDExpression;
3755 parseLogicalORExpression = extra.parseLogicalORExpression;
3756 parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
3757 parseNewExpression = extra.parseNewExpression;
3758 parseNonComputedProperty = extra.parseNonComputedProperty;
3759 parseObjectProperty = extra.parseObjectProperty;
3760 parseObjectPropertyKey = extra.parseObjectPropertyKey;
3761 parsePrimaryExpression = extra.parsePrimaryExpression;
3762 parsePostfixExpression = extra.parsePostfixExpression;
3763 parseProgram = extra.parseProgram;
3764 parsePropertyFunction = extra.parsePropertyFunction;
3765 parseRelationalExpression = extra.parseRelationalExpression;
3766 parseStatement = extra.parseStatement;
3767 parseShiftExpression = extra.parseShiftExpression;
3768 parseSwitchCase = extra.parseSwitchCase;
3769 parseUnaryExpression = extra.parseUnaryExpression;
3770 parseVariableDeclaration = extra.parseVariableDeclaration;
3771 parseVariableIdentifier = extra.parseVariableIdentifier;
3774 if (typeof extra.scanRegExp === 'function') {
3775 advance = extra.advance;
3776 scanRegExp = extra.scanRegExp;
3780 function stringToArray(str) {
3781 var length = str.length,
3784 for (i = 0; i < length; ++i) {
3785 result[i] = str.charAt(i);
3790 function parse(code, options) {
3791 var program, toString;
3794 if (typeof code !== 'string' && !(code instanceof String)) {
3795 code = toString(code);
3800 lineNumber = (source.length > 0) ? 1 : 0;
3802 length = source.length;
3807 inFunctionBody: false,
3813 if (typeof options !== 'undefined') {
3814 extra.range = (typeof options.range === 'boolean') && options.range;
3815 extra.loc = (typeof options.loc === 'boolean') && options.loc;
3816 extra.raw = (typeof options.raw === 'boolean') && options.raw;
3817 if (typeof options.tokens === 'boolean' && options.tokens) {
3820 if (typeof options.comment === 'boolean' && options.comment) {
3821 extra.comments = [];
3823 if (typeof options.tolerant === 'boolean' && options.tolerant) {
3829 if (typeof source[0] === 'undefined') {
3830 // Try first to convert to a string. This is good as fast path
3831 // for old IE which understands string indexing for string
3832 // literals only and not for string object.
3833 if (code instanceof String) {
3834 source = code.valueOf();
3837 // Force accessing the characters via an array.
3838 if (typeof source[0] === 'undefined') {
3839 source = stringToArray(code);
3846 program = parseProgram();
3847 if (typeof extra.comments !== 'undefined') {
3848 filterCommentLocation();
3849 program.comments = extra.comments;
3851 if (typeof extra.tokens !== 'undefined') {
3852 filterTokenLocation();
3853 program.tokens = extra.tokens;
3855 if (typeof extra.errors !== 'undefined') {
3856 program.errors = extra.errors;
3858 if (extra.range || extra.loc) {
3859 program.body = filterGroup(program.body);
3871 // Sync with package.json.
3872 exports.version = '1.0.2';
3874 exports.parse = parse;
3877 exports.Syntax = (function () {
3878 var name, types = {};
3880 if (typeof Object.create === 'function') {
3881 types = Object.create(null);
3884 for (name in Syntax) {
3885 if (Syntax.hasOwnProperty(name)) {
3886 types[name] = Syntax[name];
3890 if (typeof Object.freeze === 'function') {
3891 Object.freeze(types);
3898 /* vim: set sw=4 ts=4 et tw=80 : */
3902 (function(require,module){
3904 var parse = require('esprima').parse;
3905 var objectKeys = Object.keys || function (obj) {
3907 for (var key in obj) keys.push(key);
3910 var forEach = function (xs, fn) {
3911 if (xs.forEach) return xs.forEach(fn);
3912 for (var i = 0; i < xs.length; i++) {
3913 fn.call(xs, xs[i], i, xs);
3917 var isArray = Array.isArray || function (xs) {
3918 return Object.prototype.toString.call(xs) === '[object Array]';
3921 module.exports = function (src, opts, fn) {
3922 if (typeof opts === 'function') {
3926 if (typeof src === 'object') {
3931 src = src === undefined ? opts.source : src;
3933 if (typeof src !== 'string') src = String(src);
3935 var ast = parse(src, opts);
3938 chunks : src.split(''),
3939 toString : function () { return result.chunks.join('') },
3940 inspect : function () { return result.toString() }
3944 (function walk (node, parent) {
3945 insertHelpers(node, parent, result.chunks);
3947 forEach(objectKeys(node), function (key) {
3948 if (key === 'parent') return;
3950 var child = node[key];
3951 if (isArray(child)) {
3952 forEach(child, function (c) {
3953 if (c && typeof c.type === 'string') {
3958 else if (child && typeof child.type === 'string') {
3959 insertHelpers(child, node, result.chunks);
3969 function insertHelpers (node, parent, chunks) {
3970 if (!node.range) return;
3972 node.parent = parent;
3974 node.source = function () {
3975 return chunks.slice(
3976 node.range[0], node.range[1]
3980 if (node.update && typeof node.update === 'object') {
3981 var prev = node.update;
3982 forEach(objectKeys(prev), function (key) {
3983 update[key] = prev[key];
3985 node.update = update;
3988 node.update = update;
3991 function update (s) {
3992 chunks[node.range[0]] = s;
3993 for (var i = node.range[0] + 1; i < node.range[1]; i++) {
3999 window.falafel = module.exports;})(function(){return {parse: esprima.parse};},{exports: {}});
4001 var inBrowser = typeof window !== 'undefined' && this === window;
4002 var parseAndModify = (inBrowser ? window.falafel : require("falafel"));
4004 (inBrowser ? window : exports).blanket = (function(){
4005 var linesToAddTracking = [
4006 "ExpressionStatement",
4008 "ContinueStatement" ,
4009 "VariableDeclaration",
4013 "FunctionDeclaration" ,
4016 "DoWhileStatement" ,
4022 linesToAddBrackets = [
4025 "DoWhileStatement" ,
4031 copynumber = Math.floor(Math.random()*1000),
4032 coverageInfo = {},options = {
4036 customVariable: null,
4038 ignoreScriptError: false,
4039 existingRequireJS:false,
4043 branchTracking: false,
4047 testReadyCallback:null,
4049 instrumentCache:false,
4053 if (inBrowser && typeof window.blanket !== 'undefined'){
4054 __blanket = window.blanket.noConflict();
4058 noConflict: function(){
4064 _getCopyNumber: function(){
4066 //for differentiating between instances
4069 extend: function(obj) {
4070 //borrowed from underscore
4071 _blanket._extend(_blanket,obj);
4073 _extend: function(dest,source){
4075 for (var prop in source) {
4076 if ( dest[prop] instanceof Object && typeof dest[prop] !== "function"){
4077 _blanket._extend(dest[prop],source[prop]);
4079 dest[prop] = source[prop];
4084 getCovVar: function(){
4085 var opt = _blanket.options("customVariable");
4087 if (_blanket.options("debug")) {console.log("BLANKET-Using custom tracking variable:",opt);}
4088 return inBrowser ? "window."+opt : opt;
4090 return inBrowser ? "window._$blanket" : "_$jscoverage";
4092 options: function(key,value){
4093 if (typeof key !== "string"){
4094 _blanket._extend(options,key);
4095 }else if (typeof value === 'undefined'){
4096 return options[key];
4101 instrument: function(config, next){
4102 //check instrumented hash table,
4103 //return instrumented code if available.
4104 var inFile = config.inputFile,
4105 inFileName = config.inputFileName;
4106 //check instrument cache
4107 if (_blanket.options("instrumentCache") && sessionStorage && sessionStorage.getItem("blanket_instrument_store-"+inFileName)){
4108 if (_blanket.options("debug")) {console.log("BLANKET-Reading instrumentation from cache: ",inFileName);}
4109 next(sessionStorage.getItem("blanket_instrument_store-"+inFileName));
4111 var sourceArray = _blanket._prepareSource(inFile);
4112 _blanket._trackingArraySetup=[];
4113 var instrumented = parseAndModify(inFile,{loc:true,comment:true}, _blanket._addTracking(inFileName));
4114 instrumented = _blanket._trackingSetup(inFileName,sourceArray)+instrumented;
4115 if (_blanket.options("sourceURL")){
4116 instrumented += "\n//@ sourceURL="+inFileName.replace("http://","");
4118 if (_blanket.options("debug")) {console.log("BLANKET-Instrumented file: ",inFileName);}
4119 if (_blanket.options("instrumentCache") && sessionStorage){
4120 if (_blanket.options("debug")) {console.log("BLANKET-Saving instrumentation to cache: ",inFileName);}
4121 sessionStorage.setItem("blanket_instrument_store-"+inFileName,instrumented);
4126 _trackingArraySetup: [],
4127 _branchingArraySetup: [],
4128 _prepareSource: function(source){
4129 return source.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/gm,"\n").split('\n');
4131 _trackingSetup: function(filename,sourceArray){
4132 var branches = _blanket.options("branchTracking");
4133 var sourceString = sourceArray.join("',\n'");
4135 var covVar = _blanket.getCovVar();
4137 intro += "if (typeof "+covVar+" === 'undefined') "+covVar+" = {};\n";
4139 intro += "var _$branchFcn=function(f,l,c,r){ ";
4140 intro += "if (!!r) { ";
4141 intro += covVar+"[f].branchData[l][c][0] = "+covVar+"[f].branchData[l][c][0] || [];";
4142 intro += covVar+"[f].branchData[l][c][0].push(r); }";
4144 intro += covVar+"[f].branchData[l][c][1] = "+covVar+"[f].branchData[l][c][1] || [];";
4145 intro += covVar+"[f].branchData[l][c][1].push(r); }";
4146 intro += "return r;};\n";
4148 intro += "if (typeof "+covVar+"['"+filename+"'] === 'undefined'){";
4150 intro += covVar+"['"+filename+"']=[];\n";
4152 intro += covVar+"['"+filename+"'].branchData=[];\n";
4154 intro += covVar+"['"+filename+"'].source=['"+sourceString+"'];\n";
4155 //initialize array values
4156 _blanket._trackingArraySetup.sort(function(a,b){
4157 return parseInt(a,10) > parseInt(b,10);
4158 }).forEach(function(item){
4159 intro += covVar+"['"+filename+"']["+item+"]=0;\n";
4162 _blanket._branchingArraySetup.sort(function(a,b){
4163 return a.line > b.line;
4164 }).sort(function(a,b){
4165 return a.column > b.column;
4166 }).forEach(function(item){
4167 if (item.file === filename){
4168 intro += "if (typeof "+ covVar+"['"+filename+"'].branchData["+item.line+"] === 'undefined'){\n";
4169 intro += covVar+"['"+filename+"'].branchData["+item.line+"]=[];\n";
4171 intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"] = [];\n";
4172 intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].consequent = "+JSON.stringify(item.consequent)+";\n";
4173 intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].alternate = "+JSON.stringify(item.alternate)+";\n";
4181 _blockifyIf: function(node){
4182 if (linesToAddBrackets.indexOf(node.type) > -1){
4183 var bracketsExistObject = node.consequent || node.body;
4184 var bracketsExistAlt = node.alternate;
4185 if( bracketsExistAlt && bracketsExistAlt.type !== "BlockStatement") {
4186 bracketsExistAlt.update("{\n"+bracketsExistAlt.source()+"}\n");
4188 if( bracketsExistObject && bracketsExistObject.type !== "BlockStatement") {
4189 bracketsExistObject.update("{\n"+bracketsExistObject.source()+"}\n");
4193 _trackBranch: function(node,filename){
4194 //recursive on consequent and alternative
4195 var line = node.loc.start.line;
4196 var col = node.loc.start.column;
4198 _blanket._branchingArraySetup.push({
4202 consequent: node.consequent.loc,
4203 alternate: node.alternate.loc
4206 var source = node.source();
4207 var updated = "_$branchFcn"+
4208 "('"+filename+"',"+line+","+col+","+source.slice(0,source.indexOf("?"))+
4209 ")"+source.slice(source.indexOf("?"));
4210 node.update(updated);
4212 _addTracking: function (filename) {
4213 //falafel doesn't take a file name
4214 //so we include the filename in a closure
4215 //and return the function to falafel
4216 var covVar = _blanket.getCovVar();
4218 return function(node){
4219 _blanket._blockifyIf(node);
4221 if (linesToAddTracking.indexOf(node.type) > -1 && node.parent.type !== "LabeledStatement") {
4222 _blanket._checkDefs(node,filename);
4223 if (node.type === "VariableDeclaration" &&
4224 (node.parent.type === "ForStatement" || node.parent.type === "ForInStatement")){
4227 if (node.loc && node.loc.start){
4228 node.update(covVar+"['"+filename+"']["+node.loc.start.line+"]++;\n"+node.source());
4229 _blanket._trackingArraySetup.push(node.loc.start.line);
4231 //I don't think we can handle a node with no location
4232 throw new Error("The instrumenter encountered a node with no location: "+Object.keys(node));
4234 }else if (_blanket.options("branchTracking") && node.type === "ConditionalExpression"){
4235 _blanket._trackBranch(node,filename);
4239 _checkDefs: function(node,filename){
4240 // Make sure developers don't redefine window. if they do, inform them it is wrong.
4242 if (node.type === "VariableDeclaration" && node.declarations) {
4243 node.declarations.forEach(function(declaration) {
4244 if (declaration.id.name === "window") {
4245 throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
4249 if (node.type === "FunctionDeclaration" && node.params) {
4250 node.params.forEach(function(param) {
4251 if (param.name === "window") {
4252 throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
4256 //Make sure developers don't redefine the coverage variable
4257 if (node.type === "ExpressionStatement" &&
4258 node.expression && node.expression.left &&
4259 node.expression.left.object && node.expression.left.property &&
4260 node.expression.left.object.name +
4261 "." + node.expression.left.property.name === _blanket.getCovVar()) {
4262 throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
4265 //Make sure developers don't redefine the coverage variable in node
4266 if (node.type === "ExpressionStatement" &&
4267 node.expression && node.expression.left &&
4268 !node.expression.left.object && !node.expression.left.property &&
4269 node.expression.left.name === _blanket.getCovVar()) {
4270 throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
4274 setupCoverage: function(){
4275 coverageInfo.instrumentation = "blanket";
4276 coverageInfo.stats = {
4285 _checkIfSetup: function(){
4286 if (!coverageInfo.stats){
4287 throw new Error("You must call blanket.setupCoverage() first.");
4290 onTestStart: function(){
4291 if (_blanket.options("debug")) {console.log("BLANKET-Test event started");}
4292 this._checkIfSetup();
4293 coverageInfo.stats.tests++;
4294 coverageInfo.stats.pending++;
4296 onTestDone: function(total,passed){
4297 this._checkIfSetup();
4298 if(passed === total){
4299 coverageInfo.stats.passes++;
4301 coverageInfo.stats.failures++;
4303 coverageInfo.stats.pending--;
4305 onModuleStart: function(){
4306 this._checkIfSetup();
4307 coverageInfo.stats.suites++;
4309 onTestsDone: function(){
4310 if (_blanket.options("debug")) {console.log("BLANKET-Test event done");}
4311 this._checkIfSetup();
4312 coverageInfo.stats.end = new Date();
4315 this.report(coverageInfo);
4317 if (!_blanket.options("branchTracking")){
4318 delete (inBrowser ? window : global)[_blanket.getCovVar()].branchFcn;
4320 this.options("reporter").call(this,coverageInfo);
4327 (function(_blanket){
4328 var oldOptions = _blanket.options;
4330 outstandingRequireFiles:[],
4331 options: function(key,value){
4334 if (typeof key !== "string"){
4335 //key is key/value map
4338 }else if (typeof value === 'undefined'){
4340 return oldOptions(key);
4343 oldOptions(key,value);
4344 newVal[key] = value;
4347 if (newVal.adapter){
4348 _blanket._loadFile(newVal.adapter);
4351 _blanket._loadFile(newVal.loader);
4354 requiringFile: function(filename,done){
4355 if (typeof filename === "undefined"){
4356 _blanket.outstandingRequireFiles=[];
4357 }else if (typeof done === "undefined"){
4358 _blanket.outstandingRequireFiles.push(filename);
4360 _blanket.outstandingRequireFiles.splice(_blanket.outstandingRequireFiles.indexOf(filename),1);
4363 requireFilesLoaded: function(){
4364 return _blanket.outstandingRequireFiles.length === 0;
4366 showManualLoader: function(){
4367 if (document.getElementById("blanketLoaderDialog")){
4370 //copied from http://blog.avtex.com/2012/01/26/cross-browser-css-only-modal-box/
4371 var loader = "<div class='blanketDialogOverlay'>";
4372 loader += " </div>";
4373 loader += "<div class='blanketDialogVerticalOffset'>";
4374 loader += "<div class='blanketDialogBox'>";
4375 loader += "<b>Error:</b> Blanket.js encountered a cross origin request error while instrumenting the source files. ";
4376 loader += "<br><br>This is likely caused by the source files being referenced locally (using the file:// protocol). ";
4377 loader += "<br><br>Some solutions include <a href='http://askubuntu.com/questions/160245/making-google-chrome-option-allow-file-access-from-files-permanent' target='_blank'>starting Chrome with special flags</a>, <a target='_blank' href='https://github.com/remy/servedir'>running a server locally</a>, or using a browser without these CORS restrictions (Safari).";
4379 if (typeof FileReader !== "undefined"){
4380 loader += "<br>Or, try the experimental loader. When prompted, simply click on the directory containing all the source files you want covered.";
4381 loader += "<a href='javascript:document.getElementById(\"fileInput\").click();'>Start Loader</a>";
4382 loader += "<input type='file' type='application/x-javascript' accept='application/x-javascript' webkitdirectory id='fileInput' multiple onchange='window.blanket.manualFileLoader(this.files)' style='visibility:hidden;position:absolute;top:-50;left:-50'/>";
4384 loader += "<br><span style='float:right;cursor:pointer;' onclick=document.getElementById('blanketLoaderDialog').style.display='none';>Close</span>";
4385 loader += "<div style='clear:both'></div>";
4386 loader += "</div></div>";
4388 var css = ".blanketDialogWrapper {";
4389 css += "display:block;";
4390 css += "position:fixed;";
4391 css += "z-index:40001; }";
4393 css += ".blanketDialogOverlay {";
4394 css += "position:fixed;";
4395 css += "width:100%;";
4396 css += "height:100%;";
4397 css += "background-color:black;";
4398 css += "opacity:.5; ";
4399 css += "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; ";
4400 css += "filter:alpha(opacity=50); ";
4401 css += "z-index:40001; }";
4403 css += ".blanketDialogVerticalOffset { ";
4404 css += "position:fixed;";
4406 css += "width:100%;";
4407 css += "z-index:40002; }";
4409 css += ".blanketDialogBox { ";
4410 css += "width:405px; ";
4411 css += "position:relative;";
4412 css += "margin:0 auto;";
4413 css += "background-color:white;";
4414 css += "padding:10px;";
4415 css += "border:1px solid black; }";
4417 var dom = document.createElement("style");
4418 dom.innerHTML = css;
4419 document.head.appendChild(dom);
4421 var div = document.createElement("div");
4422 div.id = "blanketLoaderDialog";
4423 div.className = "blanketDialogWrapper";
4424 div.innerHTML = loader;
4425 document.body.insertBefore(div,document.body.firstChild);
4428 manualFileLoader: function(files){
4429 var toArray =Array.prototype.slice;
4430 files = toArray.call(files).filter(function(item){
4431 return item.type !== "";
4433 var sessionLength = files.length-1;
4435 var sessionArray = {};
4436 if (sessionStorage["blanketSessionLoader"]){
4437 sessionArray = JSON.parse(sessionStorage["blanketSessionLoader"]);
4441 var fileLoader = function(event){
4442 var fileContent = event.currentTarget.result;
4443 var file = files[sessionIndx];
4444 var filename = file.webkitRelativePath && file.webkitRelativePath !== '' ? file.webkitRelativePath : file.name;
4445 sessionArray[filename] = fileContent;
4447 if (sessionIndx === sessionLength){
4448 sessionStorage.setItem("blanketSessionLoader", JSON.stringify(sessionArray));
4449 document.location.reload();
4451 readFile(files[sessionIndx]);
4454 function readFile(file){
4455 var reader = new FileReader();
4456 reader.onload = fileLoader;
4457 reader.readAsText(file);
4459 readFile(files[sessionIndx]);
4461 _loadFile: function(path){
4462 if (typeof path !== "undefined"){
4463 var request = new XMLHttpRequest();
4464 request.open('GET', path, false);
4466 _blanket._addScript(request.responseText);
4469 _addScript: function(data){
4470 var script = document.createElement("script");
4471 script.type = "text/javascript";
4473 (document.body || document.getElementsByTagName('head')[0]).appendChild(script);
4475 hasAdapter: function(callback){
4476 return _blanket.options("adapter") !== null;
4478 report: function(coverage_data){
4479 if (!document.getElementById("blanketLoaderDialog")){
4480 //all found, clear it
4481 _blanket.blanketSession = null;
4483 coverage_data.files = window._$blanket;
4484 var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
4486 // Check if we have any covered files that requires reporting
4487 // otherwise just exit gracefully.
4488 if (!coverage_data.files || !Object.keys(coverage_data.files).length) {
4489 if (_blanket.options("debug")) {console.log("BLANKET-Reporting No files were instrumented.");}
4493 if (typeof coverage_data.files.branchFcn !== "undefined"){
4494 delete coverage_data.files.branchFcn;
4496 if (typeof _blanket.options("reporter") === "string"){
4497 _blanket._loadFile(_blanket.options("reporter"));
4498 _blanket.customReporter(coverage_data,_blanket.options("reporter_options"));
4499 }else if (typeof _blanket.options("reporter") === "function"){
4500 _blanket.options("reporter")(coverage_data);
4501 }else if (typeof _blanket.defaultReporter === 'function'){
4502 _blanket.defaultReporter(coverage_data);
4504 throw new Error("no reporter defined.");
4507 _bindStartTestRunner: function(bindEvent,startEvent){
4509 bindEvent(startEvent);
4511 window.addEventListener("load",startEvent,false);
4514 _loadSourceFiles: function(callback){
4515 var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
4517 var _copy = Object.create( Object.getPrototypeOf(o) );
4518 var propNames = Object.getOwnPropertyNames(o);
4520 propNames.forEach(function(name){
4521 var desc = Object.getOwnPropertyDescriptor(o, name);
4522 Object.defineProperty(_copy, name, desc);
4527 if (_blanket.options("debug")) {console.log("BLANKET-Collecting page scripts");}
4528 var scripts = _blanket.utils.collectPageScripts();
4529 //_blanket.options("filter",scripts);
4530 if (scripts.length === 0){
4534 //check session state
4535 if (sessionStorage["blanketSessionLoader"]){
4536 _blanket.blanketSession = JSON.parse(sessionStorage["blanketSessionLoader"]);
4539 scripts.forEach(function(file,indx){
4540 _blanket.utils.cache[file+".js"]={
4546 _blanket.utils.loadAll(function(test){
4548 return typeof scripts[currScript+1] !== 'undefined';
4551 if (currScript >= scripts.length){
4554 return scripts[currScript]+".js";
4558 beforeStartTestRunner: function(opts){
4560 opts.checkRequirejs = typeof opts.checkRequirejs === "undefined" ? true : opts.checkRequirejs;
4561 opts.callback = opts.callback || function() { };
4562 opts.coverage = typeof opts.coverage === "undefined" ? true : opts.coverage;
4563 if (opts.coverage) {
4564 _blanket._bindStartTestRunner(opts.bindEvent,
4566 _blanket._loadSourceFiles(function() {
4568 var allLoaded = function(){
4569 return opts.condition ? opts.condition() : _blanket.requireFilesLoaded();
4571 var check = function() {
4573 if (_blanket.options("debug")) {console.log("BLANKET-All files loaded, init start test runner callback.");}
4574 var cb = _blanket.options("testReadyCallback");
4577 if (typeof cb === "function"){
4579 }else if (typeof cb === "string"){
4580 _blanket._addScript(cb);
4587 setTimeout(check, 13);
4598 qualifyURL: function (url) {
4599 //http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
4600 var a = document.createElement('a');
4609 blanket.defaultReporter = function(coverage){
4610 var cssSytle = "#blanket-main {margin:2px;background:#EEE;color:#333;clear:both;font-family:'Helvetica Neue Light', 'HelveticaNeue-Light', 'Helvetica Neue', Calibri, Helvetica, Arial, sans-serif; font-size:17px;} #blanket-main a {color:#333;text-decoration:none;} #blanket-main a:hover {text-decoration:underline;} .blanket {margin:0;padding:5px;clear:both;border-bottom: 1px solid #FFFFFF;} .bl-error {color:red;}.bl-success {color:#5E7D00;} .bl-file{width:auto;} .bl-cl{float:left;} .blanket div.rs {margin-left:50px; width:150px; float:right} .bl-nb {padding-right:10px;} #blanket-main a.bl-logo {color: #EB1764;cursor: pointer;font-weight: bold;text-decoration: none} .bl-source{ overflow-x:scroll; background-color: #FFFFFF; border: 1px solid #CBCBCB; color: #363636; margin: 25px 20px; width: 80%;} .bl-source div{white-space: pre;font-family: monospace;} .bl-source > div > span:first-child{background-color: #EAEAEA;color: #949494;display: inline-block;padding: 0 10px;text-align: center;width: 30px;} .bl-source .miss{background-color:#e6c3c7} .bl-source span.branchWarning{color:#000;background-color:yellow;} .bl-source span.branchOkay{color:#000;background-color:transparent;}",
4612 head = document.head,
4614 body = document.body,
4616 hasBranchTracking = Object.keys(coverage.files).some(function(elem){
4617 return typeof coverage.files[elem].branchData !== 'undefined';
4619 bodyContent = "<div id='blanket-main'><div class='blanket bl-title'><div class='bl-cl bl-file'><a href='http://alex-seville.github.com/blanket/' target='_blank' class='bl-logo'>Blanket.js</a> results</div><div class='bl-cl rs'>Coverage (%)</div><div class='bl-cl rs'>Covered/Total Smts.</div>"+(hasBranchTracking ? "<div class='bl-cl rs'>Covered/Total Branches</div>":"")+"<div style='clear:both;'></div></div>",
4620 fileTemplate = "<div class='blanket {{statusclass}}'><div class='bl-cl bl-file'><span class='bl-nb'>{{fileNumber}}.</span><a href='javascript:blanket_toggleSource(\"file-{{fileNumber}}\")'>{{file}}</a></div><div class='bl-cl rs'>{{percentage}} %</div><div class='bl-cl rs'>{{numberCovered}}/{{totalSmts}}</div>"+( hasBranchTracking ? "<div class='bl-cl rs'>{{passedBranches}}/{{totalBranches}}</div>" : "" )+"<div id='file-{{fileNumber}}' class='bl-source' style='display:none;'>{{source}}</div><div style='clear:both;'></div></div>";
4621 grandTotalTemplate = "<div class='blanket grand-total {{statusclass}}'><div class='bl-cl'>{{rowTitle}}</div><div class='bl-cl rs'>{{percentage}} %</div><div class='bl-cl rs'>{{numberCovered}}/{{totalSmts}}</div>"+( hasBranchTracking ? "<div class='bl-cl rs'>{{passedBranches}}/{{totalBranches}}</div>" : "" ) + "<div style='clear:both;'></div></div>";
4623 function blanket_toggleSource(id) {
4624 var element = document.getElementById(id);
4625 if(element.style.display === 'block') {
4626 element.style.display = 'none';
4628 element.style.display = 'block';
4633 var script = document.createElement("script");
4634 script.type = "text/javascript";
4635 script.text = blanket_toggleSource.toString().replace('function ' + blanket_toggleSource.name, 'function blanket_toggleSource');
4636 body.appendChild(script);
4638 var percentage = function(number, total) {
4639 return (Math.round(((number/total) * 100)*100)/100);
4642 var appendTag = function (type, el, str) {
4643 var dom = document.createElement(type);
4644 dom.innerHTML = str;
4645 el.appendChild(dom);
4648 function escapeInvalidXmlChars(str) {
4649 return str.replace(/\&/g, "&")
4650 .replace(/</g, "<")
4651 .replace(/\>/g, ">")
4652 .replace(/\"/g, """)
4653 .replace(/\'/g, "'");
4656 function isBranchFollowed(data,bool){
4657 var mode = bool ? 0 : 1;
4658 if (typeof data === 'undefined' ||
4659 typeof data === null ||
4660 typeof data[mode] === 'undefined'){
4663 return data[mode].length > 0;
4666 var branchStack = [];
4668 function branchReport(colsIndex,src,cols,offset,lineNum){
4671 if (branchStack.length > 0){
4672 newsrc += "<span class='" + (isBranchFollowed(branchStack[0][1],branchStack[0][1].consequent === branchStack[0][0]) ? 'branchOkay' : 'branchWarning') + "'>";
4673 if (branchStack[0][0].end.line === lineNum){
4674 newsrc += escapeInvalidXmlChars(src.slice(0,branchStack[0][0].end.column)) + "</span>";
4675 src = src.slice(branchStack[0][0].end.column);
4676 branchStack.shift();
4677 if (branchStack.length > 0){
4678 newsrc += "<span class='" + (isBranchFollowed(branchStack[0][1],false) ? 'branchOkay' : 'branchWarning') + "'>";
4679 if (branchStack[0][0].end.line === lineNum){
4680 newsrc += escapeInvalidXmlChars(src.slice(0,branchStack[0][0].end.column)) + "</span>";
4681 src = src.slice(branchStack[0][0].end.column);
4682 branchStack.shift();
4684 return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
4688 return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
4691 postfix = "</span>";
4694 return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
4697 return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
4699 postfix = "</span>";
4702 var thisline = cols[colsIndex];
4705 var cons = thisline.consequent;
4706 if (cons.start.line > lineNum){
4707 branchStack.unshift([thisline.alternate,thisline]);
4708 branchStack.unshift([cons,thisline]);
4709 src = escapeInvalidXmlChars(src);
4711 var style = "<span class='" + (isBranchFollowed(thisline,true) ? 'branchOkay' : 'branchWarning') + "'>";
4712 newsrc += escapeInvalidXmlChars(src.slice(0,cons.start.column-offset)) + style;
4714 if (cols.length > colsIndex+1 &&
4715 cols[colsIndex+1].consequent.start.line === lineNum &&
4716 cols[colsIndex+1].consequent.start.column-offset < cols[colsIndex].consequent.end.column-offset)
4718 var res = branchReport(colsIndex+1,src.slice(cons.start.column-offset,cons.end.column-offset),cols,cons.start.column-offset,lineNum);
4721 cols[colsIndex+1] = cols[colsIndex+2];
4724 newsrc += escapeInvalidXmlChars(src.slice(cons.start.column-offset,cons.end.column-offset));
4726 newsrc += "</span>";
4728 var alt = thisline.alternate;
4729 if (alt.start.line > lineNum){
4730 newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset));
4731 branchStack.unshift([alt,thisline]);
4733 newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset,alt.start.column-offset));
4734 style = "<span class='" + (isBranchFollowed(thisline,false) ? 'branchOkay' : 'branchWarning') + "'>";
4736 if (cols.length > colsIndex+1 &&
4737 cols[colsIndex+1].consequent.start.line === lineNum &&
4738 cols[colsIndex+1].consequent.start.column-offset < cols[colsIndex].alternate.end.column-offset)
4740 var res2 = branchReport(colsIndex+1,src.slice(alt.start.column-offset,alt.end.column-offset),cols,alt.start.column-offset,lineNum);
4743 cols[colsIndex+1] = cols[colsIndex+2];
4746 newsrc += escapeInvalidXmlChars(src.slice(alt.start.column-offset,alt.end.column-offset));
4748 newsrc += "</span>";
4749 newsrc += escapeInvalidXmlChars(src.slice(alt.end.column-offset));
4753 return {src:src+postfix, cols:cols};
4756 var isUndefined = function(item){
4757 return typeof item !== 'undefined';
4760 var files = coverage.files;
4763 numberOfFilesCovered: 0,
4766 moduleTotalStatements : {},
4767 moduleTotalCoveredStatements : {},
4768 moduleTotalBranches : {},
4769 moduleTotalCoveredBranches : {}
4772 // check if a data-cover-modulepattern was provided for per-module coverage reporting
4773 var modulePattern = _blanket.options("modulePattern");
4774 var modulePatternRegex = ( modulePattern ? new RegExp(modulePattern) : null );
4776 for(var file in files)
4780 var statsForFile = files[file],
4782 numberOfFilesCovered = 0,
4788 for(i = 0; i < statsForFile.source.length; i +=1){
4789 var src = statsForFile.source[i];
4791 if (branchStack.length > 0 ||
4792 typeof statsForFile.branchData !== 'undefined')
4794 if (typeof statsForFile.branchData[i+1] !== 'undefined')
4796 var cols = statsForFile.branchData[i+1].filter(isUndefined);
4800 src = branchReport(colsIndex,src,cols,0,i+1).src;
4802 }else if (branchStack.length){
4803 src = branchReport(0,src,null,0,i+1).src;
4805 src = escapeInvalidXmlChars(src);
4808 src = escapeInvalidXmlChars(src);
4811 if(statsForFile[i+1]) {
4812 numberOfFilesCovered += 1;
4816 if(statsForFile[i+1] === 0){
4821 code[i + 1] = "<div class='"+lineClass+"'><span class=''>"+(i + 1)+"</span>"+src+"</div>";
4823 totals.totalSmts += totalSmts;
4824 totals.numberOfFilesCovered += numberOfFilesCovered;
4825 var totalBranches=0;
4826 var passedBranches=0;
4827 if (typeof statsForFile.branchData !== 'undefined'){
4828 for(var j=0;j<statsForFile.branchData.length;j++){
4829 if (typeof statsForFile.branchData[j] !== 'undefined'){
4830 for(var k=0;k<statsForFile.branchData[j].length;k++){
4831 if (typeof statsForFile.branchData[j][k] !== 'undefined'){
4833 if (typeof statsForFile.branchData[j][k][0] !== 'undefined' &&
4834 statsForFile.branchData[j][k][0].length > 0 &&
4835 typeof statsForFile.branchData[j][k][1] !== 'undefined' &&
4836 statsForFile.branchData[j][k][1].length > 0){
4844 totals.passedBranches += passedBranches;
4845 totals.totalBranches += totalBranches;
4847 // if "data-cover-modulepattern" was provided,
4848 // track totals per module name as well as globally
4849 if (modulePatternRegex) {
4850 var moduleName = file.match(modulePatternRegex)[1];
4852 if(!totals.moduleTotalStatements.hasOwnProperty(moduleName)) {
4853 totals.moduleTotalStatements[moduleName] = 0;
4854 totals.moduleTotalCoveredStatements[moduleName] = 0;
4857 totals.moduleTotalStatements[moduleName] += totalSmts;
4858 totals.moduleTotalCoveredStatements[moduleName] += numberOfFilesCovered;
4860 if(!totals.moduleTotalBranches.hasOwnProperty(moduleName)) {
4861 totals.moduleTotalBranches[moduleName] = 0;
4862 totals.moduleTotalCoveredBranches[moduleName] = 0;
4865 totals.moduleTotalBranches[moduleName] += totalBranches;
4866 totals.moduleTotalCoveredBranches[moduleName] += passedBranches;
4869 var result = percentage(numberOfFilesCovered, totalSmts);
4871 var output = fileTemplate.replace("{{file}}", file)
4872 .replace("{{percentage}}",result)
4873 .replace("{{numberCovered}}", numberOfFilesCovered)
4874 .replace(/\{\{fileNumber\}\}/g, fileNumber)
4875 .replace("{{totalSmts}}", totalSmts)
4876 .replace("{{totalBranches}}", totalBranches)
4877 .replace("{{passedBranches}}", passedBranches)
4878 .replace("{{source}}", code.join(" "));
4879 if(result < successRate)
4881 output = output.replace("{{statusclass}}", "bl-error");
4883 output = output.replace("{{statusclass}}", "bl-success");
4885 bodyContent += output;
4888 // create temporary function for use by the global totals reporter,
4889 // as well as the per-module totals reporter
4890 var createAggregateTotal = function(numSt, numCov, numBranch, numCovBr, moduleName) {
4892 var totalPercent = percentage(numCov, numSt);
4893 var statusClass = totalPercent < successRate ? "bl-error" : "bl-success";
4894 var rowTitle = ( moduleName ? "Total for module: " + moduleName : "Global total" );
4895 var totalsOutput = grandTotalTemplate.replace("{{rowTitle}}", rowTitle)
4896 .replace("{{percentage}}", totalPercent)
4897 .replace("{{numberCovered}}", numCov)
4898 .replace("{{totalSmts}}", numSt)
4899 .replace("{{passedBranches}}", numCovBr)
4900 .replace("{{totalBranches}}", numBranch)
4901 .replace("{{statusclass}}", statusClass);
4903 bodyContent += totalsOutput;
4906 // if "data-cover-modulepattern" was provided,
4907 // output the per-module totals alongside the global totals
4908 if (modulePatternRegex) {
4909 for (var thisModuleName in totals.moduleTotalStatements) {
4910 if (totals.moduleTotalStatements.hasOwnProperty(thisModuleName)) {
4912 var moduleTotalSt = totals.moduleTotalStatements[thisModuleName];
4913 var moduleTotalCovSt = totals.moduleTotalCoveredStatements[thisModuleName];
4915 var moduleTotalBr = totals.moduleTotalBranches[thisModuleName];
4916 var moduleTotalCovBr = totals.moduleTotalCoveredBranches[thisModuleName];
4918 createAggregateTotal(moduleTotalSt, moduleTotalCovSt, moduleTotalBr, moduleTotalCovBr, thisModuleName);
4923 createAggregateTotal(totals.totalSmts, totals.numberOfFilesCovered, totals.totalBranches, totals.passedBranches, null);
4924 bodyContent += "</div>"; //closing main
4927 appendTag('style', head, cssSytle);
4928 //appendStyle(body, headerContent);
4929 if (document.getElementById("blanket-main")){
4930 document.getElementById("blanket-main").innerHTML=
4931 bodyContent.slice(23,-6);
4933 appendTag('div', body, bodyContent);
4935 //appendHtml(body, '</div>');
4940 //http://stackoverflow.com/a/2954896
4941 var toArray =Array.prototype.slice;
4942 var scripts = toArray.call(document.scripts);
4943 toArray.call(scripts[scripts.length - 1].attributes)
4944 .forEach(function(es){
4945 if(es.nodeName === "data-cover-only"){
4946 newOptions.filter = es.nodeValue;
4948 if(es.nodeName === "data-cover-never"){
4949 newOptions.antifilter = es.nodeValue;
4951 if(es.nodeName === "data-cover-reporter"){
4952 newOptions.reporter = es.nodeValue;
4954 if (es.nodeName === "data-cover-adapter"){
4955 newOptions.adapter = es.nodeValue;
4957 if (es.nodeName === "data-cover-loader"){
4958 newOptions.loader = es.nodeValue;
4960 if (es.nodeName === "data-cover-timeout"){
4961 newOptions.timeout = es.nodeValue;
4963 if (es.nodeName === "data-cover-modulepattern") {
4964 newOptions.modulePattern = es.nodeValue;
4966 if (es.nodeName === "data-cover-reporter-options"){
4968 newOptions.reporter_options = JSON.parse(es.nodeValue);
4970 if (blanket.options("debug")){
4971 throw new Error("Invalid reporter options. Must be a valid stringified JSON object.");
4975 if (es.nodeName === "data-cover-testReadyCallback"){
4976 newOptions.testReadyCallback = es.nodeValue;
4978 if (es.nodeName === "data-cover-customVariable"){
4979 newOptions.customVariable = es.nodeValue;
4981 if (es.nodeName === "data-cover-flags"){
4982 var flags = " "+es.nodeValue+" ";
4983 if (flags.indexOf(" ignoreError ") > -1){
4984 newOptions.ignoreScriptError = true;
4986 if (flags.indexOf(" autoStart ") > -1){
4987 newOptions.autoStart = true;
4989 if (flags.indexOf(" ignoreCors ") > -1){
4990 newOptions.ignoreCors = true;
4992 if (flags.indexOf(" branchTracking ") > -1){
4993 newOptions.branchTracking = true;
4995 if (flags.indexOf(" sourceURL ") > -1){
4996 newOptions.sourceURL = true;
4998 if (flags.indexOf(" debug ") > -1){
4999 newOptions.debug = true;
5001 if (flags.indexOf(" engineOnly ") > -1){
5002 newOptions.engineOnly = true;
5004 if (flags.indexOf(" commonJS ") > -1){
5005 newOptions.commonJS = true;
5007 if (flags.indexOf(" instrumentCache ") > -1){
5008 newOptions.instrumentCache = true;
5012 blanket.options(newOptions);
5014 if (typeof requirejs !== 'undefined'){
5015 blanket.options("existingRequireJS",true);
5017 /* setup requirejs loader, if needed */
5019 if (blanket.options("commonJS")){
5020 blanket._commonjs = {};
5023 (function(_blanket){
5026 normalizeBackslashes: function(str) {
5027 return str.replace(/\\/g, '/');
5029 matchPatternAttribute: function(filename,pattern){
5030 if (typeof pattern === 'string'){
5031 if (pattern.indexOf("[") === 0){
5033 var pattenArr = pattern.slice(1,pattern.length-1).split(",");
5034 return pattenArr.some(function(elem){
5035 return _blanket.utils.matchPatternAttribute(filename,_blanket.utils.normalizeBackslashes(elem.slice(1,-1)));
5036 //return filename.indexOf(_blanket.utils.normalizeBackslashes(elem.slice(1,-1))) > -1;
5038 }else if ( pattern.indexOf("//") === 0){
5039 var ex = pattern.slice(2,pattern.lastIndexOf('/'));
5040 var mods = pattern.slice(pattern.lastIndexOf('/')+1);
5041 var regex = new RegExp(ex,mods);
5042 return regex.test(filename);
5043 }else if (pattern.indexOf("#") === 0){
5044 return window[pattern.slice(1)].call(window,filename);
5046 return filename.indexOf(_blanket.utils.normalizeBackslashes(pattern)) > -1;
5048 }else if ( pattern instanceof Array ){
5049 return pattern.some(function(elem){
5050 return _blanket.utils.matchPatternAttribute(filename,elem);
5052 }else if (pattern instanceof RegExp){
5053 return pattern.test(filename);
5054 }else if (typeof pattern === "function"){
5055 return pattern.call(window,filename);
5058 blanketEval: function(data){
5059 _blanket._addScript(data);
5061 collectPageScripts: function(){
5062 var toArray = Array.prototype.slice;
5063 var scripts = toArray.call(document.scripts);
5064 var selectedScripts=[],scriptNames=[];
5065 var filter = _blanket.options("filter");
5067 //global filter in place, data-cover-only
5068 var antimatch = _blanket.options("antifilter");
5069 selectedScripts = toArray.call(document.scripts)
5070 .filter(function(s){
5071 return toArray.call(s.attributes).filter(function(sn){
5072 return sn.nodeName === "src" && _blanket.utils.matchPatternAttribute(sn.nodeValue,filter) &&
5073 (typeof antimatch === "undefined" || !_blanket.utils.matchPatternAttribute(sn.nodeValue,antimatch));
5077 selectedScripts = toArray.call(document.querySelectorAll("script[data-cover]"));
5079 scriptNames = selectedScripts.map(function(s){
5080 return _blanket.utils.qualifyURL(
5081 toArray.call(s.attributes).filter(
5083 return sn.nodeName === "src";
5084 })[0].nodeValue).replace(".js","");
5087 _blanket.options("filter","['"+scriptNames.join("','")+"']");
5091 loadAll: function(nextScript,cb,preprocessor){
5094 * @param {nextScript} factory for priority level
5095 * @param {cb} the done callback
5097 var currScript=nextScript();
5098 var isLoaded = _blanket.utils.scriptIsLoaded(
5100 _blanket.utils.ifOrdered,
5105 if (!(_blanket.utils.cache[currScript] && _blanket.utils.cache[currScript].loaded)){
5106 var attach = function(){
5107 if (_blanket.options("debug")) {console.log("BLANKET-Mark script:"+currScript+", as loaded and move to next script.");}
5110 var whenDone = function(result){
5111 if (_blanket.options("debug")) {console.log("BLANKET-File loading finished");}
5112 if (typeof result !== 'undefined'){
5113 if (_blanket.options("debug")) {console.log("BLANKET-Add file to DOM.");}
5114 _blanket._addScript(result);
5119 _blanket.utils.attachScript(
5124 _blanket.utils.processFile(
5136 attachScript: function(options,cb){
5137 var timeout = _blanket.options("timeout") || 3000;
5138 setTimeout(function(){
5139 if (!_blanket.utils.cache[options.url].loaded){
5140 throw new Error("error loading source script");
5143 _blanket.utils.getFile(
5146 function(){ throw new Error("error loading source script");}
5149 ifOrdered: function(nextScript,cb){
5151 * ordered loading callback
5152 * @param {nextScript} factory for priority level
5153 * @param {cb} the done callback
5155 var currScript = nextScript(true);
5157 _blanket.utils.loadAll(nextScript,cb);
5159 cb(new Error("Error in loading chain."));
5162 scriptIsLoaded: function(url,orderedCb,nextScript,cb){
5164 * returns a callback that checks a loading list to see if a script is loaded.
5165 * @param {orderedCb} callback if ordered loading is being done
5166 * @param {nextScript} factory for next priority level
5167 * @param {cb} the done callback
5169 if (_blanket.options("debug")) {console.log("BLANKET-Returning function");}
5171 if (_blanket.options("debug")) {console.log("BLANKET-Marking file as loaded: "+url);}
5173 _blanket.utils.cache[url].loaded=true;
5175 if (_blanket.utils.allLoaded()){
5176 if (_blanket.options("debug")) {console.log("BLANKET-All files loaded");}
5178 }else if (orderedCb){
5179 //if it's ordered we need to
5180 //traverse down to the next
5182 if (_blanket.options("debug")) {console.log("BLANKET-Load next file.");}
5183 orderedCb(nextScript,cb);
5188 allLoaded: function (){
5190 * check if depdencies are loaded in cache
5192 var cached = Object.keys(_blanket.utils.cache);
5193 for (var i=0;i<cached.length;i++){
5194 if (!_blanket.utils.cache[cached[i]].loaded){
5200 processFile: function (content,url,cb,oldCb) {
5201 var match = _blanket.options("filter");
5202 //we check the never matches first
5203 var antimatch = _blanket.options("antifilter");
5204 if (typeof antimatch !== "undefined" &&
5205 _blanket.utils.matchPatternAttribute(url.replace(/\.js$/,""),antimatch)
5208 if (_blanket.options("debug")) {console.log("BLANKET-File will never be instrumented:"+url);}
5209 _blanket.requiringFile(url,true);
5210 }else if (_blanket.utils.matchPatternAttribute(url.replace(/\.js$/,""),match)){
5211 if (_blanket.options("debug")) {console.log("BLANKET-Attempting instrument of:"+url);}
5212 _blanket.instrument({
5215 },function(instrumented){
5217 if (_blanket.options("debug")) {console.log("BLANKET-instrument of:"+url+" was successfull.");}
5218 _blanket.utils.blanketEval(instrumented);
5220 _blanket.requiringFile(url,true);
5223 if (_blanket.options("ignoreScriptError")){
5224 //we can continue like normal if
5225 //we're ignoring script errors,
5226 //but otherwise we don't want
5227 //to completeLoad or the error might be
5229 if (_blanket.options("debug")) { console.log("BLANKET-There was an error loading the file:"+url); }
5231 _blanket.requiringFile(url,true);
5233 throw new Error("Error parsing instrumented code: "+err);
5238 if (_blanket.options("debug")) { console.log("BLANKET-Loading (without instrumenting) the file:"+url);}
5240 _blanket.requiringFile(url,true);
5244 createXhr: function(){
5246 if (typeof XMLHttpRequest !== "undefined") {
5247 return new XMLHttpRequest();
5248 } else if (typeof ActiveXObject !== "undefined") {
5249 for (i = 0; i < 3; i += 1) {
5250 progId = progIds[i];
5252 xhr = new ActiveXObject(progId);
5256 progIds = [progId]; // so faster next time
5264 getFile: function(url, callback, errback, onXhr){
5265 var foundInSession = false;
5266 if (_blanket.blanketSession){
5267 var files = Object.keys(_blanket.blanketSession);
5268 for (var i=0; i<files.length;i++ ){
5270 if (url.indexOf(key) > -1){
5271 callback(_blanket.blanketSession[key]);
5272 foundInSession=true;
5277 if (!foundInSession){
5278 var xhr = _blanket.utils.createXhr();
5279 xhr.open('GET', url, true);
5281 //Allow overrides specified in config
5286 xhr.onreadystatechange = function (evt) {
5289 //Do not explicitly handle errors, those should be
5290 //visible via console output in the browser.
5291 if (xhr.readyState === 4) {
5292 status = xhr.status;
5293 if ((status > 399 && status < 600) /*||
5295 navigator.userAgent.toLowerCase().indexOf('firefox') > -1)
5297 //An http 4xx or 5xx error. Signal an error.
5298 err = new Error(url + ' HTTP status: ' + status);
5302 callback(xhr.responseText);
5309 if (e.code && (e.code === 101 || e.code === 1012) && _blanket.options("ignoreCors") === false){
5310 //running locally and getting error from browser
5311 _blanket.showManualLoader();
5322 var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
5323 var requirejs = blanket.options("commonJS") ? blanket._commonjs.requirejs : window.requirejs;
5324 if (!_blanket.options("engineOnly") && _blanket.options("existingRequireJS")){
5326 _blanket.utils.oldloader = requirejs.load;
5328 requirejs.load = function (context, moduleName, url) {
5329 _blanket.requiringFile(url);
5330 _blanket.utils.getFile(url,
5332 _blanket.utils.processFile(
5335 function newLoader(){
5336 context.completeLoad(moduleName);
5338 function oldLoader(){
5339 _blanket.utils.oldloader(context, moduleName, url);
5343 _blanket.requiringFile();