Update V8 to version 4.6.66.
[chromium-blink-merge.git] / third_party / blanketjs / src / blanket.js
blob9377d4faf14c93ed1b9161bf02b79b9a70e54812
1 /*! blanket - v1.1.5 */
2 (function(define){
4 /*
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) {
44 'use strict';
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') {
51 factory(exports);
52 } else {
53 factory((root.esprima = {}));
55 }(this, function (exports) {
56 'use strict';
58 var Token,
59 TokenName,
60 Syntax,
61 PropertyKind,
62 Messages,
63 Regex,
64 source,
65 strict,
66 index,
67 lineNumber,
68 lineStart,
69 length,
70 buffer,
71 state,
72 extra;
74 Token = {
75 BooleanLiteral: 1,
76 EOF: 2,
77 Identifier: 3,
78 Keyword: 4,
79 NullLiteral: 5,
80 NumericLiteral: 6,
81 Punctuator: 7,
82 StringLiteral: 8
85 TokenName = {};
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';
95 Syntax = {
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',
115 Literal: 'Literal',
116 LabeledStatement: 'LabeledStatement',
117 LogicalExpression: 'LogicalExpression',
118 MemberExpression: 'MemberExpression',
119 NewExpression: 'NewExpression',
120 ObjectExpression: 'ObjectExpression',
121 Program: 'Program',
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'
138 PropertyKind = {
139 Data: 1,
140 Get: 2,
141 Set: 4
144 // Error messages should be identical to V8.
145 Messages = {
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.
182 Regex = {
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) {
193 if (!condition) {
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;
221 // 7.2 White Space
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) {
254 switch (id) {
256 // Future reserved words.
257 case 'class':
258 case 'enum':
259 case 'export':
260 case 'extends':
261 case 'import':
262 case 'super':
263 return true;
266 return false;
269 function isStrictModeReservedWord(id) {
270 switch (id) {
272 // Strict Mode reserved words.
273 case 'implements':
274 case 'interface':
275 case 'package':
276 case 'private':
277 case 'protected':
278 case 'public':
279 case 'static':
280 case 'yield':
281 case 'let':
282 return true;
285 return false;
288 function isRestrictedWord(id) {
289 return id === 'eval' || id === 'arguments';
292 // 7.6.1.1 Keywords
294 function isKeyword(id) {
295 var keyword = false;
296 switch (id.length) {
297 case 2:
298 keyword = (id === 'if') || (id === 'in') || (id === 'do');
299 break;
300 case 3:
301 keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
302 break;
303 case 4:
304 keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with');
305 break;
306 case 5:
307 keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw');
308 break;
309 case 6:
310 keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch');
311 break;
312 case 7:
313 keyword = (id === 'default') || (id === 'finally');
314 break;
315 case 8:
316 keyword = (id === 'function') || (id === 'continue') || (id === 'debugger');
317 break;
318 case 10:
319 keyword = (id === 'instanceof');
320 break;
323 if (keyword) {
324 return true;
327 switch (id) {
328 // Future reserved words.
329 // 'const' is specialized as Keyword in V8.
330 case 'const':
331 return true;
333 // For compatiblity to SpiderMonkey and ES.next
334 case 'yield':
335 case 'let':
336 return true;
339 if (strict && isStrictModeReservedWord(id)) {
340 return true;
343 return isFutureReservedWord(id);
346 // 7.4 Comments
348 function skipComment() {
349 var ch, blockComment, lineComment;
351 blockComment = false;
352 lineComment = false;
354 while (index < length) {
355 ch = source[index];
357 if (lineComment) {
358 ch = source[index++];
359 if (isLineTerminator(ch)) {
360 lineComment = false;
361 if (ch === '\r' && source[index] === '\n') {
362 ++index;
364 ++lineNumber;
365 lineStart = index;
367 } else if (blockComment) {
368 if (isLineTerminator(ch)) {
369 if (ch === '\r' && source[index + 1] === '\n') {
370 ++index;
372 ++lineNumber;
373 ++index;
374 lineStart = index;
375 if (index >= length) {
376 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
378 } else {
379 ch = source[index++];
380 if (index >= length) {
381 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
383 if (ch === '*') {
384 ch = source[index];
385 if (ch === '/') {
386 ++index;
387 blockComment = false;
391 } else if (ch === '/') {
392 ch = source[index + 1];
393 if (ch === '/') {
394 index += 2;
395 lineComment = true;
396 } else if (ch === '*') {
397 index += 2;
398 blockComment = true;
399 if (index >= length) {
400 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
402 } else {
403 break;
405 } else if (isWhiteSpace(ch)) {
406 ++index;
407 } else if (isLineTerminator(ch)) {
408 ++index;
409 if (ch === '\r' && source[index] === '\n') {
410 ++index;
412 ++lineNumber;
413 lineStart = index;
414 } else {
415 break;
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());
428 } else {
429 return '';
432 return String.fromCharCode(code);
435 function scanIdentifier() {
436 var ch, start, id, restore;
438 ch = source[index];
439 if (!isIdentifierStart(ch)) {
440 return;
443 start = index;
444 if (ch === '\\') {
445 ++index;
446 if (source[index] !== 'u') {
447 return;
449 ++index;
450 restore = index;
451 ch = scanHexEscape('u');
452 if (ch) {
453 if (ch === '\\' || !isIdentifierStart(ch)) {
454 return;
456 id = ch;
457 } else {
458 index = restore;
459 id = 'u';
461 } else {
462 id = source[index++];
465 while (index < length) {
466 ch = source[index];
467 if (!isIdentifierPart(ch)) {
468 break;
470 if (ch === '\\') {
471 ++index;
472 if (source[index] !== 'u') {
473 return;
475 ++index;
476 restore = index;
477 ch = scanHexEscape('u');
478 if (ch) {
479 if (ch === '\\' || !isIdentifierPart(ch)) {
480 return;
482 id += ch;
483 } else {
484 index = restore;
485 id += 'u';
487 } else {
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) {
495 return {
496 type: Token.Identifier,
497 value: id,
498 lineNumber: lineNumber,
499 lineStart: lineStart,
500 range: [start, index]
504 if (isKeyword(id)) {
505 return {
506 type: Token.Keyword,
507 value: id,
508 lineNumber: lineNumber,
509 lineStart: lineStart,
510 range: [start, index]
514 // 7.8.1 Null Literals
516 if (id === 'null') {
517 return {
518 type: Token.NullLiteral,
519 value: id,
520 lineNumber: lineNumber,
521 lineStart: lineStart,
522 range: [start, index]
526 // 7.8.2 Boolean Literals
528 if (id === 'true' || id === 'false') {
529 return {
530 type: Token.BooleanLiteral,
531 value: id,
532 lineNumber: lineNumber,
533 lineStart: lineStart,
534 range: [start, index]
538 return {
539 type: Token.Identifier,
540 value: id,
541 lineNumber: lineNumber,
542 lineStart: lineStart,
543 range: [start, index]
547 // 7.7 Punctuators
549 function scanPunctuator() {
550 var start = index,
551 ch1 = source[index],
552 ch2,
553 ch3,
554 ch4;
556 // Check for most common single-character punctuators.
558 if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
559 ++index;
560 return {
561 type: Token.Punctuator,
562 value: ch1,
563 lineNumber: lineNumber,
564 lineStart: lineStart,
565 range: [start, index]
569 if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
570 ++index;
571 return {
572 type: Token.Punctuator,
573 value: ch1,
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)) {
585 return {
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 === '>') {
602 if (ch4 === '=') {
603 index += 4;
604 return {
605 type: Token.Punctuator,
606 value: '>>>=',
607 lineNumber: lineNumber,
608 lineStart: lineStart,
609 range: [start, index]
614 // 3-character punctuators: === !== >>> <<= >>=
616 if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
617 index += 3;
618 return {
619 type: Token.Punctuator,
620 value: '===',
621 lineNumber: lineNumber,
622 lineStart: lineStart,
623 range: [start, index]
627 if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
628 index += 3;
629 return {
630 type: Token.Punctuator,
631 value: '!==',
632 lineNumber: lineNumber,
633 lineStart: lineStart,
634 range: [start, index]
638 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
639 index += 3;
640 return {
641 type: Token.Punctuator,
642 value: '>>>',
643 lineNumber: lineNumber,
644 lineStart: lineStart,
645 range: [start, index]
649 if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
650 index += 3;
651 return {
652 type: Token.Punctuator,
653 value: '<<=',
654 lineNumber: lineNumber,
655 lineStart: lineStart,
656 range: [start, index]
660 if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
661 index += 3;
662 return {
663 type: Token.Punctuator,
664 value: '>>=',
665 lineNumber: lineNumber,
666 lineStart: lineStart,
667 range: [start, index]
671 // 2-character punctuators: <= >= == != ++ -- << >> && ||
672 // += -= *= %= &= |= ^= /=
674 if (ch2 === '=') {
675 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
676 index += 2;
677 return {
678 type: Token.Punctuator,
679 value: ch1 + ch2,
680 lineNumber: lineNumber,
681 lineStart: lineStart,
682 range: [start, index]
687 if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
688 if ('+-<>&|'.indexOf(ch2) >= 0) {
689 index += 2;
690 return {
691 type: Token.Punctuator,
692 value: ch1 + ch2,
693 lineNumber: lineNumber,
694 lineStart: lineStart,
695 range: [start, index]
700 // The remaining 1-character punctuators.
702 if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
703 return {
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;
718 ch = source[index];
719 assert(isDecimalDigit(ch) || (ch === '.'),
720 'Numeric literal must start with a decimal digit or a decimal point');
722 start = index;
723 number = '';
724 if (ch !== '.') {
725 number = source[index++];
726 ch = 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) {
734 ch = source[index];
735 if (!isHexDigit(ch)) {
736 break;
738 number += source[index++];
741 if (number.length <= 2) {
742 // only 0x
743 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
746 if (index < length) {
747 ch = source[index];
748 if (isIdentifierStart(ch)) {
749 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
752 return {
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) {
762 ch = source[index];
763 if (!isOctalDigit(ch)) {
764 break;
766 number += source[index++];
769 if (index < length) {
770 ch = source[index];
771 if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
772 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
775 return {
776 type: Token.NumericLiteral,
777 value: parseInt(number, 8),
778 octal: true,
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) {
792 ch = source[index];
793 if (!isDecimalDigit(ch)) {
794 break;
796 number += source[index++];
800 if (ch === '.') {
801 number += source[index++];
802 while (index < length) {
803 ch = source[index];
804 if (!isDecimalDigit(ch)) {
805 break;
807 number += source[index++];
811 if (ch === 'e' || ch === 'E') {
812 number += source[index++];
814 ch = source[index];
815 if (ch === '+' || ch === '-') {
816 number += source[index++];
819 ch = source[index];
820 if (isDecimalDigit(ch)) {
821 number += source[index++];
822 while (index < length) {
823 ch = source[index];
824 if (!isDecimalDigit(ch)) {
825 break;
827 number += source[index++];
829 } else {
830 ch = 'character ' + ch;
831 if (index >= length) {
832 ch = '<end>';
834 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
838 if (index < length) {
839 ch = source[index];
840 if (isIdentifierStart(ch)) {
841 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
845 return {
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');
863 start = index;
864 ++index;
866 while (index < length) {
867 ch = source[index++];
869 if (ch === quote) {
870 quote = '';
871 break;
872 } else if (ch === '\\') {
873 ch = source[index++];
874 if (!isLineTerminator(ch)) {
875 switch (ch) {
876 case 'n':
877 str += '\n';
878 break;
879 case 'r':
880 str += '\r';
881 break;
882 case 't':
883 str += '\t';
884 break;
885 case 'u':
886 case 'x':
887 restore = index;
888 unescaped = scanHexEscape(ch);
889 if (unescaped) {
890 str += unescaped;
891 } else {
892 index = restore;
893 str += ch;
895 break;
896 case 'b':
897 str += '\b';
898 break;
899 case 'f':
900 str += '\f';
901 break;
902 case 'v':
903 str += '\v';
904 break;
906 default:
907 if (isOctalDigit(ch)) {
908 code = '01234567'.indexOf(ch);
910 // \0 is not octal escape sequence
911 if (code !== 0) {
912 octal = true;
915 if (index < length && isOctalDigit(source[index])) {
916 octal = true;
917 code = code * 8 + '01234567'.indexOf(source[index++]);
919 // 3 digits are only allowed when string starts
920 // with 0, 1, 2, 3
921 if ('0123'.indexOf(ch) >= 0 &&
922 index < length &&
923 isOctalDigit(source[index])) {
924 code = code * 8 + '01234567'.indexOf(source[index++]);
927 str += String.fromCharCode(code);
928 } else {
929 str += ch;
931 break;
933 } else {
934 ++lineNumber;
935 if (ch === '\r' && source[index] === '\n') {
936 ++index;
939 } else if (isLineTerminator(ch)) {
940 break;
941 } else {
942 str += ch;
946 if (quote !== '') {
947 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
950 return {
951 type: Token.StringLiteral,
952 value: str,
953 octal: octal,
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;
963 buffer = null;
964 skipComment();
966 start = index;
967 ch = source[index];
968 assert(ch === '/', 'Regular expression literal must start with a slash');
969 str = source[index++];
971 while (index < length) {
972 ch = source[index++];
973 str += ch;
974 if (classMarker) {
975 if (ch === ']') {
976 classMarker = false;
978 } else {
979 if (ch === '\\') {
980 ch = source[index++];
981 // ECMA-262 7.8.5
982 if (isLineTerminator(ch)) {
983 throwError({}, Messages.UnterminatedRegExp);
985 str += ch;
986 } else if (ch === '/') {
987 terminated = true;
988 break;
989 } else if (ch === '[') {
990 classMarker = true;
991 } else if (isLineTerminator(ch)) {
992 throwError({}, Messages.UnterminatedRegExp);
997 if (!terminated) {
998 throwError({}, Messages.UnterminatedRegExp);
1001 // Exclude leading and trailing slash.
1002 pattern = str.substr(1, str.length - 2);
1004 flags = '';
1005 while (index < length) {
1006 ch = source[index];
1007 if (!isIdentifierPart(ch)) {
1008 break;
1011 ++index;
1012 if (ch === '\\' && index < length) {
1013 ch = source[index];
1014 if (ch === 'u') {
1015 ++index;
1016 restore = index;
1017 ch = scanHexEscape('u');
1018 if (ch) {
1019 flags += ch;
1020 str += '\\u';
1021 for (; restore < index; ++restore) {
1022 str += source[restore];
1024 } else {
1025 index = restore;
1026 flags += 'u';
1027 str += '\\u';
1029 } else {
1030 str += '\\';
1032 } else {
1033 flags += ch;
1034 str += ch;
1038 try {
1039 value = new RegExp(pattern, flags);
1040 } catch (e) {
1041 throwError({}, Messages.InvalidRegExp);
1044 return {
1045 literal: str,
1046 value: value,
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() {
1059 var ch, token;
1061 skipComment();
1063 if (index >= length) {
1064 return {
1065 type: Token.EOF,
1066 lineNumber: lineNumber,
1067 lineStart: lineStart,
1068 range: [index, index]
1072 token = scanPunctuator();
1073 if (typeof token !== 'undefined') {
1074 return token;
1077 ch = source[index];
1079 if (ch === '\'' || ch === '"') {
1080 return scanStringLiteral();
1083 if (ch === '.' || isDecimalDigit(ch)) {
1084 return scanNumericLiteral();
1087 token = scanIdentifier();
1088 if (typeof token !== 'undefined') {
1089 return token;
1092 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
1095 function lex() {
1096 var token;
1098 if (buffer) {
1099 index = buffer.range[1];
1100 lineNumber = buffer.lineNumber;
1101 lineStart = buffer.lineStart;
1102 token = buffer;
1103 buffer = null;
1104 return token;
1107 buffer = null;
1108 return advance();
1111 function lookahead() {
1112 var pos, line, start;
1114 if (buffer !== null) {
1115 return buffer;
1118 pos = index;
1119 line = lineNumber;
1120 start = lineStart;
1121 buffer = advance();
1122 index = pos;
1123 lineNumber = line;
1124 lineStart = start;
1126 return buffer;
1129 // Return true if there is a line terminator before the next token.
1131 function peekLineTerminator() {
1132 var pos, line, start, found;
1134 pos = index;
1135 line = lineNumber;
1136 start = lineStart;
1137 skipComment();
1138 found = lineNumber !== line;
1139 index = pos;
1140 lineNumber = line;
1141 lineStart = start;
1143 return found;
1146 // Throw an exception
1148 function throwError(token, messageFormat) {
1149 var error,
1150 args = Array.prototype.slice.call(arguments, 2),
1151 msg = messageFormat.replace(
1152 /%(\d)/g,
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;
1163 } else {
1164 error = new Error('Line ' + lineNumber + ': ' + msg);
1165 error.index = index;
1166 error.lineNumber = lineNumber;
1167 error.column = index - lineStart + 1;
1170 throw error;
1173 function throwErrorTolerant() {
1174 try {
1175 throwError.apply(null, arguments);
1176 } catch (e) {
1177 if (extra.errors) {
1178 extra.errors.push(e);
1179 } else {
1180 throw 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);
1210 return;
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) {
1223 var token = lex();
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) {
1233 var token = lex();
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(),
1257 op = token.value;
1259 if (token.type !== Token.Punctuator) {
1260 return false;
1262 return op === '=' ||
1263 op === '*=' ||
1264 op === '/=' ||
1265 op === '%=' ||
1266 op === '+=' ||
1267 op === '-=' ||
1268 op === '<<=' ||
1269 op === '>>=' ||
1270 op === '>>>=' ||
1271 op === '&=' ||
1272 op === '^=' ||
1273 op === '|=';
1276 function consumeSemicolon() {
1277 var token, line;
1279 // Catch the very common case first.
1280 if (source[index] === ';') {
1281 lex();
1282 return;
1285 line = lineNumber;
1286 skipComment();
1287 if (lineNumber !== line) {
1288 return;
1291 if (match(';')) {
1292 lex();
1293 return;
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() {
1311 var elements = [];
1313 expect('[');
1315 while (!match(']')) {
1316 if (match(',')) {
1317 lex();
1318 elements.push(null);
1319 } else {
1320 elements.push(parseAssignmentExpression());
1322 if (!match(']')) {
1323 expect(',');
1328 expect(']');
1330 return {
1331 type: Syntax.ArrayExpression,
1332 elements: elements
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;
1348 return {
1349 type: Syntax.FunctionExpression,
1350 id: null,
1351 params: param,
1352 defaults: [],
1353 body: body,
1354 rest: null,
1355 generator: false,
1356 expression: false
1360 function parseObjectPropertyKey() {
1361 var token = lex();
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);
1373 return {
1374 type: Syntax.Identifier,
1375 name: token.value
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();
1392 expect('(');
1393 expect(')');
1394 return {
1395 type: Syntax.Property,
1396 key: key,
1397 value: parsePropertyFunction([]),
1398 kind: 'get'
1400 } else if (token.value === 'set' && !match(':')) {
1401 key = parseObjectPropertyKey();
1402 expect('(');
1403 token = lookahead();
1404 if (token.type !== Token.Identifier) {
1405 throwUnexpected(lex());
1407 param = [ parseVariableIdentifier() ];
1408 expect(')');
1409 return {
1410 type: Syntax.Property,
1411 key: key,
1412 value: parsePropertyFunction(param, token),
1413 kind: 'set'
1415 } else {
1416 expect(':');
1417 return {
1418 type: Syntax.Property,
1419 key: id,
1420 value: parseAssignmentExpression(),
1421 kind: 'init'
1424 } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
1425 throwUnexpected(token);
1426 } else {
1427 key = parseObjectPropertyKey();
1428 expect(':');
1429 return {
1430 type: Syntax.Property,
1431 key: key,
1432 value: parseAssignmentExpression(),
1433 kind: 'init'
1438 function parseObjectInitialiser() {
1439 var properties = [], property, name, kind, map = {}, toString = String;
1441 expect('{');
1443 while (!match('}')) {
1444 property = parseObjectProperty();
1446 if (property.key.type === Syntax.Identifier) {
1447 name = property.key.name;
1448 } else {
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);
1459 } else {
1460 if (kind === PropertyKind.Data) {
1461 throwErrorTolerant({}, Messages.AccessorDataProperty);
1462 } else if (map[name] & kind) {
1463 throwErrorTolerant({}, Messages.AccessorGetSet);
1466 map[name] |= kind;
1467 } else {
1468 map[name] = kind;
1471 properties.push(property);
1473 if (!match('}')) {
1474 expect(',');
1478 expect('}');
1480 return {
1481 type: Syntax.ObjectExpression,
1482 properties: properties
1486 // 11.1.6 The Grouping Operator
1488 function parseGroupExpression() {
1489 var expr;
1491 expect('(');
1493 expr = parseExpression();
1495 expect(')');
1497 return expr;
1501 // 11.1 Primary Expressions
1503 function parsePrimaryExpression() {
1504 var token = lookahead(),
1505 type = token.type;
1507 if (type === Token.Identifier) {
1508 return {
1509 type: Syntax.Identifier,
1510 name: lex().value
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')) {
1523 lex();
1524 return {
1525 type: Syntax.ThisExpression
1529 if (matchKeyword('function')) {
1530 return parseFunctionExpression();
1534 if (type === Token.BooleanLiteral) {
1535 lex();
1536 token.value = (token.value === 'true');
1537 return createLiteral(token);
1540 if (type === Token.NullLiteral) {
1541 lex();
1542 token.value = null;
1543 return createLiteral(token);
1546 if (match('[')) {
1547 return parseArrayInitialiser();
1550 if (match('{')) {
1551 return parseObjectInitialiser();
1554 if (match('(')) {
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() {
1568 var args = [];
1570 expect('(');
1572 if (!match(')')) {
1573 while (index < length) {
1574 args.push(parseAssignmentExpression());
1575 if (match(')')) {
1576 break;
1578 expect(',');
1582 expect(')');
1584 return args;
1587 function parseNonComputedProperty() {
1588 var token = lex();
1590 if (!isIdentifierName(token)) {
1591 throwUnexpected(token);
1594 return {
1595 type: Syntax.Identifier,
1596 name: token.value
1600 function parseNonComputedMember() {
1601 expect('.');
1603 return parseNonComputedProperty();
1606 function parseComputedMember() {
1607 var expr;
1609 expect('[');
1611 expr = parseExpression();
1613 expect(']');
1615 return expr;
1618 function parseNewExpression() {
1619 var expr;
1621 expectKeyword('new');
1623 expr = {
1624 type: Syntax.NewExpression,
1625 callee: parseLeftHandSideExpression(),
1626 'arguments': []
1629 if (match('(')) {
1630 expr['arguments'] = parseArguments();
1633 return expr;
1636 function parseLeftHandSideExpressionAllowCall() {
1637 var expr;
1639 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
1641 while (match('.') || match('[') || match('(')) {
1642 if (match('(')) {
1643 expr = {
1644 type: Syntax.CallExpression,
1645 callee: expr,
1646 'arguments': parseArguments()
1648 } else if (match('[')) {
1649 expr = {
1650 type: Syntax.MemberExpression,
1651 computed: true,
1652 object: expr,
1653 property: parseComputedMember()
1655 } else {
1656 expr = {
1657 type: Syntax.MemberExpression,
1658 computed: false,
1659 object: expr,
1660 property: parseNonComputedMember()
1665 return expr;
1669 function parseLeftHandSideExpression() {
1670 var expr;
1672 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
1674 while (match('.') || match('[')) {
1675 if (match('[')) {
1676 expr = {
1677 type: Syntax.MemberExpression,
1678 computed: true,
1679 object: expr,
1680 property: parseComputedMember()
1682 } else {
1683 expr = {
1684 type: Syntax.MemberExpression,
1685 computed: false,
1686 object: expr,
1687 property: parseNonComputedMember()
1692 return expr;
1695 // 11.3 Postfix Expressions
1697 function parsePostfixExpression() {
1698 var expr = parseLeftHandSideExpressionAllowCall(), token;
1700 token = lookahead();
1701 if (token.type !== Token.Punctuator) {
1702 return expr;
1705 if ((match('++') || match('--')) && !peekLineTerminator()) {
1706 // 11.3.1, 11.3.2
1707 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
1708 throwErrorTolerant({}, Messages.StrictLHSPostfix);
1711 if (!isLeftHandSide(expr)) {
1712 throwError({}, Messages.InvalidLHSInAssignment);
1715 expr = {
1716 type: Syntax.UpdateExpression,
1717 operator: lex().value,
1718 argument: expr,
1719 prefix: false
1723 return expr;
1726 // 11.4 Unary Operators
1728 function parseUnaryExpression() {
1729 var token, expr;
1731 token = lookahead();
1732 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
1733 return parsePostfixExpression();
1736 if (match('++') || match('--')) {
1737 token = lex();
1738 expr = parseUnaryExpression();
1739 // 11.4.4, 11.4.5
1740 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
1741 throwErrorTolerant({}, Messages.StrictLHSPrefix);
1744 if (!isLeftHandSide(expr)) {
1745 throwError({}, Messages.InvalidLHSInAssignment);
1748 expr = {
1749 type: Syntax.UpdateExpression,
1750 operator: token.value,
1751 argument: expr,
1752 prefix: true
1754 return expr;
1757 if (match('+') || match('-') || match('~') || match('!')) {
1758 expr = {
1759 type: Syntax.UnaryExpression,
1760 operator: lex().value,
1761 argument: parseUnaryExpression()
1763 return expr;
1766 if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
1767 expr = {
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);
1775 return expr;
1778 return parsePostfixExpression();
1781 // 11.5 Multiplicative Operators
1783 function parseMultiplicativeExpression() {
1784 var expr = parseUnaryExpression();
1786 while (match('*') || match('/') || match('%')) {
1787 expr = {
1788 type: Syntax.BinaryExpression,
1789 operator: lex().value,
1790 left: expr,
1791 right: parseUnaryExpression()
1795 return expr;
1798 // 11.6 Additive Operators
1800 function parseAdditiveExpression() {
1801 var expr = parseMultiplicativeExpression();
1803 while (match('+') || match('-')) {
1804 expr = {
1805 type: Syntax.BinaryExpression,
1806 operator: lex().value,
1807 left: expr,
1808 right: parseMultiplicativeExpression()
1812 return expr;
1815 // 11.7 Bitwise Shift Operators
1817 function parseShiftExpression() {
1818 var expr = parseAdditiveExpression();
1820 while (match('<<') || match('>>') || match('>>>')) {
1821 expr = {
1822 type: Syntax.BinaryExpression,
1823 operator: lex().value,
1824 left: expr,
1825 right: parseAdditiveExpression()
1829 return expr;
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')) {
1842 expr = {
1843 type: Syntax.BinaryExpression,
1844 operator: lex().value,
1845 left: expr,
1846 right: parseShiftExpression()
1850 state.allowIn = previousAllowIn;
1851 return expr;
1854 // 11.9 Equality Operators
1856 function parseEqualityExpression() {
1857 var expr = parseRelationalExpression();
1859 while (match('==') || match('!=') || match('===') || match('!==')) {
1860 expr = {
1861 type: Syntax.BinaryExpression,
1862 operator: lex().value,
1863 left: expr,
1864 right: parseRelationalExpression()
1868 return expr;
1871 // 11.10 Binary Bitwise Operators
1873 function parseBitwiseANDExpression() {
1874 var expr = parseEqualityExpression();
1876 while (match('&')) {
1877 lex();
1878 expr = {
1879 type: Syntax.BinaryExpression,
1880 operator: '&',
1881 left: expr,
1882 right: parseEqualityExpression()
1886 return expr;
1889 function parseBitwiseXORExpression() {
1890 var expr = parseBitwiseANDExpression();
1892 while (match('^')) {
1893 lex();
1894 expr = {
1895 type: Syntax.BinaryExpression,
1896 operator: '^',
1897 left: expr,
1898 right: parseBitwiseANDExpression()
1902 return expr;
1905 function parseBitwiseORExpression() {
1906 var expr = parseBitwiseXORExpression();
1908 while (match('|')) {
1909 lex();
1910 expr = {
1911 type: Syntax.BinaryExpression,
1912 operator: '|',
1913 left: expr,
1914 right: parseBitwiseXORExpression()
1918 return expr;
1921 // 11.11 Binary Logical Operators
1923 function parseLogicalANDExpression() {
1924 var expr = parseBitwiseORExpression();
1926 while (match('&&')) {
1927 lex();
1928 expr = {
1929 type: Syntax.LogicalExpression,
1930 operator: '&&',
1931 left: expr,
1932 right: parseBitwiseORExpression()
1936 return expr;
1939 function parseLogicalORExpression() {
1940 var expr = parseLogicalANDExpression();
1942 while (match('||')) {
1943 lex();
1944 expr = {
1945 type: Syntax.LogicalExpression,
1946 operator: '||',
1947 left: expr,
1948 right: parseLogicalANDExpression()
1952 return expr;
1955 // 11.12 Conditional Operator
1957 function parseConditionalExpression() {
1958 var expr, previousAllowIn, consequent;
1960 expr = parseLogicalORExpression();
1962 if (match('?')) {
1963 lex();
1964 previousAllowIn = state.allowIn;
1965 state.allowIn = true;
1966 consequent = parseAssignmentExpression();
1967 state.allowIn = previousAllowIn;
1968 expect(':');
1970 expr = {
1971 type: Syntax.ConditionalExpression,
1972 test: expr,
1973 consequent: consequent,
1974 alternate: parseAssignmentExpression()
1978 return expr;
1981 // 11.13 Assignment Operators
1983 function parseAssignmentExpression() {
1984 var token, expr;
1986 token = lookahead();
1987 expr = parseConditionalExpression();
1989 if (matchAssign()) {
1990 // LeftHandSideExpression
1991 if (!isLeftHandSide(expr)) {
1992 throwError({}, Messages.InvalidLHSInAssignment);
1995 // 11.13.1
1996 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
1997 throwErrorTolerant(token, Messages.StrictLHSAssignment);
2000 expr = {
2001 type: Syntax.AssignmentExpression,
2002 operator: lex().value,
2003 left: expr,
2004 right: parseAssignmentExpression()
2008 return expr;
2011 // 11.14 Comma Operator
2013 function parseExpression() {
2014 var expr = parseAssignmentExpression();
2016 if (match(',')) {
2017 expr = {
2018 type: Syntax.SequenceExpression,
2019 expressions: [ expr ]
2022 while (index < length) {
2023 if (!match(',')) {
2024 break;
2026 lex();
2027 expr.expressions.push(parseAssignmentExpression());
2031 return expr;
2034 // 12.1 Block
2036 function parseStatementList() {
2037 var list = [],
2038 statement;
2040 while (index < length) {
2041 if (match('}')) {
2042 break;
2044 statement = parseSourceElement();
2045 if (typeof statement === 'undefined') {
2046 break;
2048 list.push(statement);
2051 return list;
2054 function parseBlock() {
2055 var block;
2057 expect('{');
2059 block = parseStatementList();
2061 expect('}');
2063 return {
2064 type: Syntax.BlockStatement,
2065 body: block
2069 // 12.2 Variable Statement
2071 function parseVariableIdentifier() {
2072 var token = lex();
2074 if (token.type !== Token.Identifier) {
2075 throwUnexpected(token);
2078 return {
2079 type: Syntax.Identifier,
2080 name: token.value
2084 function parseVariableDeclaration(kind) {
2085 var id = parseVariableIdentifier(),
2086 init = null;
2088 // 12.2.1
2089 if (strict && isRestrictedWord(id.name)) {
2090 throwErrorTolerant({}, Messages.StrictVarName);
2093 if (kind === 'const') {
2094 expect('=');
2095 init = parseAssignmentExpression();
2096 } else if (match('=')) {
2097 lex();
2098 init = parseAssignmentExpression();
2101 return {
2102 type: Syntax.VariableDeclarator,
2103 id: id,
2104 init: init
2108 function parseVariableDeclarationList(kind) {
2109 var list = [];
2111 while (index < length) {
2112 list.push(parseVariableDeclaration(kind));
2113 if (!match(',')) {
2114 break;
2116 lex();
2119 return list;
2122 function parseVariableStatement() {
2123 var declarations;
2125 expectKeyword('var');
2127 declarations = parseVariableDeclarationList();
2129 consumeSemicolon();
2131 return {
2132 type: Syntax.VariableDeclaration,
2133 declarations: declarations,
2134 kind: 'var'
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) {
2143 var declarations;
2145 expectKeyword(kind);
2147 declarations = parseVariableDeclarationList(kind);
2149 consumeSemicolon();
2151 return {
2152 type: Syntax.VariableDeclaration,
2153 declarations: declarations,
2154 kind: kind
2158 // 12.3 Empty Statement
2160 function parseEmptyStatement() {
2161 expect(';');
2163 return {
2164 type: Syntax.EmptyStatement
2168 // 12.4 Expression Statement
2170 function parseExpressionStatement() {
2171 var expr = parseExpression();
2173 consumeSemicolon();
2175 return {
2176 type: Syntax.ExpressionStatement,
2177 expression: expr
2181 // 12.5 If statement
2183 function parseIfStatement() {
2184 var test, consequent, alternate;
2186 expectKeyword('if');
2188 expect('(');
2190 test = parseExpression();
2192 expect(')');
2194 consequent = parseStatement();
2196 if (matchKeyword('else')) {
2197 lex();
2198 alternate = parseStatement();
2199 } else {
2200 alternate = null;
2203 return {
2204 type: Syntax.IfStatement,
2205 test: test,
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');
2227 expect('(');
2229 test = parseExpression();
2231 expect(')');
2233 if (match(';')) {
2234 lex();
2237 return {
2238 type: Syntax.DoWhileStatement,
2239 body: body,
2240 test: test
2244 function parseWhileStatement() {
2245 var test, body, oldInIteration;
2247 expectKeyword('while');
2249 expect('(');
2251 test = parseExpression();
2253 expect(')');
2255 oldInIteration = state.inIteration;
2256 state.inIteration = true;
2258 body = parseStatement();
2260 state.inIteration = oldInIteration;
2262 return {
2263 type: Syntax.WhileStatement,
2264 test: test,
2265 body: body
2269 function parseForVariableDeclaration() {
2270 var token = lex();
2272 return {
2273 type: Syntax.VariableDeclaration,
2274 declarations: parseVariableDeclarationList(),
2275 kind: token.value
2279 function parseForStatement() {
2280 var init, test, update, left, right, body, oldInIteration;
2282 init = test = update = null;
2284 expectKeyword('for');
2286 expect('(');
2288 if (match(';')) {
2289 lex();
2290 } else {
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')) {
2297 lex();
2298 left = init;
2299 right = parseExpression();
2300 init = null;
2302 } else {
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);
2313 lex();
2314 left = init;
2315 right = parseExpression();
2316 init = null;
2320 if (typeof left === 'undefined') {
2321 expect(';');
2325 if (typeof left === 'undefined') {
2327 if (!match(';')) {
2328 test = parseExpression();
2330 expect(';');
2332 if (!match(')')) {
2333 update = parseExpression();
2337 expect(')');
2339 oldInIteration = state.inIteration;
2340 state.inIteration = true;
2342 body = parseStatement();
2344 state.inIteration = oldInIteration;
2346 if (typeof left === 'undefined') {
2347 return {
2348 type: Syntax.ForStatement,
2349 init: init,
2350 test: test,
2351 update: update,
2352 body: body
2356 return {
2357 type: Syntax.ForInStatement,
2358 left: left,
2359 right: right,
2360 body: body,
2361 each: false
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] === ';') {
2374 lex();
2376 if (!state.inIteration) {
2377 throwError({}, Messages.IllegalContinue);
2380 return {
2381 type: Syntax.ContinueStatement,
2382 label: null
2386 if (peekLineTerminator()) {
2387 if (!state.inIteration) {
2388 throwError({}, Messages.IllegalContinue);
2391 return {
2392 type: Syntax.ContinueStatement,
2393 label: null
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);
2406 consumeSemicolon();
2408 if (label === null && !state.inIteration) {
2409 throwError({}, Messages.IllegalContinue);
2412 return {
2413 type: Syntax.ContinueStatement,
2414 label: label
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] === ';') {
2427 lex();
2429 if (!(state.inIteration || state.inSwitch)) {
2430 throwError({}, Messages.IllegalBreak);
2433 return {
2434 type: Syntax.BreakStatement,
2435 label: null
2439 if (peekLineTerminator()) {
2440 if (!(state.inIteration || state.inSwitch)) {
2441 throwError({}, Messages.IllegalBreak);
2444 return {
2445 type: Syntax.BreakStatement,
2446 label: null
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);
2459 consumeSemicolon();
2461 if (label === null && !(state.inIteration || state.inSwitch)) {
2462 throwError({}, Messages.IllegalBreak);
2465 return {
2466 type: Syntax.BreakStatement,
2467 label: label
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();
2486 consumeSemicolon();
2487 return {
2488 type: Syntax.ReturnStatement,
2489 argument: argument
2494 if (peekLineTerminator()) {
2495 return {
2496 type: Syntax.ReturnStatement,
2497 argument: null
2501 if (!match(';')) {
2502 token = lookahead();
2503 if (!match('}') && token.type !== Token.EOF) {
2504 argument = parseExpression();
2508 consumeSemicolon();
2510 return {
2511 type: Syntax.ReturnStatement,
2512 argument: argument
2516 // 12.10 The with statement
2518 function parseWithStatement() {
2519 var object, body;
2521 if (strict) {
2522 throwErrorTolerant({}, Messages.StrictModeWith);
2525 expectKeyword('with');
2527 expect('(');
2529 object = parseExpression();
2531 expect(')');
2533 body = parseStatement();
2535 return {
2536 type: Syntax.WithStatement,
2537 object: object,
2538 body: body
2542 // 12.10 The swith statement
2544 function parseSwitchCase() {
2545 var test,
2546 consequent = [],
2547 statement;
2549 if (matchKeyword('default')) {
2550 lex();
2551 test = null;
2552 } else {
2553 expectKeyword('case');
2554 test = parseExpression();
2556 expect(':');
2558 while (index < length) {
2559 if (match('}') || matchKeyword('default') || matchKeyword('case')) {
2560 break;
2562 statement = parseStatement();
2563 if (typeof statement === 'undefined') {
2564 break;
2566 consequent.push(statement);
2569 return {
2570 type: Syntax.SwitchCase,
2571 test: test,
2572 consequent: consequent
2576 function parseSwitchStatement() {
2577 var discriminant, cases, clause, oldInSwitch, defaultFound;
2579 expectKeyword('switch');
2581 expect('(');
2583 discriminant = parseExpression();
2585 expect(')');
2587 expect('{');
2589 if (match('}')) {
2590 lex();
2591 return {
2592 type: Syntax.SwitchStatement,
2593 discriminant: discriminant
2597 cases = [];
2599 oldInSwitch = state.inSwitch;
2600 state.inSwitch = true;
2601 defaultFound = false;
2603 while (index < length) {
2604 if (match('}')) {
2605 break;
2607 clause = parseSwitchCase();
2608 if (clause.test === null) {
2609 if (defaultFound) {
2610 throwError({}, Messages.MultipleDefaultsInSwitch);
2612 defaultFound = true;
2614 cases.push(clause);
2617 state.inSwitch = oldInSwitch;
2619 expect('}');
2621 return {
2622 type: Syntax.SwitchStatement,
2623 discriminant: discriminant,
2624 cases: cases
2628 // 12.13 The throw statement
2630 function parseThrowStatement() {
2631 var argument;
2633 expectKeyword('throw');
2635 if (peekLineTerminator()) {
2636 throwError({}, Messages.NewlineAfterThrow);
2639 argument = parseExpression();
2641 consumeSemicolon();
2643 return {
2644 type: Syntax.ThrowStatement,
2645 argument: argument
2649 // 12.14 The try statement
2651 function parseCatchClause() {
2652 var param;
2654 expectKeyword('catch');
2656 expect('(');
2657 if (!match(')')) {
2658 param = parseExpression();
2659 // 12.14.1
2660 if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) {
2661 throwErrorTolerant({}, Messages.StrictCatchVariable);
2664 expect(')');
2666 return {
2667 type: Syntax.CatchClause,
2668 param: param,
2669 body: parseBlock()
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')) {
2685 lex();
2686 finalizer = parseBlock();
2689 if (handlers.length === 0 && !finalizer) {
2690 throwError({}, Messages.NoCatchOrFinally);
2693 return {
2694 type: Syntax.TryStatement,
2695 block: block,
2696 guardedHandlers: [],
2697 handlers: handlers,
2698 finalizer: finalizer
2702 // 12.15 The debugger statement
2704 function parseDebuggerStatement() {
2705 expectKeyword('debugger');
2707 consumeSemicolon();
2709 return {
2710 type: Syntax.DebuggerStatement
2714 // 12 Statements
2716 function parseStatement() {
2717 var token = lookahead(),
2718 expr,
2719 labeledBody;
2721 if (token.type === Token.EOF) {
2722 throwUnexpected(token);
2725 if (token.type === Token.Punctuator) {
2726 switch (token.value) {
2727 case ';':
2728 return parseEmptyStatement();
2729 case '{':
2730 return parseBlock();
2731 case '(':
2732 return parseExpressionStatement();
2733 default:
2734 break;
2738 if (token.type === Token.Keyword) {
2739 switch (token.value) {
2740 case 'break':
2741 return parseBreakStatement();
2742 case 'continue':
2743 return parseContinueStatement();
2744 case 'debugger':
2745 return parseDebuggerStatement();
2746 case 'do':
2747 return parseDoWhileStatement();
2748 case 'for':
2749 return parseForStatement();
2750 case 'function':
2751 return parseFunctionDeclaration();
2752 case 'if':
2753 return parseIfStatement();
2754 case 'return':
2755 return parseReturnStatement();
2756 case 'switch':
2757 return parseSwitchStatement();
2758 case 'throw':
2759 return parseThrowStatement();
2760 case 'try':
2761 return parseTryStatement();
2762 case 'var':
2763 return parseVariableStatement();
2764 case 'while':
2765 return parseWhileStatement();
2766 case 'with':
2767 return parseWithStatement();
2768 default:
2769 break;
2773 expr = parseExpression();
2775 // 12.12 Labelled Statements
2776 if ((expr.type === Syntax.Identifier) && match(':')) {
2777 lex();
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];
2787 return {
2788 type: Syntax.LabeledStatement,
2789 label: expr,
2790 body: labeledBody
2794 consumeSemicolon();
2796 return {
2797 type: Syntax.ExpressionStatement,
2798 expression: expr
2802 // 13 Function Definition
2804 function parseFunctionSourceElements() {
2805 var sourceElement, sourceElements = [], token, directive, firstRestricted,
2806 oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
2808 expect('{');
2810 while (index < length) {
2811 token = lookahead();
2812 if (token.type !== Token.StringLiteral) {
2813 break;
2816 sourceElement = parseSourceElement();
2817 sourceElements.push(sourceElement);
2818 if (sourceElement.expression.type !== Syntax.Literal) {
2819 // this is not directive
2820 break;
2822 directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
2823 if (directive === 'use strict') {
2824 strict = true;
2825 if (firstRestricted) {
2826 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
2828 } else {
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) {
2846 if (match('}')) {
2847 break;
2849 sourceElement = parseSourceElement();
2850 if (typeof sourceElement === 'undefined') {
2851 break;
2853 sourceElements.push(sourceElement);
2856 expect('}');
2858 state.labelSet = oldLabelSet;
2859 state.inIteration = oldInIteration;
2860 state.inSwitch = oldInSwitch;
2861 state.inFunctionBody = oldInFunctionBody;
2863 return {
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();
2875 if (strict) {
2876 if (isRestrictedWord(token.value)) {
2877 throwErrorTolerant(token, Messages.StrictFunctionName);
2879 } else {
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;
2889 expect('(');
2891 if (!match(')')) {
2892 paramSet = {};
2893 while (index < length) {
2894 token = lookahead();
2895 param = parseVariableIdentifier();
2896 if (strict) {
2897 if (isRestrictedWord(token.value)) {
2898 stricted = token;
2899 message = Messages.StrictParamName;
2901 if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
2902 stricted = token;
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;
2917 params.push(param);
2918 paramSet[param.name] = true;
2919 if (match(')')) {
2920 break;
2922 expect(',');
2926 expect(')');
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;
2938 return {
2939 type: Syntax.FunctionDeclaration,
2940 id: id,
2941 params: params,
2942 defaults: [],
2943 body: body,
2944 rest: null,
2945 generator: false,
2946 expression: false
2950 function parseFunctionExpression() {
2951 var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
2953 expectKeyword('function');
2955 if (!match('(')) {
2956 token = lookahead();
2957 id = parseVariableIdentifier();
2958 if (strict) {
2959 if (isRestrictedWord(token.value)) {
2960 throwErrorTolerant(token, Messages.StrictFunctionName);
2962 } else {
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;
2973 expect('(');
2975 if (!match(')')) {
2976 paramSet = {};
2977 while (index < length) {
2978 token = lookahead();
2979 param = parseVariableIdentifier();
2980 if (strict) {
2981 if (isRestrictedWord(token.value)) {
2982 stricted = token;
2983 message = Messages.StrictParamName;
2985 if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
2986 stricted = token;
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;
3001 params.push(param);
3002 paramSet[param.name] = true;
3003 if (match(')')) {
3004 break;
3006 expect(',');
3010 expect(')');
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;
3022 return {
3023 type: Syntax.FunctionExpression,
3024 id: id,
3025 params: params,
3026 defaults: [],
3027 body: body,
3028 rest: null,
3029 generator: false,
3030 expression: false
3034 // 14 Program
3036 function parseSourceElement() {
3037 var token = lookahead();
3039 if (token.type === Token.Keyword) {
3040 switch (token.value) {
3041 case 'const':
3042 case 'let':
3043 return parseConstLetDeclaration(token.value);
3044 case 'function':
3045 return parseFunctionDeclaration();
3046 default:
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) {
3062 break;
3065 sourceElement = parseSourceElement();
3066 sourceElements.push(sourceElement);
3067 if (sourceElement.expression.type !== Syntax.Literal) {
3068 // this is not directive
3069 break;
3071 directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
3072 if (directive === 'use strict') {
3073 strict = true;
3074 if (firstRestricted) {
3075 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
3077 } else {
3078 if (!firstRestricted && token.octal) {
3079 firstRestricted = token;
3084 while (index < length) {
3085 sourceElement = parseSourceElement();
3086 if (typeof sourceElement === 'undefined') {
3087 break;
3089 sourceElements.push(sourceElement);
3091 return sourceElements;
3094 function parseProgram() {
3095 var program;
3096 strict = false;
3097 program = {
3098 type: Syntax.Program,
3099 body: parseSourceElements()
3101 return program;
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
3113 // handled it.
3114 if (extra.comments.length > 0) {
3115 if (extra.comments[extra.comments.length - 1].range[1] > start) {
3116 return;
3120 extra.comments.push({
3121 type: type,
3122 value: value,
3123 range: [start, end],
3124 loc: loc
3128 function scanComment() {
3129 var comment, ch, loc, start, blockComment, lineComment;
3131 comment = '';
3132 blockComment = false;
3133 lineComment = false;
3135 while (index < length) {
3136 ch = source[index];
3138 if (lineComment) {
3139 ch = source[index++];
3140 if (isLineTerminator(ch)) {
3141 loc.end = {
3142 line: lineNumber,
3143 column: index - lineStart - 1
3145 lineComment = false;
3146 addComment('Line', comment, start, index - 1, loc);
3147 if (ch === '\r' && source[index] === '\n') {
3148 ++index;
3150 ++lineNumber;
3151 lineStart = index;
3152 comment = '';
3153 } else if (index >= length) {
3154 lineComment = false;
3155 comment += ch;
3156 loc.end = {
3157 line: lineNumber,
3158 column: length - lineStart
3160 addComment('Line', comment, start, length, loc);
3161 } else {
3162 comment += ch;
3164 } else if (blockComment) {
3165 if (isLineTerminator(ch)) {
3166 if (ch === '\r' && source[index + 1] === '\n') {
3167 ++index;
3168 comment += '\r\n';
3169 } else {
3170 comment += ch;
3172 ++lineNumber;
3173 ++index;
3174 lineStart = index;
3175 if (index >= length) {
3176 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
3178 } else {
3179 ch = source[index++];
3180 if (index >= length) {
3181 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
3183 comment += ch;
3184 if (ch === '*') {
3185 ch = source[index];
3186 if (ch === '/') {
3187 comment = comment.substr(0, comment.length - 1);
3188 blockComment = false;
3189 ++index;
3190 loc.end = {
3191 line: lineNumber,
3192 column: index - lineStart
3194 addComment('Block', comment, start, index, loc);
3195 comment = '';
3199 } else if (ch === '/') {
3200 ch = source[index + 1];
3201 if (ch === '/') {
3202 loc = {
3203 start: {
3204 line: lineNumber,
3205 column: index - lineStart
3208 start = index;
3209 index += 2;
3210 lineComment = true;
3211 if (index >= length) {
3212 loc.end = {
3213 line: lineNumber,
3214 column: index - lineStart
3216 lineComment = false;
3217 addComment('Line', comment, start, index, loc);
3219 } else if (ch === '*') {
3220 start = index;
3221 index += 2;
3222 blockComment = true;
3223 loc = {
3224 start: {
3225 line: lineNumber,
3226 column: index - lineStart - 2
3229 if (index >= length) {
3230 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
3232 } else {
3233 break;
3235 } else if (isWhiteSpace(ch)) {
3236 ++index;
3237 } else if (isLineTerminator(ch)) {
3238 ++index;
3239 if (ch === '\r' && source[index] === '\n') {
3240 ++index;
3242 ++lineNumber;
3243 lineStart = index;
3244 } else {
3245 break;
3250 function filterCommentLocation() {
3251 var i, entry, comment, comments = [];
3253 for (i = 0; i < extra.comments.length; ++i) {
3254 entry = extra.comments[i];
3255 comment = {
3256 type: entry.type,
3257 value: entry.value
3259 if (extra.range) {
3260 comment.range = entry.range;
3262 if (extra.loc) {
3263 comment.loc = entry.loc;
3265 comments.push(comment);
3268 extra.comments = comments;
3271 function collectToken() {
3272 var start, loc, token, range, value;
3274 skipComment();
3275 start = index;
3276 loc = {
3277 start: {
3278 line: lineNumber,
3279 column: index - lineStart
3283 token = extra.advance();
3284 loc.end = {
3285 line: lineNumber,
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]);
3292 extra.tokens.push({
3293 type: TokenName[token.type],
3294 value: value,
3295 range: range,
3296 loc: loc
3300 return token;
3303 function collectRegex() {
3304 var pos, loc, regex, token;
3306 skipComment();
3308 pos = index;
3309 loc = {
3310 start: {
3311 line: lineNumber,
3312 column: index - lineStart
3316 regex = extra.scanRegExp();
3317 loc.end = {
3318 line: lineNumber,
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 === '/=') {
3327 extra.tokens.pop();
3332 extra.tokens.push({
3333 type: 'RegularExpression',
3334 value: regex.literal,
3335 range: [pos, index],
3336 loc: loc
3339 return regex;
3342 function filterTokenLocation() {
3343 var i, entry, token, tokens = [];
3345 for (i = 0; i < extra.tokens.length; ++i) {
3346 entry = extra.tokens[i];
3347 token = {
3348 type: entry.type,
3349 value: entry.value
3351 if (extra.range) {
3352 token.range = entry.range;
3354 if (extra.loc) {
3355 token.loc = entry.loc;
3357 tokens.push(token);
3360 extra.tokens = tokens;
3363 function createLiteral(token) {
3364 return {
3365 type: Syntax.Literal,
3366 value: token.value
3370 function createRawLiteral(token) {
3371 return {
3372 type: Syntax.Literal,
3373 value: token.value,
3374 raw: sliceSource(token.range[0], token.range[1])
3378 function createLocationMarker() {
3379 var marker = {};
3381 marker.range = [index, index];
3382 marker.loc = {
3383 start: {
3384 line: lineNumber,
3385 column: index - lineStart
3387 end: {
3388 line: lineNumber,
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) {
3400 if (extra.range) {
3401 node.groupRange = [this.range[0], this.range[1]];
3403 if (extra.loc) {
3404 node.groupLoc = {
3405 start: {
3406 line: this.loc.start.line,
3407 column: this.loc.start.column
3409 end: {
3410 line: this.loc.end.line,
3411 column: this.loc.end.column
3417 marker.apply = function (node) {
3418 if (extra.range) {
3419 node.range = [this.range[0], this.range[1]];
3421 if (extra.loc) {
3422 node.loc = {
3423 start: {
3424 line: this.loc.start.line,
3425 column: this.loc.start.column
3427 end: {
3428 line: this.loc.end.line,
3429 column: this.loc.end.column
3435 return marker;
3438 function trackGroupExpression() {
3439 var marker, expr;
3441 skipComment();
3442 marker = createLocationMarker();
3443 expect('(');
3445 expr = parseExpression();
3447 expect(')');
3449 marker.end();
3450 marker.applyGroup(expr);
3452 return expr;
3455 function trackLeftHandSideExpression() {
3456 var marker, expr;
3458 skipComment();
3459 marker = createLocationMarker();
3461 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
3463 while (match('.') || match('[')) {
3464 if (match('[')) {
3465 expr = {
3466 type: Syntax.MemberExpression,
3467 computed: true,
3468 object: expr,
3469 property: parseComputedMember()
3471 marker.end();
3472 marker.apply(expr);
3473 } else {
3474 expr = {
3475 type: Syntax.MemberExpression,
3476 computed: false,
3477 object: expr,
3478 property: parseNonComputedMember()
3480 marker.end();
3481 marker.apply(expr);
3485 return expr;
3488 function trackLeftHandSideExpressionAllowCall() {
3489 var marker, expr;
3491 skipComment();
3492 marker = createLocationMarker();
3494 expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
3496 while (match('.') || match('[') || match('(')) {
3497 if (match('(')) {
3498 expr = {
3499 type: Syntax.CallExpression,
3500 callee: expr,
3501 'arguments': parseArguments()
3503 marker.end();
3504 marker.apply(expr);
3505 } else if (match('[')) {
3506 expr = {
3507 type: Syntax.MemberExpression,
3508 computed: true,
3509 object: expr,
3510 property: parseComputedMember()
3512 marker.end();
3513 marker.apply(expr);
3514 } else {
3515 expr = {
3516 type: Syntax.MemberExpression,
3517 computed: false,
3518 object: expr,
3519 property: parseNonComputedMember()
3521 marker.end();
3522 marker.apply(expr);
3526 return expr;
3529 function filterGroup(node) {
3530 var n, i, entry;
3532 n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {};
3533 for (i in node) {
3534 if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') {
3535 entry = node[i];
3536 if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) {
3537 n[i] = entry;
3538 } else {
3539 n[i] = filterGroup(entry);
3543 return n;
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) {
3556 var start, end;
3558 if (isBinary(node.left)) {
3559 visit(node.left);
3561 if (isBinary(node.right)) {
3562 visit(node.right);
3565 if (range) {
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];
3576 if (loc) {
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;
3580 node.loc = {
3581 start: start,
3582 end: end
3584 } else if (typeof node.loc === 'undefined') {
3585 node.loc = {
3586 start: node.left.loc.start,
3587 end: node.right.loc.end
3593 return function () {
3594 var marker, node;
3596 skipComment();
3598 marker = createLocationMarker();
3599 node = parseFunction.apply(null, arguments);
3600 marker.end();
3602 if (range && typeof node.range === 'undefined') {
3603 marker.apply(node);
3606 if (loc && typeof node.loc === 'undefined') {
3607 marker.apply(node);
3610 if (isBinary(node)) {
3611 visit(node);
3614 return node;
3619 function patch() {
3621 var wrapTracking;
3623 if (extra.comments) {
3624 extra.skipComment = skipComment;
3625 skipComment = scanComment;
3628 if (extra.raw) {
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;
3730 if (extra.raw) {
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,
3782 result = [],
3784 for (i = 0; i < length; ++i) {
3785 result[i] = str.charAt(i);
3787 return result;
3790 function parse(code, options) {
3791 var program, toString;
3793 toString = String;
3794 if (typeof code !== 'string' && !(code instanceof String)) {
3795 code = toString(code);
3798 source = code;
3799 index = 0;
3800 lineNumber = (source.length > 0) ? 1 : 0;
3801 lineStart = 0;
3802 length = source.length;
3803 buffer = null;
3804 state = {
3805 allowIn: true,
3806 labelSet: {},
3807 inFunctionBody: false,
3808 inIteration: false,
3809 inSwitch: false
3812 extra = {};
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) {
3818 extra.tokens = [];
3820 if (typeof options.comment === 'boolean' && options.comment) {
3821 extra.comments = [];
3823 if (typeof options.tolerant === 'boolean' && options.tolerant) {
3824 extra.errors = [];
3828 if (length > 0) {
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);
3844 patch();
3845 try {
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);
3861 } catch (e) {
3862 throw e;
3863 } finally {
3864 unpatch();
3865 extra = {};
3868 return program;
3871 // Sync with package.json.
3872 exports.version = '1.0.2';
3874 exports.parse = parse;
3876 // Deep copy.
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);
3894 return types;
3895 }());
3897 }));
3898 /* vim: set sw=4 ts=4 et tw=80 : */
3900 })(null);
3902 (function(require,module){
3904 var parse = require('esprima').parse;
3905 var objectKeys = Object.keys || function (obj) {
3906 var keys = [];
3907 for (var key in obj) keys.push(key);
3908 return keys;
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') {
3923 fn = opts;
3924 opts = {};
3926 if (typeof src === 'object') {
3927 opts = src;
3928 src = opts.source;
3929 delete opts.source;
3931 src = src === undefined ? opts.source : src;
3932 opts.range = true;
3933 if (typeof src !== 'string') src = String(src);
3935 var ast = parse(src, opts);
3937 var result = {
3938 chunks : src.split(''),
3939 toString : function () { return result.chunks.join('') },
3940 inspect : function () { return result.toString() }
3942 var index = 0;
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') {
3954 walk(c, node);
3958 else if (child && typeof child.type === 'string') {
3959 insertHelpers(child, node, result.chunks);
3960 walk(child, node);
3963 fn(node);
3964 })(ast, undefined);
3966 return result;
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]
3977 ).join('');
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;
3987 else {
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++) {
3994 chunks[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",
4007 "BreakStatement" ,
4008 "ContinueStatement" ,
4009 "VariableDeclaration",
4010 "ReturnStatement" ,
4011 "ThrowStatement" ,
4012 "TryStatement" ,
4013 "FunctionDeclaration" ,
4014 "IfStatement" ,
4015 "WhileStatement" ,
4016 "DoWhileStatement" ,
4017 "ForStatement" ,
4018 "ForInStatement" ,
4019 "SwitchStatement" ,
4020 "WithStatement"
4022 linesToAddBrackets = [
4023 "IfStatement" ,
4024 "WhileStatement" ,
4025 "DoWhileStatement" ,
4026 "ForStatement" ,
4027 "ForInStatement" ,
4028 "WithStatement"
4030 __blanket,
4031 copynumber = Math.floor(Math.random()*1000),
4032 coverageInfo = {},options = {
4033 reporter: null,
4034 adapter:null,
4035 filter: null,
4036 customVariable: null,
4037 loader: null,
4038 ignoreScriptError: false,
4039 existingRequireJS:false,
4040 autoStart: false,
4041 timeout: 180,
4042 ignoreCors: false,
4043 branchTracking: false,
4044 sourceURL: false,
4045 debug:false,
4046 engineOnly:false,
4047 testReadyCallback:null,
4048 commonJS:false,
4049 instrumentCache:false,
4050 modulePattern: null
4053 if (inBrowser && typeof window.blanket !== 'undefined'){
4054 __blanket = window.blanket.noConflict();
4057 _blanket = {
4058 noConflict: function(){
4059 if (__blanket){
4060 return __blanket;
4062 return _blanket;
4064 _getCopyNumber: function(){
4065 //internal method
4066 //for differentiating between instances
4067 return copynumber;
4069 extend: function(obj) {
4070 //borrowed from underscore
4071 _blanket._extend(_blanket,obj);
4073 _extend: function(dest,source){
4074 if (source) {
4075 for (var prop in source) {
4076 if ( dest[prop] instanceof Object && typeof dest[prop] !== "function"){
4077 _blanket._extend(dest[prop],source[prop]);
4078 }else{
4079 dest[prop] = source[prop];
4084 getCovVar: function(){
4085 var opt = _blanket.options("customVariable");
4086 if (opt){
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];
4097 }else{
4098 options[key]=value;
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));
4110 }else{
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);
4123 next(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'");
4134 var intro = "";
4135 var covVar = _blanket.getCovVar();
4137 intro += "if (typeof "+covVar+" === 'undefined') "+covVar+" = {};\n";
4138 if (branches){
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); }";
4143 intro += "else { ";
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";
4151 if (branches){
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";
4161 if (branches){
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";
4170 intro += "}";
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";
4177 intro += "}";
4179 return intro;
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({
4199 line: line,
4200 column: col,
4201 file:filename,
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")){
4225 return;
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);
4230 }else{
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.
4241 if (inBrowser){
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);
4264 }else{
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 = {
4277 "suites": 0,
4278 "tests": 0,
4279 "passes": 0,
4280 "pending": 0,
4281 "failures": 0,
4282 "start": new Date()
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++;
4300 }else{
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();
4314 if (inBrowser){
4315 this.report(coverageInfo);
4316 }else{
4317 if (!_blanket.options("branchTracking")){
4318 delete (inBrowser ? window : global)[_blanket.getCovVar()].branchFcn;
4320 this.options("reporter").call(this,coverageInfo);
4324 return _blanket;
4325 })();
4327 (function(_blanket){
4328 var oldOptions = _blanket.options;
4329 _blanket.extend({
4330 outstandingRequireFiles:[],
4331 options: function(key,value){
4332 var newVal={};
4334 if (typeof key !== "string"){
4335 //key is key/value map
4336 oldOptions(key);
4337 newVal = key;
4338 }else if (typeof value === 'undefined'){
4339 //accessor
4340 return oldOptions(key);
4341 }else{
4342 //setter
4343 oldOptions(key,value);
4344 newVal[key] = value;
4347 if (newVal.adapter){
4348 _blanket._loadFile(newVal.adapter);
4350 if (newVal.loader){
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);
4359 }else{
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")){
4368 return;
4370 //copied from http://blog.avtex.com/2012/01/26/cross-browser-css-only-modal-box/
4371 var loader = "<div class='blanketDialogOverlay'>";
4372 loader += "&nbsp;</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).";
4378 loader += "<br>";
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;";
4405 css += "top:30%;";
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;
4434 var sessionIndx=0;
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;
4446 sessionIndx++;
4447 if (sessionIndx === sessionLength){
4448 sessionStorage.setItem("blanketSessionLoader", JSON.stringify(sessionArray));
4449 document.location.reload();
4450 }else{
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);
4465 request.send();
4466 _blanket._addScript(request.responseText);
4469 _addScript: function(data){
4470 var script = document.createElement("script");
4471 script.type = "text/javascript";
4472 script.text = data;
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.");}
4490 return;
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);
4503 }else{
4504 throw new Error("no reporter defined.");
4507 _bindStartTestRunner: function(bindEvent,startEvent){
4508 if (bindEvent){
4509 bindEvent(startEvent);
4510 }else{
4511 window.addEventListener("load",startEvent,false);
4514 _loadSourceFiles: function(callback){
4515 var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
4516 function copy(o){
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);
4525 return _copy;
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){
4531 callback();
4532 }else{
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"]={
4541 loaded:false
4545 var currScript=-1;
4546 _blanket.utils.loadAll(function(test){
4547 if (test){
4548 return typeof scripts[currScript+1] !== 'undefined';
4550 currScript++;
4551 if (currScript >= scripts.length){
4552 return null;
4554 return scripts[currScript]+".js";
4555 },callback);
4558 beforeStartTestRunner: function(opts){
4559 opts = 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,
4565 function(){
4566 _blanket._loadSourceFiles(function() {
4568 var allLoaded = function(){
4569 return opts.condition ? opts.condition() : _blanket.requireFilesLoaded();
4571 var check = function() {
4572 if (allLoaded()) {
4573 if (_blanket.options("debug")) {console.log("BLANKET-All files loaded, init start test runner callback.");}
4574 var cb = _blanket.options("testReadyCallback");
4576 if (cb){
4577 if (typeof cb === "function"){
4578 cb(opts.callback);
4579 }else if (typeof cb === "string"){
4580 _blanket._addScript(cb);
4581 opts.callback();
4583 }else{
4584 opts.callback();
4586 } else {
4587 setTimeout(check, 13);
4590 check();
4593 }else{
4594 opts.callback();
4597 utils: {
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');
4601 a.href = url;
4602 return a.href;
4607 })(blanket);
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;}",
4611 successRate = 60,
4612 head = document.head,
4613 fileNumber = 0,
4614 body = document.body,
4615 headerContent,
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';
4627 } else {
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, "&amp;")
4650 .replace(/</g, "&lt;")
4651 .replace(/\>/g, "&gt;")
4652 .replace(/\"/g, "&quot;")
4653 .replace(/\'/g, "&apos;");
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'){
4661 return false;
4663 return data[mode].length > 0;
4666 var branchStack = [];
4668 function branchReport(colsIndex,src,cols,offset,lineNum){
4669 var newsrc="";
4670 var postfix="";
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();
4683 if (!cols){
4684 return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
4687 else if (!cols){
4688 return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
4690 else{
4691 postfix = "</span>";
4693 }else if (!cols){
4694 return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
4696 }else if(!cols){
4697 return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
4698 }else{
4699 postfix = "</span>";
4702 var thisline = cols[colsIndex];
4703 //consequent
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);
4710 }else{
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);
4719 newsrc += res.src;
4720 cols = res.cols;
4721 cols[colsIndex+1] = cols[colsIndex+2];
4722 cols.length--;
4723 }else{
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]);
4732 }else{
4733 newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset,alt.start.column-offset));
4734 style = "<span class='" + (isBranchFollowed(thisline,false) ? 'branchOkay' : 'branchWarning') + "'>";
4735 newsrc += style;
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);
4741 newsrc += res2.src;
4742 cols = res2.cols;
4743 cols[colsIndex+1] = cols[colsIndex+2];
4744 cols.length--;
4745 }else{
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));
4750 src = newsrc;
4753 return {src:src+postfix, cols:cols};
4756 var isUndefined = function(item){
4757 return typeof item !== 'undefined';
4760 var files = coverage.files;
4761 var totals = {
4762 totalSmts: 0,
4763 numberOfFilesCovered: 0,
4764 passedBranches: 0,
4765 totalBranches: 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)
4778 fileNumber++;
4780 var statsForFile = files[file],
4781 totalSmts = 0,
4782 numberOfFilesCovered = 0,
4783 code = [],
4787 var end = [];
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);
4797 var colsIndex=0;
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;
4804 }else{
4805 src = escapeInvalidXmlChars(src);
4807 }else{
4808 src = escapeInvalidXmlChars(src);
4810 var lineClass="";
4811 if(statsForFile[i+1]) {
4812 numberOfFilesCovered += 1;
4813 totalSmts += 1;
4814 lineClass = 'hit';
4815 }else{
4816 if(statsForFile[i+1] === 0){
4817 totalSmts++;
4818 lineClass = 'miss';
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'){
4832 totalBranches++;
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){
4837 passedBranches++;
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");
4882 } else {
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);
4932 }else{
4933 appendTag('div', body, bodyContent);
4935 //appendHtml(body, '</div>');
4938 (function(){
4939 var newOptions={};
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"){
4967 try{
4968 newOptions.reporter_options = JSON.parse(es.nodeValue);
4969 }catch(e){
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 = {};
5022 })();
5023 (function(_blanket){
5024 _blanket.extend({
5025 utils: {
5026 normalizeBackslashes: function(str) {
5027 return str.replace(/\\/g, '/');
5029 matchPatternAttribute: function(filename,pattern){
5030 if (typeof pattern === 'string'){
5031 if (pattern.indexOf("[") === 0){
5032 //treat as array
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);
5045 }else{
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");
5066 if(filter != null){
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));
5074 }).length === 1;
5076 }else{
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(
5082 function(sn){
5083 return sn.nodeName === "src";
5084 })[0].nodeValue).replace(".js","");
5086 if (!filter){
5087 _blanket.options("filter","['"+scriptNames.join("','")+"']");
5089 return scriptNames;
5091 loadAll: function(nextScript,cb,preprocessor){
5093 * load dependencies
5094 * @param {nextScript} factory for priority level
5095 * @param {cb} the done callback
5097 var currScript=nextScript();
5098 var isLoaded = _blanket.utils.scriptIsLoaded(
5099 currScript,
5100 _blanket.utils.ifOrdered,
5101 nextScript,
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.");}
5108 isLoaded();
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);
5116 attach();
5119 _blanket.utils.attachScript(
5121 url: currScript
5123 function (content){
5124 _blanket.utils.processFile(
5125 content,
5126 currScript,
5127 whenDone,
5128 whenDone
5132 }else{
5133 isLoaded();
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");
5142 },timeout);
5143 _blanket.utils.getFile(
5144 options.url,
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);
5156 if (currScript){
5157 _blanket.utils.loadAll(nextScript,cb);
5158 }else{
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");}
5170 return 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");}
5177 cb();
5178 }else if (orderedCb){
5179 //if it's ordered we need to
5180 //traverse down to the next
5181 //priority level
5182 if (_blanket.options("debug")) {console.log("BLANKET-Load next file.");}
5183 orderedCb(nextScript,cb);
5187 cache: {},
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){
5195 return false;
5198 return true;
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)
5207 oldCb(content);
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({
5213 inputFile: content,
5214 inputFileName: url
5215 },function(instrumented){
5216 try{
5217 if (_blanket.options("debug")) {console.log("BLANKET-instrument of:"+url+" was successfull.");}
5218 _blanket.utils.blanketEval(instrumented);
5219 cb();
5220 _blanket.requiringFile(url,true);
5222 catch(err){
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
5228 //missed.
5229 if (_blanket.options("debug")) { console.log("BLANKET-There was an error loading the file:"+url); }
5230 cb(content);
5231 _blanket.requiringFile(url,true);
5232 }else{
5233 throw new Error("Error parsing instrumented code: "+err);
5237 }else{
5238 if (_blanket.options("debug")) { console.log("BLANKET-Loading (without instrumenting) the file:"+url);}
5239 oldCb(content);
5240 _blanket.requiringFile(url,true);
5244 createXhr: function(){
5245 var xhr, i, progId;
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];
5251 try {
5252 xhr = new ActiveXObject(progId);
5253 } catch (e) {}
5255 if (xhr) {
5256 progIds = [progId]; // so faster next time
5257 break;
5262 return xhr;
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++ ){
5269 var key = files[i];
5270 if (url.indexOf(key) > -1){
5271 callback(_blanket.blanketSession[key]);
5272 foundInSession=true;
5273 return;
5277 if (!foundInSession){
5278 var xhr = _blanket.utils.createXhr();
5279 xhr.open('GET', url, true);
5281 //Allow overrides specified in config
5282 if (onXhr) {
5283 onXhr(xhr, url);
5286 xhr.onreadystatechange = function (evt) {
5287 var status, err;
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) /*||
5294 (status === 0 &&
5295 navigator.userAgent.toLowerCase().indexOf('firefox') > -1)
5296 */ ) {
5297 //An http 4xx or 5xx error. Signal an error.
5298 err = new Error(url + ' HTTP status: ' + status);
5299 err.xhr = xhr;
5300 errback(err);
5301 } else {
5302 callback(xhr.responseText);
5306 try{
5307 xhr.send(null);
5308 }catch(e){
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();
5312 } else {
5313 throw e;
5321 (function(){
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,
5331 function(content){
5332 _blanket.utils.processFile(
5333 content,
5334 url,
5335 function newLoader(){
5336 context.completeLoad(moduleName);
5338 function oldLoader(){
5339 _blanket.utils.oldloader(context, moduleName, url);
5342 }, function (err) {
5343 _blanket.requiringFile();
5344 throw err;
5348 })();
5350 })(blanket);