Skip to content

Instantly share code, notes, and snippets.

@sglanzer-deprecated
Created November 19, 2014 16:47
Show Gist options
  • Save sglanzer-deprecated/d92d164443872f69d8e9 to your computer and use it in GitHub Desktop.
Save sglanzer-deprecated/d92d164443872f69d8e9 to your computer and use it in GitHub Desktop.
Testem with blanket code coverage on AMD modules
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JSHintConfiguration" version="2.5.1" use-config-file="true">
<option asi="false" />
<option bitwise="true" />
<option boss="false" />
<option browser="true" />
<option camelcase="false" />
<option couch="false" />
<option curly="true" />
<option debug="false" />
<option devel="false" />
<option dojo="false" />
<option eqeqeq="true" />
<option eqnull="false" />
<option es3="false" />
<option esnext="false" />
<option evil="false" />
<option expr="false" />
<option forin="true" />
<option freeze="false" />
<option funcscope="false" />
<option gcl="false" />
<option globalstrict="false" />
<option immed="false" />
<option iterator="false" />
<option jquery="false" />
<option lastsemic="false" />
<option latedef="false" />
<option laxbreak="false" />
<option laxcomma="false" />
<option loopfunc="false" />
<option maxerr="50" />
<option mootools="false" />
<option moz="false" />
<option multistr="false" />
<option newcap="false" />
<option noarg="true" />
<option node="false" />
<option noempty="true" />
<option nomen="false" />
<option nonbsp="false" />
<option nonew="true" />
<option nonstandard="false" />
<option notypeof="false" />
<option noyield="false" />
<option onevar="false" />
<option passfail="false" />
<option phantom="false" />
<option plusplus="false" />
<option proto="false" />
<option prototypejs="false" />
<option quotmark="false" />
<option rhino="false" />
<option scripturl="false" />
<option shadow="false" />
<option smarttabs="false" />
<option strict="true" />
<option sub="false" />
<option supernew="false" />
<option trailing="false" />
<option undef="true" />
<option unused="false" />
<option validthis="false" />
<option white="false" />
<option worker="false" />
<option wsh="false" />
<option yui="false" />
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
</expanded-state>
<selected-state>
<State>
<id>CoffeeScript</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectRootManager" version="2" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/testem.iml" filepath="$PROJECT_DIR$/.idea/testem.iml" />
</modules>
</component>
</project>
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Perforce" />
</component>
</project>
define(function () {
if (true) {
return {
version: 8
};
}
else {
return { version: 9 };
}
});
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" href="vendor/qunit.css"/>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script type="text/javascript" src="vendor/testem.js"></script>
<script type="text/javascript" src="vendor/qunit.js"></script>
<script type="text/javascript" src="vendor/require.js"></script>
<script type="text/javascript" src="vendor/blanket.js" data-cover-only="sample" data-cover-never="-test.js"></script>
<script>
QUnit.config.autostart = false;
require({
baseUrl: "./"
}, [
"tests/sample-test.js"
], function () {
QUnit.start();
});
</script>
</body>
</html>
{
"framework": "qunit",
"test_page": "runner.html",
"launch_in_dev": ["Firefox"],
"launch_in_ci": ["Firefox"]
}
define(['../app/sample'],
function (sample) {
module("sample");
test("will return correct version from sample", function () {
var version = sample.version;
equal(version, 8);
});
});
/* globals requirejs, require */
require(['../base.qunit.test.js']);
/*! blanket - v1.1.5 */
if (typeof QUnit !== 'undefined'){ QUnit.config.autostart = false; }
(function(define){
/*
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint bitwise:true plusplus:true */
/*global esprima:true, define:true, exports:true, window: true,
throwError: true, createLiteral: true, generateStatement: true,
parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
parseFunctionDeclaration: true, parseFunctionExpression: true,
parseFunctionSourceElements: true, parseVariableIdentifier: true,
parseLeftHandSideExpression: true,
parseStatement: true, parseSourceElement: true */
(function (root, factory) {
'use strict';
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// Rhino, and plain browser loading.
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.esprima = {}));
}
}(this, function (exports) {
'use strict';
var Token,
TokenName,
Syntax,
PropertyKind,
Messages,
Regex,
source,
strict,
index,
lineNumber,
lineStart,
length,
buffer,
state,
extra;
Token = {
BooleanLiteral: 1,
EOF: 2,
Identifier: 3,
Keyword: 4,
NullLiteral: 5,
NumericLiteral: 6,
Punctuator: 7,
StringLiteral: 8
};
TokenName = {};
TokenName[Token.BooleanLiteral] = 'Boolean';
TokenName[Token.EOF] = '<end>';
TokenName[Token.Identifier] = 'Identifier';
TokenName[Token.Keyword] = 'Keyword';
TokenName[Token.NullLiteral] = 'Null';
TokenName[Token.NumericLiteral] = 'Numeric';
TokenName[Token.Punctuator] = 'Punctuator';
TokenName[Token.StringLiteral] = 'String';
Syntax = {
AssignmentExpression: 'AssignmentExpression',
ArrayExpression: 'ArrayExpression',
BlockStatement: 'BlockStatement',
BinaryExpression: 'BinaryExpression',
BreakStatement: 'BreakStatement',
CallExpression: 'CallExpression',
CatchClause: 'CatchClause',
ConditionalExpression: 'ConditionalExpression',
ContinueStatement: 'ContinueStatement',
DoWhileStatement: 'DoWhileStatement',
DebuggerStatement: 'DebuggerStatement',
EmptyStatement: 'EmptyStatement',
ExpressionStatement: 'ExpressionStatement',
ForStatement: 'ForStatement',
ForInStatement: 'ForInStatement',
FunctionDeclaration: 'FunctionDeclaration',
FunctionExpression: 'FunctionExpression',
Identifier: 'Identifier',
IfStatement: 'IfStatement',
Literal: 'Literal',
LabeledStatement: 'LabeledStatement',
LogicalExpression: 'LogicalExpression',
MemberExpression: 'MemberExpression',
NewExpression: 'NewExpression',
ObjectExpression: 'ObjectExpression',
Program: 'Program',
Property: 'Property',
ReturnStatement: 'ReturnStatement',
SequenceExpression: 'SequenceExpression',
SwitchStatement: 'SwitchStatement',
SwitchCase: 'SwitchCase',
ThisExpression: 'ThisExpression',
ThrowStatement: 'ThrowStatement',
TryStatement: 'TryStatement',
UnaryExpression: 'UnaryExpression',
UpdateExpression: 'UpdateExpression',
VariableDeclaration: 'VariableDeclaration',
VariableDeclarator: 'VariableDeclarator',
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement'
};
PropertyKind = {
Data: 1,
Get: 2,
Set: 4
};
// Error messages should be identical to V8.
Messages = {
UnexpectedToken: 'Unexpected token %0',
UnexpectedNumber: 'Unexpected number',
UnexpectedString: 'Unexpected string',
UnexpectedIdentifier: 'Unexpected identifier',
UnexpectedReserved: 'Unexpected reserved word',
UnexpectedEOS: 'Unexpected end of input',
NewlineAfterThrow: 'Illegal newline after throw',
InvalidRegExp: 'Invalid regular expression',
UnterminatedRegExp: 'Invalid regular expression: missing /',
InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
InvalidLHSInForIn: 'Invalid left-hand side in for-in',
MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
NoCatchOrFinally: 'Missing catch or finally after try',
UnknownLabel: 'Undefined label \'%0\'',
Redeclaration: '%0 \'%1\' has already been declared',
IllegalContinue: 'Illegal continue statement',
IllegalBreak: 'Illegal break statement',
IllegalReturn: 'Illegal return statement',
StrictModeWith: 'Strict mode code may not include a with statement',
StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
StrictVarName: 'Variable name may not be eval or arguments in strict mode',
StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
StrictDelete: 'Delete of an unqualified identifier in strict mode.',
StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
StrictReservedWord: 'Use of future reserved word in strict mode'
};
// See also tools/generate-unicode-regex.py.
Regex = {
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]'),
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]')
};
// Ensure the condition is true, otherwise throw an error.
// This is only to have a better contract semantic, i.e. another safety net
// to catch a logic error. The condition shall be fulfilled in normal case.
// Do NOT use this to enforce a certain condition on any user input.
function assert(condition, message) {
if (!condition) {
throw new Error('ASSERT: ' + message);
}
}
function sliceSource(from, to) {
return source.slice(from, to);
}
if (typeof 'esprima'[0] === 'undefined') {
sliceSource = function sliceArraySource(from, to) {
return source.slice(from, to).join('');
};
}
function isDecimalDigit(ch) {
return '0123456789'.indexOf(ch) >= 0;
}
function isHexDigit(ch) {
return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
}
function isOctalDigit(ch) {
return '01234567'.indexOf(ch) >= 0;
}
// 7.2 White Space
function isWhiteSpace(ch) {
return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
(ch === '\u000C') || (ch === '\u00A0') ||
(ch.charCodeAt(0) >= 0x1680 &&
'\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
}
// 7.3 Line Terminators
function isLineTerminator(ch) {
return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029');
}
// 7.6 Identifier Names and Identifiers
function isIdentifierStart(ch) {
return (ch === '$') || (ch === '_') || (ch === '\\') ||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
}
function isIdentifierPart(ch) {
return (ch === '$') || (ch === '_') || (ch === '\\') ||
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
((ch >= '0') && (ch <= '9')) ||
((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
}
// 7.6.1.2 Future Reserved Words
function isFutureReservedWord(id) {
switch (id) {
// Future reserved words.
case 'class':
case 'enum':
case 'export':
case 'extends':
case 'import':
case 'super':
return true;
}
return false;
}
function isStrictModeReservedWord(id) {
switch (id) {
// Strict Mode reserved words.
case 'implements':
case 'interface':
case 'package':
case 'private':
case 'protected':
case 'public':
case 'static':
case 'yield':
case 'let':
return true;
}
return false;
}
function isRestrictedWord(id) {
return id === 'eval' || id === 'arguments';
}
// 7.6.1.1 Keywords
function isKeyword(id) {
var keyword = false;
switch (id.length) {
case 2:
keyword = (id === 'if') || (id === 'in') || (id === 'do');
break;
case 3:
keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
break;
case 4:
keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with');
break;
case 5:
keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw');
break;
case 6:
keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch');
break;
case 7:
keyword = (id === 'default') || (id === 'finally');
break;
case 8:
keyword = (id === 'function') || (id === 'continue') || (id === 'debugger');
break;
case 10:
keyword = (id === 'instanceof');
break;
}
if (keyword) {
return true;
}
switch (id) {
// Future reserved words.
// 'const' is specialized as Keyword in V8.
case 'const':
return true;
// For compatiblity to SpiderMonkey and ES.next
case 'yield':
case 'let':
return true;
}
if (strict && isStrictModeReservedWord(id)) {
return true;
}
return isFutureReservedWord(id);
}
// 7.4 Comments
function skipComment() {
var ch, blockComment, lineComment;
blockComment = false;
lineComment = false;
while (index < length) {
ch = source[index];
if (lineComment) {
ch = source[index++];
if (isLineTerminator(ch)) {
lineComment = false;
if (ch === '\r' && source[index] === '\n') {
++index;
}
++lineNumber;
lineStart = index;
}
} else if (blockComment) {
if (isLineTerminator(ch)) {
if (ch === '\r' && source[index + 1] === '\n') {
++index;
}
++lineNumber;
++index;
lineStart = index;
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
ch = source[index++];
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
if (ch === '*') {
ch = source[index];
if (ch === '/') {
++index;
blockComment = false;
}
}
}
} else if (ch === '/') {
ch = source[index + 1];
if (ch === '/') {
index += 2;
lineComment = true;
} else if (ch === '*') {
index += 2;
blockComment = true;
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
break;
}
} else if (isWhiteSpace(ch)) {
++index;
} else if (isLineTerminator(ch)) {
++index;
if (ch === '\r' && source[index] === '\n') {
++index;
}
++lineNumber;
lineStart = index;
} else {
break;
}
}
}
function scanHexEscape(prefix) {
var i, len, ch, code = 0;
len = (prefix === 'u') ? 4 : 2;
for (i = 0; i < len; ++i) {
if (index < length && isHexDigit(source[index])) {
ch = source[index++];
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
} else {
return '';
}
}
return String.fromCharCode(code);
}
function scanIdentifier() {
var ch, start, id, restore;
ch = source[index];
if (!isIdentifierStart(ch)) {
return;
}
start = index;
if (ch === '\\') {
++index;
if (source[index] !== 'u') {
return;
}
++index;
restore = index;
ch = scanHexEscape('u');
if (ch) {
if (ch === '\\' || !isIdentifierStart(ch)) {
return;
}
id = ch;
} else {
index = restore;
id = 'u';
}
} else {
id = source[index++];
}
while (index < length) {
ch = source[index];
if (!isIdentifierPart(ch)) {
break;
}
if (ch === '\\') {
++index;
if (source[index] !== 'u') {
return;
}
++index;
restore = index;
ch = scanHexEscape('u');
if (ch) {
if (ch === '\\' || !isIdentifierPart(ch)) {
return;
}
id += ch;
} else {
index = restore;
id += 'u';
}
} else {
id += source[index++];
}
}
// There is no keyword or literal with only one character.
// Thus, it must be an identifier.
if (id.length === 1) {
return {
type: Token.Identifier,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
if (isKeyword(id)) {
return {
type: Token.Keyword,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// 7.8.1 Null Literals
if (id === 'null') {
return {
type: Token.NullLiteral,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// 7.8.2 Boolean Literals
if (id === 'true' || id === 'false') {
return {
type: Token.BooleanLiteral,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
return {
type: Token.Identifier,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// 7.7 Punctuators
function scanPunctuator() {
var start = index,
ch1 = source[index],
ch2,
ch3,
ch4;
// Check for most common single-character punctuators.
if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
++index;
return {
type: Token.Punctuator,
value: ch1,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
++index;
return {
type: Token.Punctuator,
value: ch1,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// Dot (.) can also start a floating-point number, hence the need
// to check the next character.
ch2 = source[index + 1];
if (ch1 === '.' && !isDecimalDigit(ch2)) {
return {
type: Token.Punctuator,
value: source[index++],
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// Peek more characters.
ch3 = source[index + 2];
ch4 = source[index + 3];
// 4-character punctuator: >>>=
if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
if (ch4 === '=') {
index += 4;
return {
type: Token.Punctuator,
value: '>>>=',
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
}
// 3-character punctuators: === !== >>> <<= >>=
if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
index += 3;
return {
type: Token.Punctuator,
value: '===',
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
index += 3;
return {
type: Token.Punctuator,
value: '!==',
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
index += 3;
return {
type: Token.Punctuator,
value: '>>>',
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
index += 3;
return {
type: Token.Punctuator,
value: '<<=',
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
index += 3;
return {
type: Token.Punctuator,
value: '>>=',
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// 2-character punctuators: <= >= == != ++ -- << >> && ||
// += -= *= %= &= |= ^= /=
if (ch2 === '=') {
if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
index += 2;
return {
type: Token.Punctuator,
value: ch1 + ch2,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
}
if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
if ('+-<>&|'.indexOf(ch2) >= 0) {
index += 2;
return {
type: Token.Punctuator,
value: ch1 + ch2,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
}
// The remaining 1-character punctuators.
if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
return {
type: Token.Punctuator,
value: source[index++],
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
}
// 7.8.3 Numeric Literals
function scanNumericLiteral() {
var number, start, ch;
ch = source[index];
assert(isDecimalDigit(ch) || (ch === '.'),
'Numeric literal must start with a decimal digit or a decimal point');
start = index;
number = '';
if (ch !== '.') {
number = source[index++];
ch = source[index];
// Hex number starts with '0x'.
// Octal number starts with '0'.
if (number === '0') {
if (ch === 'x' || ch === 'X') {
number += source[index++];
while (index < length) {
ch = source[index];
if (!isHexDigit(ch)) {
break;
}
number += source[index++];
}
if (number.length <= 2) {
// only 0x
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
if (index < length) {
ch = source[index];
if (isIdentifierStart(ch)) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
return {
type: Token.NumericLiteral,
value: parseInt(number, 16),
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
} else if (isOctalDigit(ch)) {
number += source[index++];
while (index < length) {
ch = source[index];
if (!isOctalDigit(ch)) {
break;
}
number += source[index++];
}
if (index < length) {
ch = source[index];
if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
return {
type: Token.NumericLiteral,
value: parseInt(number, 8),
octal: true,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// decimal number starts with '0' such as '09' is illegal.
if (isDecimalDigit(ch)) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
number += source[index++];
}
}
if (ch === '.') {
number += source[index++];
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
number += source[index++];
}
}
if (ch === 'e' || ch === 'E') {
number += source[index++];
ch = source[index];
if (ch === '+' || ch === '-') {
number += source[index++];
}
ch = source[index];
if (isDecimalDigit(ch)) {
number += source[index++];
while (index < length) {
ch = source[index];
if (!isDecimalDigit(ch)) {
break;
}
number += source[index++];
}
} else {
ch = 'character ' + ch;
if (index >= length) {
ch = '<end>';
}
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
if (index < length) {
ch = source[index];
if (isIdentifierStart(ch)) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
}
return {
type: Token.NumericLiteral,
value: parseFloat(number),
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
// 7.8.4 String Literals
function scanStringLiteral() {
var str = '', quote, start, ch, code, unescaped, restore, octal = false;
quote = source[index];
assert((quote === '\'' || quote === '"'),
'String literal must starts with a quote');
start = index;
++index;
while (index < length) {
ch = source[index++];
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
ch = source[index++];
if (!isLineTerminator(ch)) {
switch (ch) {
case 'n':
str += '\n';
break;
case 'r':
str += '\r';
break;
case 't':
str += '\t';
break;
case 'u':
case 'x':
restore = index;
unescaped = scanHexEscape(ch);
if (unescaped) {
str += unescaped;
} else {
index = restore;
str += ch;
}
break;
case 'b':
str += '\b';
break;
case 'f':
str += '\f';
break;
case 'v':
str += '\x0B';
break;
default:
if (isOctalDigit(ch)) {
code = '01234567'.indexOf(ch);
// \0 is not octal escape sequence
if (code !== 0) {
octal = true;
}
if (index < length && isOctalDigit(source[index])) {
octal = true;
code = code * 8 + '01234567'.indexOf(source[index++]);
// 3 digits are only allowed when string starts
// with 0, 1, 2, 3
if ('0123'.indexOf(ch) >= 0 &&
index < length &&
isOctalDigit(source[index])) {
code = code * 8 + '01234567'.indexOf(source[index++]);
}
}
str += String.fromCharCode(code);
} else {
str += ch;
}
break;
}
} else {
++lineNumber;
if (ch === '\r' && source[index] === '\n') {
++index;
}
}
} else if (isLineTerminator(ch)) {
break;
} else {
str += ch;
}
}
if (quote !== '') {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
return {
type: Token.StringLiteral,
value: str,
octal: octal,
lineNumber: lineNumber,
lineStart: lineStart,
range: [start, index]
};
}
function scanRegExp() {
var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false;
buffer = null;
skipComment();
start = index;
ch = source[index];
assert(ch === '/', 'Regular expression literal must start with a slash');
str = source[index++];
while (index < length) {
ch = source[index++];
str += ch;
if (ch === '\\') {
ch = source[index++];
// ECMA-262 7.8.5
if (isLineTerminator(ch)) {
throwError({}, Messages.UnterminatedRegExp);
}
str += ch;
} else if (classMarker) {
if (ch === ']') {
classMarker = false;
}
} else {
if (ch === '/') {
terminated = true;
break;
} else if (ch === '[') {
classMarker = true;
} else if (isLineTerminator(ch)) {
throwError({}, Messages.UnterminatedRegExp);
}
}
}
if (!terminated) {
throwError({}, Messages.UnterminatedRegExp);
}
// Exclude leading and trailing slash.
pattern = str.substr(1, str.length - 2);
flags = '';
while (index < length) {
ch = source[index];
if (!isIdentifierPart(ch)) {
break;
}
++index;
if (ch === '\\' && index < length) {
ch = source[index];
if (ch === 'u') {
++index;
restore = index;
ch = scanHexEscape('u');
if (ch) {
flags += ch;
str += '\\u';
for (; restore < index; ++restore) {
str += source[restore];
}
} else {
index = restore;
flags += 'u';
str += '\\u';
}
} else {
str += '\\';
}
} else {
flags += ch;
str += ch;
}
}
try {
value = new RegExp(pattern, flags);
} catch (e) {
throwError({}, Messages.InvalidRegExp);
}
return {
literal: str,
value: value,
range: [start, index]
};
}
function isIdentifierName(token) {
return token.type === Token.Identifier ||
token.type === Token.Keyword ||
token.type === Token.BooleanLiteral ||
token.type === Token.NullLiteral;
}
function advance() {
var ch, token;
skipComment();
if (index >= length) {
return {
type: Token.EOF,
lineNumber: lineNumber,
lineStart: lineStart,
range: [index, index]
};
}
token = scanPunctuator();
if (typeof token !== 'undefined') {
return token;
}
ch = source[index];
if (ch === '\'' || ch === '"') {
return scanStringLiteral();
}
if (ch === '.' || isDecimalDigit(ch)) {
return scanNumericLiteral();
}
token = scanIdentifier();
if (typeof token !== 'undefined') {
return token;
}
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
function lex() {
var token;
if (buffer) {
index = buffer.range[1];
lineNumber = buffer.lineNumber;
lineStart = buffer.lineStart;
token = buffer;
buffer = null;
return token;
}
buffer = null;
return advance();
}
function lookahead() {
var pos, line, start;
if (buffer !== null) {
return buffer;
}
pos = index;
line = lineNumber;
start = lineStart;
buffer = advance();
index = pos;
lineNumber = line;
lineStart = start;
return buffer;
}
// Return true if there is a line terminator before the next token.
function peekLineTerminator() {
var pos, line, start, found;
pos = index;
line = lineNumber;
start = lineStart;
skipComment();
found = lineNumber !== line;
index = pos;
lineNumber = line;
lineStart = start;
return found;
}
// Throw an exception
function throwError(token, messageFormat) {
var error,
args = Array.prototype.slice.call(arguments, 2),
msg = messageFormat.replace(
/%(\d)/g,
function (whole, index) {
return args[index] || '';
}
);
if (typeof token.lineNumber === 'number') {
error = new Error('Line ' + token.lineNumber + ': ' + msg);
error.index = token.range[0];
error.lineNumber = token.lineNumber;
error.column = token.range[0] - lineStart + 1;
} else {
error = new Error('Line ' + lineNumber + ': ' + msg);
error.index = index;
error.lineNumber = lineNumber;
error.column = index - lineStart + 1;
}
throw error;
}
function throwErrorTolerant() {
try {
throwError.apply(null, arguments);
} catch (e) {
if (extra.errors) {
extra.errors.push(e);
} else {
throw e;
}
}
}
// Throw an exception because of the token.
function throwUnexpected(token) {
if (token.type === Token.EOF) {
throwError(token, Messages.UnexpectedEOS);
}
if (token.type === Token.NumericLiteral) {
throwError(token, Messages.UnexpectedNumber);
}
if (token.type === Token.StringLiteral) {
throwError(token, Messages.UnexpectedString);
}
if (token.type === Token.Identifier) {
throwError(token, Messages.UnexpectedIdentifier);
}
if (token.type === Token.Keyword) {
if (isFutureReservedWord(token.value)) {
throwError(token, Messages.UnexpectedReserved);
} else if (strict && isStrictModeReservedWord(token.value)) {
throwErrorTolerant(token, Messages.StrictReservedWord);
return;
}
throwError(token, Messages.UnexpectedToken, token.value);
}
// BooleanLiteral, NullLiteral, or Punctuator.
throwError(token, Messages.UnexpectedToken, token.value);
}
// Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
function expect(value) {
var token = lex();
if (token.type !== Token.Punctuator || token.value !== value) {
throwUnexpected(token);
}
}
// Expect the next token to match the specified keyword.
// If not, an exception will be thrown.
function expectKeyword(keyword) {
var token = lex();
if (token.type !== Token.Keyword || token.value !== keyword) {
throwUnexpected(token);
}
}
// Return true if the next token matches the specified punctuator.
function match(value) {
var token = lookahead();
return token.type === Token.Punctuator && token.value === value;
}
// Return true if the next token matches the specified keyword
function matchKeyword(keyword) {
var token = lookahead();
return token.type === Token.Keyword && token.value === keyword;
}
// Return true if the next token is an assignment operator
function matchAssign() {
var token = lookahead(),
op = token.value;
if (token.type !== Token.Punctuator) {
return false;
}
return op === '=' ||
op === '*=' ||
op === '/=' ||
op === '%=' ||
op === '+=' ||
op === '-=' ||
op === '<<=' ||
op === '>>=' ||
op === '>>>=' ||
op === '&=' ||
op === '^=' ||
op === '|=';
}
function consumeSemicolon() {
var token, line;
// Catch the very common case first.
if (source[index] === ';') {
lex();
return;
}
line = lineNumber;
skipComment();
if (lineNumber !== line) {
return;
}
if (match(';')) {
lex();
return;
}
token = lookahead();
if (token.type !== Token.EOF && !match('}')) {
throwUnexpected(token);
}
}
// Return true if provided expression is LeftHandSideExpression
function isLeftHandSide(expr) {
return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
}
// 11.1.4 Array Initialiser
function parseArrayInitialiser() {
var elements = [];
expect('[');
while (!match(']')) {
if (match(',')) {
lex();
elements.push(null);
} else {
elements.push(parseAssignmentExpression());
if (!match(']')) {
expect(',');
}
}
}
expect(']');
return {
type: Syntax.ArrayExpression,
elements: elements
};
}
// 11.1.5 Object Initialiser
function parsePropertyFunction(param, first) {
var previousStrict, body;
previousStrict = strict;
body = parseFunctionSourceElements();
if (first && strict && isRestrictedWord(param[0].name)) {
throwErrorTolerant(first, Messages.StrictParamName);
}
strict = previousStrict;
return {
type: Syntax.FunctionExpression,
id: null,
params: param,
defaults: [],
body: body,
rest: null,
generator: false,
expression: false
};
}
function parseObjectPropertyKey() {
var token = lex();
// Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.
if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
if (strict && token.octal) {
throwErrorTolerant(token, Messages.StrictOctalLiteral);
}
return createLiteral(token);
}
return {
type: Syntax.Identifier,
name: token.value
};
}
function parseObjectProperty() {
var token, key, id, param;
token = lookahead();
if (token.type === Token.Identifier) {
id = parseObjectPropertyKey();
// Property Assignment: Getter and Setter.
if (token.value === 'get' && !match(':')) {
key = parseObjectPropertyKey();
expect('(');
expect(')');
return {
type: Syntax.Property,
key: key,
value: parsePropertyFunction([]),
kind: 'get'
};
} else if (token.value === 'set' && !match(':')) {
key = parseObjectPropertyKey();
expect('(');
token = lookahead();
if (token.type !== Token.Identifier) {
expect(')');
throwErrorTolerant(token, Messages.UnexpectedToken, token.value);
return {
type: Syntax.Property,
key: key,
value: parsePropertyFunction([]),
kind: 'set'
};
} else {
param = [ parseVariableIdentifier() ];
expect(')');
return {
type: Syntax.Property,
key: key,
value: parsePropertyFunction(param, token),
kind: 'set'
};
}
} else {
expect(':');
return {
type: Syntax.Property,
key: id,
value: parseAssignmentExpression(),
kind: 'init'
};
}
} else if (token.type === Token.EOF || token.type === Token.Punctuator) {
throwUnexpected(token);
} else {
key = parseObjectPropertyKey();
expect(':');
return {
type: Syntax.Property,
key: key,
value: parseAssignmentExpression(),
kind: 'init'
};
}
}
function parseObjectInitialiser() {
var properties = [], property, name, kind, map = {}, toString = String;
expect('{');
while (!match('}')) {
property = parseObjectProperty();
if (property.key.type === Syntax.Identifier) {
name = property.key.name;
} else {
name = toString(property.key.value);
}
kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
if (Object.prototype.hasOwnProperty.call(map, name)) {
if (map[name] === PropertyKind.Data) {
if (strict && kind === PropertyKind.Data) {
throwErrorTolerant({}, Messages.StrictDuplicateProperty);
} else if (kind !== PropertyKind.Data) {
throwErrorTolerant({}, Messages.AccessorDataProperty);
}
} else {
if (kind === PropertyKind.Data) {
throwErrorTolerant({}, Messages.AccessorDataProperty);
} else if (map[name] & kind) {
throwErrorTolerant({}, Messages.AccessorGetSet);
}
}
map[name] |= kind;
} else {
map[name] = kind;
}
properties.push(property);
if (!match('}')) {
expect(',');
}
}
expect('}');
return {
type: Syntax.ObjectExpression,
properties: properties
};
}
// 11.1.6 The Grouping Operator
function parseGroupExpression() {
var expr;
expect('(');
expr = parseExpression();
expect(')');
return expr;
}
// 11.1 Primary Expressions
function parsePrimaryExpression() {
var token = lookahead(),
type = token.type;
if (type === Token.Identifier) {
return {
type: Syntax.Identifier,
name: lex().value
};
}
if (type === Token.StringLiteral || type === Token.NumericLiteral) {
if (strict && token.octal) {
throwErrorTolerant(token, Messages.StrictOctalLiteral);
}
return createLiteral(lex());
}
if (type === Token.Keyword) {
if (matchKeyword('this')) {
lex();
return {
type: Syntax.ThisExpression
};
}
if (matchKeyword('function')) {
return parseFunctionExpression();
}
}
if (type === Token.BooleanLiteral) {
lex();
token.value = (token.value === 'true');
return createLiteral(token);
}
if (type === Token.NullLiteral) {
lex();
token.value = null;
return createLiteral(token);
}
if (match('[')) {
return parseArrayInitialiser();
}
if (match('{')) {
return parseObjectInitialiser();
}
if (match('(')) {
return parseGroupExpression();
}
if (match('/') || match('/=')) {
return createLiteral(scanRegExp());
}
return throwUnexpected(lex());
}
// 11.2 Left-Hand-Side Expressions
function parseArguments() {
var args = [];
expect('(');
if (!match(')')) {
while (index < length) {
args.push(parseAssignmentExpression());
if (match(')')) {
break;
}
expect(',');
}
}
expect(')');
return args;
}
function parseNonComputedProperty() {
var token = lex();
if (!isIdentifierName(token)) {
throwUnexpected(token);
}
return {
type: Syntax.Identifier,
name: token.value
};
}
function parseNonComputedMember() {
expect('.');
return parseNonComputedProperty();
}
function parseComputedMember() {
var expr;
expect('[');
expr = parseExpression();
expect(']');
return expr;
}
function parseNewExpression() {
var expr;
expectKeyword('new');
expr = {
type: Syntax.NewExpression,
callee: parseLeftHandSideExpression(),
'arguments': []
};
if (match('(')) {
expr['arguments'] = parseArguments();
}
return expr;
}
function parseLeftHandSideExpressionAllowCall() {
var expr;
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[') || match('(')) {
if (match('(')) {
expr = {
type: Syntax.CallExpression,
callee: expr,
'arguments': parseArguments()
};
} else if (match('[')) {
expr = {
type: Syntax.MemberExpression,
computed: true,
object: expr,
property: parseComputedMember()
};
} else {
expr = {
type: Syntax.MemberExpression,
computed: false,
object: expr,
property: parseNonComputedMember()
};
}
}
return expr;
}
function parseLeftHandSideExpression() {
var expr;
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[')) {
if (match('[')) {
expr = {
type: Syntax.MemberExpression,
computed: true,
object: expr,
property: parseComputedMember()
};
} else {
expr = {
type: Syntax.MemberExpression,
computed: false,
object: expr,
property: parseNonComputedMember()
};
}
}
return expr;
}
// 11.3 Postfix Expressions
function parsePostfixExpression() {
var expr = parseLeftHandSideExpressionAllowCall(), token;
token = lookahead();
if (token.type !== Token.Punctuator) {
return expr;
}
if ((match('++') || match('--')) && !peekLineTerminator()) {
// 11.3.1, 11.3.2
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
throwErrorTolerant({}, Messages.StrictLHSPostfix);
}
if (!isLeftHandSide(expr)) {
throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
}
expr = {
type: Syntax.UpdateExpression,
operator: lex().value,
argument: expr,
prefix: false
};
}
return expr;
}
// 11.4 Unary Operators
function parseUnaryExpression() {
var token, expr;
token = lookahead();
if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
return parsePostfixExpression();
}
if (match('++') || match('--')) {
token = lex();
expr = parseUnaryExpression();
// 11.4.4, 11.4.5
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
throwErrorTolerant({}, Messages.StrictLHSPrefix);
}
if (!isLeftHandSide(expr)) {
throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
}
expr = {
type: Syntax.UpdateExpression,
operator: token.value,
argument: expr,
prefix: true
};
return expr;
}
if (match('+') || match('-') || match('~') || match('!')) {
expr = {
type: Syntax.UnaryExpression,
operator: lex().value,
argument: parseUnaryExpression(),
prefix: true
};
return expr;
}
if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
expr = {
type: Syntax.UnaryExpression,
operator: lex().value,
argument: parseUnaryExpression(),
prefix: true
};
if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
throwErrorTolerant({}, Messages.StrictDelete);
}
return expr;
}
return parsePostfixExpression();
}
// 11.5 Multiplicative Operators
function parseMultiplicativeExpression() {
var expr = parseUnaryExpression();
while (match('*') || match('/') || match('%')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseUnaryExpression()
};
}
return expr;
}
// 11.6 Additive Operators
function parseAdditiveExpression() {
var expr = parseMultiplicativeExpression();
while (match('+') || match('-')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseMultiplicativeExpression()
};
}
return expr;
}
// 11.7 Bitwise Shift Operators
function parseShiftExpression() {
var expr = parseAdditiveExpression();
while (match('<<') || match('>>') || match('>>>')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseAdditiveExpression()
};
}
return expr;
}
// 11.8 Relational Operators
function parseRelationalExpression() {
var expr, previousAllowIn;
previousAllowIn = state.allowIn;
state.allowIn = true;
expr = parseShiftExpression();
while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseShiftExpression()
};
}
state.allowIn = previousAllowIn;
return expr;
}
// 11.9 Equality Operators
function parseEqualityExpression() {
var expr = parseRelationalExpression();
while (match('==') || match('!=') || match('===') || match('!==')) {
expr = {
type: Syntax.BinaryExpression,
operator: lex().value,
left: expr,
right: parseRelationalExpression()
};
}
return expr;
}
// 11.10 Binary Bitwise Operators
function parseBitwiseANDExpression() {
var expr = parseEqualityExpression();
while (match('&')) {
lex();
expr = {
type: Syntax.BinaryExpression,
operator: '&',
left: expr,
right: parseEqualityExpression()
};
}
return expr;
}
function parseBitwiseXORExpression() {
var expr = parseBitwiseANDExpression();
while (match('^')) {
lex();
expr = {
type: Syntax.BinaryExpression,
operator: '^',
left: expr,
right: parseBitwiseANDExpression()
};
}
return expr;
}
function parseBitwiseORExpression() {
var expr = parseBitwiseXORExpression();
while (match('|')) {
lex();
expr = {
type: Syntax.BinaryExpression,
operator: '|',
left: expr,
right: parseBitwiseXORExpression()
};
}
return expr;
}
// 11.11 Binary Logical Operators
function parseLogicalANDExpression() {
var expr = parseBitwiseORExpression();
while (match('&&')) {
lex();
expr = {
type: Syntax.LogicalExpression,
operator: '&&',
left: expr,
right: parseBitwiseORExpression()
};
}
return expr;
}
function parseLogicalORExpression() {
var expr = parseLogicalANDExpression();
while (match('||')) {
lex();
expr = {
type: Syntax.LogicalExpression,
operator: '||',
left: expr,
right: parseLogicalANDExpression()
};
}
return expr;
}
// 11.12 Conditional Operator
function parseConditionalExpression() {
var expr, previousAllowIn, consequent;
expr = parseLogicalORExpression();
if (match('?')) {
lex();
previousAllowIn = state.allowIn;
state.allowIn = true;
consequent = parseAssignmentExpression();
state.allowIn = previousAllowIn;
expect(':');
expr = {
type: Syntax.ConditionalExpression,
test: expr,
consequent: consequent,
alternate: parseAssignmentExpression()
};
}
return expr;
}
// 11.13 Assignment Operators
function parseAssignmentExpression() {
var token, expr;
token = lookahead();
expr = parseConditionalExpression();
if (matchAssign()) {
// LeftHandSideExpression
if (!isLeftHandSide(expr)) {
throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
}
// 11.13.1
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
throwErrorTolerant(token, Messages.StrictLHSAssignment);
}
expr = {
type: Syntax.AssignmentExpression,
operator: lex().value,
left: expr,
right: parseAssignmentExpression()
};
}
return expr;
}
// 11.14 Comma Operator
function parseExpression() {
var expr = parseAssignmentExpression();
if (match(',')) {
expr = {
type: Syntax.SequenceExpression,
expressions: [ expr ]
};
while (index < length) {
if (!match(',')) {
break;
}
lex();
expr.expressions.push(parseAssignmentExpression());
}
}
return expr;
}
// 12.1 Block
function parseStatementList() {
var list = [],
statement;
while (index < length) {
if (match('}')) {
break;
}
statement = parseSourceElement();
if (typeof statement === 'undefined') {
break;
}
list.push(statement);
}
return list;
}
function parseBlock() {
var block;
expect('{');
block = parseStatementList();
expect('}');
return {
type: Syntax.BlockStatement,
body: block
};
}
// 12.2 Variable Statement
function parseVariableIdentifier() {
var token = lex();
if (token.type !== Token.Identifier) {
throwUnexpected(token);
}
return {
type: Syntax.Identifier,
name: token.value
};
}
function parseVariableDeclaration(kind) {
var id = parseVariableIdentifier(),
init = null;
// 12.2.1
if (strict && isRestrictedWord(id.name)) {
throwErrorTolerant({}, Messages.StrictVarName);
}
if (kind === 'const') {
expect('=');
init = parseAssignmentExpression();
} else if (match('=')) {
lex();
init = parseAssignmentExpression();
}
return {
type: Syntax.VariableDeclarator,
id: id,
init: init
};
}
function parseVariableDeclarationList(kind) {
var list = [];
do {
list.push(parseVariableDeclaration(kind));
if (!match(',')) {
break;
}
lex();
} while (index < length);
return list;
}
function parseVariableStatement() {
var declarations;
expectKeyword('var');
declarations = parseVariableDeclarationList();
consumeSemicolon();
return {
type: Syntax.VariableDeclaration,
declarations: declarations,
kind: 'var'
};
}
// kind may be `const` or `let`
// Both are experimental and not in the specification yet.
// see http://wiki.ecmascript.org/doku.php?id=harmony:const
// and http://wiki.ecmascript.org/doku.php?id=harmony:let
function parseConstLetDeclaration(kind) {
var declarations;
expectKeyword(kind);
declarations = parseVariableDeclarationList(kind);
consumeSemicolon();
return {
type: Syntax.VariableDeclaration,
declarations: declarations,
kind: kind
};
}
// 12.3 Empty Statement
function parseEmptyStatement() {
expect(';');
return {
type: Syntax.EmptyStatement
};
}
// 12.4 Expression Statement
function parseExpressionStatement() {
var expr = parseExpression();
consumeSemicolon();
return {
type: Syntax.ExpressionStatement,
expression: expr
};
}
// 12.5 If statement
function parseIfStatement() {
var test, consequent, alternate;
expectKeyword('if');
expect('(');
test = parseExpression();
expect(')');
consequent = parseStatement();
if (matchKeyword('else')) {
lex();
alternate = parseStatement();
} else {
alternate = null;
}
return {
type: Syntax.IfStatement,
test: test,
consequent: consequent,
alternate: alternate
};
}
// 12.6 Iteration Statements
function parseDoWhileStatement() {
var body, test, oldInIteration;
expectKeyword('do');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
expectKeyword('while');
expect('(');
test = parseExpression();
expect(')');
if (match(';')) {
lex();
}
return {
type: Syntax.DoWhileStatement,
body: body,
test: test
};
}
function parseWhileStatement() {
var test, body, oldInIteration;
expectKeyword('while');
expect('(');
test = parseExpression();
expect(')');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
return {
type: Syntax.WhileStatement,
test: test,
body: body
};
}
function parseForVariableDeclaration() {
var token = lex();
return {
type: Syntax.VariableDeclaration,
declarations: parseVariableDeclarationList(),
kind: token.value
};
}
function parseForStatement() {
var init, test, update, left, right, body, oldInIteration;
init = test = update = null;
expectKeyword('for');
expect('(');
if (match(';')) {
lex();
} else {
if (matchKeyword('var') || matchKeyword('let')) {
state.allowIn = false;
init = parseForVariableDeclaration();
state.allowIn = true;
if (init.declarations.length === 1 && matchKeyword('in')) {
lex();
left = init;
right = parseExpression();
init = null;
}
} else {
state.allowIn = false;
init = parseExpression();
state.allowIn = true;
if (matchKeyword('in')) {
// LeftHandSideExpression
if (!isLeftHandSide(init)) {
throwErrorTolerant({}, Messages.InvalidLHSInForIn);
}
lex();
left = init;
right = parseExpression();
init = null;
}
}
if (typeof left === 'undefined') {
expect(';');
}
}
if (typeof left === 'undefined') {
if (!match(';')) {
test = parseExpression();
}
expect(';');
if (!match(')')) {
update = parseExpression();
}
}
expect(')');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
if (typeof left === 'undefined') {
return {
type: Syntax.ForStatement,
init: init,
test: test,
update: update,
body: body
};
}
return {
type: Syntax.ForInStatement,
left: left,
right: right,
body: body,
each: false
};
}
// 12.7 The continue statement
function parseContinueStatement() {
var token, label = null;
expectKeyword('continue');
// Optimize the most common form: 'continue;'.
if (source[index] === ';') {
lex();
if (!state.inIteration) {
throwError({}, Messages.IllegalContinue);
}
return {
type: Syntax.ContinueStatement,
label: null
};
}
if (peekLineTerminator()) {
if (!state.inIteration) {
throwError({}, Messages.IllegalContinue);
}
return {
type: Syntax.ContinueStatement,
label: null
};
}
token = lookahead();
if (token.type === Token.Identifier) {
label = parseVariableIdentifier();
if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
throwError({}, Messages.UnknownLabel, label.name);
}
}
consumeSemicolon();
if (label === null && !state.inIteration) {
throwError({}, Messages.IllegalContinue);
}
return {
type: Syntax.ContinueStatement,
label: label
};
}
// 12.8 The break statement
function parseBreakStatement() {
var token, label = null;
expectKeyword('break');
// Optimize the most common form: 'break;'.
if (source[index] === ';') {
lex();
if (!(state.inIteration || state.inSwitch)) {
throwError({}, Messages.IllegalBreak);
}
return {
type: Syntax.BreakStatement,
label: null
};
}
if (peekLineTerminator()) {
if (!(state.inIteration || state.inSwitch)) {
throwError({}, Messages.IllegalBreak);
}
return {
type: Syntax.BreakStatement,
label: null
};
}
token = lookahead();
if (token.type === Token.Identifier) {
label = parseVariableIdentifier();
if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
throwError({}, Messages.UnknownLabel, label.name);
}
}
consumeSemicolon();
if (label === null && !(state.inIteration || state.inSwitch)) {
throwError({}, Messages.IllegalBreak);
}
return {
type: Syntax.BreakStatement,
label: label
};
}
// 12.9 The return statement
function parseReturnStatement() {
var token, argument = null;
expectKeyword('return');
if (!state.inFunctionBody) {
throwErrorTolerant({}, Messages.IllegalReturn);
}
// 'return' followed by a space and an identifier is very common.
if (source[index] === ' ') {
if (isIdentifierStart(source[index + 1])) {
argument = parseExpression();
consumeSemicolon();
return {
type: Syntax.ReturnStatement,
argument: argument
};
}
}
if (peekLineTerminator()) {
return {
type: Syntax.ReturnStatement,
argument: null
};
}
if (!match(';')) {
token = lookahead();
if (!match('}') && token.type !== Token.EOF) {
argument = parseExpression();
}
}
consumeSemicolon();
return {
type: Syntax.ReturnStatement,
argument: argument
};
}
// 12.10 The with statement
function parseWithStatement() {
var object, body;
if (strict) {
throwErrorTolerant({}, Messages.StrictModeWith);
}
expectKeyword('with');
expect('(');
object = parseExpression();
expect(')');
body = parseStatement();
return {
type: Syntax.WithStatement,
object: object,
body: body
};
}
// 12.10 The swith statement
function parseSwitchCase() {
var test,
consequent = [],
statement;
if (matchKeyword('default')) {
lex();
test = null;
} else {
expectKeyword('case');
test = parseExpression();
}
expect(':');
while (index < length) {
if (match('}') || matchKeyword('default') || matchKeyword('case')) {
break;
}
statement = parseStatement();
if (typeof statement === 'undefined') {
break;
}
consequent.push(statement);
}
return {
type: Syntax.SwitchCase,
test: test,
consequent: consequent
};
}
function parseSwitchStatement() {
var discriminant, cases, clause, oldInSwitch, defaultFound;
expectKeyword('switch');
expect('(');
discriminant = parseExpression();
expect(')');
expect('{');
cases = [];
if (match('}')) {
lex();
return {
type: Syntax.SwitchStatement,
discriminant: discriminant,
cases: cases
};
}
oldInSwitch = state.inSwitch;
state.inSwitch = true;
defaultFound = false;
while (index < length) {
if (match('}')) {
break;
}
clause = parseSwitchCase();
if (clause.test === null) {
if (defaultFound) {
throwError({}, Messages.MultipleDefaultsInSwitch);
}
defaultFound = true;
}
cases.push(clause);
}
state.inSwitch = oldInSwitch;
expect('}');
return {
type: Syntax.SwitchStatement,
discriminant: discriminant,
cases: cases
};
}
// 12.13 The throw statement
function parseThrowStatement() {
var argument;
expectKeyword('throw');
if (peekLineTerminator()) {
throwError({}, Messages.NewlineAfterThrow);
}
argument = parseExpression();
consumeSemicolon();
return {
type: Syntax.ThrowStatement,
argument: argument
};
}
// 12.14 The try statement
function parseCatchClause() {
var param;
expectKeyword('catch');
expect('(');
if (match(')')) {
throwUnexpected(lookahead());
}
param = parseVariableIdentifier();
// 12.14.1
if (strict && isRestrictedWord(param.name)) {
throwErrorTolerant({}, Messages.StrictCatchVariable);
}
expect(')');
return {
type: Syntax.CatchClause,
param: param,
body: parseBlock()
};
}
function parseTryStatement() {
var block, handlers = [], finalizer = null;
expectKeyword('try');
block = parseBlock();
if (matchKeyword('catch')) {
handlers.push(parseCatchClause());
}
if (matchKeyword('finally')) {
lex();
finalizer = parseBlock();
}
if (handlers.length === 0 && !finalizer) {
throwError({}, Messages.NoCatchOrFinally);
}
return {
type: Syntax.TryStatement,
block: block,
guardedHandlers: [],
handlers: handlers,
finalizer: finalizer
};
}
// 12.15 The debugger statement
function parseDebuggerStatement() {
expectKeyword('debugger');
consumeSemicolon();
return {
type: Syntax.DebuggerStatement
};
}
// 12 Statements
function parseStatement() {
var token = lookahead(),
expr,
labeledBody;
if (token.type === Token.EOF) {
throwUnexpected(token);
}
if (token.type === Token.Punctuator) {
switch (token.value) {
case ';':
return parseEmptyStatement();
case '{':
return parseBlock();
case '(':
return parseExpressionStatement();
default:
break;
}
}
if (token.type === Token.Keyword) {
switch (token.value) {
case 'break':
return parseBreakStatement();
case 'continue':
return parseContinueStatement();
case 'debugger':
return parseDebuggerStatement();
case 'do':
return parseDoWhileStatement();
case 'for':
return parseForStatement();
case 'function':
return parseFunctionDeclaration();
case 'if':
return parseIfStatement();
case 'return':
return parseReturnStatement();
case 'switch':
return parseSwitchStatement();
case 'throw':
return parseThrowStatement();
case 'try':
return parseTryStatement();
case 'var':
return parseVariableStatement();
case 'while':
return parseWhileStatement();
case 'with':
return parseWithStatement();
default:
break;
}
}
expr = parseExpression();
// 12.12 Labelled Statements
if ((expr.type === Syntax.Identifier) && match(':')) {
lex();
if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) {
throwError({}, Messages.Redeclaration, 'Label', expr.name);
}
state.labelSet[expr.name] = true;
labeledBody = parseStatement();
delete state.labelSet[expr.name];
return {
type: Syntax.LabeledStatement,
label: expr,
body: labeledBody
};
}
consumeSemicolon();
return {
type: Syntax.ExpressionStatement,
expression: expr
};
}
// 13 Function Definition
function parseFunctionSourceElements() {
var sourceElement, sourceElements = [], token, directive, firstRestricted,
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
expect('{');
while (index < length) {
token = lookahead();
if (token.type !== Token.StringLiteral) {
break;
}
sourceElement = parseSourceElement();
sourceElements.push(sourceElement);
if (sourceElement.expression.type !== Syntax.Literal) {
// this is not directive
break;
}
directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
if (directive === 'use strict') {
strict = true;
if (firstRestricted) {
throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
}
} else {
if (!firstRestricted && token.octal) {
firstRestricted = token;
}
}
}
oldLabelSet = state.labelSet;
oldInIteration = state.inIteration;
oldInSwitch = state.inSwitch;
oldInFunctionBody = state.inFunctionBody;
state.labelSet = {};
state.inIteration = false;
state.inSwitch = false;
state.inFunctionBody = true;
while (index < length) {
if (match('}')) {
break;
}
sourceElement = parseSourceElement();
if (typeof sourceElement === 'undefined') {
break;
}
sourceElements.push(sourceElement);
}
expect('}');
state.labelSet = oldLabelSet;
state.inIteration = oldInIteration;
state.inSwitch = oldInSwitch;
state.inFunctionBody = oldInFunctionBody;
return {
type: Syntax.BlockStatement,
body: sourceElements
};
}
function parseFunctionDeclaration() {
var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet;
expectKeyword('function');
token = lookahead();
id = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
throwErrorTolerant(token, Messages.StrictFunctionName);
}
} else {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictFunctionName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
}
}
expect('(');
if (!match(')')) {
paramSet = {};
while (index < length) {
token = lookahead();
param = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
stricted = token;
message = Messages.StrictParamName;
}
if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
stricted = token;
message = Messages.StrictParamDupe;
}
} else if (!firstRestricted) {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictParamName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
} else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
firstRestricted = token;
message = Messages.StrictParamDupe;
}
}
params.push(param);
paramSet[param.name] = true;
if (match(')')) {
break;
}
expect(',');
}
}
expect(')');
previousStrict = strict;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwError(firstRestricted, message);
}
if (strict && stricted) {
throwErrorTolerant(stricted, message);
}
strict = previousStrict;
return {
type: Syntax.FunctionDeclaration,
id: id,
params: params,
defaults: [],
body: body,
rest: null,
generator: false,
expression: false
};
}
function parseFunctionExpression() {
var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
expectKeyword('function');
if (!match('(')) {
token = lookahead();
id = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
throwErrorTolerant(token, Messages.StrictFunctionName);
}
} else {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictFunctionName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
}
}
}
expect('(');
if (!match(')')) {
paramSet = {};
while (index < length) {
token = lookahead();
param = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
stricted = token;
message = Messages.StrictParamName;
}
if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
stricted = token;
message = Messages.StrictParamDupe;
}
} else if (!firstRestricted) {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictParamName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
} else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
firstRestricted = token;
message = Messages.StrictParamDupe;
}
}
params.push(param);
paramSet[param.name] = true;
if (match(')')) {
break;
}
expect(',');
}
}
expect(')');
previousStrict = strict;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwError(firstRestricted, message);
}
if (strict && stricted) {
throwErrorTolerant(stricted, message);
}
strict = previousStrict;
return {
type: Syntax.FunctionExpression,
id: id,
params: params,
defaults: [],
body: body,
rest: null,
generator: false,
expression: false
};
}
// 14 Program
function parseSourceElement() {
var token = lookahead();
if (token.type === Token.Keyword) {
switch (token.value) {
case 'const':
case 'let':
return parseConstLetDeclaration(token.value);
case 'function':
return parseFunctionDeclaration();
default:
return parseStatement();
}
}
if (token.type !== Token.EOF) {
return parseStatement();
}
}
function parseSourceElements() {
var sourceElement, sourceElements = [], token, directive, firstRestricted;
while (index < length) {
token = lookahead();
if (token.type !== Token.StringLiteral) {
break;
}
sourceElement = parseSourceElement();
sourceElements.push(sourceElement);
if (sourceElement.expression.type !== Syntax.Literal) {
// this is not directive
break;
}
directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
if (directive === 'use strict') {
strict = true;
if (firstRestricted) {
throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
}
} else {
if (!firstRestricted && token.octal) {
firstRestricted = token;
}
}
}
while (index < length) {
sourceElement = parseSourceElement();
if (typeof sourceElement === 'undefined') {
break;
}
sourceElements.push(sourceElement);
}
return sourceElements;
}
function parseProgram() {
var program;
strict = false;
program = {
type: Syntax.Program,
body: parseSourceElements()
};
return program;
}
// The following functions are needed only when the option to preserve
// the comments is active.
function addComment(type, value, start, end, loc) {
assert(typeof start === 'number', 'Comment must have valid position');
// Because the way the actual token is scanned, often the comments
// (if any) are skipped twice during the lexical analysis.
// Thus, we need to skip adding a comment if the comment array already
// handled it.
if (extra.comments.length > 0) {
if (extra.comments[extra.comments.length - 1].range[1] > start) {
return;
}
}
extra.comments.push({
type: type,
value: value,
range: [start, end],
loc: loc
});
}
function scanComment() {
var comment, ch, loc, start, blockComment, lineComment;
comment = '';
blockComment = false;
lineComment = false;
while (index < length) {
ch = source[index];
if (lineComment) {
ch = source[index++];
if (isLineTerminator(ch)) {
loc.end = {
line: lineNumber,
column: index - lineStart - 1
};
lineComment = false;
addComment('Line', comment, start, index - 1, loc);
if (ch === '\r' && source[index] === '\n') {
++index;
}
++lineNumber;
lineStart = index;
comment = '';
} else if (index >= length) {
lineComment = false;
comment += ch;
loc.end = {
line: lineNumber,
column: length - lineStart
};
addComment('Line', comment, start, length, loc);
} else {
comment += ch;
}
} else if (blockComment) {
if (isLineTerminator(ch)) {
if (ch === '\r' && source[index + 1] === '\n') {
++index;
comment += '\r\n';
} else {
comment += ch;
}
++lineNumber;
++index;
lineStart = index;
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
ch = source[index++];
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
comment += ch;
if (ch === '*') {
ch = source[index];
if (ch === '/') {
comment = comment.substr(0, comment.length - 1);
blockComment = false;
++index;
loc.end = {
line: lineNumber,
column: index - lineStart
};
addComment('Block', comment, start, index, loc);
comment = '';
}
}
}
} else if (ch === '/') {
ch = source[index + 1];
if (ch === '/') {
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
start = index;
index += 2;
lineComment = true;
if (index >= length) {
loc.end = {
line: lineNumber,
column: index - lineStart
};
lineComment = false;
addComment('Line', comment, start, index, loc);
}
} else if (ch === '*') {
start = index;
index += 2;
blockComment = true;
loc = {
start: {
line: lineNumber,
column: index - lineStart - 2
}
};
if (index >= length) {
throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
}
} else {
break;
}
} else if (isWhiteSpace(ch)) {
++index;
} else if (isLineTerminator(ch)) {
++index;
if (ch === '\r' && source[index] === '\n') {
++index;
}
++lineNumber;
lineStart = index;
} else {
break;
}
}
}
function filterCommentLocation() {
var i, entry, comment, comments = [];
for (i = 0; i < extra.comments.length; ++i) {
entry = extra.comments[i];
comment = {
type: entry.type,
value: entry.value
};
if (extra.range) {
comment.range = entry.range;
}
if (extra.loc) {
comment.loc = entry.loc;
}
comments.push(comment);
}
extra.comments = comments;
}
function collectToken() {
var start, loc, token, range, value;
skipComment();
start = index;
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
token = extra.advance();
loc.end = {
line: lineNumber,
column: index - lineStart
};
if (token.type !== Token.EOF) {
range = [token.range[0], token.range[1]];
value = sliceSource(token.range[0], token.range[1]);
extra.tokens.push({
type: TokenName[token.type],
value: value,
range: range,
loc: loc
});
}
return token;
}
function collectRegex() {
var pos, loc, regex, token;
skipComment();
pos = index;
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
regex = extra.scanRegExp();
loc.end = {
line: lineNumber,
column: index - lineStart
};
// Pop the previous token, which is likely '/' or '/='
if (extra.tokens.length > 0) {
token = extra.tokens[extra.tokens.length - 1];
if (token.range[0] === pos && token.type === 'Punctuator') {
if (token.value === '/' || token.value === '/=') {
extra.tokens.pop();
}
}
}
extra.tokens.push({
type: 'RegularExpression',
value: regex.literal,
range: [pos, index],
loc: loc
});
return regex;
}
function filterTokenLocation() {
var i, entry, token, tokens = [];
for (i = 0; i < extra.tokens.length; ++i) {
entry = extra.tokens[i];
token = {
type: entry.type,
value: entry.value
};
if (extra.range) {
token.range = entry.range;
}
if (extra.loc) {
token.loc = entry.loc;
}
tokens.push(token);
}
extra.tokens = tokens;
}
function createLiteral(token) {
return {
type: Syntax.Literal,
value: token.value
};
}
function createRawLiteral(token) {
return {
type: Syntax.Literal,
value: token.value,
raw: sliceSource(token.range[0], token.range[1])
};
}
function createLocationMarker() {
var marker = {};
marker.range = [index, index];
marker.loc = {
start: {
line: lineNumber,
column: index - lineStart
},
end: {
line: lineNumber,
column: index - lineStart
}
};
marker.end = function () {
this.range[1] = index;
this.loc.end.line = lineNumber;
this.loc.end.column = index - lineStart;
};
marker.applyGroup = function (node) {
if (extra.range) {
node.groupRange = [this.range[0], this.range[1]];
}
if (extra.loc) {
node.groupLoc = {
start: {
line: this.loc.start.line,
column: this.loc.start.column
},
end: {
line: this.loc.end.line,
column: this.loc.end.column
}
};
}
};
marker.apply = function (node) {
if (extra.range) {
node.range = [this.range[0], this.range[1]];
}
if (extra.loc) {
node.loc = {
start: {
line: this.loc.start.line,
column: this.loc.start.column
},
end: {
line: this.loc.end.line,
column: this.loc.end.column
}
};
}
};
return marker;
}
function trackGroupExpression() {
var marker, expr;
skipComment();
marker = createLocationMarker();
expect('(');
expr = parseExpression();
expect(')');
marker.end();
marker.applyGroup(expr);
return expr;
}
function trackLeftHandSideExpression() {
var marker, expr;
skipComment();
marker = createLocationMarker();
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[')) {
if (match('[')) {
expr = {
type: Syntax.MemberExpression,
computed: true,
object: expr,
property: parseComputedMember()
};
marker.end();
marker.apply(expr);
} else {
expr = {
type: Syntax.MemberExpression,
computed: false,
object: expr,
property: parseNonComputedMember()
};
marker.end();
marker.apply(expr);
}
}
return expr;
}
function trackLeftHandSideExpressionAllowCall() {
var marker, expr;
skipComment();
marker = createLocationMarker();
expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
while (match('.') || match('[') || match('(')) {
if (match('(')) {
expr = {
type: Syntax.CallExpression,
callee: expr,
'arguments': parseArguments()
};
marker.end();
marker.apply(expr);
} else if (match('[')) {
expr = {
type: Syntax.MemberExpression,
computed: true,
object: expr,
property: parseComputedMember()
};
marker.end();
marker.apply(expr);
} else {
expr = {
type: Syntax.MemberExpression,
computed: false,
object: expr,
property: parseNonComputedMember()
};
marker.end();
marker.apply(expr);
}
}
return expr;
}
function filterGroup(node) {
var n, i, entry;
n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {};
for (i in node) {
if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') {
entry = node[i];
if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) {
n[i] = entry;
} else {
n[i] = filterGroup(entry);
}
}
}
return n;
}
function wrapTrackingFunction(range, loc) {
return function (parseFunction) {
function isBinary(node) {
return node.type === Syntax.LogicalExpression ||
node.type === Syntax.BinaryExpression;
}
function visit(node) {
var start, end;
if (isBinary(node.left)) {
visit(node.left);
}
if (isBinary(node.right)) {
visit(node.right);
}
if (range) {
if (node.left.groupRange || node.right.groupRange) {
start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0];
end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1];
node.range = [start, end];
} else if (typeof node.range === 'undefined') {
start = node.left.range[0];
end = node.right.range[1];
node.range = [start, end];
}
}
if (loc) {
if (node.left.groupLoc || node.right.groupLoc) {
start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start;
end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end;
node.loc = {
start: start,
end: end
};
} else if (typeof node.loc === 'undefined') {
node.loc = {
start: node.left.loc.start,
end: node.right.loc.end
};
}
}
}
return function () {
var marker, node;
skipComment();
marker = createLocationMarker();
node = parseFunction.apply(null, arguments);
marker.end();
if (range && typeof node.range === 'undefined') {
marker.apply(node);
}
if (loc && typeof node.loc === 'undefined') {
marker.apply(node);
}
if (isBinary(node)) {
visit(node);
}
return node;
};
};
}
function patch() {
var wrapTracking;
if (extra.comments) {
extra.skipComment = skipComment;
skipComment = scanComment;
}
if (extra.raw) {
extra.createLiteral = createLiteral;
createLiteral = createRawLiteral;
}
if (extra.range || extra.loc) {
extra.parseGroupExpression = parseGroupExpression;
extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
parseGroupExpression = trackGroupExpression;
parseLeftHandSideExpression = trackLeftHandSideExpression;
parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall;
wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
extra.parseAdditiveExpression = parseAdditiveExpression;
extra.parseAssignmentExpression = parseAssignmentExpression;
extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
extra.parseBitwiseORExpression = parseBitwiseORExpression;
extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
extra.parseBlock = parseBlock;
extra.parseFunctionSourceElements = parseFunctionSourceElements;
extra.parseCatchClause = parseCatchClause;
extra.parseComputedMember = parseComputedMember;
extra.parseConditionalExpression = parseConditionalExpression;
extra.parseConstLetDeclaration = parseConstLetDeclaration;
extra.parseEqualityExpression = parseEqualityExpression;
extra.parseExpression = parseExpression;
extra.parseForVariableDeclaration = parseForVariableDeclaration;
extra.parseFunctionDeclaration = parseFunctionDeclaration;
extra.parseFunctionExpression = parseFunctionExpression;
extra.parseLogicalANDExpression = parseLogicalANDExpression;
extra.parseLogicalORExpression = parseLogicalORExpression;
extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
extra.parseNewExpression = parseNewExpression;
extra.parseNonComputedProperty = parseNonComputedProperty;
extra.parseObjectProperty = parseObjectProperty;
extra.parseObjectPropertyKey = parseObjectPropertyKey;
extra.parsePostfixExpression = parsePostfixExpression;
extra.parsePrimaryExpression = parsePrimaryExpression;
extra.parseProgram = parseProgram;
extra.parsePropertyFunction = parsePropertyFunction;
extra.parseRelationalExpression = parseRelationalExpression;
extra.parseStatement = parseStatement;
extra.parseShiftExpression = parseShiftExpression;
extra.parseSwitchCase = parseSwitchCase;
extra.parseUnaryExpression = parseUnaryExpression;
extra.parseVariableDeclaration = parseVariableDeclaration;
extra.parseVariableIdentifier = parseVariableIdentifier;
parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression);
parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression);
parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression);
parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression);
parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression);
parseBlock = wrapTracking(extra.parseBlock);
parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements);
parseCatchClause = wrapTracking(extra.parseCatchClause);
parseComputedMember = wrapTracking(extra.parseComputedMember);
parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
parseExpression = wrapTracking(extra.parseExpression);
parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression);
parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
parseNewExpression = wrapTracking(extra.parseNewExpression);
parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
parseObjectProperty = wrapTracking(extra.parseObjectProperty);
parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
parsePostfixExpression = wrapTracking(extra.parsePostfixExpression);
parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression);
parseProgram = wrapTracking(extra.parseProgram);
parsePropertyFunction = wrapTracking(extra.parsePropertyFunction);
parseRelationalExpression = wrapTracking(extra.parseRelationalExpression);
parseStatement = wrapTracking(extra.parseStatement);
parseShiftExpression = wrapTracking(extra.parseShiftExpression);
parseSwitchCase = wrapTracking(extra.parseSwitchCase);
parseUnaryExpression = wrapTracking(extra.parseUnaryExpression);
parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration);
parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier);
}
if (typeof extra.tokens !== 'undefined') {
extra.advance = advance;
extra.scanRegExp = scanRegExp;
advance = collectToken;
scanRegExp = collectRegex;
}
}
function unpatch() {
if (typeof extra.skipComment === 'function') {
skipComment = extra.skipComment;
}
if (extra.raw) {
createLiteral = extra.createLiteral;
}
if (extra.range || extra.loc) {
parseAdditiveExpression = extra.parseAdditiveExpression;
parseAssignmentExpression = extra.parseAssignmentExpression;
parseBitwiseANDExpression = extra.parseBitwiseANDExpression;
parseBitwiseORExpression = extra.parseBitwiseORExpression;
parseBitwiseXORExpression = extra.parseBitwiseXORExpression;
parseBlock = extra.parseBlock;
parseFunctionSourceElements = extra.parseFunctionSourceElements;
parseCatchClause = extra.parseCatchClause;
parseComputedMember = extra.parseComputedMember;
parseConditionalExpression = extra.parseConditionalExpression;
parseConstLetDeclaration = extra.parseConstLetDeclaration;
parseEqualityExpression = extra.parseEqualityExpression;
parseExpression = extra.parseExpression;
parseForVariableDeclaration = extra.parseForVariableDeclaration;
parseFunctionDeclaration = extra.parseFunctionDeclaration;
parseFunctionExpression = extra.parseFunctionExpression;
parseGroupExpression = extra.parseGroupExpression;
parseLeftHandSideExpression = extra.parseLeftHandSideExpression;
parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall;
parseLogicalANDExpression = extra.parseLogicalANDExpression;
parseLogicalORExpression = extra.parseLogicalORExpression;
parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
parseNewExpression = extra.parseNewExpression;
parseNonComputedProperty = extra.parseNonComputedProperty;
parseObjectProperty = extra.parseObjectProperty;
parseObjectPropertyKey = extra.parseObjectPropertyKey;
parsePrimaryExpression = extra.parsePrimaryExpression;
parsePostfixExpression = extra.parsePostfixExpression;
parseProgram = extra.parseProgram;
parsePropertyFunction = extra.parsePropertyFunction;
parseRelationalExpression = extra.parseRelationalExpression;
parseStatement = extra.parseStatement;
parseShiftExpression = extra.parseShiftExpression;
parseSwitchCase = extra.parseSwitchCase;
parseUnaryExpression = extra.parseUnaryExpression;
parseVariableDeclaration = extra.parseVariableDeclaration;
parseVariableIdentifier = extra.parseVariableIdentifier;
}
if (typeof extra.scanRegExp === 'function') {
advance = extra.advance;
scanRegExp = extra.scanRegExp;
}
}
function stringToArray(str) {
var length = str.length,
result = [],
i;
for (i = 0; i < length; ++i) {
result[i] = str.charAt(i);
}
return result;
}
function parse(code, options) {
var program, toString;
toString = String;
if (typeof code !== 'string' && !(code instanceof String)) {
code = toString(code);
}
source = code;
index = 0;
lineNumber = (source.length > 0) ? 1 : 0;
lineStart = 0;
length = source.length;
buffer = null;
state = {
allowIn: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
inSwitch: false
};
extra = {};
if (typeof options !== 'undefined') {
extra.range = (typeof options.range === 'boolean') && options.range;
extra.loc = (typeof options.loc === 'boolean') && options.loc;
extra.raw = (typeof options.raw === 'boolean') && options.raw;
if (typeof options.tokens === 'boolean' && options.tokens) {
extra.tokens = [];
}
if (typeof options.comment === 'boolean' && options.comment) {
extra.comments = [];
}
if (typeof options.tolerant === 'boolean' && options.tolerant) {
extra.errors = [];
}
}
if (length > 0) {
if (typeof source[0] === 'undefined') {
// Try first to convert to a string. This is good as fast path
// for old IE which understands string indexing for string
// literals only and not for string object.
if (code instanceof String) {
source = code.valueOf();
}
// Force accessing the characters via an array.
if (typeof source[0] === 'undefined') {
source = stringToArray(code);
}
}
}
patch();
try {
program = parseProgram();
if (typeof extra.comments !== 'undefined') {
filterCommentLocation();
program.comments = extra.comments;
}
if (typeof extra.tokens !== 'undefined') {
filterTokenLocation();
program.tokens = extra.tokens;
}
if (typeof extra.errors !== 'undefined') {
program.errors = extra.errors;
}
if (extra.range || extra.loc) {
program.body = filterGroup(program.body);
}
} catch (e) {
throw e;
} finally {
unpatch();
extra = {};
}
return program;
}
// Sync with package.json.
exports.version = '1.0.4';
exports.parse = parse;
// Deep copy.
exports.Syntax = (function () {
var name, types = {};
if (typeof Object.create === 'function') {
types = Object.create(null);
}
for (name in Syntax) {
if (Syntax.hasOwnProperty(name)) {
types[name] = Syntax[name];
}
}
if (typeof Object.freeze === 'function') {
Object.freeze(types);
}
return types;
}());
}));
/* vim: set sw=4 ts=4 et tw=80 : */
})(null);
(function(require,module){
var parse = require('esprima').parse;
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
};
var forEach = function (xs, fn) {
if (xs.forEach) return xs.forEach(fn);
for (var i = 0; i < xs.length; i++) {
fn.call(xs, xs[i], i, xs);
}
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
module.exports = function (src, opts, fn) {
if (typeof opts === 'function') {
fn = opts;
opts = {};
}
if (typeof src === 'object') {
opts = src;
src = opts.source;
delete opts.source;
}
src = src === undefined ? opts.source : src;
opts.range = true;
if (typeof src !== 'string') src = String(src);
var ast = parse(src, opts);
var result = {
chunks : src.split(''),
toString : function () { return result.chunks.join('') },
inspect : function () { return result.toString() }
};
var index = 0;
(function walk (node, parent) {
insertHelpers(node, parent, result.chunks);
forEach(objectKeys(node), function (key) {
if (key === 'parent') return;
var child = node[key];
if (isArray(child)) {
forEach(child, function (c) {
if (c && typeof c.type === 'string') {
walk(c, node);
}
});
}
else if (child && typeof child.type === 'string') {
insertHelpers(child, node, result.chunks);
walk(child, node);
}
});
fn(node);
})(ast, undefined);
return result;
};
function insertHelpers (node, parent, chunks) {
if (!node.range) return;
node.parent = parent;
node.source = function () {
return chunks.slice(
node.range[0], node.range[1]
).join('');
};
if (node.update && typeof node.update === 'object') {
var prev = node.update;
forEach(objectKeys(prev), function (key) {
update[key] = prev[key];
});
node.update = update;
}
else {
node.update = update;
}
function update (s) {
chunks[node.range[0]] = s;
for (var i = node.range[0] + 1; i < node.range[1]; i++) {
chunks[i] = '';
}
};
}
window.falafel = module.exports;})(function(){return {parse: esprima.parse};},{exports: {}});
var inBrowser = typeof window !== 'undefined' && this === window;
var parseAndModify = (inBrowser ? window.falafel : require("falafel"));
(inBrowser ? window : exports).blanket = (function(){
var linesToAddTracking = [
"ExpressionStatement",
"BreakStatement" ,
"ContinueStatement" ,
"VariableDeclaration",
"ReturnStatement" ,
"ThrowStatement" ,
"TryStatement" ,
"FunctionDeclaration" ,
"IfStatement" ,
"WhileStatement" ,
"DoWhileStatement" ,
"ForStatement" ,
"ForInStatement" ,
"SwitchStatement" ,
"WithStatement"
],
linesToAddBrackets = [
"IfStatement" ,
"WhileStatement" ,
"DoWhileStatement" ,
"ForStatement" ,
"ForInStatement" ,
"WithStatement"
],
__blanket,
copynumber = Math.floor(Math.random()*1000),
coverageInfo = {},options = {
reporter: null,
adapter:null,
filter: null,
customVariable: null,
loader: null,
ignoreScriptError: false,
existingRequireJS:false,
autoStart: false,
timeout: 180,
ignoreCors: false,
branchTracking: false,
sourceURL: false,
debug:false,
engineOnly:false,
testReadyCallback:null,
commonJS:false,
instrumentCache:false,
modulePattern: null
};
if (inBrowser && typeof window.blanket !== 'undefined'){
__blanket = window.blanket.noConflict();
}
_blanket = {
noConflict: function(){
if (__blanket){
return __blanket;
}
return _blanket;
},
_getCopyNumber: function(){
//internal method
//for differentiating between instances
return copynumber;
},
extend: function(obj) {
//borrowed from underscore
_blanket._extend(_blanket,obj);
},
_extend: function(dest,source){
if (source) {
for (var prop in source) {
if ( dest[prop] instanceof Object && typeof dest[prop] !== "function"){
_blanket._extend(dest[prop],source[prop]);
}else{
dest[prop] = source[prop];
}
}
}
},
getCovVar: function(){
var opt = _blanket.options("customVariable");
if (opt){
if (_blanket.options("debug")) {console.log("BLANKET-Using custom tracking variable:",opt);}
return inBrowser ? "window."+opt : opt;
}
return inBrowser ? "window._$blanket" : "_$jscoverage";
},
options: function(key,value){
if (typeof key !== "string"){
_blanket._extend(options,key);
}else if (typeof value === 'undefined'){
return options[key];
}else{
options[key]=value;
}
},
instrument: function(config, next){
//check instrumented hash table,
//return instrumented code if available.
var inFile = config.inputFile,
inFileName = config.inputFileName;
//check instrument cache
if (_blanket.options("instrumentCache") && sessionStorage && sessionStorage.getItem("blanket_instrument_store-"+inFileName)){
if (_blanket.options("debug")) {console.log("BLANKET-Reading instrumentation from cache: ",inFileName);}
next(sessionStorage.getItem("blanket_instrument_store-"+inFileName));
}else{
var sourceArray = _blanket._prepareSource(inFile);
_blanket._trackingArraySetup=[];
//remove shebang
inFile = inFile.replace(/^\#\!.*/, "");
var instrumented = parseAndModify(inFile,{loc:true,comment:true}, _blanket._addTracking(inFileName));
instrumented = _blanket._trackingSetup(inFileName,sourceArray)+instrumented;
if (_blanket.options("sourceURL")){
instrumented += "\n//@ sourceURL="+inFileName.replace("http://","");
}
if (_blanket.options("debug")) {console.log("BLANKET-Instrumented file: ",inFileName);}
if (_blanket.options("instrumentCache") && sessionStorage){
if (_blanket.options("debug")) {console.log("BLANKET-Saving instrumentation to cache: ",inFileName);}
sessionStorage.setItem("blanket_instrument_store-"+inFileName,instrumented);
}
next(instrumented);
}
},
_trackingArraySetup: [],
_branchingArraySetup: [],
_prepareSource: function(source){
return source.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/gm,"\n").split('\n');
},
_trackingSetup: function(filename,sourceArray){
var branches = _blanket.options("branchTracking");
var sourceString = sourceArray.join("',\n'");
var intro = "";
var covVar = _blanket.getCovVar();
intro += "if (typeof "+covVar+" === 'undefined') "+covVar+" = {};\n";
if (branches){
intro += "var _$branchFcn=function(f,l,c,r){ ";
intro += "if (!!r) { ";
intro += covVar+"[f].branchData[l][c][0] = "+covVar+"[f].branchData[l][c][0] || [];";
intro += covVar+"[f].branchData[l][c][0].push(r); }";
intro += "else { ";
intro += covVar+"[f].branchData[l][c][1] = "+covVar+"[f].branchData[l][c][1] || [];";
intro += covVar+"[f].branchData[l][c][1].push(r); }";
intro += "return r;};\n";
}
intro += "if (typeof "+covVar+"['"+filename+"'] === 'undefined'){";
intro += covVar+"['"+filename+"']=[];\n";
if (branches){
intro += covVar+"['"+filename+"'].branchData=[];\n";
}
intro += covVar+"['"+filename+"'].source=['"+sourceString+"'];\n";
//initialize array values
_blanket._trackingArraySetup.sort(function(a,b){
return parseInt(a,10) > parseInt(b,10);
}).forEach(function(item){
intro += covVar+"['"+filename+"']["+item+"]=0;\n";
});
if (branches){
_blanket._branchingArraySetup.sort(function(a,b){
return a.line > b.line;
}).sort(function(a,b){
return a.column > b.column;
}).forEach(function(item){
if (item.file === filename){
intro += "if (typeof "+ covVar+"['"+filename+"'].branchData["+item.line+"] === 'undefined'){\n";
intro += covVar+"['"+filename+"'].branchData["+item.line+"]=[];\n";
intro += "}";
intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"] = [];\n";
intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].consequent = "+JSON.stringify(item.consequent)+";\n";
intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].alternate = "+JSON.stringify(item.alternate)+";\n";
}
});
}
intro += "}";
return intro;
},
_blockifyIf: function(node){
if (linesToAddBrackets.indexOf(node.type) > -1){
var bracketsExistObject = node.consequent || node.body;
var bracketsExistAlt = node.alternate;
if( bracketsExistAlt && bracketsExistAlt.type !== "BlockStatement") {
bracketsExistAlt.update("{\n"+bracketsExistAlt.source()+"}\n");
}
if( bracketsExistObject && bracketsExistObject.type !== "BlockStatement") {
bracketsExistObject.update("{\n"+bracketsExistObject.source()+"}\n");
}
}
},
_trackBranch: function(node,filename){
//recursive on consequent and alternative
var line = node.loc.start.line;
var col = node.loc.start.column;
_blanket._branchingArraySetup.push({
line: line,
column: col,
file:filename,
consequent: node.consequent.loc,
alternate: node.alternate.loc
});
var updated = "_$branchFcn"+
"('"+filename+"',"+line+","+col+","+node.test.source()+
")?"+node.consequent.source()+":"+node.alternate.source();
node.update(updated);
},
_addTracking: function (filename) {
//falafel doesn't take a file name
//so we include the filename in a closure
//and return the function to falafel
var covVar = _blanket.getCovVar();
return function(node){
_blanket._blockifyIf(node);
if (linesToAddTracking.indexOf(node.type) > -1 && node.parent.type !== "LabeledStatement") {
_blanket._checkDefs(node,filename);
if (node.type === "VariableDeclaration" &&
(node.parent.type === "ForStatement" || node.parent.type === "ForInStatement")){
return;
}
if (node.loc && node.loc.start){
node.update(covVar+"['"+filename+"']["+node.loc.start.line+"]++;\n"+node.source());
_blanket._trackingArraySetup.push(node.loc.start.line);
}else{
//I don't think we can handle a node with no location
throw new Error("The instrumenter encountered a node with no location: "+Object.keys(node));
}
}else if (_blanket.options("branchTracking") && node.type === "ConditionalExpression"){
_blanket._trackBranch(node,filename);
}
};
},
_checkDefs: function(node,filename){
// Make sure developers don't redefine window. if they do, inform them it is wrong.
if (inBrowser){
if (node.type === "VariableDeclaration" && node.declarations) {
node.declarations.forEach(function(declaration) {
if (declaration.id.name === "window") {
throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
}
});
}
if (node.type === "FunctionDeclaration" && node.params) {
node.params.forEach(function(param) {
if (param.name === "window") {
throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
}
});
}
//Make sure developers don't redefine the coverage variable
if (node.type === "ExpressionStatement" &&
node.expression && node.expression.left &&
node.expression.left.object && node.expression.left.property &&
node.expression.left.object.name +
"." + node.expression.left.property.name === _blanket.getCovVar()) {
throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
}
}else{
//Make sure developers don't redefine the coverage variable in node
if (node.type === "ExpressionStatement" &&
node.expression && node.expression.left &&
!node.expression.left.object && !node.expression.left.property &&
node.expression.left.name === _blanket.getCovVar()) {
throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
}
}
},
setupCoverage: function(){
coverageInfo.instrumentation = "blanket";
coverageInfo.stats = {
"suites": 0,
"tests": 0,
"passes": 0,
"pending": 0,
"failures": 0,
"start": new Date()
};
},
_checkIfSetup: function(){
if (!coverageInfo.stats){
throw new Error("You must call blanket.setupCoverage() first.");
}
},
onTestStart: function(){
if (_blanket.options("debug")) {console.log("BLANKET-Test event started");}
this._checkIfSetup();
coverageInfo.stats.tests++;
coverageInfo.stats.pending++;
},
onTestDone: function(total,passed){
this._checkIfSetup();
if(passed === total){
coverageInfo.stats.passes++;
}else{
coverageInfo.stats.failures++;
}
coverageInfo.stats.pending--;
},
onModuleStart: function(){
this._checkIfSetup();
coverageInfo.stats.suites++;
},
onTestsDone: function(){
if (_blanket.options("debug")) {console.log("BLANKET-Test event done");}
this._checkIfSetup();
coverageInfo.stats.end = new Date();
if (inBrowser){
this.report(coverageInfo);
}else{
if (!_blanket.options("branchTracking")){
delete (inBrowser ? window : global)[_blanket.getCovVar()].branchFcn;
}
this.options("reporter").call(this,coverageInfo);
}
}
};
return _blanket;
})();
(function(_blanket){
var oldOptions = _blanket.options;
_blanket.extend({
outstandingRequireFiles:[],
options: function(key,value){
var newVal={};
if (typeof key !== "string"){
//key is key/value map
oldOptions(key);
newVal = key;
}else if (typeof value === 'undefined'){
//accessor
return oldOptions(key);
}else{
//setter
oldOptions(key,value);
newVal[key] = value;
}
if (newVal.adapter){
_blanket._loadFile(newVal.adapter);
}
if (newVal.loader){
_blanket._loadFile(newVal.loader);
}
},
requiringFile: function(filename,done){
if (typeof filename === "undefined"){
_blanket.outstandingRequireFiles=[];
}else if (typeof done === "undefined"){
_blanket.outstandingRequireFiles.push(filename);
}else{
_blanket.outstandingRequireFiles.splice(_blanket.outstandingRequireFiles.indexOf(filename),1);
}
},
requireFilesLoaded: function(){
return _blanket.outstandingRequireFiles.length === 0;
},
showManualLoader: function(){
if (document.getElementById("blanketLoaderDialog")){
return;
}
//copied from http://blog.avtex.com/2012/01/26/cross-browser-css-only-modal-box/
var loader = "<div class='blanketDialogOverlay'>";
loader += "&nbsp;</div>";
loader += "<div class='blanketDialogVerticalOffset'>";
loader += "<div class='blanketDialogBox'>";
loader += "<b>Error:</b> Blanket.js encountered a cross origin request error while instrumenting the source files. ";
loader += "<br><br>This is likely caused by the source files being referenced locally (using the file:// protocol). ";
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).";
loader += "<br>";
if (typeof FileReader !== "undefined"){
loader += "<br>Or, try the experimental loader. When prompted, simply click on the directory containing all the source files you want covered.";
loader += "<a href='javascript:document.getElementById(\"fileInput\").click();'>Start Loader</a>";
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'/>";
}
loader += "<br><span style='float:right;cursor:pointer;' onclick=document.getElementById('blanketLoaderDialog').style.display='none';>Close</span>";
loader += "<div style='clear:both'></div>";
loader += "</div></div>";
var css = ".blanketDialogWrapper {";
css += "display:block;";
css += "position:fixed;";
css += "z-index:40001; }";
css += ".blanketDialogOverlay {";
css += "position:fixed;";
css += "width:100%;";
css += "height:100%;";
css += "background-color:black;";
css += "opacity:.5; ";
css += "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; ";
css += "filter:alpha(opacity=50); ";
css += "z-index:40001; }";
css += ".blanketDialogVerticalOffset { ";
css += "position:fixed;";
css += "top:30%;";
css += "width:100%;";
css += "z-index:40002; }";
css += ".blanketDialogBox { ";
css += "width:405px; ";
css += "position:relative;";
css += "margin:0 auto;";
css += "background-color:white;";
css += "padding:10px;";
css += "border:1px solid black; }";
var dom = document.createElement("style");
dom.innerHTML = css;
document.head.appendChild(dom);
var div = document.createElement("div");
div.id = "blanketLoaderDialog";
div.className = "blanketDialogWrapper";
div.innerHTML = loader;
document.body.insertBefore(div,document.body.firstChild);
},
manualFileLoader: function(files){
var toArray =Array.prototype.slice;
files = toArray.call(files).filter(function(item){
return item.type !== "";
});
var sessionLength = files.length-1;
var sessionIndx=0;
var sessionArray = {};
if (sessionStorage["blanketSessionLoader"]){
sessionArray = JSON.parse(sessionStorage["blanketSessionLoader"]);
}
var fileLoader = function(event){
var fileContent = event.currentTarget.result;
var file = files[sessionIndx];
var filename = file.webkitRelativePath && file.webkitRelativePath !== '' ? file.webkitRelativePath : file.name;
sessionArray[filename] = fileContent;
sessionIndx++;
if (sessionIndx === sessionLength){
sessionStorage.setItem("blanketSessionLoader", JSON.stringify(sessionArray));
document.location.reload();
}else{
readFile(files[sessionIndx]);
}
};
function readFile(file){
var reader = new FileReader();
reader.onload = fileLoader;
reader.readAsText(file);
}
readFile(files[sessionIndx]);
},
_loadFile: function(path){
if (typeof path !== "undefined"){
var request = new XMLHttpRequest();
request.open('GET', path, false);
request.send();
_blanket._addScript(request.responseText);
}
},
_addScript: function(data){
var script = document.createElement("script");
script.type = "text/javascript";
script.text = data;
(document.body || document.getElementsByTagName('head')[0]).appendChild(script);
},
hasAdapter: function(callback){
return _blanket.options("adapter") !== null;
},
report: function(coverage_data){
if (!document.getElementById("blanketLoaderDialog")){
//all found, clear it
_blanket.blanketSession = null;
}
coverage_data.files = window._$blanket;
var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
// Check if we have any covered files that requires reporting
// otherwise just exit gracefully.
if (!coverage_data.files || !Object.keys(coverage_data.files).length) {
if (_blanket.options("debug")) {console.log("BLANKET-Reporting No files were instrumented.");}
return;
}
if (typeof coverage_data.files.branchFcn !== "undefined"){
delete coverage_data.files.branchFcn;
}
if (typeof _blanket.options("reporter") === "string"){
_blanket._loadFile(_blanket.options("reporter"));
_blanket.customReporter(coverage_data,_blanket.options("reporter_options"));
}else if (typeof _blanket.options("reporter") === "function"){
_blanket.options("reporter")(coverage_data,_blanket.options("reporter_options"));
}else if (typeof _blanket.defaultReporter === 'function'){
_blanket.defaultReporter(coverage_data,_blanket.options("reporter_options"));
}else{
throw new Error("no reporter defined.");
}
},
_bindStartTestRunner: function(bindEvent,startEvent){
if (bindEvent){
bindEvent(startEvent);
}else{
window.addEventListener("load",startEvent,false);
}
},
_loadSourceFiles: function(callback){
var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
function copy(o){
var _copy = Object.create( Object.getPrototypeOf(o) );
var propNames = Object.getOwnPropertyNames(o);
propNames.forEach(function(name){
var desc = Object.getOwnPropertyDescriptor(o, name);
Object.defineProperty(_copy, name, desc);
});
return _copy;
}
if (_blanket.options("debug")) {console.log("BLANKET-Collecting page scripts");}
var scripts = _blanket.utils.collectPageScripts();
//_blanket.options("filter",scripts);
if (scripts.length === 0){
callback();
}else{
//check session state
if (sessionStorage["blanketSessionLoader"]){
_blanket.blanketSession = JSON.parse(sessionStorage["blanketSessionLoader"]);
}
scripts.forEach(function(file,indx){
_blanket.utils.cache[file]={
loaded:false
};
});
var currScript=-1;
_blanket.utils.loadAll(function(test){
if (test){
return typeof scripts[currScript+1] !== 'undefined';
}
currScript++;
if (currScript >= scripts.length){
return null;
}
return scripts[currScript];
},callback);
}
},
beforeStartTestRunner: function(opts){
opts = opts || {};
opts.checkRequirejs = typeof opts.checkRequirejs === "undefined" ? true : opts.checkRequirejs;
opts.callback = opts.callback || function() { };
opts.coverage = typeof opts.coverage === "undefined" ? true : opts.coverage;
if (opts.coverage) {
_blanket._bindStartTestRunner(opts.bindEvent,
function(){
_blanket._loadSourceFiles(function() {
var allLoaded = function(){
return opts.condition ? opts.condition() : _blanket.requireFilesLoaded();
};
var check = function() {
if (allLoaded()) {
if (_blanket.options("debug")) {console.log("BLANKET-All files loaded, init start test runner callback.");}
var cb = _blanket.options("testReadyCallback");
if (cb){
if (typeof cb === "function"){
cb(opts.callback);
}else if (typeof cb === "string"){
_blanket._addScript(cb);
opts.callback();
}
}else{
opts.callback();
}
} else {
setTimeout(check, 13);
}
};
check();
});
});
}else{
opts.callback();
}
},
utils: {
qualifyURL: function (url) {
//http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
var a = document.createElement('a');
a.href = url;
return a.href;
}
}
});
})(blanket);
blanket.defaultReporter = function(coverage){
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;}",
successRate = 60,
head = document.head,
fileNumber = 0,
body = document.body,
headerContent,
hasBranchTracking = Object.keys(coverage.files).some(function(elem){
return typeof coverage.files[elem].branchData !== 'undefined';
}),
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>",
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>";
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>";
function blanket_toggleSource(id) {
var element = document.getElementById(id);
if(element.style.display === 'block') {
element.style.display = 'none';
} else {
element.style.display = 'block';
}
}
var script = document.createElement("script");
script.type = "text/javascript";
script.text = blanket_toggleSource.toString().replace('function ' + blanket_toggleSource.name, 'function blanket_toggleSource');
body.appendChild(script);
var percentage = function(number, total) {
return (Math.round(((number/total) * 100)*100)/100);
};
var appendTag = function (type, el, str) {
var dom = document.createElement(type);
dom.innerHTML = str;
el.appendChild(dom);
};
function escapeInvalidXmlChars(str) {
return str.replace(/\&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/\>/g, "&gt;")
.replace(/\"/g, "&quot;")
.replace(/\'/g, "&apos;");
}
function isBranchFollowed(data,bool){
var mode = bool ? 0 : 1;
if (typeof data === 'undefined' ||
typeof data === null ||
typeof data[mode] === 'undefined'){
return false;
}
return data[mode].length > 0;
}
var branchStack = [];
function branchReport(colsIndex,src,cols,offset,lineNum){
var newsrc="";
var postfix="";
if (branchStack.length > 0){
newsrc += "<span class='" + (isBranchFollowed(branchStack[0][1],branchStack[0][1].consequent === branchStack[0][0]) ? 'branchOkay' : 'branchWarning') + "'>";
if (branchStack[0][0].end.line === lineNum){
newsrc += escapeInvalidXmlChars(src.slice(0,branchStack[0][0].end.column)) + "</span>";
src = src.slice(branchStack[0][0].end.column);
branchStack.shift();
if (branchStack.length > 0){
newsrc += "<span class='" + (isBranchFollowed(branchStack[0][1],false) ? 'branchOkay' : 'branchWarning') + "'>";
if (branchStack[0][0].end.line === lineNum){
newsrc += escapeInvalidXmlChars(src.slice(0,branchStack[0][0].end.column)) + "</span>";
src = src.slice(branchStack[0][0].end.column);
branchStack.shift();
if (!cols){
return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
}
}
else if (!cols){
return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
}
else{
postfix = "</span>";
}
}else if (!cols){
return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
}
}else if(!cols){
return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
}else{
postfix = "</span>";
}
}
var thisline = cols[colsIndex];
//consequent
var cons = thisline.consequent;
if (cons.start.line > lineNum){
branchStack.unshift([thisline.alternate,thisline]);
branchStack.unshift([cons,thisline]);
src = escapeInvalidXmlChars(src);
}else{
var style = "<span class='" + (isBranchFollowed(thisline,true) ? 'branchOkay' : 'branchWarning') + "'>";
newsrc += escapeInvalidXmlChars(src.slice(0,cons.start.column-offset)) + style;
if (cols.length > colsIndex+1 &&
cols[colsIndex+1].consequent.start.line === lineNum &&
cols[colsIndex+1].consequent.start.column-offset < cols[colsIndex].consequent.end.column-offset)
{
var res = branchReport(colsIndex+1,src.slice(cons.start.column-offset,cons.end.column-offset),cols,cons.start.column-offset,lineNum);
newsrc += res.src;
cols = res.cols;
cols[colsIndex+1] = cols[colsIndex+2];
cols.length--;
}else{
newsrc += escapeInvalidXmlChars(src.slice(cons.start.column-offset,cons.end.column-offset));
}
newsrc += "</span>";
var alt = thisline.alternate;
if (alt.start.line > lineNum){
newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset));
branchStack.unshift([alt,thisline]);
}else{
newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset,alt.start.column-offset));
style = "<span class='" + (isBranchFollowed(thisline,false) ? 'branchOkay' : 'branchWarning') + "'>";
newsrc += style;
if (cols.length > colsIndex+1 &&
cols[colsIndex+1].consequent.start.line === lineNum &&
cols[colsIndex+1].consequent.start.column-offset < cols[colsIndex].alternate.end.column-offset)
{
var res2 = branchReport(colsIndex+1,src.slice(alt.start.column-offset,alt.end.column-offset),cols,alt.start.column-offset,lineNum);
newsrc += res2.src;
cols = res2.cols;
cols[colsIndex+1] = cols[colsIndex+2];
cols.length--;
}else{
newsrc += escapeInvalidXmlChars(src.slice(alt.start.column-offset,alt.end.column-offset));
}
newsrc += "</span>";
newsrc += escapeInvalidXmlChars(src.slice(alt.end.column-offset));
src = newsrc;
}
}
return {src:src+postfix, cols:cols};
}
var isUndefined = function(item){
return typeof item !== 'undefined';
};
var files = coverage.files;
var totals = {
totalSmts: 0,
numberOfFilesCovered: 0,
passedBranches: 0,
totalBranches: 0,
moduleTotalStatements : {},
moduleTotalCoveredStatements : {},
moduleTotalBranches : {},
moduleTotalCoveredBranches : {}
};
// check if a data-cover-modulepattern was provided for per-module coverage reporting
var modulePattern = _blanket.options("modulePattern");
var modulePatternRegex = ( modulePattern ? new RegExp(modulePattern) : null );
for(var file in files)
{
fileNumber++;
var statsForFile = files[file],
totalSmts = 0,
numberOfFilesCovered = 0,
code = [],
i;
var end = [];
for(i = 0; i < statsForFile.source.length; i +=1){
var src = statsForFile.source[i];
if (branchStack.length > 0 ||
typeof statsForFile.branchData !== 'undefined')
{
if (typeof statsForFile.branchData[i+1] !== 'undefined')
{
var cols = statsForFile.branchData[i+1].filter(isUndefined);
var colsIndex=0;
src = branchReport(colsIndex,src,cols,0,i+1).src;
}else if (branchStack.length){
src = branchReport(0,src,null,0,i+1).src;
}else{
src = escapeInvalidXmlChars(src);
}
}else{
src = escapeInvalidXmlChars(src);
}
var lineClass="";
if(statsForFile[i+1]) {
numberOfFilesCovered += 1;
totalSmts += 1;
lineClass = 'hit';
}else{
if(statsForFile[i+1] === 0){
totalSmts++;
lineClass = 'miss';
}
}
code[i + 1] = "<div class='"+lineClass+"'><span class=''>"+(i + 1)+"</span>"+src+"</div>";
}
totals.totalSmts += totalSmts;
totals.numberOfFilesCovered += numberOfFilesCovered;
var totalBranches=0;
var passedBranches=0;
if (typeof statsForFile.branchData !== 'undefined'){
for(var j=0;j<statsForFile.branchData.length;j++){
if (typeof statsForFile.branchData[j] !== 'undefined'){
for(var k=0;k<statsForFile.branchData[j].length;k++){
if (typeof statsForFile.branchData[j][k] !== 'undefined'){
totalBranches++;
if (typeof statsForFile.branchData[j][k][0] !== 'undefined' &&
statsForFile.branchData[j][k][0].length > 0 &&
typeof statsForFile.branchData[j][k][1] !== 'undefined' &&
statsForFile.branchData[j][k][1].length > 0){
passedBranches++;
}
}
}
}
}
}
totals.passedBranches += passedBranches;
totals.totalBranches += totalBranches;
// if "data-cover-modulepattern" was provided,
// track totals per module name as well as globally
if (modulePatternRegex) {
var moduleName = file.match(modulePatternRegex)[1];
if(!totals.moduleTotalStatements.hasOwnProperty(moduleName)) {
totals.moduleTotalStatements[moduleName] = 0;
totals.moduleTotalCoveredStatements[moduleName] = 0;
}
totals.moduleTotalStatements[moduleName] += totalSmts;
totals.moduleTotalCoveredStatements[moduleName] += numberOfFilesCovered;
if(!totals.moduleTotalBranches.hasOwnProperty(moduleName)) {
totals.moduleTotalBranches[moduleName] = 0;
totals.moduleTotalCoveredBranches[moduleName] = 0;
}
totals.moduleTotalBranches[moduleName] += totalBranches;
totals.moduleTotalCoveredBranches[moduleName] += passedBranches;
}
var result = percentage(numberOfFilesCovered, totalSmts);
var output = fileTemplate.replace("{{file}}", file)
.replace("{{percentage}}",result)
.replace("{{numberCovered}}", numberOfFilesCovered)
.replace(/\{\{fileNumber\}\}/g, fileNumber)
.replace("{{totalSmts}}", totalSmts)
.replace("{{totalBranches}}", totalBranches)
.replace("{{passedBranches}}", passedBranches)
.replace("{{source}}", code.join(" "));
if(result < successRate)
{
output = output.replace("{{statusclass}}", "bl-error");
} else {
output = output.replace("{{statusclass}}", "bl-success");
}
bodyContent += output;
}
// create temporary function for use by the global totals reporter,
// as well as the per-module totals reporter
var createAggregateTotal = function(numSt, numCov, numBranch, numCovBr, moduleName) {
var totalPercent = percentage(numCov, numSt);
var statusClass = totalPercent < successRate ? "bl-error" : "bl-success";
var rowTitle = ( moduleName ? "Total for module: " + moduleName : "Global total" );
var totalsOutput = grandTotalTemplate.replace("{{rowTitle}}", rowTitle)
.replace("{{percentage}}", totalPercent)
.replace("{{numberCovered}}", numCov)
.replace("{{totalSmts}}", numSt)
.replace("{{passedBranches}}", numCovBr)
.replace("{{totalBranches}}", numBranch)
.replace("{{statusclass}}", statusClass);
bodyContent += totalsOutput;
};
// if "data-cover-modulepattern" was provided,
// output the per-module totals alongside the global totals
if (modulePatternRegex) {
for (var thisModuleName in totals.moduleTotalStatements) {
if (totals.moduleTotalStatements.hasOwnProperty(thisModuleName)) {
var moduleTotalSt = totals.moduleTotalStatements[thisModuleName];
var moduleTotalCovSt = totals.moduleTotalCoveredStatements[thisModuleName];
var moduleTotalBr = totals.moduleTotalBranches[thisModuleName];
var moduleTotalCovBr = totals.moduleTotalCoveredBranches[thisModuleName];
createAggregateTotal(moduleTotalSt, moduleTotalCovSt, moduleTotalBr, moduleTotalCovBr, thisModuleName);
}
}
}
createAggregateTotal(totals.totalSmts, totals.numberOfFilesCovered, totals.totalBranches, totals.passedBranches, null);
bodyContent += "</div>"; //closing main
appendTag('style', head, cssSytle);
//appendStyle(body, headerContent);
if (document.getElementById("blanket-main")){
document.getElementById("blanket-main").innerHTML=
bodyContent.slice(23,-6);
}else{
appendTag('div', body, bodyContent);
}
//appendHtml(body, '</div>');
};
(function(){
var newOptions={};
//http://stackoverflow.com/a/2954896
var toArray =Array.prototype.slice;
var scripts = toArray.call(document.scripts);
toArray.call(scripts[scripts.length - 1].attributes)
.forEach(function(es){
if(es.nodeName === "data-cover-only"){
newOptions.filter = es.nodeValue;
}
if(es.nodeName === "data-cover-never"){
newOptions.antifilter = es.nodeValue;
}
if(es.nodeName === "data-cover-reporter"){
newOptions.reporter = es.nodeValue;
}
if (es.nodeName === "data-cover-adapter"){
newOptions.adapter = es.nodeValue;
}
if (es.nodeName === "data-cover-loader"){
newOptions.loader = es.nodeValue;
}
if (es.nodeName === "data-cover-timeout"){
newOptions.timeout = es.nodeValue;
}
if (es.nodeName === "data-cover-modulepattern") {
newOptions.modulePattern = es.nodeValue;
}
if (es.nodeName === "data-cover-reporter-options"){
try{
newOptions.reporter_options = JSON.parse(es.nodeValue);
}catch(e){
if (blanket.options("debug")){
throw new Error("Invalid reporter options. Must be a valid stringified JSON object.");
}
}
}
if (es.nodeName === "data-cover-testReadyCallback"){
newOptions.testReadyCallback = es.nodeValue;
}
if (es.nodeName === "data-cover-customVariable"){
newOptions.customVariable = es.nodeValue;
}
if (es.nodeName === "data-cover-flags"){
var flags = " "+es.nodeValue+" ";
if (flags.indexOf(" ignoreError ") > -1){
newOptions.ignoreScriptError = true;
}
if (flags.indexOf(" autoStart ") > -1){
newOptions.autoStart = true;
}
if (flags.indexOf(" ignoreCors ") > -1){
newOptions.ignoreCors = true;
}
if (flags.indexOf(" branchTracking ") > -1){
newOptions.branchTracking = true;
}
if (flags.indexOf(" sourceURL ") > -1){
newOptions.sourceURL = true;
}
if (flags.indexOf(" debug ") > -1){
newOptions.debug = true;
}
if (flags.indexOf(" engineOnly ") > -1){
newOptions.engineOnly = true;
}
if (flags.indexOf(" commonJS ") > -1){
newOptions.commonJS = true;
}
if (flags.indexOf(" instrumentCache ") > -1){
newOptions.instrumentCache = true;
}
}
});
blanket.options(newOptions);
if (typeof requirejs !== 'undefined'){
blanket.options("existingRequireJS",true);
}
/* setup requirejs loader, if needed */
if (blanket.options("commonJS")){
blanket._commonjs = {};
}
})();
(function(_blanket){
_blanket.extend({
utils: {
normalizeBackslashes: function(str) {
return str.replace(/\\/g, '/');
},
matchPatternAttribute: function(filename,pattern){
if (typeof pattern === 'string'){
if (pattern.indexOf("[") === 0){
//treat as array
var pattenArr = pattern.slice(1,pattern.length-1).split(",");
return pattenArr.some(function(elem){
return _blanket.utils.matchPatternAttribute(filename,_blanket.utils.normalizeBackslashes(elem.slice(1,-1)));
//return filename.indexOf(_blanket.utils.normalizeBackslashes(elem.slice(1,-1))) > -1;
});
}else if ( pattern.indexOf("//") === 0){
var ex = pattern.slice(2,pattern.lastIndexOf('/'));
var mods = pattern.slice(pattern.lastIndexOf('/')+1);
var regex = new RegExp(ex,mods);
return regex.test(filename);
}else if (pattern.indexOf("#") === 0){
return window[pattern.slice(1)].call(window,filename);
}else{
return filename.indexOf(_blanket.utils.normalizeBackslashes(pattern)) > -1;
}
}else if ( pattern instanceof Array ){
return pattern.some(function(elem){
return _blanket.utils.matchPatternAttribute(filename,elem);
});
}else if (pattern instanceof RegExp){
return pattern.test(filename);
}else if (typeof pattern === "function"){
return pattern.call(window,filename);
}
},
blanketEval: function(data){
_blanket._addScript(data);
},
collectPageScripts: function(){
var toArray = Array.prototype.slice;
var scripts = toArray.call(document.scripts);
var selectedScripts=[],scriptNames=[];
var filter = _blanket.options("filter");
if(filter != null){
//global filter in place, data-cover-only
var antimatch = _blanket.options("antifilter");
selectedScripts = toArray.call(document.scripts)
.filter(function(s){
return toArray.call(s.attributes).filter(function(sn){
return sn.nodeName === "src" && _blanket.utils.matchPatternAttribute(sn.nodeValue,filter) &&
(typeof antimatch === "undefined" || !_blanket.utils.matchPatternAttribute(sn.nodeValue,antimatch));
}).length === 1;
});
}else{
selectedScripts = toArray.call(document.querySelectorAll("script[data-cover]"));
}
scriptNames = selectedScripts.map(function(s){
return _blanket.utils.qualifyURL(
toArray.call(s.attributes).filter(
function(sn){
return sn.nodeName === "src";
})[0].nodeValue);
});
if (!filter){
_blanket.options("filter","['"+scriptNames.join("','")+"']");
}
return scriptNames;
},
loadAll: function(nextScript,cb,preprocessor){
/**
* load dependencies
* @param {nextScript} factory for priority level
* @param {cb} the done callback
*/
var currScript=nextScript();
var isLoaded = _blanket.utils.scriptIsLoaded(
currScript,
_blanket.utils.ifOrdered,
nextScript,
cb
);
if (!(_blanket.utils.cache[currScript] && _blanket.utils.cache[currScript].loaded)){
var attach = function(){
if (_blanket.options("debug")) {console.log("BLANKET-Mark script:"+currScript+", as loaded and move to next script.");}
isLoaded();
};
var whenDone = function(result){
if (_blanket.options("debug")) {console.log("BLANKET-File loading finished");}
if (typeof result !== 'undefined'){
if (_blanket.options("debug")) {console.log("BLANKET-Add file to DOM.");}
_blanket._addScript(result);
}
attach();
};
_blanket.utils.attachScript(
{
url: currScript
},
function (content){
_blanket.utils.processFile(
content,
currScript,
whenDone,
whenDone
);
}
);
}else{
isLoaded();
}
},
attachScript: function(options,cb){
var timeout = _blanket.options("timeout") || 3000;
setTimeout(function(){
if (!_blanket.utils.cache[options.url].loaded){
throw new Error("error loading source script");
}
},timeout);
_blanket.utils.getFile(
options.url,
cb,
function(){ throw new Error("error loading source script");}
);
},
ifOrdered: function(nextScript,cb){
/**
* ordered loading callback
* @param {nextScript} factory for priority level
* @param {cb} the done callback
*/
var currScript = nextScript(true);
if (currScript){
_blanket.utils.loadAll(nextScript,cb);
}else{
cb(new Error("Error in loading chain."));
}
},
scriptIsLoaded: function(url,orderedCb,nextScript,cb){
/**
* returns a callback that checks a loading list to see if a script is loaded.
* @param {orderedCb} callback if ordered loading is being done
* @param {nextScript} factory for next priority level
* @param {cb} the done callback
*/
if (_blanket.options("debug")) {console.log("BLANKET-Returning function");}
return function(){
if (_blanket.options("debug")) {console.log("BLANKET-Marking file as loaded: "+url);}
_blanket.utils.cache[url].loaded=true;
if (_blanket.utils.allLoaded()){
if (_blanket.options("debug")) {console.log("BLANKET-All files loaded");}
cb();
}else if (orderedCb){
//if it's ordered we need to
//traverse down to the next
//priority level
if (_blanket.options("debug")) {console.log("BLANKET-Load next file.");}
orderedCb(nextScript,cb);
}
};
},
cache: {},
allLoaded: function (){
/**
* check if depdencies are loaded in cache
*/
var cached = Object.keys(_blanket.utils.cache);
for (var i=0;i<cached.length;i++){
if (!_blanket.utils.cache[cached[i]].loaded){
return false;
}
}
return true;
},
processFile: function (content,url,cb,oldCb) {
var match = _blanket.options("filter");
//we check the never matches first
var antimatch = _blanket.options("antifilter");
if (typeof antimatch !== "undefined" &&
_blanket.utils.matchPatternAttribute(url,antimatch)
){
oldCb(content);
if (_blanket.options("debug")) {console.log("BLANKET-File will never be instrumented:"+url);}
_blanket.requiringFile(url,true);
}else if (_blanket.utils.matchPatternAttribute(url,match)){
if (_blanket.options("debug")) {console.log("BLANKET-Attempting instrument of:"+url);}
_blanket.instrument({
inputFile: content,
inputFileName: url
},function(instrumented){
try{
if (_blanket.options("debug")) {console.log("BLANKET-instrument of:"+url+" was successfull.");}
_blanket.utils.blanketEval(instrumented);
cb();
_blanket.requiringFile(url,true);
}
catch(err){
if (_blanket.options("ignoreScriptError")){
//we can continue like normal if
//we're ignoring script errors,
//but otherwise we don't want
//to completeLoad or the error might be
//missed.
if (_blanket.options("debug")) { console.log("BLANKET-There was an error loading the file:"+url); }
cb(content);
_blanket.requiringFile(url,true);
}else{
throw new Error("Error parsing instrumented code: "+err);
}
}
});
}else{
if (_blanket.options("debug")) { console.log("BLANKET-Loading (without instrumenting) the file:"+url);}
oldCb(content);
_blanket.requiringFile(url,true);
}
},
cacheXhrConstructor: function(){
var Constructor, createXhr, i, progId;
if (typeof XMLHttpRequest !== "undefined") {
Constructor = XMLHttpRequest;
this.createXhr = function() { return new Constructor(); };
} else if (typeof ActiveXObject !== "undefined") {
Constructor = ActiveXObject;
for (i = 0; i < 3; i += 1) {
progId = progIds[i];
try {
new ActiveXObject(progId);
break;
} catch (e) {}
}
this.createXhr = function() { return new Constructor(progId); };
}
},
craeteXhr: function () {
throw new Error("cacheXhrConstructor is supposed to overwrite this function.");
},
getFile: function(url, callback, errback, onXhr){
var foundInSession = false;
if (_blanket.blanketSession){
var files = Object.keys(_blanket.blanketSession);
for (var i=0; i<files.length;i++ ){
var key = files[i];
if (url.indexOf(key) > -1){
callback(_blanket.blanketSession[key]);
foundInSession=true;
return;
}
}
}
if (!foundInSession){
var xhr = _blanket.utils.createXhr();
xhr.open('GET', url, true);
//Allow overrides specified in config
if (onXhr) {
onXhr(xhr, url);
}
xhr.onreadystatechange = function (evt) {
var status, err;
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
status = xhr.status;
if ((status > 399 && status < 600) /*||
(status === 0 &&
navigator.userAgent.toLowerCase().indexOf('firefox') > -1)
*/ ) {
//An http 4xx or 5xx error. Signal an error.
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
errback(err);
} else {
callback(xhr.responseText);
}
}
};
try{
xhr.send(null);
}catch(e){
if (e.code && (e.code === 101 || e.code === 1012) && _blanket.options("ignoreCors") === false){
//running locally and getting error from browser
_blanket.showManualLoader();
} else {
throw e;
}
}
}
}
}
});
(function(){
var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
var requirejs = blanket.options("commonJS") ? blanket._commonjs.requirejs : window.requirejs;
if (!_blanket.options("engineOnly") && _blanket.options("existingRequireJS")){
_blanket.utils.oldloader = requirejs.load;
requirejs.load = function (context, moduleName, url) {
_blanket.requiringFile(url);
_blanket.utils.getFile(url,
function(content){
_blanket.utils.processFile(
content,
url,
function newLoader(){
context.completeLoad(moduleName);
},
function oldLoader(){
_blanket.utils.oldloader(context, moduleName, url);
}
);
}, function (err) {
_blanket.requiringFile();
throw err;
});
};
}
// Save the XHR constructor, just in case frameworks like Sinon would sandbox it.
_blanket.utils.cacheXhrConstructor();
})();
})(blanket);
(function(){
if (typeof QUnit !== 'undefined'){
//check to make sure requirejs is completed before we start the test runner
var allLoaded = function() {
return window.QUnit.config.queue.length > 0 && blanket.noConflict().requireFilesLoaded();
};
if (!QUnit.config.urlConfig[0].tooltip){
//older versions we run coverage automatically
//and we change how events are binded
QUnit.begin=function(){
blanket.noConflict().setupCoverage();
};
QUnit.done=function(failures, total) {
blanket.noConflict().onTestsDone();
};
QUnit.moduleStart=function( details ) {
blanket.noConflict().onModuleStart();
};
QUnit.testStart=function( details ) {
blanket.noConflict().onTestStart();
};
QUnit.testDone=function( details ) {
blanket.noConflict().onTestDone(details.total,details.passed);
};
blanket.beforeStartTestRunner({
condition: allLoaded,
callback: QUnit.start
});
}else{
QUnit.config.urlConfig.push({
id: "coverage",
label: "Enable coverage",
tooltip: "Enable code coverage."
});
if ( QUnit.urlParams.coverage || blanket.options("autoStart") ) {
QUnit.begin(function(){
blanket.noConflict().setupCoverage();
});
QUnit.done(function(failures, total) {
blanket.noConflict().onTestsDone();
});
QUnit.moduleStart(function( details ) {
blanket.noConflict().onModuleStart();
});
QUnit.testStart(function( details ) {
blanket.noConflict().onTestStart();
});
QUnit.testDone(function( details ) {
blanket.noConflict().onTestDone(details.total,details.passed);
});
blanket.noConflict().beforeStartTestRunner({
condition: allLoaded,
callback: function(){
if (!(blanket.options("existingRequireJS") && !blanket.options("autoStart"))){
QUnit.start();
}
}
});
}else{
if (blanket.options("existingRequireJS")){ requirejs.load = _blanket.utils.oldloader; }
blanket.noConflict().beforeStartTestRunner({
condition: allLoaded,
callback: function(){
if (!(blanket.options("existingRequireJS") && !blanket.options("autoStart"))){
QUnit.start();
}
},
coverage:false
});
}
}
}
})();
/*!
* QUnit 1.15.0
* http://qunitjs.com/
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-08-08T16:00Z
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699A4;
background-color: #0D3349;
font-size: 1.5em;
line-height: 1em;
font-weight: 400;
border-radius: 5px 5px 0 0;
}
#qunit-header a {
text-decoration: none;
color: #C2CCD1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #FFF;
}
#qunit-testrunner-toolbar label {
display: inline-block;
padding: 0 0.5em 0 0.1em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 1em 0.5em 1em;
color: #5E740B;
background-color: #EEE;
overflow: hidden;
}
#qunit-userAgent {
padding: 0.5em 1em 0.5em 1em;
background-color: #2B81AF;
color: #FFF;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 1em 0.4em 1em;
border-bottom: 1px solid #FFF;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #C2CCD1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #FFF;
border-radius: 5px;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: 0.2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 0.5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #E0F2BE;
color: #374E0C;
text-decoration: none;
}
#qunit-tests ins {
background-color: #FFCACA;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: #000; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #FFF;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3C510C;
background-color: #FFF;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #FFF;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
}
#qunit-tests .fail { color: #000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: #008000; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 1em 0.5em 1em;
color: #2B81AF;
background-color: #D2E0E6;
border-bottom: 1px solid #FFF;
}
#qunit-testresult .module-name {
font-weight: 700;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}
/*!
* QUnit 1.15.0
* http://qunitjs.com/
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-08-08T16:00Z
*/
(function( window ) {
var QUnit,
config,
onErrorFnPrev,
fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
// Keep a local reference to Date (GH-283)
Date = window.Date,
now = Date.now || function() {
return new Date().getTime();
},
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
defined = {
document: typeof window.document !== "undefined",
setTimeout: typeof window.setTimeout !== "undefined",
sessionStorage: (function() {
var x = "qunit-test-string";
try {
sessionStorage.setItem( x, x );
sessionStorage.removeItem( x );
return true;
} catch ( e ) {
return false;
}
}())
},
/**
* Provides a normalized error string, correcting an issue
* with IE 7 (and prior) where Error.prototype.toString is
* not properly implemented
*
* Based on http://es5.github.com/#x15.11.4.4
*
* @param {String|Error} error
* @return {String} error message
*/
errorString = function( error ) {
var name, message,
errorString = error.toString();
if ( errorString.substring( 0, 7 ) === "[object" ) {
name = error.name ? error.name.toString() : "Error";
message = error.message ? error.message.toString() : "";
if ( name && message ) {
return name + ": " + message;
} else if ( name ) {
return name;
} else if ( message ) {
return message;
} else {
return "Error";
}
} else {
return errorString;
}
},
/**
* Makes a clone of an object using only Array or Object as base,
* and copies over the own enumerable properties.
*
* @param {Object} obj
* @return {Object} New object with only the own properties (recursively).
*/
objectValues = function( obj ) {
var key, val,
vals = QUnit.is( "array", obj ) ? [] : {};
for ( key in obj ) {
if ( hasOwn.call( obj, key ) ) {
val = obj[ key ];
vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
}
}
return vals;
};
// Root QUnit object.
// `QUnit` initialized at top of scope
QUnit = {
// call on start of module test to prepend name to all tests
module: function( name, testEnvironment ) {
config.currentModule = name;
config.currentModuleTestEnvironment = testEnvironment;
config.modules[ name ] = true;
},
asyncTest: function( testName, expected, callback ) {
if ( arguments.length === 2 ) {
callback = expected;
expected = null;
}
QUnit.test( testName, expected, callback, true );
},
test: function( testName, expected, callback, async ) {
var test;
if ( arguments.length === 2 ) {
callback = expected;
expected = null;
}
test = new Test({
testName: testName,
expected: expected,
async: async,
callback: callback,
module: config.currentModule,
moduleTestEnvironment: config.currentModuleTestEnvironment,
stack: sourceFromStacktrace( 2 )
});
if ( !validTest( test ) ) {
return;
}
test.queue();
},
start: function( count ) {
var message;
// QUnit hasn't been initialized yet.
// Note: RequireJS (et al) may delay onLoad
if ( config.semaphore === undefined ) {
QUnit.begin(function() {
// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
setTimeout(function() {
QUnit.start( count );
});
});
return;
}
config.semaphore -= count || 1;
// don't start until equal number of stop-calls
if ( config.semaphore > 0 ) {
return;
}
// Set the starting time when the first test is run
QUnit.config.started = QUnit.config.started || now();
// ignore if start is called more often then stop
if ( config.semaphore < 0 ) {
config.semaphore = 0;
message = "Called start() while already started (QUnit.config.semaphore was 0 already)";
if ( config.current ) {
QUnit.pushFailure( message, sourceFromStacktrace( 2 ) );
} else {
throw new Error( message );
}
return;
}
// A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) {
setTimeout(function() {
if ( config.semaphore > 0 ) {
return;
}
if ( config.timeout ) {
clearTimeout( config.timeout );
}
config.blocking = false;
process( true );
}, 13 );
} else {
config.blocking = false;
process( true );
}
},
stop: function( count ) {
config.semaphore += count || 1;
config.blocking = true;
if ( config.testTimeout && defined.setTimeout ) {
clearTimeout( config.timeout );
config.timeout = setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
}, config.testTimeout );
}
}
};
// We use the prototype to distinguish between properties that should
// be exposed as globals (and in exports) and those that shouldn't
(function() {
function F() {}
F.prototype = QUnit;
QUnit = new F();
// Make F QUnit's constructor so that we can add to the prototype later
QUnit.constructor = F;
}());
/**
* Config object: Maintain internal state
* Later exposed as QUnit.config
* `config` initialized at top of scope
*/
config = {
// The queue of tests to run
queue: [],
// block until document ready
blocking: true,
// when enabled, show only failing tests
// gets persisted through sessionStorage and can be changed in UI via checkbox
hidepassed: false,
// by default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked
reorder: true,
// by default, modify document.title when suite is done
altertitle: true,
// by default, scroll to top of the page when suite is done
scrolltop: true,
// when enabled, all tests must call expect()
requireExpects: false,
// add checkboxes that are persisted in the query-string
// when enabled, the id is set to `true` as a `QUnit.config` property
urlConfig: [
{
id: "noglobals",
label: "Check for Globals",
tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
},
{
id: "notrycatch",
label: "No try-catch",
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
}
],
// Set of all modules.
modules: {},
callbacks: {}
};
// Initialize more QUnit.config and QUnit.urlParams
(function() {
var i, current,
location = window.location || { search: "", protocol: "file:" },
params = location.search.slice( 1 ).split( "&" ),
length = params.length,
urlParams = {};
if ( params[ 0 ] ) {
for ( i = 0; i < length; i++ ) {
current = params[ i ].split( "=" );
current[ 0 ] = decodeURIComponent( current[ 0 ] );
// allow just a key to turn on a flag, e.g., test.html?noglobals
current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
if ( urlParams[ current[ 0 ] ] ) {
urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
} else {
urlParams[ current[ 0 ] ] = current[ 1 ];
}
}
}
QUnit.urlParams = urlParams;
// String search anywhere in moduleName+testName
config.filter = urlParams.filter;
// Exact match of the module name
config.module = urlParams.module;
config.testNumber = [];
if ( urlParams.testNumber ) {
// Ensure that urlParams.testNumber is an array
urlParams.testNumber = [].concat( urlParams.testNumber );
for ( i = 0; i < urlParams.testNumber.length; i++ ) {
current = urlParams.testNumber[ i ];
config.testNumber.push( parseInt( current, 10 ) );
}
}
// Figure out if we're running the tests from a server or not
QUnit.isLocal = location.protocol === "file:";
}());
extend( QUnit, {
config: config,
// Safe object type checking
is: function( type, obj ) {
return QUnit.objectType( obj ) === type;
},
objectType: function( obj ) {
if ( typeof obj === "undefined" ) {
return "undefined";
}
// Consider: typeof null === object
if ( obj === null ) {
return "null";
}
var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
type = match && match[ 1 ] || "";
switch ( type ) {
case "Number":
if ( isNaN( obj ) ) {
return "nan";
}
return "number";
case "String":
case "Boolean":
case "Array":
case "Date":
case "RegExp":
case "Function":
return type.toLowerCase();
}
if ( typeof obj === "object" ) {
return "object";
}
return undefined;
},
url: function( params ) {
params = extend( extend( {}, QUnit.urlParams ), params );
var key,
querystring = "?";
for ( key in params ) {
if ( hasOwn.call( params, key ) ) {
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
}
return window.location.protocol + "//" + window.location.host +
window.location.pathname + querystring.slice( 0, -1 );
},
extend: extend
});
/**
* @deprecated: Created for backwards compatibility with test runner that set the hook function
* into QUnit.{hook}, instead of invoking it and passing the hook function.
* QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
* Doing this allows us to tell if the following methods have been overwritten on the actual
* QUnit object.
*/
extend( QUnit.constructor.prototype, {
// Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes
begin: registerLoggingCallback( "begin" ),
// done: { failed, passed, total, runtime }
done: registerLoggingCallback( "done" ),
// log: { result, actual, expected, message }
log: registerLoggingCallback( "log" ),
// testStart: { name }
testStart: registerLoggingCallback( "testStart" ),
// testDone: { name, failed, passed, total, runtime }
testDone: registerLoggingCallback( "testDone" ),
// moduleStart: { name }
moduleStart: registerLoggingCallback( "moduleStart" ),
// moduleDone: { name, failed, passed, total }
moduleDone: registerLoggingCallback( "moduleDone" )
});
QUnit.load = function() {
runLoggingCallbacks( "begin", {
totalTests: Test.count
});
// Initialize the configuration options
extend( config, {
stats: { all: 0, bad: 0 },
moduleStats: { all: 0, bad: 0 },
started: 0,
updateRate: 1000,
autostart: true,
filter: "",
semaphore: 1
}, true );
config.blocking = false;
if ( config.autostart ) {
QUnit.start();
}
};
// `onErrorFnPrev` initialized at top of scope
// Preserve other handlers
onErrorFnPrev = window.onerror;
// Cover uncaught exceptions
// Returning true will suppress the default browser handler,
// returning false will let it run.
window.onerror = function( error, filePath, linerNr ) {
var ret = false;
if ( onErrorFnPrev ) {
ret = onErrorFnPrev( error, filePath, linerNr );
}
// Treat return value as window.onerror itself does,
// Only do our handling if not suppressed.
if ( ret !== true ) {
if ( QUnit.config.current ) {
if ( QUnit.config.current.ignoreGlobalErrors ) {
return true;
}
QUnit.pushFailure( error, filePath + ":" + linerNr );
} else {
QUnit.test( "global failure", extend(function() {
QUnit.pushFailure( error, filePath + ":" + linerNr );
}, { validTest: validTest } ) );
}
return false;
}
return ret;
};
function done() {
config.autorun = true;
// Log the last module results
if ( config.previousModule ) {
runLoggingCallbacks( "moduleDone", {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
});
}
delete config.previousModule;
var runtime = now() - config.started,
passed = config.stats.all - config.stats.bad;
runLoggingCallbacks( "done", {
failed: config.stats.bad,
passed: passed,
total: config.stats.all,
runtime: runtime
});
}
/** @return Boolean: true if this test should be ran */
function validTest( test ) {
var include,
filter = config.filter && config.filter.toLowerCase(),
module = config.module && config.module.toLowerCase(),
fullName = ( test.module + ": " + test.testName ).toLowerCase();
// Internally-generated tests are always valid
if ( test.callback && test.callback.validTest === validTest ) {
delete test.callback.validTest;
return true;
}
if ( config.testNumber.length > 0 ) {
if ( inArray( test.testNumber, config.testNumber ) < 0 ) {
return false;
}
}
if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
return false;
}
if ( !filter ) {
return true;
}
include = filter.charAt( 0 ) !== "!";
if ( !include ) {
filter = filter.slice( 1 );
}
// If the filter matches, we need to honour include
if ( fullName.indexOf( filter ) !== -1 ) {
return include;
}
// Otherwise, do the opposite
return !include;
}
// Doesn't support IE6 to IE9
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
function extractStacktrace( e, offset ) {
offset = offset === undefined ? 4 : offset;
var stack, include, i;
if ( e.stacktrace ) {
// Opera 12.x
return e.stacktrace.split( "\n" )[ offset + 3 ];
} else if ( e.stack ) {
// Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node
stack = e.stack.split( "\n" );
if ( /^error$/i.test( stack[ 0 ] ) ) {
stack.shift();
}
if ( fileName ) {
include = [];
for ( i = offset; i < stack.length; i++ ) {
if ( stack[ i ].indexOf( fileName ) !== -1 ) {
break;
}
include.push( stack[ i ] );
}
if ( include.length ) {
return include.join( "\n" );
}
}
return stack[ offset ];
} else if ( e.sourceURL ) {
// Safari < 6
// exclude useless self-reference for generated Error objects
if ( /qunit.js$/.test( e.sourceURL ) ) {
return;
}
// for actual exceptions, this is useful
return e.sourceURL + ":" + e.line;
}
}
function sourceFromStacktrace( offset ) {
try {
throw new Error();
} catch ( e ) {
return extractStacktrace( e, offset );
}
}
function synchronize( callback, last ) {
config.queue.push( callback );
if ( config.autorun && !config.blocking ) {
process( last );
}
}
function process( last ) {
function next() {
process( last );
}
var start = now();
config.depth = config.depth ? config.depth + 1 : 1;
while ( config.queue.length && !config.blocking ) {
if ( !defined.setTimeout || config.updateRate <= 0 || ( ( now() - start ) < config.updateRate ) ) {
config.queue.shift()();
} else {
setTimeout( next, 13 );
break;
}
}
config.depth--;
if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
done();
}
}
function saveGlobal() {
config.pollution = [];
if ( config.noglobals ) {
for ( var key in window ) {
if ( hasOwn.call( window, key ) ) {
// in Opera sometimes DOM element ids show up here, ignore them
if ( /^qunit-test-output/.test( key ) ) {
continue;
}
config.pollution.push( key );
}
}
}
}
function checkPollution() {
var newGlobals,
deletedGlobals,
old = config.pollution;
saveGlobal();
newGlobals = diff( config.pollution, old );
if ( newGlobals.length > 0 ) {
QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
}
deletedGlobals = diff( old, config.pollution );
if ( deletedGlobals.length > 0 ) {
QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
}
}
// returns a new Array with the elements that are in a but not in b
function diff( a, b ) {
var i, j,
result = a.slice();
for ( i = 0; i < result.length; i++ ) {
for ( j = 0; j < b.length; j++ ) {
if ( result[ i ] === b[ j ] ) {
result.splice( i, 1 );
i--;
break;
}
}
}
return result;
}
function extend( a, b, undefOnly ) {
for ( var prop in b ) {
if ( hasOwn.call( b, prop ) ) {
// Avoid "Member not found" error in IE8 caused by messing with window.constructor
if ( !( prop === "constructor" && a === window ) ) {
if ( b[ prop ] === undefined ) {
delete a[ prop ];
} else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
a[ prop ] = b[ prop ];
}
}
}
}
return a;
}
function registerLoggingCallback( key ) {
// Initialize key collection of logging callback
if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
config.callbacks[ key ] = [];
}
return function( callback ) {
config.callbacks[ key ].push( callback );
};
}
function runLoggingCallbacks( key, args ) {
var i, l, callbacks;
callbacks = config.callbacks[ key ];
for ( i = 0, l = callbacks.length; i < l; i++ ) {
callbacks[ i ]( args );
}
}
// from jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
}
function Test( settings ) {
extend( this, settings );
this.assert = new Assert( this );
this.assertions = [];
this.testNumber = ++Test.count;
}
Test.count = 0;
Test.prototype = {
setup: function() {
if (
// Emit moduleStart when we're switching from one module to another
this.module !== config.previousModule ||
// They could be equal (both undefined) but if the previousModule property doesn't
// yet exist it means this is the first test in a suite that isn't wrapped in a
// module, in which case we'll just emit a moduleStart event for 'undefined'.
// Without this, reporters can get testStart before moduleStart which is a problem.
!hasOwn.call( config, "previousModule" )
) {
if ( hasOwn.call( config, "previousModule" ) ) {
runLoggingCallbacks( "moduleDone", {
name: config.previousModule,
failed: config.moduleStats.bad,
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all
});
}
config.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 };
runLoggingCallbacks( "moduleStart", {
name: this.module
});
}
config.current = this;
this.testEnvironment = extend({
setup: function() {},
teardown: function() {}
}, this.moduleTestEnvironment );
this.started = now();
runLoggingCallbacks( "testStart", {
name: this.testName,
module: this.module,
testNumber: this.testNumber
});
if ( !config.pollution ) {
saveGlobal();
}
if ( config.notrycatch ) {
this.testEnvironment.setup.call( this.testEnvironment, this.assert );
return;
}
try {
this.testEnvironment.setup.call( this.testEnvironment, this.assert );
} catch ( e ) {
this.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
}
},
run: function() {
config.current = this;
if ( this.async ) {
QUnit.stop();
}
this.callbackStarted = now();
if ( config.notrycatch ) {
this.callback.call( this.testEnvironment, this.assert );
this.callbackRuntime = now() - this.callbackStarted;
return;
}
try {
this.callback.call( this.testEnvironment, this.assert );
this.callbackRuntime = now() - this.callbackStarted;
} catch ( e ) {
this.callbackRuntime = now() - this.callbackStarted;
this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
// else next test will carry the responsibility
saveGlobal();
// Restart the tests if they're blocking
if ( config.blocking ) {
QUnit.start();
}
}
},
teardown: function() {
config.current = this;
if ( config.notrycatch ) {
if ( typeof this.callbackRuntime === "undefined" ) {
this.callbackRuntime = now() - this.callbackStarted;
}
this.testEnvironment.teardown.call( this.testEnvironment, this.assert );
return;
} else {
try {
this.testEnvironment.teardown.call( this.testEnvironment, this.assert );
} catch ( e ) {
this.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
}
}
checkPollution();
},
finish: function() {
config.current = this;
if ( config.requireExpects && this.expected === null ) {
this.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
} else if ( this.expected !== null && this.expected !== this.assertions.length ) {
this.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
} else if ( this.expected === null && !this.assertions.length ) {
this.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
}
var i,
bad = 0;
this.runtime = now() - this.started;
config.stats.all += this.assertions.length;
config.moduleStats.all += this.assertions.length;
for ( i = 0; i < this.assertions.length; i++ ) {
if ( !this.assertions[ i ].result ) {
bad++;
config.stats.bad++;
config.moduleStats.bad++;
}
}
runLoggingCallbacks( "testDone", {
name: this.testName,
module: this.module,
failed: bad,
passed: this.assertions.length - bad,
total: this.assertions.length,
runtime: this.runtime,
// HTML Reporter use
assertions: this.assertions,
testNumber: this.testNumber,
// DEPRECATED: this property will be removed in 2.0.0, use runtime instead
duration: this.runtime
});
config.current = undefined;
},
queue: function() {
var bad,
test = this;
function run() {
// each of these can by async
synchronize(function() {
test.setup();
});
synchronize(function() {
test.run();
});
synchronize(function() {
test.teardown();
});
synchronize(function() {
test.finish();
});
}
// `bad` initialized at top of scope
// defer when previous test run passed, if storage is available
bad = QUnit.config.reorder && defined.sessionStorage &&
+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
if ( bad ) {
run();
} else {
synchronize( run, true );
}
},
push: function( result, actual, expected, message ) {
var source,
details = {
module: this.module,
name: this.testName,
result: result,
message: message,
actual: actual,
expected: expected,
testNumber: this.testNumber
};
if ( !result ) {
source = sourceFromStacktrace();
if ( source ) {
details.source = source;
}
}
runLoggingCallbacks( "log", details );
this.assertions.push({
result: !!result,
message: message
});
},
pushFailure: function( message, source, actual ) {
if ( !this instanceof Test ) {
throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace( 2 ) );
}
var details = {
module: this.module,
name: this.testName,
result: false,
message: message || "error",
actual: actual || null,
testNumber: this.testNumber
};
if ( source ) {
details.source = source;
}
runLoggingCallbacks( "log", details );
this.assertions.push({
result: false,
message: message
});
}
};
QUnit.pushFailure = function() {
if ( !QUnit.config.current ) {
throw new Error( "pushFailure() assertion outside test context, in " + sourceFromStacktrace( 2 ) );
}
// Gets current test obj
var currentTest = QUnit.config.current.assert.test;
return currentTest.pushFailure.apply( currentTest, arguments );
};
function Assert( testContext ) {
this.test = testContext;
}
// Assert helpers
QUnit.assert = Assert.prototype = {
// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
expect: function( asserts ) {
if ( arguments.length === 1 ) {
this.test.expected = asserts;
} else {
return this.test.expected;
}
},
// Exports test.push() to the user API
push: function() {
var assert = this;
// Backwards compatibility fix.
// Allows the direct use of global exported assertions and QUnit.assert.*
// Although, it's use is not recommended as it can leak assertions
// to other tests from async tests, because we only get a reference to the current test,
// not exactly the test where assertion were intended to be called.
if ( !QUnit.config.current ) {
throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
}
if ( !( assert instanceof Assert ) ) {
assert = QUnit.config.current.assert;
}
return assert.test.push.apply( assert.test, arguments );
},
/**
* Asserts rough true-ish result.
* @name ok
* @function
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function( result, message ) {
message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
QUnit.dump.parse( result ) );
if ( !!result ) {
this.push( true, result, true, message );
} else {
this.test.pushFailure( message, null, result );
}
},
/**
* Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
* @name equal
* @function
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/
equal: function( actual, expected, message ) {
/*jshint eqeqeq:false */
this.push( expected == actual, actual, expected, message );
},
/**
* @name notEqual
* @function
*/
notEqual: function( actual, expected, message ) {
/*jshint eqeqeq:false */
this.push( expected != actual, actual, expected, message );
},
/**
* @name propEqual
* @function
*/
propEqual: function( actual, expected, message ) {
actual = objectValues( actual );
expected = objectValues( expected );
this.push( QUnit.equiv( actual, expected ), actual, expected, message );
},
/**
* @name notPropEqual
* @function
*/
notPropEqual: function( actual, expected, message ) {
actual = objectValues( actual );
expected = objectValues( expected );
this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
},
/**
* @name deepEqual
* @function
*/
deepEqual: function( actual, expected, message ) {
this.push( QUnit.equiv( actual, expected ), actual, expected, message );
},
/**
* @name notDeepEqual
* @function
*/
notDeepEqual: function( actual, expected, message ) {
this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
},
/**
* @name strictEqual
* @function
*/
strictEqual: function( actual, expected, message ) {
this.push( expected === actual, actual, expected, message );
},
/**
* @name notStrictEqual
* @function
*/
notStrictEqual: function( actual, expected, message ) {
this.push( expected !== actual, actual, expected, message );
},
"throws": function( block, expected, message ) {
var actual, expectedType,
expectedOutput = expected,
ok = false;
// 'expected' is optional unless doing string comparison
if ( message == null && typeof expected === "string" ) {
message = expected;
expected = null;
}
this.test.ignoreGlobalErrors = true;
try {
block.call( this.test.testEnvironment );
} catch (e) {
actual = e;
}
this.test.ignoreGlobalErrors = false;
if ( actual ) {
expectedType = QUnit.objectType( expected );
// we don't want to validate thrown error
if ( !expected ) {
ok = true;
expectedOutput = null;
// expected is a regexp
} else if ( expectedType === "regexp" ) {
ok = expected.test( errorString( actual ) );
// expected is a string
} else if ( expectedType === "string" ) {
ok = expected === errorString( actual );
// expected is a constructor, maybe an Error constructor
} else if ( expectedType === "function" && actual instanceof expected ) {
ok = true;
// expected is an Error object
} else if ( expectedType === "object" ) {
ok = actual instanceof expected.constructor &&
actual.name === expected.name &&
actual.message === expected.message;
// expected is a validation function which returns true if validation passed
} else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
expectedOutput = null;
ok = true;
}
this.push( ok, actual, expectedOutput, message );
} else {
this.test.pushFailure( message, null, "No exception was thrown." );
}
}
};
// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = (function() {
// Call the o related callback with the given arguments.
function bindCallbacks( o, callbacks, args ) {
var prop = QUnit.objectType( o );
if ( prop ) {
if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
return callbacks[ prop ].apply( callbacks, args );
} else {
return callbacks[ prop ]; // or undefined
}
}
}
// the real equiv function
var innerEquiv,
// stack to decide between skip/abort functions
callers = [],
// stack to avoiding loops from circular referencing
parents = [],
parentsB = [],
getProto = Object.getPrototypeOf || function( obj ) {
/* jshint camelcase: false, proto: true */
return obj.__proto__;
},
callbacks = (function() {
// for string, boolean, number and null
function useStrictEquality( b, a ) {
/*jshint eqeqeq:false */
if ( b instanceof a.constructor || a instanceof b.constructor ) {
// to catch short annotation VS 'new' annotation of a
// declaration
// e.g. var i = 1;
// var j = new Number(1);
return a == b;
} else {
return a === b;
}
}
return {
"string": useStrictEquality,
"boolean": useStrictEquality,
"number": useStrictEquality,
"null": useStrictEquality,
"undefined": useStrictEquality,
"nan": function( b ) {
return isNaN( b );
},
"date": function( b, a ) {
return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
},
"regexp": function( b, a ) {
return QUnit.objectType( b ) === "regexp" &&
// the regex itself
a.source === b.source &&
// and its modifiers
a.global === b.global &&
// (gmi) ...
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline &&
a.sticky === b.sticky;
},
// - skip when the property is a method of an instance (OOP)
// - abort otherwise,
// initial === would have catch identical references anyway
"function": function() {
var caller = callers[ callers.length - 1 ];
return caller !== Object && typeof caller !== "undefined";
},
"array": function( b, a ) {
var i, j, len, loop, aCircular, bCircular;
// b could be an object literal here
if ( QUnit.objectType( b ) !== "array" ) {
return false;
}
len = a.length;
if ( len !== b.length ) {
// safe and faster
return false;
}
// track reference to avoid circular references
parents.push( a );
parentsB.push( b );
for ( i = 0; i < len; i++ ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
aCircular = parents[ j ] === a[ i ];
bCircular = parentsB[ j ] === b[ i ];
if ( aCircular || bCircular ) {
if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
loop = true;
} else {
parents.pop();
parentsB.pop();
return false;
}
}
}
if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
parents.pop();
parentsB.pop();
return false;
}
}
parents.pop();
parentsB.pop();
return true;
},
"object": function( b, a ) {
/*jshint forin:false */
var i, j, loop, aCircular, bCircular,
// Default to true
eq = true,
aProperties = [],
bProperties = [];
// comparing constructors is more strict than using
// instanceof
if ( a.constructor !== b.constructor ) {
// Allow objects with no prototype to be equivalent to
// objects with Object as their constructor.
if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
return false;
}
}
// stack constructor before traversing properties
callers.push( a.constructor );
// track reference to avoid circular references
parents.push( a );
parentsB.push( b );
// be strict: don't ensure hasOwnProperty and go deep
for ( i in a ) {
loop = false;
for ( j = 0; j < parents.length; j++ ) {
aCircular = parents[ j ] === a[ i ];
bCircular = parentsB[ j ] === b[ i ];
if ( aCircular || bCircular ) {
if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
loop = true;
} else {
eq = false;
break;
}
}
}
aProperties.push( i );
if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
eq = false;
break;
}
}
parents.pop();
parentsB.pop();
callers.pop(); // unstack, we are done
for ( i in b ) {
bProperties.push( i ); // collect b's properties
}
// Ensures identical properties name
return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
}
};
}());
innerEquiv = function() { // can take multiple arguments
var args = [].slice.apply( arguments );
if ( args.length < 2 ) {
return true; // end transition
}
return ( (function( a, b ) {
if ( a === b ) {
return true; // catch the most you can
} else if ( a === null || b === null || typeof a === "undefined" ||
typeof b === "undefined" ||
QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
// don't lose time with error prone cases
return false;
} else {
return bindCallbacks( a, callbacks, [ b, a ] );
}
// apply transition with (1..n) arguments
}( args[ 0 ], args[ 1 ] ) ) && innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
};
return innerEquiv;
}());
// Based on jsDump by Ariel Flesler
// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
QUnit.dump = (function() {
function quote( str ) {
return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
}
function literal( o ) {
return o + "";
}
function join( pre, arr, post ) {
var s = dump.separator(),
base = dump.indent(),
inner = dump.indent( 1 );
if ( arr.join ) {
arr = arr.join( "," + s + inner );
}
if ( !arr ) {
return pre + post;
}
return [ pre, inner + arr, base + post ].join( s );
}
function array( arr, stack ) {
var i = arr.length,
ret = new Array( i );
this.up();
while ( i-- ) {
ret[ i ] = this.parse( arr[ i ], undefined, stack );
}
this.down();
return join( "[", ret, "]" );
}
var reName = /^function (\w+)/,
dump = {
// type is used mostly internally, you can fix a (custom)type in advance
parse: function( obj, type, stack ) {
stack = stack || [];
var inStack, res,
parser = this.parsers[ type || this.typeOf( obj ) ];
type = typeof parser;
inStack = inArray( obj, stack );
if ( inStack !== -1 ) {
return "recursion(" + ( inStack - stack.length ) + ")";
}
if ( type === "function" ) {
stack.push( obj );
res = parser.call( this, obj, stack );
stack.pop();
return res;
}
return ( type === "string" ) ? parser : this.parsers.error;
},
typeOf: function( obj ) {
var type;
if ( obj === null ) {
type = "null";
} else if ( typeof obj === "undefined" ) {
type = "undefined";
} else if ( QUnit.is( "regexp", obj ) ) {
type = "regexp";
} else if ( QUnit.is( "date", obj ) ) {
type = "date";
} else if ( QUnit.is( "function", obj ) ) {
type = "function";
} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
type = "window";
} else if ( obj.nodeType === 9 ) {
type = "document";
} else if ( obj.nodeType ) {
type = "node";
} else if (
// native arrays
toString.call( obj ) === "[object Array]" ||
// NodeList objects
( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && typeof obj[ 0 ] === "undefined" ) ) )
) {
type = "array";
} else if ( obj.constructor === Error.prototype.constructor ) {
type = "error";
} else {
type = typeof obj;
}
return type;
},
separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
},
// extra can be a number, shortcut for increasing-calling-decreasing
indent: function( extra ) {
if ( !this.multiline ) {
return "";
}
var chr = this.indentChar;
if ( this.HTML ) {
chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
}
return new Array( this.depth + ( extra || 0 ) ).join( chr );
},
up: function( a ) {
this.depth += a || 1;
},
down: function( a ) {
this.depth -= a || 1;
},
setParser: function( name, parser ) {
this.parsers[ name ] = parser;
},
// The next 3 are exposed so you can use them
quote: quote,
literal: literal,
join: join,
//
depth: 1,
// This is the list of parsers, to modify them, use dump.setParser
parsers: {
window: "[Window]",
document: "[Document]",
error: function( error ) {
return "Error(\"" + error.message + "\")";
},
unknown: "[Unknown]",
"null": "null",
"undefined": "undefined",
"function": function( fn ) {
var ret = "function",
// functions never have name in IE
name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
if ( name ) {
ret += " " + name;
}
ret += "( ";
ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
return join( ret, dump.parse( fn, "functionCode" ), "}" );
},
array: array,
nodelist: array,
"arguments": array,
object: function( map, stack ) {
/*jshint forin:false */
var ret = [], keys, key, val, i, nonEnumerableProperties;
dump.up();
keys = [];
for ( key in map ) {
keys.push( key );
}
// Some properties are not always enumerable on Error objects.
nonEnumerableProperties = [ "message", "name" ];
for ( i in nonEnumerableProperties ) {
key = nonEnumerableProperties[ i ];
if ( key in map && !( key in keys ) ) {
keys.push( key );
}
}
keys.sort();
for ( i = 0; i < keys.length; i++ ) {
key = keys[ i ];
val = map[ key ];
ret.push( dump.parse( key, "key" ) + ": " + dump.parse( val, undefined, stack ) );
}
dump.down();
return join( "{", ret, "}" );
},
node: function( node ) {
var len, i, val,
open = dump.HTML ? "&lt;" : "<",
close = dump.HTML ? "&gt;" : ">",
tag = node.nodeName.toLowerCase(),
ret = open + tag,
attrs = node.attributes;
if ( attrs ) {
for ( i = 0, len = attrs.length; i < len; i++ ) {
val = attrs[ i ].nodeValue;
// IE6 includes all attributes in .attributes, even ones not explicitly set.
// Those have values like undefined, null, 0, false, "" or "inherit".
if ( val && val !== "inherit" ) {
ret += " " + attrs[ i ].nodeName + "=" + dump.parse( val, "attribute" );
}
}
}
ret += close;
// Show content of TextNode or CDATASection
if ( node.nodeType === 3 || node.nodeType === 4 ) {
ret += node.nodeValue;
}
return ret + open + "/" + tag + close;
},
// function calls it internally, it's the arguments part of the function
functionArgs: function( fn ) {
var args,
l = fn.length;
if ( !l ) {
return "";
}
args = new Array( l );
while ( l-- ) {
// 97 is 'a'
args[ l ] = String.fromCharCode( 97 + l );
}
return " " + args.join( ", " ) + " ";
},
// object calls it internally, the key part of an item in a map
key: quote,
// function calls it internally, it's the content of the function
functionCode: "[code]",
// node calls it internally, it's an html attribute value
attribute: quote,
string: quote,
date: quote,
regexp: literal,
number: literal,
"boolean": literal
},
// if true, entities are escaped ( <, >, \t, space and \n )
HTML: false,
// indentation unit
indentChar: " ",
// if true, items in a collection, are separated by a \n, else just a space.
multiline: true
};
return dump;
}());
// back compat
QUnit.jsDump = QUnit.dump;
// For browser, export only select globals
if ( typeof window !== "undefined" ) {
// Deprecated
// Extend assert methods to QUnit and Global scope through Backwards compatibility
(function() {
var i,
assertions = Assert.prototype;
function applyCurrent( current ) {
return function() {
var assert = new Assert( QUnit.config.current );
current.apply( assert, arguments );
};
}
for ( i in assertions ) {
QUnit[ i ] = applyCurrent( assertions[ i ] );
}
})();
(function() {
var i, l,
keys = [
"test",
"module",
"expect",
"asyncTest",
"start",
"stop",
"ok",
"equal",
"notEqual",
"propEqual",
"notPropEqual",
"deepEqual",
"notDeepEqual",
"strictEqual",
"notStrictEqual",
"throws"
];
for ( i = 0, l = keys.length; i < l; i++ ) {
window[ keys[ i ] ] = QUnit[ keys[ i ] ];
}
})();
window.QUnit = QUnit;
}
// For CommonJS environments, export everything
if ( typeof module !== "undefined" && module.exports ) {
module.exports = QUnit;
}
// Get a reference to the global object, like window in browsers
}( (function() {
return this;
})() ));
/*istanbul ignore next */
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/)
* Modified by Chu Alan "sprite"
*
* Released under the MIT license.
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*
* Usage: QUnit.diff(expected, actual)
*
* QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
*/
QUnit.diff = (function() {
var hasOwn = Object.prototype.hasOwnProperty;
/*jshint eqeqeq:false, eqnull:true */
function diff( o, n ) {
var i,
ns = {},
os = {};
for ( i = 0; i < n.length; i++ ) {
if ( !hasOwn.call( ns, n[ i ] ) ) {
ns[ n[ i ] ] = {
rows: [],
o: null
};
}
ns[ n[ i ] ].rows.push( i );
}
for ( i = 0; i < o.length; i++ ) {
if ( !hasOwn.call( os, o[ i ] ) ) {
os[ o[ i ] ] = {
rows: [],
n: null
};
}
os[ o[ i ] ].rows.push( i );
}
for ( i in ns ) {
if ( hasOwn.call( ns, i ) ) {
if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) {
n[ ns[ i ].rows[ 0 ] ] = {
text: n[ ns[ i ].rows[ 0 ] ],
row: os[ i ].rows[ 0 ]
};
o[ os[ i ].rows[ 0 ] ] = {
text: o[ os[ i ].rows[ 0 ] ],
row: ns[ i ].rows[ 0 ]
};
}
}
}
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null &&
n[ i + 1 ] == o[ n[ i ].row + 1 ] ) {
n[ i + 1 ] = {
text: n[ i + 1 ],
row: n[ i ].row + 1
};
o[ n[ i ].row + 1 ] = {
text: o[ n[ i ].row + 1 ],
row: i + 1
};
}
}
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null &&
n[ i - 1 ] == o[ n[ i ].row - 1 ] ) {
n[ i - 1 ] = {
text: n[ i - 1 ],
row: n[ i ].row - 1
};
o[ n[ i ].row - 1 ] = {
text: o[ n[ i ].row - 1 ],
row: i - 1
};
}
}
return {
o: o,
n: n
};
}
return function( o, n ) {
o = o.replace( /\s+$/, "" );
n = n.replace( /\s+$/, "" );
var i, pre,
str = "",
out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ),
oSpace = o.match( /\s+/g ),
nSpace = n.match( /\s+/g );
if ( oSpace == null ) {
oSpace = [ " " ];
} else {
oSpace.push( " " );
}
if ( nSpace == null ) {
nSpace = [ " " ];
} else {
nSpace.push( " " );
}
if ( out.n.length === 0 ) {
for ( i = 0; i < out.o.length; i++ ) {
str += "<del>" + out.o[ i ] + oSpace[ i ] + "</del>";
}
} else {
if ( out.n[ 0 ].text == null ) {
for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) {
str += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
}
}
for ( i = 0; i < out.n.length; i++ ) {
if ( out.n[ i ].text == null ) {
str += "<ins>" + out.n[ i ] + nSpace[ i ] + "</ins>";
} else {
// `pre` initialized at top of scope
pre = "";
for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) {
pre += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
}
str += " " + out.n[ i ].text + nSpace[ i ] + pre;
}
}
}
return str;
};
}());
(function() {
// Deprecated QUnit.init - Ref #530
// Re-initialize the configuration options
QUnit.init = function() {
var tests, banner, result, qunit,
config = QUnit.config;
config.stats = { all: 0, bad: 0 };
config.moduleStats = { all: 0, bad: 0 };
config.started = 0;
config.updateRate = 1000;
config.blocking = false;
config.autostart = true;
config.autorun = false;
config.filter = "";
config.queue = [];
config.semaphore = 1;
// Return on non-browser environments
// This is necessary to not break on node tests
if ( typeof window === "undefined" ) {
return;
}
qunit = id( "qunit" );
if ( qunit ) {
qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
"<h2 id='qunit-banner'></h2>" +
"<div id='qunit-testrunner-toolbar'></div>" +
"<h2 id='qunit-userAgent'></h2>" +
"<ol id='qunit-tests'></ol>";
}
tests = id( "qunit-tests" );
banner = id( "qunit-banner" );
result = id( "qunit-testresult" );
if ( tests ) {
tests.innerHTML = "";
}
if ( banner ) {
banner.className = "";
}
if ( result ) {
result.parentNode.removeChild( result );
}
if ( tests ) {
result = document.createElement( "p" );
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests );
result.innerHTML = "Running...<br/>&nbsp;";
}
};
// Resets the test setup. Useful for tests that modify the DOM.
/*
DEPRECATED: Use multiple tests instead of resetting inside a test.
Use testStart or testDone for custom cleanup.
This method will throw an error in 2.0, and will be removed in 2.1
*/
QUnit.reset = function() {
// Return on non-browser environments
// This is necessary to not break on node tests
if ( typeof window === "undefined" ) {
return;
}
var fixture = id( "qunit-fixture" );
if ( fixture ) {
fixture.innerHTML = config.fixture;
}
};
// Don't load the HTML Reporter on non-Browser environments
if ( typeof window === "undefined" ) {
return;
}
var config = QUnit.config,
hasOwn = Object.prototype.hasOwnProperty,
defined = {
document: typeof window.document !== "undefined",
sessionStorage: (function() {
var x = "qunit-test-string";
try {
sessionStorage.setItem( x, x );
sessionStorage.removeItem( x );
return true;
} catch ( e ) {
return false;
}
}())
};
/**
* Escape text for attribute or text content.
*/
function escapeText( s ) {
if ( !s ) {
return "";
}
s = s + "";
// Both single quotes and double quotes (for attributes)
return s.replace( /['"<>&]/g, function( s ) {
switch ( s ) {
case "'":
return "&#039;";
case "\"":
return "&quot;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case "&":
return "&amp;";
}
});
}
/**
* @param {HTMLElement} elem
* @param {string} type
* @param {Function} fn
*/
function addEvent( elem, type, fn ) {
if ( elem.addEventListener ) {
// Standards-based browsers
elem.addEventListener( type, fn, false );
} else if ( elem.attachEvent ) {
// support: IE <9
elem.attachEvent( "on" + type, fn );
}
}
/**
* @param {Array|NodeList} elems
* @param {string} type
* @param {Function} fn
*/
function addEvents( elems, type, fn ) {
var i = elems.length;
while ( i-- ) {
addEvent( elems[ i ], type, fn );
}
}
function hasClass( elem, name ) {
return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
}
function addClass( elem, name ) {
if ( !hasClass( elem, name ) ) {
elem.className += ( elem.className ? " " : "" ) + name;
}
}
function toggleClass( elem, name ) {
if ( hasClass( elem, name ) ) {
removeClass( elem, name );
} else {
addClass( elem, name );
}
}
function removeClass( elem, name ) {
var set = " " + elem.className + " ";
// Class name may appear multiple times
while ( set.indexOf( " " + name + " " ) >= 0 ) {
set = set.replace( " " + name + " ", " " );
}
// trim for prettiness
elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
}
function id( name ) {
return defined.document && document.getElementById && document.getElementById( name );
}
function getUrlConfigHtml() {
var i, j, val,
escaped, escapedTooltip,
selection = false,
len = config.urlConfig.length,
urlConfigHtml = "";
for ( i = 0; i < len; i++ ) {
val = config.urlConfig[ i ];
if ( typeof val === "string" ) {
val = {
id: val,
label: val
};
}
escaped = escapeText( val.id );
escapedTooltip = escapeText( val.tooltip );
config[ val.id ] = QUnit.urlParams[ val.id ];
if ( !val.value || typeof val.value === "string" ) {
urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
"' name='" + escaped + "' type='checkbox'" +
( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
( config[ val.id ] ? " checked='checked'" : "" ) +
" title='" + escapedTooltip + "'><label for='qunit-urlconfig-" + escaped +
"' title='" + escapedTooltip + "'>" + val.label + "</label>";
} else {
urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
"' title='" + escapedTooltip + "'>" + val.label +
": </label><select id='qunit-urlconfig-" + escaped +
"' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
if ( QUnit.is( "array", val.value ) ) {
for ( j = 0; j < val.value.length; j++ ) {
escaped = escapeText( val.value[ j ] );
urlConfigHtml += "<option value='" + escaped + "'" +
( config[ val.id ] === val.value[ j ] ?
( selection = true ) && " selected='selected'" : "" ) +
">" + escaped + "</option>";
}
} else {
for ( j in val.value ) {
if ( hasOwn.call( val.value, j ) ) {
urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
( config[ val.id ] === j ?
( selection = true ) && " selected='selected'" : "" ) +
">" + escapeText( val.value[ j ] ) + "</option>";
}
}
}
if ( config[ val.id ] && !selection ) {
escaped = escapeText( config[ val.id ] );
urlConfigHtml += "<option value='" + escaped +
"' selected='selected' disabled='disabled'>" + escaped + "</option>";
}
urlConfigHtml += "</select>";
}
}
return urlConfigHtml;
}
function toolbarUrlConfigContainer() {
var urlConfigContainer = document.createElement( "span" );
urlConfigContainer.innerHTML = getUrlConfigHtml();
// For oldIE support:
// * Add handlers to the individual elements instead of the container
// * Use "click" instead of "change" for checkboxes
// * Fallback from event.target to event.srcElement
addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", function( event ) {
var params = {},
target = event.target || event.srcElement;
params[ target.name ] = target.checked ?
target.defaultValue || true :
undefined;
window.location = QUnit.url( params );
});
addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", function( event ) {
var params = {},
target = event.target || event.srcElement;
params[ target.name ] = target.options[ target.selectedIndex ].value || undefined;
window.location = QUnit.url( params );
});
return urlConfigContainer;
}
function getModuleNames() {
var i,
moduleNames = [];
for ( i in config.modules ) {
if ( config.modules.hasOwnProperty( i ) ) {
moduleNames.push( i );
}
}
moduleNames.sort(function( a, b ) {
return a.localeCompare( b );
});
return moduleNames;
}
function toolbarModuleFilterHtml() {
var i,
moduleFilterHtml = "",
moduleNames = getModuleNames();
if ( moduleNames.length <= 1 ) {
return false;
}
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
"<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
( config.module === undefined ? "selected='selected'" : "" ) +
">< All Modules ></option>";
for ( i = 0; i < moduleNames.length; i++ ) {
moduleFilterHtml += "<option value='" +
escapeText( encodeURIComponent( moduleNames[ i ] ) ) + "' " +
( config.module === moduleNames[ i ] ? "selected='selected'" : "" ) +
">" + escapeText( moduleNames[ i ] ) + "</option>";
}
moduleFilterHtml += "</select>";
return moduleFilterHtml;
}
function toolbarModuleFilter() {
var moduleFilter = document.createElement( "span" ),
moduleFilterHtml = toolbarModuleFilterHtml();
if ( !moduleFilterHtml ) {
return false;
}
moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
moduleFilter.innerHTML = moduleFilterHtml;
addEvent( moduleFilter.lastChild, "change", function() {
var selectBox = moduleFilter.getElementsByTagName( "select" )[ 0 ],
selectedModule = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value );
window.location = QUnit.url({
module: ( selectedModule === "" ) ? undefined : selectedModule,
// Remove any existing filters
filter: undefined,
testNumber: undefined
});
});
return moduleFilter;
}
function toolbarFilter() {
var testList = id( "qunit-tests" ),
filter = document.createElement( "input" );
filter.type = "checkbox";
filter.id = "qunit-filter-pass";
addEvent( filter, "click", function() {
if ( filter.checked ) {
addClass( testList, "hidepass" );
if ( defined.sessionStorage ) {
sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
}
} else {
removeClass( testList, "hidepass" );
if ( defined.sessionStorage ) {
sessionStorage.removeItem( "qunit-filter-passed-tests" );
}
}
});
if ( config.hidepassed || defined.sessionStorage &&
sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
filter.checked = true;
addClass( testList, "hidepass" );
}
return filter;
}
function toolbarLabel() {
var label = document.createElement( "label" );
label.setAttribute( "for", "qunit-filter-pass" );
label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
label.innerHTML = "Hide passed tests";
return label;
}
function appendToolbar() {
var moduleFilter,
toolbar = id( "qunit-testrunner-toolbar" );
if ( toolbar ) {
toolbar.appendChild( toolbarFilter() );
toolbar.appendChild( toolbarLabel() );
toolbar.appendChild( toolbarUrlConfigContainer() );
moduleFilter = toolbarModuleFilter();
if ( moduleFilter ) {
toolbar.appendChild( moduleFilter );
}
}
}
function appendBanner() {
var banner = id( "qunit-banner" );
if ( banner ) {
banner.className = "";
banner.innerHTML = "<a href='" +
QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) +
"'>" + banner.innerHTML + "</a> ";
}
}
function appendTestResults() {
var tests = id( "qunit-tests" ),
result = id( "qunit-testresult" );
if ( result ) {
result.parentNode.removeChild( result );
}
if ( tests ) {
tests.innerHTML = "";
result = document.createElement( "p" );
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore( result, tests );
result.innerHTML = "Running...<br>&nbsp;";
}
}
function storeFixture() {
var fixture = id( "qunit-fixture" );
if ( fixture ) {
config.fixture = fixture.innerHTML;
}
}
function appendUserAgent() {
var userAgent = id( "qunit-userAgent" );
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
}
// HTML Reporter initialization and load
QUnit.begin(function() {
var qunit = id( "qunit" );
if ( qunit ) {
qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
"<h2 id='qunit-banner'></h2>" +
"<div id='qunit-testrunner-toolbar'></div>" +
"<h2 id='qunit-userAgent'></h2>" +
"<ol id='qunit-tests'></ol>";
}
appendBanner();
appendTestResults();
appendUserAgent();
appendToolbar();
storeFixture();
});
QUnit.done(function( details ) {
var i, key,
banner = id( "qunit-banner" ),
tests = id( "qunit-tests" ),
html = [
"Tests completed in ",
details.runtime,
" milliseconds.<br>",
"<span class='passed'>",
details.passed,
"</span> assertions of <span class='total'>",
details.total,
"</span> passed, <span class='failed'>",
details.failed,
"</span> failed."
].join( "" );
if ( banner ) {
banner.className = details.failed ? "qunit-fail" : "qunit-pass";
}
if ( tests ) {
id( "qunit-testresult" ).innerHTML = html;
}
if ( config.altertitle && defined.document && document.title ) {
// show ✖ for good, ✔ for bad suite result in title
// use escape sequences in case file gets loaded with non-utf-8-charset
document.title = [
( details.failed ? "\u2716" : "\u2714" ),
document.title.replace( /^[\u2714\u2716] /i, "" )
].join( " " );
}
// clear own sessionStorage items if all tests passed
if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
for ( i = 0; i < sessionStorage.length; i++ ) {
key = sessionStorage.key( i++ );
if ( key.indexOf( "qunit-test-" ) === 0 ) {
sessionStorage.removeItem( key );
}
}
}
// scroll back to top to show results
if ( config.scrolltop && window.scrollTo ) {
window.scrollTo( 0, 0 );
}
});
function getNameHtml( name, module ) {
var nameHtml = "";
if ( module ) {
nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
}
nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
return nameHtml;
}
QUnit.testStart(function( details ) {
var a, b, li, running, assertList,
name = getNameHtml( details.name, details.module ),
tests = id( "qunit-tests" );
if ( tests ) {
b = document.createElement( "strong" );
b.innerHTML = name;
a = document.createElement( "a" );
a.innerHTML = "Rerun";
a.href = QUnit.url({ testNumber: details.testNumber });
li = document.createElement( "li" );
li.appendChild( b );
li.appendChild( a );
li.className = "running";
li.id = "qunit-test-output" + details.testNumber;
assertList = document.createElement( "ol" );
assertList.className = "qunit-assert-list";
li.appendChild( assertList );
tests.appendChild( li );
}
running = id( "qunit-testresult" );
if ( running ) {
running.innerHTML = "Running: <br>" + name;
}
});
QUnit.log(function( details ) {
var assertList, assertLi,
message, expected, actual,
testItem = id( "qunit-test-output" + details.testNumber );
if ( !testItem ) {
return;
}
message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>";
// pushFailure doesn't provide details.expected
// when it calls, it's implicit to also not show expected and diff stuff
// Also, we need to check details.expected existence, as it can exist and be undefined
if ( !details.result && hasOwn.call( details, "expected" ) ) {
expected = escapeText( QUnit.dump.parse( details.expected ) );
actual = escapeText( QUnit.dump.parse( details.actual ) );
message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
expected +
"</pre></td></tr>";
if ( actual !== expected ) {
message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
actual + "</pre></td></tr>" +
"<tr class='test-diff'><th>Diff: </th><td><pre>" +
QUnit.diff( expected, actual ) + "</pre></td></tr>";
}
if ( details.source ) {
message += "<tr class='test-source'><th>Source: </th><td><pre>" +
escapeText( details.source ) + "</pre></td></tr>";
}
message += "</table>";
// this occours when pushFailure is set and we have an extracted stack trace
} else if ( !details.result && details.source ) {
message += "<table>" +
"<tr class='test-source'><th>Source: </th><td><pre>" +
escapeText( details.source ) + "</pre></td></tr>" +
"</table>";
}
assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
assertLi = document.createElement( "li" );
assertLi.className = details.result ? "pass" : "fail";
assertLi.innerHTML = message;
assertList.appendChild( assertLi );
});
QUnit.testDone(function( details ) {
var testTitle, time, testItem, assertList,
good, bad, testCounts,
tests = id( "qunit-tests" );
// QUnit.reset() is deprecated and will be replaced for a new
// fixture reset function on QUnit 2.0/2.1.
// It's still called here for backwards compatibility handling
QUnit.reset();
if ( !tests ) {
return;
}
testItem = id( "qunit-test-output" + details.testNumber );
assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
good = details.passed;
bad = details.failed;
// store result when possible
if ( config.reorder && defined.sessionStorage ) {
if ( bad ) {
sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
} else {
sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
}
}
if ( bad === 0 ) {
addClass( assertList, "qunit-collapsed" );
}
// testItem.firstChild is the test name
testTitle = testItem.firstChild;
testCounts = bad ?
"<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
"";
testTitle.innerHTML += " <b class='counts'>(" + testCounts +
details.assertions.length + ")</b>";
addEvent( testTitle, "click", function() {
toggleClass( assertList, "qunit-collapsed" );
});
time = document.createElement( "span" );
time.className = "runtime";
time.innerHTML = details.runtime + " ms";
testItem.className = bad ? "fail" : "pass";
testItem.insertBefore( time, assertList );
});
if ( !defined.document || document.readyState === "complete" ) {
config.autorun = true;
}
if ( defined.document ) {
addEvent( window, "load", QUnit.load );
}
})();
/** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 2.1.15 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
//Not using strict: uneven strict support in browsers, #392, and causes
//problems with requirejs.exec()/transpiler plugins that may not be strict.
/*jslint regexp: true, nomen: true, sloppy: true */
/*global window, navigator, document, importScripts, setTimeout, opera */
var requirejs, require, define;
(function (global) {
var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.15',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
currDirRegExp = /^\.\//,
op = Object.prototype,
ostring = op.toString,
hasOwn = op.hasOwnProperty,
ap = Array.prototype,
apsp = ap.splice,
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
//PS3 indicates loaded and complete, but need to wait for complete
//specifically. Sequence is 'loading', 'loaded', execution,
// then 'complete'. The UA check is unfortunate, but not sure how
//to feature test w/o causing perf issues.
readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
/^complete$/ : /^(complete|loaded)$/,
defContextName = '_',
//Oh the tragedy, detecting opera. See the usage of isOpera for reason.
isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
contexts = {},
cfg = {},
globalDefQueue = [],
useInteractive = false;
function isFunction(it) {
return ostring.call(it) === '[object Function]';
}
function isArray(it) {
return ostring.call(it) === '[object Array]';
}
/**
* Helper function for iterating over an array. If the func returns
* a true value, it will break out of the loop.
*/
function each(ary, func) {
if (ary) {
var i;
for (i = 0; i < ary.length; i += 1) {
if (ary[i] && func(ary[i], i, ary)) {
break;
}
}
}
}
/**
* Helper function for iterating over an array backwards. If the func
* returns a true value, it will break out of the loop.
*/
function eachReverse(ary, func) {
if (ary) {
var i;
for (i = ary.length - 1; i > -1; i -= 1) {
if (ary[i] && func(ary[i], i, ary)) {
break;
}
}
}
}
function hasProp(obj, prop) {
return hasOwn.call(obj, prop);
}
function getOwn(obj, prop) {
return hasProp(obj, prop) && obj[prop];
}
/**
* Cycles over properties in an object and calls a function for each
* property value. If the function returns a truthy value, then the
* iteration is stopped.
*/
function eachProp(obj, func) {
var prop;
for (prop in obj) {
if (hasProp(obj, prop)) {
if (func(obj[prop], prop)) {
break;
}
}
}
}
/**
* Simple function to mix in properties from source into target,
* but only if target does not already have a property of the same name.
*/
function mixin(target, source, force, deepStringMixin) {
if (source) {
eachProp(source, function (value, prop) {
if (force || !hasProp(target, prop)) {
if (deepStringMixin && typeof value === 'object' && value &&
!isArray(value) && !isFunction(value) &&
!(value instanceof RegExp)) {
if (!target[prop]) {
target[prop] = {};
}
mixin(target[prop], value, force, deepStringMixin);
} else {
target[prop] = value;
}
}
});
}
return target;
}
//Similar to Function.prototype.bind, but the 'this' object is specified
//first, since it is easier to read/figure out what 'this' will be.
function bind(obj, fn) {
return function () {
return fn.apply(obj, arguments);
};
}
function scripts() {
return document.getElementsByTagName('script');
}
function defaultOnError(err) {
throw err;
}
//Allow getting a global that is expressed in
//dot notation, like 'a.b.c'.
function getGlobal(value) {
if (!value) {
return value;
}
var g = global;
each(value.split('.'), function (part) {
g = g[part];
});
return g;
}
/**
* Constructs an error with a pointer to an URL with more information.
* @param {String} id the error ID that maps to an ID on a web page.
* @param {String} message human readable error.
* @param {Error} [err] the original error, if there is one.
*
* @returns {Error}
*/
function makeError(id, msg, err, requireModules) {
var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
e.requireType = id;
e.requireModules = requireModules;
if (err) {
e.originalError = err;
}
return e;
}
if (typeof define !== 'undefined') {
//If a define is already in play via another AMD loader,
//do not overwrite.
return;
}
if (typeof requirejs !== 'undefined') {
if (isFunction(requirejs)) {
//Do not overwrite an existing requirejs instance.
return;
}
cfg = requirejs;
requirejs = undefined;
}
//Allow for a require config object
if (typeof require !== 'undefined' && !isFunction(require)) {
//assume it is a config object.
cfg = require;
require = undefined;
}
function newContext(contextName) {
var inCheckLoaded, Module, context, handlers,
checkLoadedTimeoutId,
config = {
//Defaults. Do not set a default for map
//config to speed up normalize(), which
//will run faster if there is no default.
waitSeconds: 7,
baseUrl: './',
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
},
registry = {},
//registry of just enabled modules, to speed
//cycle breaking code when lots of modules
//are registered, but not activated.
enabledRegistry = {},
undefEvents = {},
defQueue = [],
defined = {},
urlFetched = {},
bundlesMap = {},
requireCounter = 1,
unnormalizedCounter = 1;
/**
* Trims the . and .. from an array of path segments.
* It will keep a leading path segment if a .. will become
* the first path segment, to help with module name lookups,
* which act like paths, but can be remapped. But the end result,
* all paths that use this function should look normalized.
* NOTE: this method MODIFIES the input array.
* @param {Array} ary the array of path segments.
*/
function trimDots(ary) {
var i, part;
for (i = 0; i < ary.length; i++) {
part = ary[i];
if (part === '.') {
ary.splice(i, 1);
i -= 1;
} else if (part === '..') {
// If at the start, or previous value is still ..,
// keep them so that when converted to a path it may
// still work when converted to a path, even though
// as an ID it is less than ideal. In larger point
// releases, may be better to just kick out an error.
if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') {
continue;
} else if (i > 0) {
ary.splice(i - 1, 2);
i -= 2;
}
}
}
}
/**
* Given a relative module name, like ./something, normalize it to
* a real name that can be mapped to a path.
* @param {String} name the relative name
* @param {String} baseName a real name that the name arg is relative
* to.
* @param {Boolean} applyMap apply the map config to the value. Should
* only be done if this normalization is for a dependency ID.
* @returns {String} normalized name
*/
function normalize(name, baseName, applyMap) {
var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
foundMap, foundI, foundStarMap, starI, normalizedBaseParts,
baseParts = (baseName && baseName.split('/')),
map = config.map,
starMap = map && map['*'];
//Adjust any relative paths.
if (name) {
name = name.split('/');
lastIndex = name.length - 1;
// If wanting node ID compatibility, strip .js from end
// of IDs. Have to do this here, and not in nameToUrl
// because node allows either .js or non .js to map
// to same file.
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
// Starts with a '.' so need the baseName
if (name[0].charAt(0) === '.' && baseParts) {
//Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = normalizedBaseParts.concat(name);
}
trimDots(name);
name = name.join('/');
}
//Apply map config if available.
if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/');
outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
nameSegment = nameParts.slice(0, i).join('/');
if (baseParts) {
//Find the longest baseName segment match in the config.
//So, do joins on the biggest to smallest lengths of baseParts.
for (j = baseParts.length; j > 0; j -= 1) {
mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
//baseName segment has config, find if it has one for
//this name.
if (mapValue) {
mapValue = getOwn(mapValue, nameSegment);
if (mapValue) {
//Match, update name to the new value.
foundMap = mapValue;
foundI = i;
break outerLoop;
}
}
}
}
//Check for a star map match, but just hold on to it,
//if there is a shorter segment match later in a matching
//config, then favor over this star map.
if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
foundStarMap = getOwn(starMap, nameSegment);
starI = i;
}
}
if (!foundMap && foundStarMap) {
foundMap = foundStarMap;
foundI = starI;
}
if (foundMap) {
nameParts.splice(0, foundI, foundMap);
name = nameParts.join('/');
}
}
// If the name points to a package's name, use
// the package main instead.
pkgMain = getOwn(config.pkgs, name);
return pkgMain ? pkgMain : name;
}
function removeScript(name) {
if (isBrowser) {
each(scripts(), function (scriptNode) {
if (scriptNode.getAttribute('data-requiremodule') === name &&
scriptNode.getAttribute('data-requirecontext') === context.contextName) {
scriptNode.parentNode.removeChild(scriptNode);
return true;
}
});
}
}
function hasPathFallback(id) {
var pathConfig = getOwn(config.paths, id);
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
//Pop off the first array value, since it failed, and
//retry
pathConfig.shift();
context.require.undef(id);
//Custom require that does not do map translation, since
//ID is "absolute", already mapped/resolved.
context.makeRequire(null, {
skipMap: true
})([id]);
return true;
}
}
//Turns a plugin!resource to [plugin, resource]
//with the plugin being undefined if the name
//did not have a plugin prefix.
function splitPrefix(name) {
var prefix,
index = name ? name.indexOf('!') : -1;
if (index > -1) {
prefix = name.substring(0, index);
name = name.substring(index + 1, name.length);
}
return [prefix, name];
}
/**
* Creates a module mapping that includes plugin prefix, module
* name, and path. If parentModuleMap is provided it will
* also normalize the name via require.normalize()
*
* @param {String} name the module name
* @param {String} [parentModuleMap] parent module map
* for the module name, used to resolve relative names.
* @param {Boolean} isNormalized: is the ID already normalized.
* This is true if this call is done for a define() module ID.
* @param {Boolean} applyMap: apply the map config to the ID.
* Should only be true if this map is for a dependency.
*
* @returns {Object}
*/
function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
var url, pluginModule, suffix, nameParts,
prefix = null,
parentName = parentModuleMap ? parentModuleMap.name : null,
originalName = name,
isDefine = true,
normalizedName = '';
//If no name, then it means it is a require call, generate an
//internal name.
if (!name) {
isDefine = false;
name = '_@r' + (requireCounter += 1);
}
nameParts = splitPrefix(name);
prefix = nameParts[0];
name = nameParts[1];
if (prefix) {
prefix = normalize(prefix, parentName, applyMap);
pluginModule = getOwn(defined, prefix);
}
//Account for relative paths if there is a base name.
if (name) {
if (prefix) {
if (pluginModule && pluginModule.normalize) {
//Plugin is loaded, use its normalize method.
normalizedName = pluginModule.normalize(name, function (name) {
return normalize(name, parentName, applyMap);
});
} else {
// If nested plugin references, then do not try to
// normalize, as it will not normalize correctly. This
// places a restriction on resourceIds, and the longer
// term solution is not to normalize until plugins are
// loaded and all normalizations to allow for async
// loading of a loader plugin. But for now, fixes the
// common uses. Details in #1131
normalizedName = name.indexOf('!') === -1 ?
normalize(name, parentName, applyMap) :
name;
}
} else {
//A regular module.
normalizedName = normalize(name, parentName, applyMap);
//Normalized name may be a plugin ID due to map config
//application in normalize. The map config values must
//already be normalized, so do not need to redo that part.
nameParts = splitPrefix(normalizedName);
prefix = nameParts[0];
normalizedName = nameParts[1];
isNormalized = true;
url = context.nameToUrl(normalizedName);
}
}
//If the id is a plugin id that cannot be determined if it needs
//normalization, stamp it with a unique ID so two matching relative
//ids that may conflict can be separate.
suffix = prefix && !pluginModule && !isNormalized ?
'_unnormalized' + (unnormalizedCounter += 1) :
'';
return {
prefix: prefix,
name: normalizedName,
parentMap: parentModuleMap,
unnormalized: !!suffix,
url: url,
originalName: originalName,
isDefine: isDefine,
id: (prefix ?
prefix + '!' + normalizedName :
normalizedName) + suffix
};
}
function getModule(depMap) {
var id = depMap.id,
mod = getOwn(registry, id);
if (!mod) {
mod = registry[id] = new context.Module(depMap);
}
return mod;
}
function on(depMap, name, fn) {
var id = depMap.id,
mod = getOwn(registry, id);
if (hasProp(defined, id) &&
(!mod || mod.defineEmitComplete)) {
if (name === 'defined') {
fn(defined[id]);
}
} else {
mod = getModule(depMap);
if (mod.error && name === 'error') {
fn(mod.error);
} else {
mod.on(name, fn);
}
}
}
function onError(err, errback) {
var ids = err.requireModules,
notified = false;
if (errback) {
errback(err);
} else {
each(ids, function (id) {
var mod = getOwn(registry, id);
if (mod) {
//Set error on module, so it skips timeout checks.
mod.error = err;
if (mod.events.error) {
notified = true;
mod.emit('error', err);
}
}
});
if (!notified) {
req.onError(err);
}
}
}
/**
* Internal method to transfer globalQueue items to this context's
* defQueue.
*/
function takeGlobalQueue() {
//Push all the globalDefQueue items into the context's defQueue
if (globalDefQueue.length) {
//Array splice in the values since the context code has a
//local var ref to defQueue, so cannot just reassign the one
//on context.
apsp.apply(defQueue,
[defQueue.length, 0].concat(globalDefQueue));
globalDefQueue = [];
}
}
handlers = {
'require': function (mod) {
if (mod.require) {
return mod.require;
} else {
return (mod.require = context.makeRequire(mod.map));
}
},
'exports': function (mod) {
mod.usingExports = true;
if (mod.map.isDefine) {
if (mod.exports) {
return (defined[mod.map.id] = mod.exports);
} else {
return (mod.exports = defined[mod.map.id] = {});
}
}
},
'module': function (mod) {
if (mod.module) {
return mod.module;
} else {
return (mod.module = {
id: mod.map.id,
uri: mod.map.url,
config: function () {
return getOwn(config.config, mod.map.id) || {};
},
exports: mod.exports || (mod.exports = {})
});
}
}
};
function cleanRegistry(id) {
//Clean up machinery used for waiting modules.
delete registry[id];
delete enabledRegistry[id];
}
function breakCycle(mod, traced, processed) {
var id = mod.map.id;
if (mod.error) {
mod.emit('error', mod.error);
} else {
traced[id] = true;
each(mod.depMaps, function (depMap, i) {
var depId = depMap.id,
dep = getOwn(registry, depId);
//Only force things that have not completed
//being defined, so still in the registry,
//and only if it has not been matched up
//in the module already.
if (dep && !mod.depMatched[i] && !processed[depId]) {
if (getOwn(traced, depId)) {
mod.defineDep(i, defined[depId]);
mod.check(); //pass false?
} else {
breakCycle(dep, traced, processed);
}
}
});
processed[id] = true;
}
}
function checkLoaded() {
var err, usingPathFallback,
waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
noLoads = [],
reqCalls = [],
stillLoading = false,
needCycleCheck = true;
//Do not bother if this call was a result of a cycle break.
if (inCheckLoaded) {
return;
}
inCheckLoaded = true;
//Figure out the state of all the modules.
eachProp(enabledRegistry, function (mod) {
var map = mod.map,
modId = map.id;
//Skip things that are not enabled or in error state.
if (!mod.enabled) {
return;
}
if (!map.isDefine) {
reqCalls.push(mod);
}
if (!mod.error) {
//If the module should be executed, and it has not
//been inited and time is up, remember it.
if (!mod.inited && expired) {
if (hasPathFallback(modId)) {
usingPathFallback = true;
stillLoading = true;
} else {
noLoads.push(modId);
removeScript(modId);
}
} else if (!mod.inited && mod.fetched && map.isDefine) {
stillLoading = true;
if (!map.prefix) {
//No reason to keep looking for unfinished
//loading. If the only stillLoading is a
//plugin resource though, keep going,
//because it may be that a plugin resource
//is waiting on a non-plugin cycle.
return (needCycleCheck = false);
}
}
}
});
if (expired && noLoads.length) {
//If wait time expired, throw error of unloaded modules.
err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
err.contextName = context.contextName;
return onError(err);
}
//Not expired, check for a cycle.
if (needCycleCheck) {
each(reqCalls, function (mod) {
breakCycle(mod, {}, {});
});
}
//If still waiting on loads, and the waiting load is something
//other than a plugin resource, or there are still outstanding
//scripts, then just try back later.
if ((!expired || usingPathFallback) && stillLoading) {
//Something is still waiting to load. Wait for it, but only
//if a timeout is not already in effect.
if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
checkLoadedTimeoutId = setTimeout(function () {
checkLoadedTimeoutId = 0;
checkLoaded();
}, 50);
}
}
inCheckLoaded = false;
}
Module = function (map) {
this.events = getOwn(undefEvents, map.id) || {};
this.map = map;
this.shim = getOwn(config.shim, map.id);
this.depExports = [];
this.depMaps = [];
this.depMatched = [];
this.pluginMaps = {};
this.depCount = 0;
/* this.exports this.factory
this.depMaps = [],
this.enabled, this.fetched
*/
};
Module.prototype = {
init: function (depMaps, factory, errback, options) {
options = options || {};
//Do not do more inits if already done. Can happen if there
//are multiple define calls for the same module. That is not
//a normal, common case, but it is also not unexpected.
if (this.inited) {
return;
}
this.factory = factory;
if (errback) {
//Register for errors on this module.
this.on('error', errback);
} else if (this.events.error) {
//If no errback already, but there are error listeners
//on this module, set up an errback to pass to the deps.
errback = bind(this, function (err) {
this.emit('error', err);
});
}
//Do a copy of the dependency array, so that
//source inputs are not modified. For example
//"shim" deps are passed in here directly, and
//doing a direct modification of the depMaps array
//would affect that config.
this.depMaps = depMaps && depMaps.slice(0);
this.errback = errback;
//Indicate this module has be initialized
this.inited = true;
this.ignore = options.ignore;
//Could have option to init this module in enabled mode,
//or could have been previously marked as enabled. However,
//the dependencies are not known until init is called. So
//if enabled previously, now trigger dependencies as enabled.
if (options.enabled || this.enabled) {
//Enable this module and dependencies.
//Will call this.check()
this.enable();
} else {
this.check();
}
},
defineDep: function (i, depExports) {
//Because of cycles, defined callback for a given
//export can be called more than once.
if (!this.depMatched[i]) {
this.depMatched[i] = true;
this.depCount -= 1;
this.depExports[i] = depExports;
}
},
fetch: function () {
if (this.fetched) {
return;
}
this.fetched = true;
context.startTime = (new Date()).getTime();
var map = this.map;
//If the manager is for a plugin managed resource,
//ask the plugin to load it now.
if (this.shim) {
context.makeRequire(this.map, {
enableBuildCallback: true
})(this.shim.deps || [], bind(this, function () {
return map.prefix ? this.callPlugin() : this.load();
}));
} else {
//Regular dependency.
return map.prefix ? this.callPlugin() : this.load();
}
},
load: function () {
var url = this.map.url;
//Regular dependency.
if (!urlFetched[url]) {
urlFetched[url] = true;
context.load(this.map.id, url);
}
},
/**
* Checks if the module is ready to define itself, and if so,
* define it.
*/
check: function () {
if (!this.enabled || this.enabling) {
return;
}
var err, cjsModule,
id = this.map.id,
depExports = this.depExports,
exports = this.exports,
factory = this.factory;
if (!this.inited) {
this.fetch();
} else if (this.error) {
this.emit('error', this.error);
} else if (!this.defining) {
//The factory could trigger another require call
//that would result in checking this module to
//define itself again. If already in the process
//of doing that, skip this work.
this.defining = true;
if (this.depCount < 1 && !this.defined) {
if (isFunction(factory)) {
//If there is an error listener, favor passing
//to that instead of throwing an error. However,
//only do it for define()'d modules. require
//errbacks should not be called for failures in
//their callbacks (#699). However if a global
//onError is set, use that.
if ((this.events.error && this.map.isDefine) ||
req.onError !== defaultOnError) {
try {
exports = context.execCb(id, factory, depExports, exports);
} catch (e) {
err = e;
}
} else {
exports = context.execCb(id, factory, depExports, exports);
}
// Favor return value over exports. If node/cjs in play,
// then will not have a return value anyway. Favor
// module.exports assignment over exports object.
if (this.map.isDefine && exports === undefined) {
cjsModule = this.module;
if (cjsModule) {
exports = cjsModule.exports;
} else if (this.usingExports) {
//exports already set the defined value.
exports = this.exports;
}
}
if (err) {
err.requireMap = this.map;
err.requireModules = this.map.isDefine ? [this.map.id] : null;
err.requireType = this.map.isDefine ? 'define' : 'require';
return onError((this.error = err));
}
} else {
//Just a literal value
exports = factory;
}
this.exports = exports;
if (this.map.isDefine && !this.ignore) {
defined[id] = exports;
if (req.onResourceLoad) {
req.onResourceLoad(context, this.map, this.depMaps);
}
}
//Clean up
cleanRegistry(id);
this.defined = true;
}
//Finished the define stage. Allow calling check again
//to allow define notifications below in the case of a
//cycle.
this.defining = false;
if (this.defined && !this.defineEmitted) {
this.defineEmitted = true;
this.emit('defined', this.exports);
this.defineEmitComplete = true;
}
}
},
callPlugin: function () {
var map = this.map,
id = map.id,
//Map already normalized the prefix.
pluginMap = makeModuleMap(map.prefix);
//Mark this as a dependency for this plugin, so it
//can be traced for cycles.
this.depMaps.push(pluginMap);
on(pluginMap, 'defined', bind(this, function (plugin) {
var load, normalizedMap, normalizedMod,
bundleId = getOwn(bundlesMap, this.map.id),
name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, {
enableBuildCallback: true
});
//If current map is not normalized, wait for that
//normalized name to load instead of continuing.
if (this.map.unnormalized) {
//Normalize the ID if the plugin allows it.
if (plugin.normalize) {
name = plugin.normalize(name, function (name) {
return normalize(name, parentName, true);
}) || '';
}
//prefix and name should already be normalized, no need
//for applying map config again either.
normalizedMap = makeModuleMap(map.prefix + '!' + name,
this.map.parentMap);
on(normalizedMap,
'defined', bind(this, function (value) {
this.init([], function () { return value; }, null, {
enabled: true,
ignore: true
});
}));
normalizedMod = getOwn(registry, normalizedMap.id);
if (normalizedMod) {
//Mark this as a dependency for this plugin, so it
//can be traced for cycles.
this.depMaps.push(normalizedMap);
if (this.events.error) {
normalizedMod.on('error', bind(this, function (err) {
this.emit('error', err);
}));
}
normalizedMod.enable();
}
return;
}
//If a paths config, then just load that file instead to
//resolve the plugin, as it is built into that paths layer.
if (bundleId) {
this.map.url = context.nameToUrl(bundleId);
this.load();
return;
}
load = bind(this, function (value) {
this.init([], function () { return value; }, null, {
enabled: true
});
});
load.error = bind(this, function (err) {
this.inited = true;
this.error = err;
err.requireModules = [id];
//Remove temp unnormalized modules for this module,
//since they will never be resolved otherwise now.
eachProp(registry, function (mod) {
if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
cleanRegistry(mod.map.id);
}
});
onError(err);
});
//Allow plugins to load other code without having to know the
//context or how to 'complete' the load.
load.fromText = bind(this, function (text, textAlt) {
/*jslint evil: true */
var moduleName = map.name,
moduleMap = makeModuleMap(moduleName),
hasInteractive = useInteractive;
//As of 2.1.0, support just passing the text, to reinforce
//fromText only being called once per resource. Still
//support old style of passing moduleName but discard
//that moduleName in favor of the internal ref.
if (textAlt) {
text = textAlt;
}
//Turn off interactive script matching for IE for any define
//calls in the text, then turn it back on at the end.
if (hasInteractive) {
useInteractive = false;
}
//Prime the system by creating a module instance for
//it.
getModule(moduleMap);
//Transfer any config to this other module.
if (hasProp(config.config, id)) {
config.config[moduleName] = config.config[id];
}
try {
req.exec(text);
} catch (e) {
return onError(makeError('fromtexteval',
'fromText eval for ' + id +
' failed: ' + e,
e,
[id]));
}
if (hasInteractive) {
useInteractive = true;
}
//Mark this as a dependency for the plugin
//resource
this.depMaps.push(moduleMap);
//Support anonymous modules.
context.completeLoad(moduleName);
//Bind the value of that module to the value for this
//resource ID.
localRequire([moduleName], load);
});
//Use parentName here since the plugin's name is not reliable,
//could be some weird string with no path that actually wants to
//reference the parentName's path.
plugin.load(map.name, localRequire, load, config);
}));
context.enable(pluginMap, this);
this.pluginMaps[pluginMap.id] = pluginMap;
},
enable: function () {
enabledRegistry[this.map.id] = this;
this.enabled = true;
//Set flag mentioning that the module is enabling,
//so that immediate calls to the defined callbacks
//for dependencies do not trigger inadvertent load
//with the depCount still being zero.
this.enabling = true;
//Enable each dependency
each(this.depMaps, bind(this, function (depMap, i) {
var id, mod, handler;
if (typeof depMap === 'string') {
//Dependency needs to be converted to a depMap
//and wired up to this module.
depMap = makeModuleMap(depMap,
(this.map.isDefine ? this.map : this.map.parentMap),
false,
!this.skipMap);
this.depMaps[i] = depMap;
handler = getOwn(handlers, depMap.id);
if (handler) {
this.depExports[i] = handler(this);
return;
}
this.depCount += 1;
on(depMap, 'defined', bind(this, function (depExports) {
this.defineDep(i, depExports);
this.check();
}));
if (this.errback) {
on(depMap, 'error', bind(this, this.errback));
}
}
id = depMap.id;
mod = registry[id];
//Skip special modules like 'require', 'exports', 'module'
//Also, don't call enable if it is already enabled,
//important in circular dependency cases.
if (!hasProp(handlers, id) && mod && !mod.enabled) {
context.enable(depMap, this);
}
}));
//Enable each plugin that is used in
//a dependency
eachProp(this.pluginMaps, bind(this, function (pluginMap) {
var mod = getOwn(registry, pluginMap.id);
if (mod && !mod.enabled) {
context.enable(pluginMap, this);
}
}));
this.enabling = false;
this.check();
},
on: function (name, cb) {
var cbs = this.events[name];
if (!cbs) {
cbs = this.events[name] = [];
}
cbs.push(cb);
},
emit: function (name, evt) {
each(this.events[name], function (cb) {
cb(evt);
});
if (name === 'error') {
//Now that the error handler was triggered, remove
//the listeners, since this broken Module instance
//can stay around for a while in the registry.
delete this.events[name];
}
}
};
function callGetModule(args) {
//Skip modules already defined.
if (!hasProp(defined, args[0])) {
getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
}
}
function removeListener(node, func, name, ieName) {
//Favor detachEvent because of IE9
//issue, see attachEvent/addEventListener comment elsewhere
//in this file.
if (node.detachEvent && !isOpera) {
//Probably IE. If not it will throw an error, which will be
//useful to know.
if (ieName) {
node.detachEvent(ieName, func);
}
} else {
node.removeEventListener(name, func, false);
}
}
/**
* Given an event from a script node, get the requirejs info from it,
* and then removes the event listeners on the node.
* @param {Event} evt
* @returns {Object}
*/
function getScriptData(evt) {
//Using currentTarget instead of target for Firefox 2.0's sake. Not
//all old browsers will be supported, but this one was easy enough
//to support and still makes sense.
var node = evt.currentTarget || evt.srcElement;
//Remove the listeners once here.
removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
removeListener(node, context.onScriptError, 'error');
return {
node: node,
id: node && node.getAttribute('data-requiremodule')
};
}
function intakeDefines() {
var args;
//Any defined modules in the global queue, intake them now.
takeGlobalQueue();
//Make sure any remaining defQueue items get properly processed.
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
} else {
//args are id, deps, factory. Should be normalized by the
//define() function.
callGetModule(args);
}
}
}
context = {
config: config,
contextName: contextName,
registry: registry,
defined: defined,
urlFetched: urlFetched,
defQueue: defQueue,
Module: Module,
makeModuleMap: makeModuleMap,
nextTick: req.nextTick,
onError: onError,
/**
* Set a configuration for the context.
* @param {Object} cfg config object to integrate.
*/
configure: function (cfg) {
//Make sure the baseUrl ends in a slash.
if (cfg.baseUrl) {
if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
cfg.baseUrl += '/';
}
}
//Save off the paths since they require special processing,
//they are additive.
var shim = config.shim,
objs = {
paths: true,
bundles: true,
config: true,
map: true
};
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
if (!config[prop]) {
config[prop] = {};
}
mixin(config[prop], value, true, true);
} else {
config[prop] = value;
}
});
//Reverse map the bundles
if (cfg.bundles) {
eachProp(cfg.bundles, function (value, prop) {
each(value, function (v) {
if (v !== prop) {
bundlesMap[v] = prop;
}
});
});
}
//Merge shim
if (cfg.shim) {
eachProp(cfg.shim, function (value, id) {
//Normalize the structure
if (isArray(value)) {
value = {
deps: value
};
}
if ((value.exports || value.init) && !value.exportsFn) {
value.exportsFn = context.makeShimExports(value);
}
shim[id] = value;
});
config.shim = shim;
}
//Adjust packages if necessary.
if (cfg.packages) {
each(cfg.packages, function (pkgObj) {
var location, name;
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
name = pkgObj.name;
location = pkgObj.location;
if (location) {
config.paths[name] = pkgObj.location;
}
//Save pointer to main module ID for pkg name.
//Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package
//envs have different conventions: some use a module name,
//some use a file name.
config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
.replace(currDirRegExp, '')
.replace(jsSuffixRegExp, '');
});
}
//If there are any "waiting to execute" modules in the registry,
//update the maps for them, since their info, like URLs to load,
//may have changed.
eachProp(registry, function (mod, id) {
//If module already has init called, since it is too
//late to modify them, and ignore unnormalized ones
//since they are transient.
if (!mod.inited && !mod.map.unnormalized) {
mod.map = makeModuleMap(id);
}
});
//If a deps array or a config callback is specified, then call
//require with those args. This is useful when require is defined as a
//config object before require.js is loaded.
if (cfg.deps || cfg.callback) {
context.require(cfg.deps || [], cfg.callback);
}
},
makeShimExports: function (value) {
function fn() {
var ret;
if (value.init) {
ret = value.init.apply(global, arguments);
}
return ret || (value.exports && getGlobal(value.exports));
}
return fn;
},
makeRequire: function (relMap, options) {
options = options || {};
function localRequire(deps, callback, errback) {
var id, map, requireMod;
if (options.enableBuildCallback && callback && isFunction(callback)) {
callback.__requireJsBuild = true;
}
if (typeof deps === 'string') {
if (isFunction(callback)) {
//Invalid call
return onError(makeError('requireargs', 'Invalid require call'), errback);
}
//If require|exports|module are requested, get the
//value for them from the special handlers. Caveat:
//this only works while module is being defined.
if (relMap && hasProp(handlers, deps)) {
return handlers[deps](registry[relMap.id]);
}
//Synchronous access to one module. If require.get is
//available (as in the Node adapter), prefer that.
if (req.get) {
return req.get(context, deps, relMap, localRequire);
}
//Normalize module name, if it contains . or ..
map = makeModuleMap(deps, relMap, false, true);
id = map.id;
if (!hasProp(defined, id)) {
return onError(makeError('notloaded', 'Module name "' +
id +
'" has not been loaded yet for context: ' +
contextName +
(relMap ? '' : '. Use require([])')));
}
return defined[id];
}
//Grab defines waiting in the global queue.
intakeDefines();
//Mark all the dependencies as needing to be loaded.
context.nextTick(function () {
//Some defines could have been added since the
//require call, collect them.
intakeDefines();
requireMod = getModule(makeModuleMap(null, relMap));
//Store if map config should be applied to this require
//call for dependencies.
requireMod.skipMap = options.skipMap;
requireMod.init(deps, callback, errback, {
enabled: true
});
checkLoaded();
});
return localRequire;
}
mixin(localRequire, {
isBrowser: isBrowser,
/**
* Converts a module name + .extension into an URL path.
* *Requires* the use of a module name. It does not support using
* plain URLs like nameToUrl.
*/
toUrl: function (moduleNamePlusExt) {
var ext,
index = moduleNamePlusExt.lastIndexOf('.'),
segment = moduleNamePlusExt.split('/')[0],
isRelative = segment === '.' || segment === '..';
//Have a file extension alias, and it is not the
//dots from a relative path.
if (index !== -1 && (!isRelative || index > 1)) {
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
}
return context.nameToUrl(normalize(moduleNamePlusExt,
relMap && relMap.id, true), ext, true);
},
defined: function (id) {
return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
},
specified: function (id) {
id = makeModuleMap(id, relMap, false, true).id;
return hasProp(defined, id) || hasProp(registry, id);
}
});
//Only allow undef on top level require calls
if (!relMap) {
localRequire.undef = function (id) {
//Bind any waiting define() calls to this context,
//fix for #408
takeGlobalQueue();
var map = makeModuleMap(id, relMap, true),
mod = getOwn(registry, id);
removeScript(id);
delete defined[id];
delete urlFetched[map.url];
delete undefEvents[id];
//Clean queued defines too. Go backwards
//in array so that the splices do not
//mess up the iteration.
eachReverse(defQueue, function(args, i) {
if(args[0] === id) {
defQueue.splice(i, 1);
}
});
if (mod) {
//Hold on to listeners in case the
//module will be attempted to be reloaded
//using a different config.
if (mod.events.defined) {
undefEvents[id] = mod.events;
}
cleanRegistry(id);
}
};
}
return localRequire;
},
/**
* Called to enable a module if it is still in the registry
* awaiting enablement. A second arg, parent, the parent module,
* is passed in for context, when this method is overridden by
* the optimizer. Not shown here to keep code compact.
*/
enable: function (depMap) {
var mod = getOwn(registry, depMap.id);
if (mod) {
getModule(depMap).enable();
}
},
/**
* Internal method used by environment adapters to complete a load event.
* A load event could be a script load or just a load pass from a synchronous
* load call.
* @param {String} moduleName the name of the module to potentially complete.
*/
completeLoad: function (moduleName) {
var found, args, mod,
shim = getOwn(config.shim, moduleName) || {},
shExports = shim.exports;
takeGlobalQueue();
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
args[0] = moduleName;
//If already found an anonymous module and bound it
//to this name, then this is some other anon module
//waiting for its completeLoad to fire.
if (found) {
break;
}
found = true;
} else if (args[0] === moduleName) {
//Found matching define call for this script!
found = true;
}
callGetModule(args);
}
//Do this after the cycle of callGetModule in case the result
//of those calls/init calls changes the registry.
mod = getOwn(registry, moduleName);
if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {
if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
if (hasPathFallback(moduleName)) {
return;
} else {
return onError(makeError('nodefine',
'No define call for ' + moduleName,
null,
[moduleName]));
}
} else {
//A script that does not call define(), so just simulate
//the call for it.
callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
}
}
checkLoaded();
},
/**
* Converts a module name to a file path. Supports cases where
* moduleName may actually be just an URL.
* Note that it **does not** call normalize on the moduleName,
* it is assumed to have already been normalized. This is an
* internal API, not a public one. Use toUrl for the public API.
*/
nameToUrl: function (moduleName, ext, skipExt) {
var paths, syms, i, parentModule, url,
parentPath, bundleId,
pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain) {
moduleName = pkgMain;
}
bundleId = getOwn(bundlesMap, moduleName);
if (bundleId) {
return context.nameToUrl(bundleId, ext, skipExt);
}
//If a colon is in the URL, it indicates a protocol is used and it is just
//an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
//or ends with .js, then assume the user meant to use an url and not a module id.
//The slash is important for protocol-less URLs as well as full paths.
if (req.jsExtRegExp.test(moduleName)) {
//Just a plain path, not module name lookup, so just return it.
//Add extension if it is included. This is a bit wonky, only non-.js things pass
//an extension, this method probably needs to be reworked.
url = moduleName + (ext || '');
} else {
//A module that needs to be converted to a path.
paths = config.paths;
syms = moduleName.split('/');
//For each module name segment, see if there is a path
//registered for it. Start with most specific name
//and work up from it.
for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/');
parentPath = getOwn(paths, parentModule);
if (parentPath) {
//If an array, it means there are a few choices,
//Choose the one that is desired
if (isArray(parentPath)) {
parentPath = parentPath[0];
}
syms.splice(0, i, parentPath);
break;
}
}
//Join the path parts together, then figure out if baseUrl is needed.
url = syms.join('/');
url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js'));
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
}
return config.urlArgs ? url +
((url.indexOf('?') === -1 ? '?' : '&') +
config.urlArgs) : url;
},
//Delegates to req.load. Broken out as a separate function to
//allow overriding in the optimizer.
load: function (id, url) {
req.load(context, id, url);
},
/**
* Executes a module callback function. Broken out as a separate function
* solely to allow the build system to sequence the files in the built
* layer in the right sequence.
*
* @private
*/
execCb: function (name, callback, args, exports) {
return callback.apply(exports, args);
},
/**
* callback for script loads, used to check status of loading.
*
* @param {Event} evt the event from the browser for the script
* that was loaded.
*/
onScriptLoad: function (evt) {
//Using currentTarget instead of target for Firefox 2.0's sake. Not
//all old browsers will be supported, but this one was easy enough
//to support and still makes sense.
if (evt.type === 'load' ||
(readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
//Reset interactive script so a script node is not held onto for
//to long.
interactiveScript = null;
//Pull out the name of the module and the context.
var data = getScriptData(evt);
context.completeLoad(data.id);
}
},
/**
* Callback for script errors.
*/
onScriptError: function (evt) {
var data = getScriptData(evt);
if (!hasPathFallback(data.id)) {
return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
}
}
};
context.require = context.makeRequire();
return context;
}
/**
* Main entry point.
*
* If the only argument to require is a string, then the module that
* is represented by that string is fetched for the appropriate context.
*
* If the first argument is an array, then it will be treated as an array
* of dependency string names to fetch. An optional function callback can
* be specified to execute when all of those dependencies are available.
*
* Make a local req variable to help Caja compliance (it assumes things
* on a require that are not standardized), and to give a short
* name for minification/local scope use.
*/
req = requirejs = function (deps, callback, errback, optional) {
//Find the right context, use default
var context, config,
contextName = defContextName;
// Determine if have config object in the call.
if (!isArray(deps) && typeof deps !== 'string') {
// deps is a config object
config = deps;
if (isArray(callback)) {
// Adjust args if there are dependencies
deps = callback;
callback = errback;
errback = optional;
} else {
deps = [];
}
}
if (config && config.context) {
contextName = config.context;
}
context = getOwn(contexts, contextName);
if (!context) {
context = contexts[contextName] = req.s.newContext(contextName);
}
if (config) {
context.configure(config);
}
return context.require(deps, callback, errback);
};
/**
* Support require.config() to make it easier to cooperate with other
* AMD loaders on globally agreed names.
*/
req.config = function (config) {
return req(config);
};
/**
* Execute something after the current tick
* of the event loop. Override for other envs
* that have a better solution than setTimeout.
* @param {Function} fn function to execute later.
*/
req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
setTimeout(fn, 4);
} : function (fn) { fn(); };
/**
* Export require as a global, but only if it does not already exist.
*/
if (!require) {
require = req;
}
req.version = version;
//Used to filter out dependencies that are already paths.
req.jsExtRegExp = /^\/|:|\?|\.js$/;
req.isBrowser = isBrowser;
s = req.s = {
contexts: contexts,
newContext: newContext
};
//Create default context.
req({});
//Exports some context-sensitive methods on global require.
each([
'toUrl',
'undef',
'defined',
'specified'
], function (prop) {
//Reference from contexts instead of early binding to default context,
//so that during builds, the latest instance of the default context
//with its config gets used.
req[prop] = function () {
var ctx = contexts[defContextName];
return ctx.require[prop].apply(ctx, arguments);
};
});
if (isBrowser) {
head = s.head = document.getElementsByTagName('head')[0];
//If BASE tag is in play, using appendChild is a problem for IE6.
//When that browser dies, this can be removed. Details in this jQuery bug:
//http://dev.jquery.com/ticket/2709
baseElement = document.getElementsByTagName('base')[0];
if (baseElement) {
head = s.head = baseElement.parentNode;
}
}
/**
* Any errors that require explicitly generates will be passed to this
* function. Intercept/override it if you want custom error handling.
* @param {Error} err the error object.
*/
req.onError = defaultOnError;
/**
* Creates the node for the load command. Only used in browser envs.
*/
req.createNode = function (config, moduleName, url) {
var node = config.xhtml ?
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
return node;
};
/**
* Does the request to load a module for the browser case.
* Make this a separate function to allow other environments
* to override it.
*
* @param {Object} context the require context to find state.
* @param {String} moduleName the name of the module.
* @param {Object} url the URL to the module.
*/
req.load = function (context, moduleName, url) {
var config = (context && context.config) || {},
node;
if (isBrowser) {
//In the browser so use a script tag
node = req.createNode(config, moduleName, url);
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
//Set up load listener. Test attachEvent first because IE9 has
//a subtle issue in its addEventListener and script onload firings
//that do not match the behavior of all other browsers with
//addEventListener support, which fire the onload event for a
//script right after the script execution. See:
//https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
//UNFORTUNATELY Opera implements attachEvent but does not follow the script
//script execution mode.
if (node.attachEvent &&
//Check if node.attachEvent is artificially added by custom script or
//natively supported by browser
//read https://github.com/jrburke/requirejs/issues/187
//if we can NOT find [native code] then it must NOT natively supported.
//in IE8, node.attachEvent does not have toString()
//Note the test for "[native code" with no closing brace, see:
//https://github.com/jrburke/requirejs/issues/273
!(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
!isOpera) {
//Probably IE. IE (at least 6-8) do not fire
//script onload right after executing the script, so
//we cannot tie the anonymous define call to a name.
//However, IE reports the script as being in 'interactive'
//readyState at the time of the define call.
useInteractive = true;
node.attachEvent('onreadystatechange', context.onScriptLoad);
//It would be great to add an error handler here to catch
//404s in IE9+. However, onreadystatechange will fire before
//the error handler, so that does not help. If addEventListener
//is used, then IE will fire error before load, but we cannot
//use that pathway given the connect.microsoft.com issue
//mentioned above about not doing the 'script execute,
//then fire the script load event listener before execute
//next script' that other browsers do.
//Best hope: IE10 fixes the issues,
//and then destroys all installs of IE 6-9.
//node.attachEvent('onerror', context.onScriptError);
} else {
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);
}
node.src = url;
//For some cache cases in IE 6-8, the script executes before the end
//of the appendChild execution, so to tie an anonymous define
//call to the module name (which is stored on the node), hold on
//to a reference to this node, but clear after the DOM insertion.
currentlyAddingScript = node;
if (baseElement) {
head.insertBefore(node, baseElement);
} else {
head.appendChild(node);
}
currentlyAddingScript = null;
return node;
} else if (isWebWorker) {
try {
//In a web worker, use importScripts. This is not a very
//efficient use of importScripts, importScripts will block until
//its script is downloaded and evaluated. However, if web workers
//are in play, the expectation that a build has been done so that
//only one script needs to be loaded anyway. This may need to be
//reevaluated if other use cases become common.
importScripts(url);
//Account for anonymous modules
context.completeLoad(moduleName);
} catch (e) {
context.onError(makeError('importscripts',
'importScripts failed for ' +
moduleName + ' at ' + url,
e,
[moduleName]));
}
}
};
function getInteractiveScript() {
if (interactiveScript && interactiveScript.readyState === 'interactive') {
return interactiveScript;
}
eachReverse(scripts(), function (script) {
if (script.readyState === 'interactive') {
return (interactiveScript = script);
}
});
return interactiveScript;
}
//Look for a data-main script attribute, which could also adjust the baseUrl.
if (isBrowser && !cfg.skipDataMain) {
//Figure out baseUrl. Get it from the script tag with require.js in it.
eachReverse(scripts(), function (script) {
//Set the 'head' where we can append children by
//using the script's parent.
if (!head) {
head = script.parentNode;
}
//Look for a data-main attribute to set main script for the page
//to load. If it is there, the path to data main becomes the
//baseUrl, if it is not already set.
dataMain = script.getAttribute('data-main');
if (dataMain) {
//Preserve dataMain in case it is a path (i.e. contains '?')
mainScript = dataMain;
//Set final baseUrl if there is not already an explicit one.
if (!cfg.baseUrl) {
//Pull off the directory of data-main for use as the
//baseUrl.
src = mainScript.split('/');
mainScript = src.pop();
subPath = src.length ? src.join('/') + '/' : './';
cfg.baseUrl = subPath;
}
//Strip off any trailing .js since mainScript is now
//like a module name.
mainScript = mainScript.replace(jsSuffixRegExp, '');
//If mainScript is still a path, fall back to dataMain
if (req.jsExtRegExp.test(mainScript)) {
mainScript = dataMain;
}
//Put the data-main script in the files to load.
cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
return true;
}
});
}
/**
* The function that handles definitions of modules. Differs from
* require() in that a string for the module should be the first argument,
* and the function to execute after dependencies are loaded should
* return a value to define the module corresponding to the first argument's
* name.
*/
define = function (name, deps, callback) {
var node, context;
//Allow for anonymous modules
if (typeof name !== 'string') {
//Adjust args appropriately
callback = deps;
deps = name;
name = null;
}
//This module may not have dependencies
if (!isArray(deps)) {
callback = deps;
deps = null;
}
//If no name, and callback is a function, then figure out if it a
//CommonJS thing with dependencies.
if (!deps && isFunction(callback)) {
deps = [];
//Remove comments from the callback string,
//look for require calls, and pull them into the dependencies,
//but only if there are function args.
if (callback.length) {
callback
.toString()
.replace(commentRegExp, '')
.replace(cjsRequireRegExp, function (match, dep) {
deps.push(dep);
});
//May be a CommonJS thing even without require calls, but still
//could use exports, and module. Avoid doing exports and module
//work though if it just needs require.
//REQUIRES the function to expect the CommonJS variables in the
//order listed below.
deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
}
}
//If in IE 6-8 and hit an anonymous define() call, do the interactive
//work.
if (useInteractive) {
node = currentlyAddingScript || getInteractiveScript();
if (node) {
if (!name) {
name = node.getAttribute('data-requiremodule');
}
context = contexts[node.getAttribute('data-requirecontext')];
}
}
//Always save off evaluating the def call until the script onload handler.
//This allows multiple modules to be in a file without prematurely
//tracing dependencies, and allows for anonymous module support,
//where the module name is not known until the script onload event
//occurs. If no context, use the global queue, and get it processed
//in the onscript load callback.
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
};
define.amd = {
jQuery: true
};
/**
* Executes the text. Normally just uses eval, but can be modified
* to use a better, environment-specific call. Only used for transpiling
* loader plugins, not for plain JS modules.
* @param {String} text the text to execute/evaluate.
*/
req.exec = function (text) {
/*jslint evil: true */
return eval(text);
};
//Set up with config info.
req(cfg);
}(this));
#!/usr/bin/env node
var log = require('npmlog')
var program = require('commander')
var progOptions = program
var Config = require('./lib/config')
var Api = require('./lib/api')
var appMode = 'dev'
program
.version(require(__dirname + '/package').version)
.usage('[options]')
.option('-f, --file [file]', 'config file - defaults to testem.json or testem.yml')
.option('-p, --port [num]', 'server port - defaults to 7357', Number)
.option('--host [hostname]', 'host name - defaults to localhost', String)
.option('-l, --launch [list]', 'list of launchers to launch(comma separated)')
.option('-s, --skip [list]', 'list of launchers to skip(comma separated)')
.option('-d, --debug', 'output debug to debug log - testem.log')
.option('-t, --test_page [page]', 'the html page to drive the tests')
.option('-g, --growl', 'turn on growl notifications')
program
.command('launchers')
.description('Print the list of available launchers (browsers & process launchers)')
.action(act(function(env){
env.__proto__ = program
progOptions = env
appMode = 'launchers'
}))
program
.command('ci')
.description('Continuous integration mode')
.option('-T, --timeout [sec]', 'timeout a browser after [sec] seconds', null)
.option('-P, --parallel [num]', 'number of browsers to run in parallel, defaults to 1', Number)
.option('-b, --bail_on_uncaught_error', 'Bail on any uncaught errors')
.option('-R, --reporter [reporter]', 'Test reporter to use [tap|dot|xunit]', 'tap')
.action(act(function(env){
env.__proto__ = program
progOptions = env
appMode = 'ci'
}))
program
.command('server')
.description('Run just the server')
.action(act(function(env){
env.__proto__ = program
progOptions = env
appMode = 'server'
}))
program.on('--help', function(){
console.log(' Keyboard Controls (in dev mode):\n')
console.log(' ENTER run the tests')
console.log(' q quit')
console.log(' LEFT ARROW move to the next browser tab on the left')
console.log(' RIGHT ARROW move to the next browser tab on the right')
console.log(' TAB switch between top and bottom panel (split mode only)')
console.log(' UP ARROW scroll up in the target text panel')
console.log(' DOWN ARROW scroll down in the target text panel')
console.log(' SPACE page down in the target text panel')
console.log(' b page up in the target text panel')
console.log(' d half a page down in the target text panel')
console.log(' u half a page up in the target text panel')
console.log()
})
main()
function main(){
program.parse(process.argv)
var config = new Config(appMode, progOptions)
if (appMode === 'launchers'){
config.read(function(){
config.printLauncherInfo()
})
}else{
var api = new Api()
if (appMode === 'ci'){
api.startCI(progOptions)
}else if (appMode === 'dev'){
api.startDev(progOptions)
}else if (appMode === 'server'){
api.startServer(progOptions)
}
}
}
// this is to workaround the weird behavior in command where
// if you provide additional command line arguments that aren't
// options, it goes in as a string as the 1st arguments of the
// "action" callback, we don't want this
function act(fun){
return function(){
var options = arguments[arguments.length - 1]
fun(options)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment