summaryrefslogtreecommitdiffstats
path: root/dom/system/gonk/tests/marionette
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/system/gonk/tests/marionette
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/system/gonk/tests/marionette')
-rw-r--r--dom/system/gonk/tests/marionette/head.js345
-rw-r--r--dom/system/gonk/tests/marionette/manifest.ini19
-rw-r--r--dom/system/gonk/tests/marionette/ril_jshint/README.md9
-rw-r--r--dom/system/gonk/tests/marionette/ril_jshint/jshint.js11096
-rw-r--r--dom/system/gonk/tests/marionette/ril_jshint/jshintrc118
-rw-r--r--dom/system/gonk/tests/marionette/test_all_network_info.js106
-rw-r--r--dom/system/gonk/tests/marionette/test_data_connection.js70
-rw-r--r--dom/system/gonk/tests/marionette/test_data_connection_proxy.js99
-rw-r--r--dom/system/gonk/tests/marionette/test_dsds_numRadioInterfaces.js43
-rw-r--r--dom/system/gonk/tests/marionette/test_fakevolume.js25
-rw-r--r--dom/system/gonk/tests/marionette/test_geolocation.js117
-rw-r--r--dom/system/gonk/tests/marionette/test_multiple_data_connection.js89
-rw-r--r--dom/system/gonk/tests/marionette/test_network_active_changed.js52
-rw-r--r--dom/system/gonk/tests/marionette/test_network_interface_list_service.js95
-rw-r--r--dom/system/gonk/tests/marionette/test_network_interface_mtu.js100
-rw-r--r--dom/system/gonk/tests/marionette/test_ril_code_quality.py371
-rw-r--r--dom/system/gonk/tests/marionette/test_screen_state.js47
-rw-r--r--dom/system/gonk/tests/marionette/test_timezone_changes.js135
18 files changed, 12936 insertions, 0 deletions
diff --git a/dom/system/gonk/tests/marionette/head.js b/dom/system/gonk/tests/marionette/head.js
new file mode 100644
index 000000000..5a6ee1272
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/head.js
@@ -0,0 +1,345 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_CONTEXT = "chrome";
+
+const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled";
+const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
+const SETTINGS_KEY_WIFI_ENABLED = "wifi.enabled";
+
+const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
+const TOPIC_NETWORK_ACTIVE_CHANGED = "network-active-changed";
+
+const NETWORK_STATE_UNKNOWN = Ci.nsINetworkInfo.NETWORK_STATE_UNKNOWN;
+const NETWORK_STATE_CONNECTING = Ci.nsINetworkInfo.NETWORK_STATE_CONNECTING;
+const NETWORK_STATE_CONNECTED = Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED;
+const NETWORK_STATE_DISCONNECTING = Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTING;
+const NETWORK_STATE_DISCONNECTED = Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTED;
+
+const NETWORK_TYPE_MOBILE = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE;
+const NETWORK_TYPE_MOBILE_MMS = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_MMS;
+const NETWORK_TYPE_MOBILE_SUPL = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_SUPL;
+const NETWORK_TYPE_MOBILE_IMS = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_IMS;
+const NETWORK_TYPE_MOBILE_DUN = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN;
+const NETWORK_TYPE_MOBILE_FOTA = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_FOTA;
+
+const networkTypes = [
+ NETWORK_TYPE_MOBILE,
+ NETWORK_TYPE_MOBILE_MMS,
+ NETWORK_TYPE_MOBILE_SUPL,
+ NETWORK_TYPE_MOBILE_IMS,
+ NETWORK_TYPE_MOBILE_DUN,
+ NETWORK_TYPE_MOBILE_FOTA
+];
+
+var Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
+
+var ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
+ok(ril, "ril.constructor is " + ril.constructor);
+
+var radioInterface = ril.getRadioInterface(0);
+ok(radioInterface, "radioInterface.constructor is " + radioInterface.constrctor);
+
+var _pendingEmulatorShellCmdCount = 0;
+var _pendingEmulatorCmdCount = 0;
+
+/**
+ * Send emulator shell command with safe guard.
+ *
+ * We should only call |finish()| after all emulator shell command transactions
+ * end, so here comes with the pending counter. Resolve when the emulator
+ * shell gives response. Never reject.
+ *
+ * Fulfill params:
+ * result -- an array of emulator shell response lines.
+ *
+ * @param aCommands
+ * A string array commands to be passed to emulator through adb shell.
+ *
+ * @return A deferred promise.
+ */
+function runEmulatorShellCmdSafe(aCommands) {
+ return new Promise(function(aResolve, aReject) {
+ ++_pendingEmulatorShellCmdCount;
+ runEmulatorShell(aCommands, function(aResult) {
+ --_pendingEmulatorShellCmdCount;
+
+ log("Emulator shell response: " + JSON.stringify(aResult));
+ aResolve(aResult);
+ });
+ });
+}
+
+/**
+ * Send emulator command with safe guard.
+ *
+ * We should only call |finish()| after all emulator command transactions
+ * end, so here comes with the pending counter. Resolve when the emulator
+ * gives positive response, and reject otherwise.
+ *
+ * Fulfill params:
+ * result -- an array of emulator response lines.
+ * Reject params:
+ * result -- an array of emulator response lines.
+ *
+ * @param aCommand
+ * A string command to be passed to emulator through its telnet console.
+ *
+ * @return A deferred promise.
+ */
+function runEmulatorCmdSafe(aCommand) {
+ log(aCommand);
+ return new Promise(function(aResolve, aReject) {
+ ++_pendingEmulatorCmdCount;
+ runEmulatorCmd(aCommand, function(aResult) {
+ --_pendingEmulatorCmdCount;
+
+ log("Emulator console response: " + JSON.stringify(aResult));
+ if (Array.isArray(aResult) &&
+ aResult[aResult.length - 1] === "OK") {
+ aResolve(aResult);
+ } else {
+ aReject(aResult);
+ }
+ });
+ });
+}
+
+/**
+ * Get mozSettings value specified by @aKey.
+ *
+ * Resolve if that mozSettings value is retrieved successfully, reject
+ * otherwise.
+ *
+ * Fulfill params: The corresponding mozSettings value of the key.
+ * Reject params: (none)
+ *
+ * @param aKey
+ * A string.
+ * @param aAllowError [optional]
+ * A boolean value. If set to true, an error response won't be treated
+ * as test failure. Default: false.
+ *
+ * @return A deferred promise.
+ */
+function getSettings(aKey, aAllowError) {
+ let request = window.navigator.mozSettings.createLock().get(aKey);
+ return request.then(function resolve(aValue) {
+ log("getSettings(" + aKey + ") - success");
+ return aValue[aKey];
+ }, function reject(aError) {
+ ok(aAllowError, "getSettings(" + aKey + ") - error");
+ });
+}
+
+/**
+ * Set mozSettings values.
+ *
+ * Resolve if that mozSettings value is set successfully, reject otherwise.
+ *
+ * Fulfill params: (none)
+ * Reject params: (none)
+ *
+ * @param aKey
+ * A string key.
+ * @param aValue
+ * An object value.
+ * @param aAllowError [optional]
+ * A boolean value. If set to true, an error response won't be treated
+ * as test failure. Default: false.
+ *
+ * @return A deferred promise.
+ */
+function setSettings(aKey, aValue, aAllowError) {
+ let settings = {};
+ settings[aKey] = aValue;
+ let lock = window.navigator.mozSettings.createLock();
+ let request = lock.set(settings);
+ let deferred = Promise.defer();
+ lock.onsettingstransactionsuccess = function () {
+ log("setSettings(" + JSON.stringify(settings) + ") - success");
+ deferred.resolve();
+ };
+ lock.onsettingstransactionfailure = function () {
+ ok(aAllowError, "setSettings(" + JSON.stringify(settings) + ") - error");
+ // We resolve even though we've thrown an error, since the ok()
+ // will do that.
+ deferred.resolve();
+ };
+ return deferred.promise;
+}
+
+/**
+ * Wait for observer event.
+ *
+ * Resolve if that topic event occurs. Never reject.
+ *
+ * Fulfill params: the subject passed.
+ *
+ * @param aTopic
+ * A string topic name.
+ *
+ * @return A deferred promise.
+ */
+function waitForObserverEvent(aTopic) {
+ let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+ let deferred = Promise.defer();
+
+ obs.addObserver(function observer(subject, topic, data) {
+ if (topic === aTopic) {
+ obs.removeObserver(observer, aTopic);
+ deferred.resolve(subject);
+ }
+ }, aTopic, false);
+
+ return deferred.promise;
+}
+
+/**
+ * Wait for one named event.
+ *
+ * Resolve if that named event occurs. Never reject.
+ *
+ * Fulfill params: the DOMEvent passed.
+ *
+ * @param aEventTarget
+ * An EventTarget object.
+ * @param aEventName
+ * A string event name.
+ * @param aMatchFun [optional]
+ * A matching function returns true or false to filter the event.
+ *
+ * @return A deferred promise.
+ */
+function waitForTargetEvent(aEventTarget, aEventName, aMatchFun) {
+ return new Promise(function(aResolve, aReject) {
+ aEventTarget.addEventListener(aEventName, function onevent(aEvent) {
+ if (!aMatchFun || aMatchFun(aEvent)) {
+ aEventTarget.removeEventListener(aEventName, onevent);
+ ok(true, "Event '" + aEventName + "' got.");
+ aResolve(aEvent);
+ }
+ });
+ });
+}
+
+/**
+ * Set the default data connection enabling state, wait for
+ * "network-connection-state-changed" event and verify state.
+ *
+ * Fulfill params: instance of nsIRilNetworkInfo of the network connected.
+ *
+ * @param aEnabled
+ * A boolean state.
+ *
+ * @return A deferred promise.
+ */
+function setDataEnabledAndWait(aEnabled) {
+ let promises = [];
+ promises.push(waitForObserverEvent(TOPIC_CONNECTION_STATE_CHANGED)
+ .then(function(aSubject) {
+ ok(aSubject instanceof Ci.nsIRilNetworkInfo,
+ "subject should be an instance of nsIRilNetworkInfo");
+ is(aSubject.type, NETWORK_TYPE_MOBILE,
+ "subject.type should be " + NETWORK_TYPE_MOBILE);
+ is(aSubject.state,
+ aEnabled ? Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED
+ : Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTED,
+ "subject.state should be " + aEnabled ? "CONNECTED" : "DISCONNECTED");
+
+ return aSubject;
+ }));
+ promises.push(setSettings(SETTINGS_KEY_DATA_ENABLED, aEnabled));
+
+ return Promise.all(promises).then(aValues => aValues[0]);
+}
+
+/**
+ * Setup a certain type of data connection, wait for
+ * "network-connection-state-changed" event and verify state.
+ *
+ * Fulfill params: instance of nsIRilNetworkInfo of the network connected.
+ *
+ * @param aNetworkType
+ * The mobile network type to setup.
+ *
+ * @return A deferred promise.
+ */
+function setupDataCallAndWait(aNetworkType) {
+ log("setupDataCallAndWait: " + aNetworkType);
+
+ let promises = [];
+ promises.push(waitForObserverEvent(TOPIC_CONNECTION_STATE_CHANGED)
+ .then(function(aSubject) {
+ ok(aSubject instanceof Ci.nsIRilNetworkInfo,
+ "subject should be an instance of nsIRilNetworkInfo");
+ is(aSubject.type, aNetworkType,
+ "subject.type should be " + aNetworkType);
+ is(aSubject.state, Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED,
+ "subject.state should be CONNECTED");
+
+ return aSubject;
+ }));
+ promises.push(radioInterface.setupDataCallByType(aNetworkType));
+
+ return Promise.all(promises).then(aValues => aValues[0]);
+}
+
+/**
+ * Deactivate a certain type of data connection, wait for
+ * "network-connection-state-changed" event and verify state.
+ *
+ * Fulfill params: (none)
+ *
+ * @param aNetworkType
+ * The mobile network type to deactivate.
+ *
+ * @return A deferred promise.
+ */
+function deactivateDataCallAndWait(aNetworkType) {
+ log("deactivateDataCallAndWait: " + aNetworkType);
+
+ let promises = [];
+ promises.push(waitForObserverEvent(TOPIC_CONNECTION_STATE_CHANGED)
+ .then(function(aSubject) {
+ ok(aSubject instanceof Ci.nsIRilNetworkInfo,
+ "subject should be an instance of nsIRilNetworkInfo");
+ is(aSubject.type, aNetworkType,
+ "subject.type should be " + aNetworkType);
+ is(aSubject.state, Ci.nsINetworkInfo.NETWORK_STATE_DISCONNECTED,
+ "subject.state should be DISCONNECTED");
+ }));
+ promises.push(radioInterface.deactivateDataCallByType(aNetworkType));
+
+ return Promise.all(promises);
+}
+
+/**
+ * Wait for pending emulator transactions and call |finish()|.
+ */
+function cleanUp() {
+ // Use ok here so that we have at least one test run.
+ ok(true, ":: CLEANING UP ::");
+
+ waitFor(finish, function() {
+ return _pendingEmulatorShellCmdCount === 0 &&
+ _pendingEmulatorCmdCount === 0;
+ });
+}
+
+/**
+ * Basic test routine helper.
+ *
+ * This helper does nothing but clean-ups.
+ *
+ * @param aTestCaseMain
+ * A function that takes no parameter.
+ */
+function startTestBase(aTestCaseMain) {
+ Promise.resolve()
+ .then(aTestCaseMain)
+ .then(cleanUp, function(aException) {
+ ok(false, "promise rejects during test: " + aException);
+ cleanUp();
+ });
+}
diff --git a/dom/system/gonk/tests/marionette/manifest.ini b/dom/system/gonk/tests/marionette/manifest.ini
new file mode 100644
index 000000000..528fe3baf
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/manifest.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+run-if = buildapp == 'b2g'
+
+[test_geolocation.js]
+skip-if = true # Bug 808783
+[test_fakevolume.js]
+[test_ril_code_quality.py]
+[test_screen_state.js]
+[test_dsds_numRadioInterfaces.js]
+[test_data_connection.js]
+[test_network_active_changed.js]
+[test_multiple_data_connection.js]
+[test_data_connection_proxy.js]
+[test_network_interface_list_service.js]
+[test_all_network_info.js]
+[test_network_interface_mtu.js]
+skip-if = android_version < '19'
+[test_timezone_changes.js]
+skip-if = android_version < '19'
diff --git a/dom/system/gonk/tests/marionette/ril_jshint/README.md b/dom/system/gonk/tests/marionette/ril_jshint/README.md
new file mode 100644
index 000000000..a63967d63
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/ril_jshint/README.md
@@ -0,0 +1,9 @@
+Test RIL Code Quality
+=====================
+
+For more information, please refer to
+
+* Bug 880643 - B2G RIL: Add a code quality test on try server for RIL javascript code in gecko
+* Slide: https://speakerdeck.com/aknow/improve-code-quality-of-ril-code-by-jshint
+* Document: https://hackpad.com/Code-Quality-Test-For-RIL-Javascript-Code-In-Gecko-cz5j7YIGiw8
+
diff --git a/dom/system/gonk/tests/marionette/ril_jshint/jshint.js b/dom/system/gonk/tests/marionette/ril_jshint/jshint.js
new file mode 100644
index 000000000..ec5263a5b
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/ril_jshint/jshint.js
@@ -0,0 +1,11096 @@
+//2.1.3
+var JSHINT;
+(function () {
+var require;
+require=(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+
+process.nextTick = (function () {
+ var canSetImmediate = typeof window !== 'undefined'
+ && window.setImmediate;
+ var canPost = typeof window !== 'undefined'
+ && window.postMessage && window.addEventListener
+ ;
+
+ if (canSetImmediate) {
+ return function (f) { return window.setImmediate(f) };
+ }
+
+ if (canPost) {
+ var queue = [];
+ window.addEventListener('message', function (ev) {
+ if (ev.source === window && ev.data === 'process-tick') {
+ ev.stopPropagation();
+ if (queue.length > 0) {
+ var fn = queue.shift();
+ fn();
+ }
+ }
+ }, true);
+
+ return function nextTick(fn) {
+ queue.push(fn);
+ window.postMessage('process-tick', '*');
+ };
+ }
+
+ return function nextTick(fn) {
+ setTimeout(fn, 0);
+ };
+})();
+
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+}
+
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+
+},{}],2:[function(require,module,exports){
+(function(process){if (!process.EventEmitter) process.EventEmitter = function () {};
+
+var EventEmitter = exports.EventEmitter = process.EventEmitter;
+var isArray = typeof Array.isArray === 'function'
+ ? Array.isArray
+ : function (xs) {
+ return Object.prototype.toString.call(xs) === '[object Array]'
+ }
+;
+function indexOf (xs, x) {
+ if (xs.indexOf) return xs.indexOf(x);
+ for (var i = 0; i < xs.length; i++) {
+ if (x === xs[i]) return i;
+ }
+ return -1;
+}
+
+// By default EventEmitters will print a warning if more than
+// 10 listeners are added to it. This is a useful default which
+// helps finding memory leaks.
+//
+// Obviously not all Emitters should be limited to 10. This function allows
+// that to be increased. Set to zero for unlimited.
+var defaultMaxListeners = 10;
+EventEmitter.prototype.setMaxListeners = function(n) {
+ if (!this._events) this._events = {};
+ this._events.maxListeners = n;
+};
+
+
+EventEmitter.prototype.emit = function(type) {
+ // If there is no 'error' event listener then throw.
+ if (type === 'error') {
+ if (!this._events || !this._events.error ||
+ (isArray(this._events.error) && !this._events.error.length))
+ {
+ if (arguments[1] instanceof Error) {
+ throw arguments[1]; // Unhandled 'error' event
+ } else {
+ throw new Error("Uncaught, unspecified 'error' event.");
+ }
+ return false;
+ }
+ }
+
+ if (!this._events) return false;
+ var handler = this._events[type];
+ if (!handler) return false;
+
+ if (typeof handler == 'function') {
+ switch (arguments.length) {
+ // fast cases
+ case 1:
+ handler.call(this);
+ break;
+ case 2:
+ handler.call(this, arguments[1]);
+ break;
+ case 3:
+ handler.call(this, arguments[1], arguments[2]);
+ break;
+ // slower
+ default:
+ var args = Array.prototype.slice.call(arguments, 1);
+ handler.apply(this, args);
+ }
+ return true;
+
+ } else if (isArray(handler)) {
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ var listeners = handler.slice();
+ for (var i = 0, l = listeners.length; i < l; i++) {
+ listeners[i].apply(this, args);
+ }
+ return true;
+
+ } else {
+ return false;
+ }
+};
+
+// EventEmitter is defined in src/node_events.cc
+// EventEmitter.prototype.emit() is also defined there.
+EventEmitter.prototype.addListener = function(type, listener) {
+ if ('function' !== typeof listener) {
+ throw new Error('addListener only takes instances of Function');
+ }
+
+ if (!this._events) this._events = {};
+
+ // To avoid recursion in the case that type == "newListeners"! Before
+ // adding it to the listeners, first emit "newListeners".
+ this.emit('newListener', type, listener);
+
+ if (!this._events[type]) {
+ // Optimize the case of one listener. Don't need the extra array object.
+ this._events[type] = listener;
+ } else if (isArray(this._events[type])) {
+
+ // Check for listener leak
+ if (!this._events[type].warned) {
+ var m;
+ if (this._events.maxListeners !== undefined) {
+ m = this._events.maxListeners;
+ } else {
+ m = defaultMaxListeners;
+ }
+
+ if (m && m > 0 && this._events[type].length > m) {
+ this._events[type].warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +
+ 'leak detected. %d listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ this._events[type].length);
+ console.trace();
+ }
+ }
+
+ // If we've already got an array, just append.
+ this._events[type].push(listener);
+ } else {
+ // Adding the second element, need to change to array.
+ this._events[type] = [this._events[type], listener];
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+
+EventEmitter.prototype.once = function(type, listener) {
+ var self = this;
+ self.on(type, function g() {
+ self.removeListener(type, g);
+ listener.apply(this, arguments);
+ });
+
+ return this;
+};
+
+EventEmitter.prototype.removeListener = function(type, listener) {
+ if ('function' !== typeof listener) {
+ throw new Error('removeListener only takes instances of Function');
+ }
+
+ // does not use listeners(), so no side effect of creating _events[type]
+ if (!this._events || !this._events[type]) return this;
+
+ var list = this._events[type];
+
+ if (isArray(list)) {
+ var i = indexOf(list, listener);
+ if (i < 0) return this;
+ list.splice(i, 1);
+ if (list.length == 0)
+ delete this._events[type];
+ } else if (this._events[type] === listener) {
+ delete this._events[type];
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.removeAllListeners = function(type) {
+ if (arguments.length === 0) {
+ this._events = {};
+ return this;
+ }
+
+ // does not use listeners(), so no side effect of creating _events[type]
+ if (type && this._events && this._events[type]) this._events[type] = null;
+ return this;
+};
+
+EventEmitter.prototype.listeners = function(type) {
+ if (!this._events) this._events = {};
+ if (!this._events[type]) this._events[type] = [];
+ if (!isArray(this._events[type])) {
+ this._events[type] = [this._events[type]];
+ }
+ return this._events[type];
+};
+
+})(require("__browserify_process"))
+},{"__browserify_process":1}],3:[function(require,module,exports){
+(function(){// jshint -W001
+
+"use strict";
+
+// Identifiers provided by the ECMAScript standard.
+
+exports.reservedVars = {
+ arguments : false,
+ NaN : false
+};
+
+exports.ecmaIdentifiers = {
+ Array : false,
+ Boolean : false,
+ Date : false,
+ decodeURI : false,
+ decodeURIComponent : false,
+ encodeURI : false,
+ encodeURIComponent : false,
+ Error : false,
+ "eval" : false,
+ EvalError : false,
+ Function : false,
+ hasOwnProperty : false,
+ isFinite : false,
+ isNaN : false,
+ JSON : false,
+ Math : false,
+ Map : false,
+ Number : false,
+ Object : false,
+ parseInt : false,
+ parseFloat : false,
+ RangeError : false,
+ ReferenceError : false,
+ RegExp : false,
+ Set : false,
+ String : false,
+ SyntaxError : false,
+ TypeError : false,
+ URIError : false,
+ WeakMap : false
+};
+
+// Global variables commonly provided by a web browser environment.
+
+exports.browser = {
+ ArrayBuffer : false,
+ ArrayBufferView : false,
+ Audio : false,
+ Blob : false,
+ addEventListener : false,
+ applicationCache : false,
+ atob : false,
+ blur : false,
+ btoa : false,
+ clearInterval : false,
+ clearTimeout : false,
+ close : false,
+ closed : false,
+ DataView : false,
+ DOMParser : false,
+ defaultStatus : false,
+ document : false,
+ Element : false,
+ ElementTimeControl : false,
+ event : false,
+ FileReader : false,
+ Float32Array : false,
+ Float64Array : false,
+ FormData : false,
+ focus : false,
+ frames : false,
+ getComputedStyle : false,
+ HTMLElement : false,
+ HTMLAnchorElement : false,
+ HTMLBaseElement : false,
+ HTMLBlockquoteElement: false,
+ HTMLBodyElement : false,
+ HTMLBRElement : false,
+ HTMLButtonElement : false,
+ HTMLCanvasElement : false,
+ HTMLDirectoryElement : false,
+ HTMLDivElement : false,
+ HTMLDListElement : false,
+ HTMLFieldSetElement : false,
+ HTMLFontElement : false,
+ HTMLFormElement : false,
+ HTMLFrameElement : false,
+ HTMLFrameSetElement : false,
+ HTMLHeadElement : false,
+ HTMLHeadingElement : false,
+ HTMLHRElement : false,
+ HTMLHtmlElement : false,
+ HTMLIFrameElement : false,
+ HTMLImageElement : false,
+ HTMLInputElement : false,
+ HTMLIsIndexElement : false,
+ HTMLLabelElement : false,
+ HTMLLayerElement : false,
+ HTMLLegendElement : false,
+ HTMLLIElement : false,
+ HTMLLinkElement : false,
+ HTMLMapElement : false,
+ HTMLMenuElement : false,
+ HTMLMetaElement : false,
+ HTMLModElement : false,
+ HTMLObjectElement : false,
+ HTMLOListElement : false,
+ HTMLOptGroupElement : false,
+ HTMLOptionElement : false,
+ HTMLParagraphElement : false,
+ HTMLParamElement : false,
+ HTMLPreElement : false,
+ HTMLQuoteElement : false,
+ HTMLScriptElement : false,
+ HTMLSelectElement : false,
+ HTMLStyleElement : false,
+ HTMLTableCaptionElement: false,
+ HTMLTableCellElement : false,
+ HTMLTableColElement : false,
+ HTMLTableElement : false,
+ HTMLTableRowElement : false,
+ HTMLTableSectionElement: false,
+ HTMLTextAreaElement : false,
+ HTMLTitleElement : false,
+ HTMLUListElement : false,
+ HTMLVideoElement : false,
+ history : false,
+ Int16Array : false,
+ Int32Array : false,
+ Int8Array : false,
+ Image : false,
+ length : false,
+ localStorage : false,
+ location : false,
+ MessageChannel : false,
+ MessageEvent : false,
+ MessagePort : false,
+ moveBy : false,
+ moveTo : false,
+ MutationObserver : false,
+ name : false,
+ Node : false,
+ NodeFilter : false,
+ navigator : false,
+ onbeforeunload : true,
+ onblur : true,
+ onerror : true,
+ onfocus : true,
+ onload : true,
+ onresize : true,
+ onunload : true,
+ open : false,
+ openDatabase : false,
+ opener : false,
+ Option : false,
+ parent : false,
+ print : false,
+ removeEventListener : false,
+ resizeBy : false,
+ resizeTo : false,
+ screen : false,
+ scroll : false,
+ scrollBy : false,
+ scrollTo : false,
+ sessionStorage : false,
+ setInterval : false,
+ setTimeout : false,
+ SharedWorker : false,
+ status : false,
+ SVGAElement : false,
+ SVGAltGlyphDefElement: false,
+ SVGAltGlyphElement : false,
+ SVGAltGlyphItemElement: false,
+ SVGAngle : false,
+ SVGAnimateColorElement: false,
+ SVGAnimateElement : false,
+ SVGAnimateMotionElement: false,
+ SVGAnimateTransformElement: false,
+ SVGAnimatedAngle : false,
+ SVGAnimatedBoolean : false,
+ SVGAnimatedEnumeration: false,
+ SVGAnimatedInteger : false,
+ SVGAnimatedLength : false,
+ SVGAnimatedLengthList: false,
+ SVGAnimatedNumber : false,
+ SVGAnimatedNumberList: false,
+ SVGAnimatedPathData : false,
+ SVGAnimatedPoints : false,
+ SVGAnimatedPreserveAspectRatio: false,
+ SVGAnimatedRect : false,
+ SVGAnimatedString : false,
+ SVGAnimatedTransformList: false,
+ SVGAnimationElement : false,
+ SVGCSSRule : false,
+ SVGCircleElement : false,
+ SVGClipPathElement : false,
+ SVGColor : false,
+ SVGColorProfileElement: false,
+ SVGColorProfileRule : false,
+ SVGComponentTransferFunctionElement: false,
+ SVGCursorElement : false,
+ SVGDefsElement : false,
+ SVGDescElement : false,
+ SVGDocument : false,
+ SVGElement : false,
+ SVGElementInstance : false,
+ SVGElementInstanceList: false,
+ SVGEllipseElement : false,
+ SVGExternalResourcesRequired: false,
+ SVGFEBlendElement : false,
+ SVGFEColorMatrixElement: false,
+ SVGFEComponentTransferElement: false,
+ SVGFECompositeElement: false,
+ SVGFEConvolveMatrixElement: false,
+ SVGFEDiffuseLightingElement: false,
+ SVGFEDisplacementMapElement: false,
+ SVGFEDistantLightElement: false,
+ SVGFEDropShadowElement: false,
+ SVGFEFloodElement : false,
+ SVGFEFuncAElement : false,
+ SVGFEFuncBElement : false,
+ SVGFEFuncGElement : false,
+ SVGFEFuncRElement : false,
+ SVGFEGaussianBlurElement: false,
+ SVGFEImageElement : false,
+ SVGFEMergeElement : false,
+ SVGFEMergeNodeElement: false,
+ SVGFEMorphologyElement: false,
+ SVGFEOffsetElement : false,
+ SVGFEPointLightElement: false,
+ SVGFESpecularLightingElement: false,
+ SVGFESpotLightElement: false,
+ SVGFETileElement : false,
+ SVGFETurbulenceElement: false,
+ SVGFilterElement : false,
+ SVGFilterPrimitiveStandardAttributes: false,
+ SVGFitToViewBox : false,
+ SVGFontElement : false,
+ SVGFontFaceElement : false,
+ SVGFontFaceFormatElement: false,
+ SVGFontFaceNameElement: false,
+ SVGFontFaceSrcElement: false,
+ SVGFontFaceUriElement: false,
+ SVGForeignObjectElement: false,
+ SVGGElement : false,
+ SVGGlyphElement : false,
+ SVGGlyphRefElement : false,
+ SVGGradientElement : false,
+ SVGHKernElement : false,
+ SVGICCColor : false,
+ SVGImageElement : false,
+ SVGLangSpace : false,
+ SVGLength : false,
+ SVGLengthList : false,
+ SVGLineElement : false,
+ SVGLinearGradientElement: false,
+ SVGLocatable : false,
+ SVGMPathElement : false,
+ SVGMarkerElement : false,
+ SVGMaskElement : false,
+ SVGMatrix : false,
+ SVGMetadataElement : false,
+ SVGMissingGlyphElement: false,
+ SVGNumber : false,
+ SVGNumberList : false,
+ SVGPaint : false,
+ SVGPathElement : false,
+ SVGPathSeg : false,
+ SVGPathSegArcAbs : false,
+ SVGPathSegArcRel : false,
+ SVGPathSegClosePath : false,
+ SVGPathSegCurvetoCubicAbs: false,
+ SVGPathSegCurvetoCubicRel: false,
+ SVGPathSegCurvetoCubicSmoothAbs: false,
+ SVGPathSegCurvetoCubicSmoothRel: false,
+ SVGPathSegCurvetoQuadraticAbs: false,
+ SVGPathSegCurvetoQuadraticRel: false,
+ SVGPathSegCurvetoQuadraticSmoothAbs: false,
+ SVGPathSegCurvetoQuadraticSmoothRel: false,
+ SVGPathSegLinetoAbs : false,
+ SVGPathSegLinetoHorizontalAbs: false,
+ SVGPathSegLinetoHorizontalRel: false,
+ SVGPathSegLinetoRel : false,
+ SVGPathSegLinetoVerticalAbs: false,
+ SVGPathSegLinetoVerticalRel: false,
+ SVGPathSegList : false,
+ SVGPathSegMovetoAbs : false,
+ SVGPathSegMovetoRel : false,
+ SVGPatternElement : false,
+ SVGPoint : false,
+ SVGPointList : false,
+ SVGPolygonElement : false,
+ SVGPolylineElement : false,
+ SVGPreserveAspectRatio: false,
+ SVGRadialGradientElement: false,
+ SVGRect : false,
+ SVGRectElement : false,
+ SVGRenderingIntent : false,
+ SVGSVGElement : false,
+ SVGScriptElement : false,
+ SVGSetElement : false,
+ SVGStopElement : false,
+ SVGStringList : false,
+ SVGStylable : false,
+ SVGStyleElement : false,
+ SVGSwitchElement : false,
+ SVGSymbolElement : false,
+ SVGTRefElement : false,
+ SVGTSpanElement : false,
+ SVGTests : false,
+ SVGTextContentElement: false,
+ SVGTextElement : false,
+ SVGTextPathElement : false,
+ SVGTextPositioningElement: false,
+ SVGTitleElement : false,
+ SVGTransform : false,
+ SVGTransformList : false,
+ SVGTransformable : false,
+ SVGURIReference : false,
+ SVGUnitTypes : false,
+ SVGUseElement : false,
+ SVGVKernElement : false,
+ SVGViewElement : false,
+ SVGViewSpec : false,
+ SVGZoomAndPan : false,
+ TimeEvent : false,
+ top : false,
+ Uint16Array : false,
+ Uint32Array : false,
+ Uint8Array : false,
+ Uint8ClampedArray : false,
+ WebSocket : false,
+ window : false,
+ Worker : false,
+ XMLHttpRequest : false,
+ XMLSerializer : false,
+ XPathEvaluator : false,
+ XPathException : false,
+ XPathExpression : false,
+ XPathNSResolver : false,
+ XPathResult : false
+};
+
+exports.devel = {
+ alert : false,
+ confirm: false,
+ console: false,
+ Debug : false,
+ opera : false,
+ prompt : false
+};
+
+exports.worker = {
+ importScripts: true,
+ postMessage : true,
+ self : true
+};
+
+// Widely adopted global names that are not part of ECMAScript standard
+exports.nonstandard = {
+ escape : false,
+ unescape: false
+};
+
+// Globals provided by popular JavaScript environments.
+
+exports.couch = {
+ "require" : false,
+ respond : false,
+ getRow : false,
+ emit : false,
+ send : false,
+ start : false,
+ sum : false,
+ log : false,
+ exports : false,
+ module : false,
+ provides : false
+};
+
+exports.node = {
+ __filename : false,
+ __dirname : false,
+ Buffer : false,
+ DataView : false,
+ console : false,
+ exports : true, // In Node it is ok to exports = module.exports = foo();
+ GLOBAL : false,
+ global : false,
+ module : false,
+ process : false,
+ require : false,
+ setTimeout : false,
+ clearTimeout : false,
+ setInterval : false,
+ clearInterval : false,
+ setImmediate : false, // v0.9.1+
+ clearImmediate: false // v0.9.1+
+};
+
+exports.phantom = {
+ phantom : true,
+ require : true,
+ WebPage : true
+};
+
+exports.rhino = {
+ defineClass : false,
+ deserialize : false,
+ gc : false,
+ help : false,
+ importPackage: false,
+ "java" : false,
+ load : false,
+ loadClass : false,
+ print : false,
+ quit : false,
+ readFile : false,
+ readUrl : false,
+ runCommand : false,
+ seal : false,
+ serialize : false,
+ spawn : false,
+ sync : false,
+ toint32 : false,
+ version : false
+};
+
+exports.wsh = {
+ ActiveXObject : true,
+ Enumerator : true,
+ GetObject : true,
+ ScriptEngine : true,
+ ScriptEngineBuildVersion : true,
+ ScriptEngineMajorVersion : true,
+ ScriptEngineMinorVersion : true,
+ VBArray : true,
+ WSH : true,
+ WScript : true,
+ XDomainRequest : true
+};
+
+// Globals provided by popular JavaScript libraries.
+
+exports.dojo = {
+ dojo : false,
+ dijit : false,
+ dojox : false,
+ define : false,
+ "require": false
+};
+
+exports.jquery = {
+ "$" : false,
+ jQuery : false
+};
+
+exports.mootools = {
+ "$" : false,
+ "$$" : false,
+ Asset : false,
+ Browser : false,
+ Chain : false,
+ Class : false,
+ Color : false,
+ Cookie : false,
+ Core : false,
+ Document : false,
+ DomReady : false,
+ DOMEvent : false,
+ DOMReady : false,
+ Drag : false,
+ Element : false,
+ Elements : false,
+ Event : false,
+ Events : false,
+ Fx : false,
+ Group : false,
+ Hash : false,
+ HtmlTable : false,
+ Iframe : false,
+ IframeShim : false,
+ InputValidator: false,
+ instanceOf : false,
+ Keyboard : false,
+ Locale : false,
+ Mask : false,
+ MooTools : false,
+ Native : false,
+ Options : false,
+ OverText : false,
+ Request : false,
+ Scroller : false,
+ Slick : false,
+ Slider : false,
+ Sortables : false,
+ Spinner : false,
+ Swiff : false,
+ Tips : false,
+ Type : false,
+ typeOf : false,
+ URI : false,
+ Window : false
+};
+
+exports.prototypejs = {
+ "$" : false,
+ "$$" : false,
+ "$A" : false,
+ "$F" : false,
+ "$H" : false,
+ "$R" : false,
+ "$break" : false,
+ "$continue" : false,
+ "$w" : false,
+ Abstract : false,
+ Ajax : false,
+ Class : false,
+ Enumerable : false,
+ Element : false,
+ Event : false,
+ Field : false,
+ Form : false,
+ Hash : false,
+ Insertion : false,
+ ObjectRange : false,
+ PeriodicalExecuter: false,
+ Position : false,
+ Prototype : false,
+ Selector : false,
+ Template : false,
+ Toggle : false,
+ Try : false,
+ Autocompleter : false,
+ Builder : false,
+ Control : false,
+ Draggable : false,
+ Draggables : false,
+ Droppables : false,
+ Effect : false,
+ Sortable : false,
+ SortableObserver : false,
+ Sound : false,
+ Scriptaculous : false
+};
+
+exports.yui = {
+ YUI : false,
+ Y : false,
+ YUI_config: false
+};
+
+
+})()
+},{}],4:[function(require,module,exports){
+"use strict";
+
+var state = {
+ syntax: {},
+
+ reset: function () {
+ this.tokens = {
+ prev: null,
+ next: null,
+ curr: null
+ };
+
+ this.option = {};
+ this.ignored = {};
+ this.directive = {};
+ this.jsonMode = false;
+ this.jsonWarnings = [];
+ this.lines = [];
+ this.tab = "";
+ this.cache = {}; // Node.JS doesn't have Map. Sniff.
+ }
+};
+
+exports.state = state;
+
+},{}],5:[function(require,module,exports){
+(function(){"use strict";
+
+exports.register = function (linter) {
+ // Check for properties named __proto__. This special property was
+ // deprecated and then re-introduced for ES6.
+
+ linter.on("Identifier", function style_scanProto(data) {
+ if (linter.getOption("proto")) {
+ return;
+ }
+
+ if (data.name === "__proto__") {
+ linter.warn("W103", {
+ line: data.line,
+ char: data.char,
+ data: [ data.name ]
+ });
+ }
+ });
+
+ // Check for properties named __iterator__. This is a special property
+ // available only in browsers with JavaScript 1.7 implementation.
+
+ linter.on("Identifier", function style_scanIterator(data) {
+ if (linter.getOption("iterator")) {
+ return;
+ }
+
+ if (data.name === "__iterator__") {
+ linter.warn("W104", {
+ line: data.line,
+ char: data.char,
+ data: [ data.name ]
+ });
+ }
+ });
+
+ // Check for dangling underscores.
+
+ linter.on("Identifier", function style_scanDangling(data) {
+ if (!linter.getOption("nomen")) {
+ return;
+ }
+
+ // Underscore.js
+ if (data.name === "_") {
+ return;
+ }
+
+ // In Node, __dirname and __filename should be ignored.
+ if (linter.getOption("node")) {
+ if (/^(__dirname|__filename)$/.test(data.name) && !data.isProperty) {
+ return;
+ }
+ }
+
+ if (/^(_+.*|.*_+)$/.test(data.name)) {
+ linter.warn("W105", {
+ line: data.line,
+ char: data.from,
+ data: [ "dangling '_'", data.name ]
+ });
+ }
+ });
+
+ // Check that all identifiers are using camelCase notation.
+ // Exceptions: names like MY_VAR and _myVar.
+
+ linter.on("Identifier", function style_scanCamelCase(data) {
+ if (!linter.getOption("camelcase")) {
+ return;
+ }
+
+ if (data.name.replace(/^_+/, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) {
+ linter.warn("W106", {
+ line: data.line,
+ char: data.from,
+ data: [ data.name ]
+ });
+ }
+ });
+
+ // Enforce consistency in style of quoting.
+
+ linter.on("String", function style_scanQuotes(data) {
+ var quotmark = linter.getOption("quotmark");
+ var code;
+
+ if (!quotmark) {
+ return;
+ }
+
+ // If quotmark is set to 'single' warn about all double-quotes.
+
+ if (quotmark === "single" && data.quote !== "'") {
+ code = "W109";
+ }
+
+ // If quotmark is set to 'double' warn about all single-quotes.
+
+ if (quotmark === "double" && data.quote !== "\"") {
+ code = "W108";
+ }
+
+ // If quotmark is set to true, remember the first quotation style
+ // and then warn about all others.
+
+ if (quotmark === true) {
+ if (!linter.getCache("quotmark")) {
+ linter.setCache("quotmark", data.quote);
+ }
+
+ if (linter.getCache("quotmark") !== data.quote) {
+ code = "W110";
+ }
+ }
+
+ if (code) {
+ linter.warn(code, {
+ line: data.line,
+ char: data.char,
+ });
+ }
+ });
+
+ linter.on("Number", function style_scanNumbers(data) {
+ if (data.value.charAt(0) === ".") {
+ // Warn about a leading decimal point.
+ linter.warn("W008", {
+ line: data.line,
+ char: data.char,
+ data: [ data.value ]
+ });
+ }
+
+ if (data.value.substr(data.value.length - 1) === ".") {
+ // Warn about a trailing decimal point.
+ linter.warn("W047", {
+ line: data.line,
+ char: data.char,
+ data: [ data.value ]
+ });
+ }
+
+ if (/^00+/.test(data.value)) {
+ // Multiple leading zeroes.
+ linter.warn("W046", {
+ line: data.line,
+ char: data.char,
+ data: [ data.value ]
+ });
+ }
+ });
+
+ // Warn about script URLs.
+
+ linter.on("String", function style_scanJavaScriptURLs(data) {
+ var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i;
+
+ if (linter.getOption("scripturl")) {
+ return;
+ }
+
+ if (re.test(data.value)) {
+ linter.warn("W107", {
+ line: data.line,
+ char: data.char
+ });
+ }
+ });
+};
+})()
+},{}],6:[function(require,module,exports){
+/*
+ * Regular expressions. Some of these are stupidly long.
+ */
+
+/*jshint maxlen:1000 */
+
+"use string";
+
+// Unsafe comment or string (ax)
+exports.unsafeString =
+ /@cc|<\/?|script|\]\s*\]|<\s*!|&lt/i;
+
+// Unsafe characters that are silently deleted by one or more browsers (cx)
+exports.unsafeChars =
+ /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
+
+// Characters in strings that need escaping (nx and nxg)
+exports.needEsc =
+ /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
+
+exports.needEscGlobal =
+ /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+
+// Star slash (lx)
+exports.starSlash = /\*\//;
+
+// Identifier (ix)
+exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
+
+// JavaScript URL (jx)
+exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i;
+
+// Catches /* falls through */ comments (ft)
+//exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/;
+exports.fallsThrough = /^\s*\/\/\s*Falls?\sthrough.*\s*$/;
+
+},{}],7:[function(require,module,exports){
+(function(global){/*global window, global*/
+var util = require("util")
+var assert = require("assert")
+
+var slice = Array.prototype.slice
+var console
+var times = {}
+
+if (typeof global !== "undefined" && global.console) {
+ console = global.console
+} else if (typeof window !== "undefined" && window.console) {
+ console = window.console
+} else {
+ console = window.console = {}
+}
+
+var functions = [
+ [log, "log"]
+ , [info, "info"]
+ , [warn, "warn"]
+ , [error, "error"]
+ , [time, "time"]
+ , [timeEnd, "timeEnd"]
+ , [trace, "trace"]
+ , [dir, "dir"]
+ , [assert, "assert"]
+]
+
+for (var i = 0; i < functions.length; i++) {
+ var tuple = functions[i]
+ var f = tuple[0]
+ var name = tuple[1]
+
+ if (!console[name]) {
+ console[name] = f
+ }
+}
+
+module.exports = console
+
+function log() {}
+
+function info() {
+ console.log.apply(console, arguments)
+}
+
+function warn() {
+ console.log.apply(console, arguments)
+}
+
+function error() {
+ console.warn.apply(console, arguments)
+}
+
+function time(label) {
+ times[label] = Date.now()
+}
+
+function timeEnd(label) {
+ var time = times[label]
+ if (!time) {
+ throw new Error("No such label: " + label)
+ }
+
+ var duration = Date.now() - time
+ console.log(label + ": " + duration + "ms")
+}
+
+function trace() {
+ var err = new Error()
+ err.name = "Trace"
+ err.message = util.format.apply(null, arguments)
+ console.error(err.stack)
+}
+
+function dir(object) {
+ console.log(util.inspect(object) + "\n")
+}
+
+function assert(expression) {
+ if (!expression) {
+ var arr = slice.call(arguments, 1)
+ assert.ok(false, util.format.apply(null, arr))
+ }
+}
+
+})(window)
+},{"util":8,"assert":9}],10:[function(require,module,exports){
+(function(){/*
+ * Lexical analysis and token construction.
+ */
+
+"use strict";
+
+var _ = require("underscore");
+var events = require("events");
+var reg = require("./reg.js");
+var state = require("./state.js").state;
+
+// Some of these token types are from JavaScript Parser API
+// while others are specific to JSHint parser.
+// JS Parser API: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+
+var Token = {
+ Identifier: 1,
+ Punctuator: 2,
+ NumericLiteral: 3,
+ StringLiteral: 4,
+ Comment: 5,
+ Keyword: 6,
+ NullLiteral: 7,
+ BooleanLiteral: 8,
+ RegExp: 9
+};
+
+// This is auto generated from the unicode tables.
+// The tables are at:
+// http://www.fileformat.info/info/unicode/category/Lu/list.htm
+// http://www.fileformat.info/info/unicode/category/Ll/list.htm
+// http://www.fileformat.info/info/unicode/category/Lt/list.htm
+// http://www.fileformat.info/info/unicode/category/Lm/list.htm
+// http://www.fileformat.info/info/unicode/category/Lo/list.htm
+// http://www.fileformat.info/info/unicode/category/Nl/list.htm
+
+var unicodeLetterTable = [
+ 170, 170, 181, 181, 186, 186, 192, 214,
+ 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750,
+ 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908,
+ 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366,
+ 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610,
+ 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775,
+ 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957,
+ 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069,
+ 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361,
+ 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431,
+ 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482,
+ 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529,
+ 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608,
+ 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654,
+ 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736,
+ 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785,
+ 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867,
+ 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929,
+ 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970,
+ 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001,
+ 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123,
+ 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212,
+ 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261,
+ 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344,
+ 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455,
+ 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526,
+ 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716,
+ 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743,
+ 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760,
+ 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805,
+ 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138,
+ 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198,
+ 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346,
+ 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696,
+ 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789,
+ 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880,
+ 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740,
+ 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900,
+ 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000,
+ 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312,
+ 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516,
+ 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823,
+ 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141,
+ 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409,
+ 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013,
+ 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061,
+ 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140,
+ 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188,
+ 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455,
+ 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486,
+ 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521,
+ 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358,
+ 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621,
+ 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694,
+ 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726,
+ 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295,
+ 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438,
+ 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589,
+ 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312,
+ 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124,
+ 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539,
+ 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783,
+ 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921,
+ 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042,
+ 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259,
+ 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442,
+ 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595,
+ 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697,
+ 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714,
+ 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798,
+ 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032,
+ 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045,
+ 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279,
+ 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316,
+ 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433,
+ 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019,
+ 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370,
+ 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495,
+ 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594,
+ 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786,
+ 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334,
+ 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511,
+ 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592,
+ 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669,
+ 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115,
+ 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405,
+ 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687,
+ 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894,
+ 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964,
+ 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980,
+ 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069,
+ 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121,
+ 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144,
+ 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570,
+ 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686,
+ 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779,
+ 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972,
+ 177984, 177984, 178205, 178205, 194560, 195101
+];
+
+var identifierStartTable = [];
+
+for (var i = 0; i < 128; i++) {
+ identifierStartTable[i] =
+ i === 36 || // $
+ i >= 65 && i <= 90 || // A-Z
+ i === 95 || // _
+ i >= 97 && i <= 122; // a-z
+}
+
+var identifierPartTable = [];
+
+for (var i = 0; i < 128; i++) {
+ identifierPartTable[i] =
+ identifierStartTable[i] || // $, _, A-Z, a-z
+ i >= 48 && i <= 57; // 0-9
+}
+
+// Object that handles postponed lexing verifications that checks the parsed
+// environment state.
+
+function asyncTrigger() {
+ var _checks = [];
+
+ return {
+ push: function (fn) {
+ _checks.push(fn);
+ },
+
+ check: function () {
+ for (var check in _checks) {
+ _checks[check]();
+ }
+
+ _checks.splice(0, _checks.length);
+ }
+ };
+}
+
+/*
+ * Lexer for JSHint.
+ *
+ * This object does a char-by-char scan of the provided source code
+ * and produces a sequence of tokens.
+ *
+ * var lex = new Lexer("var i = 0;");
+ * lex.start();
+ * lex.token(); // returns the next token
+ *
+ * You have to use the token() method to move the lexer forward
+ * but you don't have to use its return value to get tokens. In addition
+ * to token() method returning the next token, the Lexer object also
+ * emits events.
+ *
+ * lex.on("Identifier", function (data) {
+ * if (data.name.indexOf("_") >= 0) {
+ * // Produce a warning.
+ * }
+ * });
+ *
+ * Note that the token() method returns tokens in a JSLint-compatible
+ * format while the event emitter uses a slightly modified version of
+ * Mozilla's JavaScript Parser API. Eventually, we will move away from
+ * JSLint format.
+ */
+function Lexer(source) {
+ var lines = source;
+
+ if (typeof lines === "string") {
+ lines = lines
+ .replace(/\r\n/g, "\n")
+ .replace(/\r/g, "\n")
+ .split("\n");
+ }
+
+ // If the first line is a shebang (#!), make it a blank and move on.
+ // Shebangs are used by Node scripts.
+
+ if (lines[0] && lines[0].substr(0, 2) === "#!") {
+ lines[0] = "";
+ }
+
+ this.emitter = new events.EventEmitter();
+ this.source = source;
+ this.lines = lines;
+ this.prereg = true;
+
+ this.line = 0;
+ this.char = 1;
+ this.from = 1;
+ this.input = "";
+
+ for (var i = 0; i < state.option.indent; i += 1) {
+ state.tab += " ";
+ }
+}
+
+Lexer.prototype = {
+ _lines: [],
+
+ get lines() {
+ this._lines = state.lines;
+ return this._lines;
+ },
+
+ set lines(val) {
+ this._lines = val;
+ state.lines = this._lines;
+ },
+
+ /*
+ * Return the next i character without actually moving the
+ * char pointer.
+ */
+ peek: function (i) {
+ return this.input.charAt(i || 0);
+ },
+
+ /*
+ * Move the char pointer forward i times.
+ */
+ skip: function (i) {
+ i = i || 1;
+ this.char += i;
+ this.input = this.input.slice(i);
+ },
+
+ /*
+ * Subscribe to a token event. The API for this method is similar
+ * Underscore.js i.e. you can subscribe to multiple events with
+ * one call:
+ *
+ * lex.on("Identifier Number", function (data) {
+ * // ...
+ * });
+ */
+ on: function (names, listener) {
+ names.split(" ").forEach(function (name) {
+ this.emitter.on(name, listener);
+ }.bind(this));
+ },
+
+ /*
+ * Trigger a token event. All arguments will be passed to each
+ * listener.
+ */
+ trigger: function () {
+ this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments));
+ },
+
+ /*
+ * Postpone a token event. the checking condition is set as
+ * last parameter, and the trigger function is called in a
+ * stored callback. To be later called using the check() function
+ * by the parser. This avoids parser's peek() to give the lexer
+ * a false context.
+ */
+ triggerAsync: function (type, args, checks, fn) {
+ checks.push(function () {
+ if (fn()) {
+ this.trigger(type, args);
+ }
+ }.bind(this));
+ },
+
+ /*
+ * Extract a punctuator out of the next sequence of characters
+ * or return 'null' if its not possible.
+ *
+ * This method's implementation was heavily influenced by the
+ * scanPunctuator function in the Esprima parser's source code.
+ */
+ scanPunctuator: function () {
+ var ch1 = this.peek();
+ var ch2, ch3, ch4;
+
+ switch (ch1) {
+ // Most common single-character punctuators
+ case ".":
+ if ((/^[0-9]$/).test(this.peek(1))) {
+ return null;
+ }
+ if (this.peek(1) === "." && this.peek(2) === ".") {
+ return {
+ type: Token.Punctuator,
+ value: "..."
+ };
+ }
+ /* falls through */
+ case "(":
+ case ")":
+ case ";":
+ case ",":
+ case "{":
+ case "}":
+ case "[":
+ case "]":
+ case ":":
+ case "~":
+ case "?":
+ return {
+ type: Token.Punctuator,
+ value: ch1
+ };
+
+ // A pound sign (for Node shebangs)
+ case "#":
+ return {
+ type: Token.Punctuator,
+ value: ch1
+ };
+
+ // We're at the end of input
+ case "":
+ return null;
+ }
+
+ // Peek more characters
+
+ ch2 = this.peek(1);
+ ch3 = this.peek(2);
+ ch4 = this.peek(3);
+
+ // 4-character punctuator: >>>=
+
+ if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") {
+ return {
+ type: Token.Punctuator,
+ value: ">>>="
+ };
+ }
+
+ // 3-character punctuators: === !== >>> <<= >>=
+
+ if (ch1 === "=" && ch2 === "=" && ch3 === "=") {
+ return {
+ type: Token.Punctuator,
+ value: "==="
+ };
+ }
+
+ if (ch1 === "!" && ch2 === "=" && ch3 === "=") {
+ return {
+ type: Token.Punctuator,
+ value: "!=="
+ };
+ }
+
+ if (ch1 === ">" && ch2 === ">" && ch3 === ">") {
+ return {
+ type: Token.Punctuator,
+ value: ">>>"
+ };
+ }
+
+ if (ch1 === "<" && ch2 === "<" && ch3 === "=") {
+ return {
+ type: Token.Punctuator,
+ value: "<<="
+ };
+ }
+
+ if (ch1 === ">" && ch2 === ">" && ch3 === "=") {
+ return {
+ type: Token.Punctuator,
+ value: ">>="
+ };
+ }
+
+ // Fat arrow punctuator
+ if (ch1 === "=" && ch2 === ">") {
+ return {
+ type: Token.Punctuator,
+ value: ch1 + ch2
+ };
+ }
+
+ // 2-character punctuators: <= >= == != ++ -- << >> && ||
+ // += -= *= %= &= |= ^= (but not /=, see below)
+ if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) {
+ return {
+ type: Token.Punctuator,
+ value: ch1 + ch2
+ };
+ }
+
+ if ("<>=!+-*%&|^".indexOf(ch1) >= 0) {
+ if (ch2 === "=") {
+ return {
+ type: Token.Punctuator,
+ value: ch1 + ch2
+ };
+ }
+
+ return {
+ type: Token.Punctuator,
+ value: ch1
+ };
+ }
+
+ // Special case: /=. We need to make sure that this is an
+ // operator and not a regular expression.
+
+ if (ch1 === "/") {
+ if (ch2 === "=" && /\/=(?!(\S*\/[gim]?))/.test(this.input)) {
+ // /= is not a part of a regular expression, return it as a
+ // punctuator.
+ return {
+ type: Token.Punctuator,
+ value: "/="
+ };
+ }
+
+ return {
+ type: Token.Punctuator,
+ value: "/"
+ };
+ }
+
+ return null;
+ },
+
+ /*
+ * Extract a comment out of the next sequence of characters and/or
+ * lines or return 'null' if its not possible. Since comments can
+ * span across multiple lines this method has to move the char
+ * pointer.
+ *
+ * In addition to normal JavaScript comments (// and /*) this method
+ * also recognizes JSHint- and JSLint-specific comments such as
+ * /*jshint, /*jslint, /*globals and so on.
+ */
+ scanComments: function () {
+ var ch1 = this.peek();
+ var ch2 = this.peek(1);
+ var rest = this.input.substr(2);
+ var startLine = this.line;
+ var startChar = this.char;
+
+ // Create a comment token object and make sure it
+ // has all the data JSHint needs to work with special
+ // comments.
+
+ function commentToken(label, body, opt) {
+ var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"];
+ var isSpecial = false;
+ var value = label + body;
+ var commentType = "plain";
+ opt = opt || {};
+
+ if (opt.isMultiline) {
+ value += "*/";
+ }
+
+ special.forEach(function (str) {
+ if (isSpecial) {
+ return;
+ }
+
+ // Don't recognize any special comments other than jshint for single-line
+ // comments. This introduced many problems with legit comments.
+ if (label === "//" && str !== "jshint") {
+ return;
+ }
+
+ if (body.substr(0, str.length) === str) {
+ isSpecial = true;
+ label = label + str;
+ body = body.substr(str.length);
+ }
+
+ if (!isSpecial && body.charAt(0) === " " && body.substr(1, str.length) === str) {
+ isSpecial = true;
+ label = label + " " + str;
+ body = body.substr(str.length + 1);
+ }
+
+ if (!isSpecial) {
+ return;
+ }
+
+ switch (str) {
+ case "member":
+ commentType = "members";
+ break;
+ case "global":
+ commentType = "globals";
+ break;
+ default:
+ commentType = str;
+ }
+ });
+
+ return {
+ type: Token.Comment,
+ commentType: commentType,
+ value: value,
+ body: body,
+ isSpecial: isSpecial,
+ isMultiline: opt.isMultiline || false,
+ isMalformed: opt.isMalformed || false
+ };
+ }
+
+ // End of unbegun comment. Raise an error and skip that input.
+ if (ch1 === "*" && ch2 === "/") {
+ this.trigger("error", {
+ code: "E018",
+ line: startLine,
+ character: startChar
+ });
+
+ this.skip(2);
+ return null;
+ }
+
+ // Comments must start either with // or /*
+ if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) {
+ return null;
+ }
+
+ // One-line comment
+ if (ch2 === "/") {
+ this.skip(this.input.length); // Skip to the EOL.
+ return commentToken("//", rest);
+ }
+
+ var body = "";
+
+ /* Multi-line comment */
+ if (ch2 === "*") {
+ this.skip(2);
+
+ while (this.peek() !== "*" || this.peek(1) !== "/") {
+ if (this.peek() === "") { // End of Line
+ body += "\n";
+
+ // If we hit EOF and our comment is still unclosed,
+ // trigger an error and end the comment implicitly.
+ if (!this.nextLine()) {
+ this.trigger("error", {
+ code: "E017",
+ line: startLine,
+ character: startChar
+ });
+
+ return commentToken("/*", body, {
+ isMultiline: true,
+ isMalformed: true
+ });
+ }
+ } else {
+ body += this.peek();
+ this.skip();
+ }
+ }
+
+ this.skip(2);
+ return commentToken("/*", body, { isMultiline: true });
+ }
+ },
+
+ /*
+ * Extract a keyword out of the next sequence of characters or
+ * return 'null' if its not possible.
+ */
+ scanKeyword: function () {
+ var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input);
+ var keywords = [
+ "if", "in", "do", "var", "for", "new",
+ "try", "let", "this", "else", "case",
+ "void", "with", "enum", "while", "break",
+ "catch", "throw", "const", "yield", "class",
+ "super", "return", "typeof", "delete",
+ "switch", "export", "import", "default",
+ "finally", "extends", "function", "continue",
+ "debugger", "instanceof"
+ ];
+
+ if (result && keywords.indexOf(result[0]) >= 0) {
+ return {
+ type: Token.Keyword,
+ value: result[0]
+ };
+ }
+
+ return null;
+ },
+
+ /*
+ * Extract a JavaScript identifier out of the next sequence of
+ * characters or return 'null' if its not possible. In addition,
+ * to Identifier this method can also produce BooleanLiteral
+ * (true/false) and NullLiteral (null).
+ */
+ scanIdentifier: function () {
+ var id = "";
+ var index = 0;
+ var type, char;
+
+ // Detects any character in the Unicode categories "Uppercase
+ // letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter
+ // (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or
+ // "Letter number (Nl)".
+ //
+ // Both approach and unicodeLetterTable were borrowed from
+ // Google's Traceur.
+
+ function isUnicodeLetter(code) {
+ for (var i = 0; i < unicodeLetterTable.length;) {
+ if (code < unicodeLetterTable[i++]) {
+ return false;
+ }
+
+ if (code <= unicodeLetterTable[i++]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function isHexDigit(str) {
+ return (/^[0-9a-fA-F]$/).test(str);
+ }
+
+ var readUnicodeEscapeSequence = function () {
+ /*jshint validthis:true */
+ index += 1;
+
+ if (this.peek(index) !== "u") {
+ return null;
+ }
+
+ var ch1 = this.peek(index + 1);
+ var ch2 = this.peek(index + 2);
+ var ch3 = this.peek(index + 3);
+ var ch4 = this.peek(index + 4);
+ var code;
+
+ if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) {
+ code = parseInt(ch1 + ch2 + ch3 + ch4, 16);
+
+ if (isUnicodeLetter(code)) {
+ index += 5;
+ return "\\u" + ch1 + ch2 + ch3 + ch4;
+ }
+
+ return null;
+ }
+
+ return null;
+ }.bind(this);
+
+ var getIdentifierStart = function () {
+ /*jshint validthis:true */
+ var chr = this.peek(index);
+ var code = chr.charCodeAt(0);
+
+ if (code === 92) {
+ return readUnicodeEscapeSequence();
+ }
+
+ if (code < 128) {
+ if (identifierStartTable[code]) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }
+
+ if (isUnicodeLetter(code)) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }.bind(this);
+
+ var getIdentifierPart = function () {
+ /*jshint validthis:true */
+ var chr = this.peek(index);
+ var code = chr.charCodeAt(0);
+
+ if (code === 92) {
+ return readUnicodeEscapeSequence();
+ }
+
+ if (code < 128) {
+ if (identifierPartTable[code]) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }
+
+ if (isUnicodeLetter(code)) {
+ index += 1;
+ return chr;
+ }
+
+ return null;
+ }.bind(this);
+
+ char = getIdentifierStart();
+ if (char === null) {
+ return null;
+ }
+
+ id = char;
+ for (;;) {
+ char = getIdentifierPart();
+
+ if (char === null) {
+ break;
+ }
+
+ id += char;
+ }
+
+ switch (id) {
+ case "true":
+ case "false":
+ type = Token.BooleanLiteral;
+ break;
+ case "null":
+ type = Token.NullLiteral;
+ break;
+ default:
+ type = Token.Identifier;
+ }
+
+ return {
+ type: type,
+ value: id
+ };
+ },
+
+ /*
+ * Extract a numeric literal out of the next sequence of
+ * characters or return 'null' if its not possible. This method
+ * supports all numeric literals described in section 7.8.3
+ * of the EcmaScript 5 specification.
+ *
+ * This method's implementation was heavily influenced by the
+ * scanNumericLiteral function in the Esprima parser's source code.
+ */
+ scanNumericLiteral: function () {
+ var index = 0;
+ var value = "";
+ var length = this.input.length;
+ var char = this.peek(index);
+ var bad;
+
+ function isDecimalDigit(str) {
+ return (/^[0-9]$/).test(str);
+ }
+
+ function isOctalDigit(str) {
+ return (/^[0-7]$/).test(str);
+ }
+
+ function isHexDigit(str) {
+ return (/^[0-9a-fA-F]$/).test(str);
+ }
+
+ function isIdentifierStart(ch) {
+ return (ch === "$") || (ch === "_") || (ch === "\\") ||
+ (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z");
+ }
+
+ // Numbers must start either with a decimal digit or a point.
+
+ if (char !== "." && !isDecimalDigit(char)) {
+ return null;
+ }
+
+ if (char !== ".") {
+ value = this.peek(index);
+ index += 1;
+ char = this.peek(index);
+
+ if (value === "0") {
+ // Base-16 numbers.
+ if (char === "x" || char === "X") {
+ index += 1;
+ value += char;
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isHexDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+
+ if (value.length <= 2) { // 0x
+ return {
+ type: Token.NumericLiteral,
+ value: value,
+ isMalformed: true
+ };
+ }
+
+ if (index < length) {
+ char = this.peek(index);
+ if (isIdentifierStart(char)) {
+ return null;
+ }
+ }
+
+ return {
+ type: Token.NumericLiteral,
+ value: value,
+ base: 16,
+ isMalformed: false
+ };
+ }
+
+ // Base-8 numbers.
+ if (isOctalDigit(char)) {
+ index += 1;
+ value += char;
+ bad = false;
+
+ while (index < length) {
+ char = this.peek(index);
+
+ // Numbers like '019' (note the 9) are not valid octals
+ // but we still parse them and mark as malformed.
+
+ if (isDecimalDigit(char)) {
+ bad = true;
+ } else if (!isOctalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+
+ if (index < length) {
+ char = this.peek(index);
+ if (isIdentifierStart(char)) {
+ return null;
+ }
+ }
+
+ return {
+ type: Token.NumericLiteral,
+ value: value,
+ base: 8,
+ isMalformed: false
+ };
+ }
+
+ // Decimal numbers that start with '0' such as '09' are illegal
+ // but we still parse them and return as malformed.
+
+ if (isDecimalDigit(char)) {
+ index += 1;
+ value += char;
+ }
+ }
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isDecimalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+ }
+
+ // Decimal digits.
+
+ if (char === ".") {
+ value += char;
+ index += 1;
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isDecimalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+ }
+
+ // Exponent part.
+
+ if (char === "e" || char === "E") {
+ value += char;
+ index += 1;
+ char = this.peek(index);
+
+ if (char === "+" || char === "-") {
+ value += this.peek(index);
+ index += 1;
+ }
+
+ char = this.peek(index);
+ if (isDecimalDigit(char)) {
+ value += char;
+ index += 1;
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!isDecimalDigit(char)) {
+ break;
+ }
+ value += char;
+ index += 1;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ if (index < length) {
+ char = this.peek(index);
+ if (isIdentifierStart(char)) {
+ return null;
+ }
+ }
+
+ return {
+ type: Token.NumericLiteral,
+ value: value,
+ base: 10,
+ isMalformed: !isFinite(value)
+ };
+ },
+
+ /*
+ * Extract a string out of the next sequence of characters and/or
+ * lines or return 'null' if its not possible. Since strings can
+ * span across multiple lines this method has to move the char
+ * pointer.
+ *
+ * This method recognizes pseudo-multiline JavaScript strings:
+ *
+ * var str = "hello\
+ * world";
+ */
+ scanStringLiteral: function (checks) {
+ /*jshint loopfunc:true */
+ var quote = this.peek();
+
+ // String must start with a quote.
+ if (quote !== "\"" && quote !== "'") {
+ return null;
+ }
+
+ // In JSON strings must always use double quotes.
+ this.triggerAsync("warning", {
+ code: "W108",
+ line: this.line,
+ character: this.char // +1?
+ }, checks, function () { return state.jsonMode && quote !== "\""; });
+
+ var value = "";
+ var startLine = this.line;
+ var startChar = this.char;
+ var allowNewLine = false;
+
+ this.skip();
+
+ while (this.peek() !== quote) {
+ while (this.peek() === "") { // End Of Line
+
+ // If an EOL is not preceded by a backslash, show a warning
+ // and proceed like it was a legit multi-line string where
+ // author simply forgot to escape the newline symbol.
+ //
+ // Another approach is to implicitly close a string on EOL
+ // but it generates too many false positives.
+
+ if (!allowNewLine) {
+ this.trigger("warning", {
+ code: "W112",
+ line: this.line,
+ character: this.char
+ });
+ } else {
+ allowNewLine = false;
+
+ // Otherwise show a warning if multistr option was not set.
+ // For JSON, show warning no matter what.
+
+ this.triggerAsync("warning", {
+ code: "W043",
+ line: this.line,
+ character: this.char
+ }, checks, function () { return !state.option.multistr; });
+
+ this.triggerAsync("warning", {
+ code: "W042",
+ line: this.line,
+ character: this.char
+ }, checks, function () { return state.jsonMode && state.option.multistr; });
+ }
+
+ // If we get an EOF inside of an unclosed string, show an
+ // error and implicitly close it at the EOF point.
+
+ if (!this.nextLine()) {
+ this.trigger("error", {
+ code: "E029",
+ line: startLine,
+ character: startChar
+ });
+
+ return {
+ type: Token.StringLiteral,
+ value: value,
+ isUnclosed: true,
+ quote: quote
+ };
+ }
+ }
+
+ allowNewLine = false;
+ var char = this.peek();
+ var jump = 1; // A length of a jump, after we're done
+ // parsing this character.
+
+ if (char < " ") {
+ // Warn about a control character in a string.
+ this.trigger("warning", {
+ code: "W113",
+ line: this.line,
+ character: this.char,
+ data: [ "<non-printable>" ]
+ });
+ }
+
+ // Special treatment for some escaped characters.
+
+ if (char === "\\") {
+ this.skip();
+ char = this.peek();
+
+ switch (char) {
+ case "'":
+ this.triggerAsync("warning", {
+ code: "W114",
+ line: this.line,
+ character: this.char,
+ data: [ "\\'" ]
+ }, checks, function () {return state.jsonMode; });
+ break;
+ case "b":
+ char = "\b";
+ break;
+ case "f":
+ char = "\f";
+ break;
+ case "n":
+ char = "\n";
+ break;
+ case "r":
+ char = "\r";
+ break;
+ case "t":
+ char = "\t";
+ break;
+ case "0":
+ char = "\0";
+
+ // Octal literals fail in strict mode.
+ // Check if the number is between 00 and 07.
+ var n = parseInt(this.peek(1), 10);
+ this.triggerAsync("warning", {
+ code: "W115",
+ line: this.line,
+ character: this.char
+ }, checks,
+ function () { return n >= 0 && n <= 7 && state.directive["use strict"]; });
+ break;
+ case "u":
+ char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16));
+ jump = 5;
+ break;
+ case "v":
+ this.triggerAsync("warning", {
+ code: "W114",
+ line: this.line,
+ character: this.char,
+ data: [ "\\v" ]
+ }, checks, function () { return state.jsonMode; });
+
+ char = "\v";
+ break;
+ case "x":
+ var x = parseInt(this.input.substr(1, 2), 16);
+
+ this.triggerAsync("warning", {
+ code: "W114",
+ line: this.line,
+ character: this.char,
+ data: [ "\\x-" ]
+ }, checks, function () { return state.jsonMode; });
+
+ char = String.fromCharCode(x);
+ jump = 3;
+ break;
+ case "\\":
+ case "\"":
+ case "/":
+ break;
+ case "":
+ allowNewLine = true;
+ char = "";
+ break;
+ case "!":
+ if (value.slice(value.length - 2) === "<") {
+ break;
+ }
+
+ /*falls through */
+ default:
+ // Weird escaping.
+ this.trigger("warning", {
+ code: "W044",
+ line: this.line,
+ character: this.char
+ });
+ }
+ }
+
+ value += char;
+ this.skip(jump);
+ }
+
+ this.skip();
+ return {
+ type: Token.StringLiteral,
+ value: value,
+ isUnclosed: false,
+ quote: quote
+ };
+ },
+
+ /*
+ * Extract a regular expression out of the next sequence of
+ * characters and/or lines or return 'null' if its not possible.
+ *
+ * This method is platform dependent: it accepts almost any
+ * regular expression values but then tries to compile and run
+ * them using system's RegExp object. This means that there are
+ * rare edge cases where one JavaScript engine complains about
+ * your regular expression while others don't.
+ */
+ scanRegExp: function () {
+ var index = 0;
+ var length = this.input.length;
+ var char = this.peek();
+ var value = char;
+ var body = "";
+ var flags = [];
+ var malformed = false;
+ var isCharSet = false;
+ var terminated;
+
+ var scanUnexpectedChars = function () {
+ // Unexpected control character
+ if (char < " ") {
+ malformed = true;
+ this.trigger("warning", {
+ code: "W048",
+ line: this.line,
+ character: this.char
+ });
+ }
+
+ // Unexpected escaped character
+ if (char === "<") {
+ malformed = true;
+ this.trigger("warning", {
+ code: "W049",
+ line: this.line,
+ character: this.char,
+ data: [ char ]
+ });
+ }
+ }.bind(this);
+
+ // Regular expressions must start with '/'
+ if (!this.prereg || char !== "/") {
+ return null;
+ }
+
+ index += 1;
+ terminated = false;
+
+ // Try to get everything in between slashes. A couple of
+ // cases aside (see scanUnexpectedChars) we don't really
+ // care whether the resulting expression is valid or not.
+ // We will check that later using the RegExp object.
+
+ while (index < length) {
+ char = this.peek(index);
+ value += char;
+ body += char;
+
+ if (isCharSet) {
+ if (char === "]") {
+ if (this.peek(index - 1) !== "\\" || this.peek(index - 2) === "\\") {
+ isCharSet = false;
+ }
+ }
+
+ if (char === "\\") {
+ index += 1;
+ char = this.peek(index);
+ body += char;
+ value += char;
+
+ scanUnexpectedChars();
+ }
+
+ index += 1;
+ continue;
+ }
+
+ if (char === "\\") {
+ index += 1;
+ char = this.peek(index);
+ body += char;
+ value += char;
+
+ scanUnexpectedChars();
+
+ if (char === "/") {
+ index += 1;
+ continue;
+ }
+
+ if (char === "[") {
+ index += 1;
+ continue;
+ }
+ }
+
+ if (char === "[") {
+ isCharSet = true;
+ index += 1;
+ continue;
+ }
+
+ if (char === "/") {
+ body = body.substr(0, body.length - 1);
+ terminated = true;
+ index += 1;
+ break;
+ }
+
+ index += 1;
+ }
+
+ // A regular expression that was never closed is an
+ // error from which we cannot recover.
+
+ if (!terminated) {
+ this.trigger("error", {
+ code: "E015",
+ line: this.line,
+ character: this.from
+ });
+
+ return void this.trigger("fatal", {
+ line: this.line,
+ from: this.from
+ });
+ }
+
+ // Parse flags (if any).
+
+ while (index < length) {
+ char = this.peek(index);
+ if (!/[gim]/.test(char)) {
+ break;
+ }
+ flags.push(char);
+ value += char;
+ index += 1;
+ }
+
+ // Check regular expression for correctness.
+
+ try {
+ new RegExp(body, flags.join(""));
+ } catch (err) {
+ malformed = true;
+ this.trigger("error", {
+ code: "E016",
+ line: this.line,
+ character: this.char,
+ data: [ err.message ] // Platform dependent!
+ });
+ }
+
+ return {
+ type: Token.RegExp,
+ value: value,
+ flags: flags,
+ isMalformed: malformed
+ };
+ },
+
+ /*
+ * Scan for any occurence of mixed tabs and spaces. If smarttabs option
+ * is on, ignore tabs followed by spaces.
+ *
+ * Tabs followed by one space followed by a block comment are allowed.
+ */
+ scanMixedSpacesAndTabs: function () {
+ var at, match;
+
+ if (state.option.smarttabs) {
+ // Negative look-behind for "//"
+ match = this.input.match(/(\/\/|^\s?\*)? \t/);
+ at = match && !match[1] ? 0 : -1;
+ } else {
+ at = this.input.search(/ \t|\t [^\*]/);
+ }
+
+ return at;
+ },
+
+ /*
+ * Scan for characters that get silently deleted by one or more browsers.
+ */
+ scanUnsafeChars: function () {
+ return this.input.search(reg.unsafeChars);
+ },
+
+ /*
+ * Produce the next raw token or return 'null' if no tokens can be matched.
+ * This method skips over all space characters.
+ */
+ next: function (checks) {
+ this.from = this.char;
+
+ // Move to the next non-space character.
+ var start;
+ if (/\s/.test(this.peek())) {
+ start = this.char;
+
+ while (/\s/.test(this.peek())) {
+ this.from += 1;
+ this.skip();
+ }
+
+ if (this.peek() === "") { // EOL
+ if (!/^\s*$/.test(this.lines[this.line - 1]) && state.option.trailing) {
+ this.trigger("warning", { code: "W102", line: this.line, character: start });
+ }
+ }
+ }
+
+ // Methods that work with multi-line structures and move the
+ // character pointer.
+
+ var match = this.scanComments() ||
+ this.scanStringLiteral(checks);
+
+ if (match) {
+ return match;
+ }
+
+ // Methods that don't move the character pointer.
+
+ match =
+ this.scanRegExp() ||
+ this.scanPunctuator() ||
+ this.scanKeyword() ||
+ this.scanIdentifier() ||
+ this.scanNumericLiteral();
+
+ if (match) {
+ this.skip(match.value.length);
+ return match;
+ }
+
+ // No token could be matched, give up.
+
+ return null;
+ },
+
+ /*
+ * Switch to the next line and reset all char pointers. Once
+ * switched, this method also checks for mixed spaces and tabs
+ * and other minor warnings.
+ */
+ nextLine: function () {
+ var char;
+
+ if (this.line >= this.lines.length) {
+ return false;
+ }
+
+ this.input = this.lines[this.line];
+ this.line += 1;
+ this.char = 1;
+ this.from = 1;
+
+ char = this.scanMixedSpacesAndTabs();
+ if (char >= 0) {
+ this.trigger("warning", { code: "W099", line: this.line, character: char + 1 });
+ }
+
+ this.input = this.input.replace(/\t/g, state.tab);
+ char = this.scanUnsafeChars();
+
+ if (char >= 0) {
+ this.trigger("warning", { code: "W100", line: this.line, character: char });
+ }
+
+ // If there is a limit on line length, warn when lines get too
+ // long.
+
+ if (state.option.maxlen && state.option.maxlen < this.input.length) {
+ this.trigger("warning", { code: "W101", line: this.line, character: this.input.length });
+ }
+
+ return true;
+ },
+
+ /*
+ * This is simply a synonym for nextLine() method with a friendlier
+ * public name.
+ */
+ start: function () {
+ this.nextLine();
+ },
+
+ /*
+ * Produce the next token. This function is called by advance() to get
+ * the next token. It retuns a token in a JSLint-compatible format.
+ */
+ token: function () {
+ /*jshint loopfunc:true */
+ var checks = asyncTrigger();
+ var token;
+
+
+ function isReserved(token, isProperty) {
+ if (!token.reserved) {
+ return false;
+ }
+
+ if (token.meta && token.meta.isFutureReservedWord) {
+ // ES3 FutureReservedWord in an ES5 environment.
+ if (state.option.inES5(true) && !token.meta.es5) {
+ return false;
+ }
+
+ // Some ES5 FutureReservedWord identifiers are active only
+ // within a strict mode environment.
+ if (token.meta.strictOnly) {
+ if (!state.option.strict && !state.directive["use strict"]) {
+ return false;
+ }
+ }
+
+ if (isProperty) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Produce a token object.
+ var create = function (type, value, isProperty) {
+ /*jshint validthis:true */
+ var obj;
+
+ if (type !== "(endline)" && type !== "(end)") {
+ this.prereg = false;
+ }
+
+ if (type === "(punctuator)") {
+ switch (value) {
+ case ".":
+ case ")":
+ case "~":
+ case "#":
+ case "]":
+ this.prereg = false;
+ break;
+ default:
+ this.prereg = true;
+ }
+
+ obj = Object.create(state.syntax[value] || state.syntax["(error)"]);
+ }
+
+ if (type === "(identifier)") {
+ if (value === "return" || value === "case" || value === "typeof") {
+ this.prereg = true;
+ }
+
+ if (_.has(state.syntax, value)) {
+ obj = Object.create(state.syntax[value] || state.syntax["(error)"]);
+
+ // If this can't be a reserved keyword, reset the object.
+ if (!isReserved(obj, isProperty && type === "(identifier)")) {
+ obj = null;
+ }
+ }
+ }
+
+ if (!obj) {
+ obj = Object.create(state.syntax[type]);
+ }
+
+ obj.identifier = (type === "(identifier)");
+ obj.type = obj.type || type;
+ obj.value = value;
+ obj.line = this.line;
+ obj.character = this.char;
+ obj.from = this.from;
+
+ if (isProperty && obj.identifier) {
+ obj.isProperty = isProperty;
+ }
+
+ obj.check = checks.check;
+
+ return obj;
+ }.bind(this);
+
+ for (;;) {
+ if (!this.input.length) {
+ return create(this.nextLine() ? "(endline)" : "(end)", "");
+ }
+
+ token = this.next(checks);
+
+ if (!token) {
+ if (this.input.length) {
+ // Unexpected character.
+ this.trigger("error", {
+ code: "E024",
+ line: this.line,
+ character: this.char,
+ data: [ this.peek() ]
+ });
+
+ this.input = "";
+ }
+
+ continue;
+ }
+
+ switch (token.type) {
+ case Token.StringLiteral:
+ this.triggerAsync("String", {
+ line: this.line,
+ char: this.char,
+ from: this.from,
+ value: token.value,
+ quote: token.quote
+ }, checks, function () { return true; });
+
+ return create("(string)", token.value);
+ case Token.Identifier:
+ this.trigger("Identifier", {
+ line: this.line,
+ char: this.char,
+ from: this.form,
+ name: token.value,
+ isProperty: state.tokens.curr.id === "."
+ });
+
+ /* falls through */
+ case Token.Keyword:
+ case Token.NullLiteral:
+ case Token.BooleanLiteral:
+ return create("(identifier)", token.value, state.tokens.curr.id === ".");
+
+ case Token.NumericLiteral:
+ if (token.isMalformed) {
+ this.trigger("warning", {
+ code: "W045",
+ line: this.line,
+ character: this.char,
+ data: [ token.value ]
+ });
+ }
+
+ this.triggerAsync("warning", {
+ code: "W114",
+ line: this.line,
+ character: this.char,
+ data: [ "0x-" ]
+ }, checks, function () { return token.base === 16 && state.jsonMode; });
+
+ this.triggerAsync("warning", {
+ code: "W115",
+ line: this.line,
+ character: this.char
+ }, checks, function () {
+ return state.directive["use strict"] && token.base === 8;
+ });
+
+ this.trigger("Number", {
+ line: this.line,
+ char: this.char,
+ from: this.from,
+ value: token.value,
+ base: token.base,
+ isMalformed: token.malformed
+ });
+
+ return create("(number)", token.value);
+
+ case Token.RegExp:
+ return create("(regexp)", token.value);
+
+ case Token.Comment:
+ state.tokens.curr.comment = true;
+
+ if (token.isSpecial) {
+ return {
+ value: token.value,
+ body: token.body,
+ type: token.commentType,
+ isSpecial: token.isSpecial,
+ line: this.line,
+ character: this.char,
+ from: this.from
+ };
+ }
+
+ break;
+
+ case "":
+ break;
+
+ default:
+ return create("(punctuator)", token.value);
+ }
+ }
+ }
+};
+
+exports.Lexer = Lexer;
+
+})()
+},{"events":2,"./reg.js":6,"./state.js":4,"underscore":11}],"jshint":[function(require,module,exports){
+module.exports=require('E/GbHF');
+},{}],"E/GbHF":[function(require,module,exports){
+(function(){/*!
+ * JSHint, by JSHint Community.
+ *
+ * This file (and this file only) is licensed under the same slightly modified
+ * MIT license that JSLint is. It stops evil-doers everywhere:
+ *
+ * Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * The Software shall be used for Good, not Evil.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*jshint quotmark:double */
+/*global console:true */
+/*exported console */
+
+var _ = require("underscore");
+var events = require("events");
+var vars = require("../shared/vars.js");
+var messages = require("../shared/messages.js");
+var Lexer = require("./lex.js").Lexer;
+var reg = require("./reg.js");
+var state = require("./state.js").state;
+var style = require("./style.js");
+
+// We need this module here because environments such as IE and Rhino
+// don't necessarilly expose the 'console' API and browserify uses
+// it to log things. It's a sad state of affair, really.
+var console = require("console-browserify");
+
+// We build the application inside a function so that we produce only a singleton
+// variable. That function will be invoked immediately, and its return value is
+// the JSHINT function itself.
+
+var JSHINT = (function () {
+ "use strict";
+
+ var anonname, // The guessed name for anonymous functions.
+ api, // Extension API
+
+ // These are operators that should not be used with the ! operator.
+ bang = {
+ "<" : true,
+ "<=" : true,
+ "==" : true,
+ "===": true,
+ "!==": true,
+ "!=" : true,
+ ">" : true,
+ ">=" : true,
+ "+" : true,
+ "-" : true,
+ "*" : true,
+ "/" : true,
+ "%" : true
+ },
+
+ // These are the JSHint boolean options.
+ boolOptions = {
+ asi : true, // if automatic semicolon insertion should be tolerated
+ bitwise : true, // if bitwise operators should not be allowed
+ boss : true, // if advanced usage of assignments should be allowed
+ browser : true, // if the standard browser globals should be predefined
+ camelcase : true, // if identifiers should be required in camel case
+ couch : true, // if CouchDB globals should be predefined
+ curly : true, // if curly braces around all blocks should be required
+ debug : true, // if debugger statements should be allowed
+ devel : true, // if logging globals should be predefined (console, alert, etc.)
+ dojo : true, // if Dojo Toolkit globals should be predefined
+ eqeqeq : true, // if === should be required
+ eqnull : true, // if == null comparisons should be tolerated
+ es3 : true, // if ES3 syntax should be allowed
+ es5 : true, // if ES5 syntax should be allowed (is now set per default)
+ esnext : true, // if es.next specific syntax should be allowed
+ moz : true, // if mozilla specific syntax should be allowed
+ evil : true, // if eval should be allowed
+ expr : true, // if ExpressionStatement should be allowed as Programs
+ forin : true, // if for in statements must filter
+ funcscope : true, // if only function scope should be used for scope tests
+ gcl : true, // if JSHint should be compatible with Google Closure Linter
+ globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict')
+ immed : true, // if immediate invocations must be wrapped in parens
+ iterator : true, // if the `__iterator__` property should be allowed
+ jquery : true, // if jQuery globals should be predefined
+ lastsemic : true, // if semicolons may be ommitted for the trailing
+ // statements inside of a one-line blocks.
+ laxbreak : true, // if line breaks should not be checked
+ laxcomma : true, // if line breaks should not be checked around commas
+ loopfunc : true, // if functions should be allowed to be defined within
+ // loops
+ mootools : true, // if MooTools globals should be predefined
+ multistr : true, // allow multiline strings
+ newcap : true, // if constructor names must be capitalized
+ noarg : true, // if arguments.caller and arguments.callee should be
+ // disallowed
+ node : true, // if the Node.js environment globals should be
+ // predefined
+ noempty : true, // if empty blocks should be disallowed
+ nonew : true, // if using `new` for side-effects should be disallowed
+ nonstandard : true, // if non-standard (but widely adopted) globals should
+ // be predefined
+ nomen : true, // if names should be checked
+ onevar : true, // if only one var statement per function should be
+ // allowed
+ passfail : true, // if the scan should stop on first error
+ phantom : true, // if PhantomJS symbols should be allowed
+ plusplus : true, // if increment/decrement should not be allowed
+ proto : true, // if the `__proto__` property should be allowed
+ prototypejs : true, // if Prototype and Scriptaculous globals should be
+ // predefined
+ rhino : true, // if the Rhino environment globals should be predefined
+ undef : true, // if variables should be declared before used
+ scripturl : true, // if script-targeted URLs should be tolerated
+ shadow : true, // if variable shadowing should be tolerated
+ smarttabs : true, // if smarttabs should be tolerated
+ // (http://www.emacswiki.org/emacs/SmartTabs)
+ strict : true, // require the "use strict"; pragma
+ sub : true, // if all forms of subscript notation are tolerated
+ supernew : true, // if `new function () { ... };` and `new Object;`
+ // should be tolerated
+ trailing : true, // if trailing whitespace rules apply
+ validthis : true, // if 'this' inside a non-constructor function is valid.
+ // This is a function scoped option only.
+ withstmt : true, // if with statements should be allowed
+ white : true, // if strict whitespace rules apply
+ worker : true, // if Web Worker script symbols should be allowed
+ wsh : true, // if the Windows Scripting Host environment globals
+ // should be predefined
+ yui : true, // YUI variables should be predefined
+
+ // Obsolete options
+ onecase : true, // if one case switch statements should be allowed
+ regexp : true, // if the . should not be allowed in regexp literals
+ regexdash : true // if unescaped first/last dash (-) inside brackets
+ // should be tolerated
+ },
+
+ // These are the JSHint options that can take any value
+ // (we use this object to detect invalid options)
+ valOptions = {
+ maxlen : false,
+ indent : false,
+ maxerr : false,
+ predef : false,
+ quotmark : false, //'single'|'double'|true
+ scope : false,
+ maxstatements: false, // {int} max statements per function
+ maxdepth : false, // {int} max nested block depth per function
+ maxparams : false, // {int} max params per function
+ maxcomplexity: false, // {int} max cyclomatic complexity per function
+ unused : true, // warn if variables are unused. Available options:
+ // false - don't check for unused variables
+ // true - "vars" + check last function param
+ // "vars" - skip checking unused function params
+ // "strict" - "vars" + check all function params
+ latedef : false // warn if the variable is used before its definition
+ // false - don't emit any warnings
+ // true - warn if any variable is used before its definition
+ // "nofunc" - warn for any variable but function declarations
+ },
+
+ // These are JSHint boolean options which are shared with JSLint
+ // where the definition in JSHint is opposite JSLint
+ invertedOptions = {
+ bitwise : true,
+ forin : true,
+ newcap : true,
+ nomen : true,
+ plusplus: true,
+ regexp : true,
+ undef : true,
+ white : true,
+
+ // Inverted and renamed, use JSHint name here
+ eqeqeq : true,
+ onevar : true,
+ strict : true
+ },
+
+ // These are JSHint boolean options which are shared with JSLint
+ // where the name has been changed but the effect is unchanged
+ renamedOptions = {
+ eqeq : "eqeqeq",
+ vars : "onevar",
+ windows: "wsh",
+ sloppy : "strict"
+ },
+
+ declared, // Globals that were declared using /*global ... */ syntax.
+ exported, // Variables that are used outside of the current file.
+
+ functionicity = [
+ "closure", "exception", "global", "label",
+ "outer", "unused", "var"
+ ],
+
+ funct, // The current function
+ functions, // All of the functions
+
+ global, // The global scope
+ implied, // Implied globals
+ inblock,
+ indent,
+ lookahead,
+ lex,
+ member,
+ membersOnly,
+ noreach,
+ predefined, // Global variables defined by option
+
+ scope, // The current scope
+ stack,
+ unuseds,
+ urls,
+ warnings,
+
+ extraModules = [],
+ emitter = new events.EventEmitter();
+
+ function checkOption(name, t) {
+ name = name.trim();
+
+ if (/^[+-]W\d{3}$/g.test(name)) {
+ return true;
+ }
+
+ if (valOptions[name] === undefined && boolOptions[name] === undefined) {
+ if (t.type !== "jslint") {
+ error("E001", t, name);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function isString(obj) {
+ return Object.prototype.toString.call(obj) === "[object String]";
+ }
+
+ function isIdentifier(tkn, value) {
+ if (!tkn)
+ return false;
+
+ if (!tkn.identifier || tkn.value !== value)
+ return false;
+
+ return true;
+ }
+
+ function isReserved(token) {
+ if (!token.reserved) {
+ return false;
+ }
+
+ if (token.meta && token.meta.isFutureReservedWord) {
+ // ES3 FutureReservedWord in an ES5 environment.
+ if (state.option.inES5(true) && !token.meta.es5) {
+ return false;
+ }
+
+ // Some ES5 FutureReservedWord identifiers are active only
+ // within a strict mode environment.
+ if (token.meta.strictOnly) {
+ if (!state.option.strict && !state.directive["use strict"]) {
+ return false;
+ }
+ }
+
+ if (token.isProperty) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function supplant(str, data) {
+ return str.replace(/\{([^{}]*)\}/g, function (a, b) {
+ var r = data[b];
+ return typeof r === "string" || typeof r === "number" ? r : a;
+ });
+ }
+
+ function combine(t, o) {
+ var n;
+ for (n in o) {
+ if (_.has(o, n) && !_.has(JSHINT.blacklist, n)) {
+ t[n] = o[n];
+ }
+ }
+ }
+
+ function updatePredefined() {
+ Object.keys(JSHINT.blacklist).forEach(function (key) {
+ delete predefined[key];
+ });
+ }
+
+ function assume() {
+ if (state.option.es5) {
+ warning("I003");
+ }
+ if (state.option.couch) {
+ combine(predefined, vars.couch);
+ }
+
+ if (state.option.rhino) {
+ combine(predefined, vars.rhino);
+ }
+
+ if (state.option.phantom) {
+ combine(predefined, vars.phantom);
+ }
+
+ if (state.option.prototypejs) {
+ combine(predefined, vars.prototypejs);
+ }
+
+ if (state.option.node) {
+ combine(predefined, vars.node);
+ }
+
+ if (state.option.devel) {
+ combine(predefined, vars.devel);
+ }
+
+ if (state.option.dojo) {
+ combine(predefined, vars.dojo);
+ }
+
+ if (state.option.browser) {
+ combine(predefined, vars.browser);
+ }
+
+ if (state.option.nonstandard) {
+ combine(predefined, vars.nonstandard);
+ }
+
+ if (state.option.jquery) {
+ combine(predefined, vars.jquery);
+ }
+
+ if (state.option.mootools) {
+ combine(predefined, vars.mootools);
+ }
+
+ if (state.option.worker) {
+ combine(predefined, vars.worker);
+ }
+
+ if (state.option.wsh) {
+ combine(predefined, vars.wsh);
+ }
+
+ if (state.option.globalstrict && state.option.strict !== false) {
+ state.option.strict = true;
+ }
+
+ if (state.option.yui) {
+ combine(predefined, vars.yui);
+ }
+
+ // Let's assume that chronologically ES3 < ES5 < ES6/ESNext < Moz
+
+ state.option.inMoz = function (strict) {
+ if (strict) {
+ return state.option.moz && !state.option.esnext;
+ }
+ return state.option.moz;
+ };
+
+ state.option.inESNext = function (strict) {
+ if (strict) {
+ return !state.option.moz && state.option.esnext;
+ }
+ return state.option.moz || state.option.esnext;
+ };
+
+ state.option.inES5 = function (/* strict */) {
+ return !state.option.es3;
+ };
+
+ state.option.inES3 = function (strict) {
+ if (strict) {
+ return !state.option.moz && !state.option.esnext && state.option.es3;
+ }
+ return state.option.es3;
+ };
+ }
+
+ // Produce an error warning.
+ function quit(code, line, chr) {
+ var percentage = Math.floor((line / state.lines.length) * 100);
+ var message = messages.errors[code].desc;
+
+ throw {
+ name: "JSHintError",
+ line: line,
+ character: chr,
+ message: message + " (" + percentage + "% scanned).",
+ raw: message
+ };
+ }
+
+ function isundef(scope, code, token, a) {
+ return JSHINT.undefs.push([scope, code, token, a]);
+ }
+
+ function warning(code, t, a, b, c, d) {
+ var ch, l, w, msg;
+
+ if (/^W\d{3}$/.test(code)) {
+ if (state.ignored[code])
+ return;
+
+ msg = messages.warnings[code];
+ } else if (/E\d{3}/.test(code)) {
+ msg = messages.errors[code];
+ } else if (/I\d{3}/.test(code)) {
+ msg = messages.info[code];
+ }
+
+ t = t || state.tokens.next;
+ if (t.id === "(end)") { // `~
+ t = state.tokens.curr;
+ }
+
+ l = t.line || 0;
+ ch = t.from || 0;
+
+ w = {
+ id: "(error)",
+ raw: msg.desc,
+ code: msg.code,
+ evidence: state.lines[l - 1] || "",
+ line: l,
+ character: ch,
+ scope: JSHINT.scope,
+ a: a,
+ b: b,
+ c: c,
+ d: d
+ };
+
+ w.reason = supplant(msg.desc, w);
+ JSHINT.errors.push(w);
+
+ if (state.option.passfail) {
+ quit("E042", l, ch);
+ }
+
+ warnings += 1;
+ if (warnings >= state.option.maxerr) {
+ quit("E043", l, ch);
+ }
+
+ return w;
+ }
+
+ function warningAt(m, l, ch, a, b, c, d) {
+ return warning(m, {
+ line: l,
+ from: ch
+ }, a, b, c, d);
+ }
+
+ function error(m, t, a, b, c, d) {
+ warning(m, t, a, b, c, d);
+ }
+
+ function errorAt(m, l, ch, a, b, c, d) {
+ return error(m, {
+ line: l,
+ from: ch
+ }, a, b, c, d);
+ }
+
+ // Tracking of "internal" scripts, like eval containing a static string
+ function addInternalSrc(elem, src) {
+ var i;
+ i = {
+ id: "(internal)",
+ elem: elem,
+ value: src
+ };
+ JSHINT.internals.push(i);
+ return i;
+ }
+
+ function addlabel(t, type, tkn, islet) {
+ // Define t in the current function in the current scope.
+ if (type === "exception") {
+ if (_.has(funct["(context)"], t)) {
+ if (funct[t] !== true && !state.option.node) {
+ warning("W002", state.tokens.next, t);
+ }
+ }
+ }
+
+ if (_.has(funct, t) && !funct["(global)"]) {
+ if (funct[t] === true) {
+ if (state.option.latedef) {
+ if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) ||
+ !_.contains([funct[t], type], "unction")) {
+ warning("W003", state.tokens.next, t);
+ }
+ }
+ } else {
+ if (!state.option.shadow && type !== "exception" ||
+ (funct["(blockscope)"].getlabel(t))) {
+ warning("W004", state.tokens.next, t);
+ }
+ }
+ }
+
+ // a double definition of a let variable in same block throws a TypeError
+ //if (funct["(blockscope)"] && funct["(blockscope)"].current.has(t)) {
+ // error("E044", state.tokens.next, t);
+ //}
+
+ // if the identifier is from a let, adds it only to the current blockscope
+ if (islet) {
+ funct["(blockscope)"].current.add(t, type, state.tokens.curr);
+ } else {
+
+ funct[t] = type;
+
+ if (tkn) {
+ funct["(tokens)"][t] = tkn;
+ }
+
+ if (funct["(global)"]) {
+ global[t] = funct;
+ if (_.has(implied, t)) {
+ if (state.option.latedef) {
+ if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) ||
+ !_.contains([funct[t], type], "unction")) {
+ warning("W003", state.tokens.next, t);
+ }
+ }
+
+ delete implied[t];
+ }
+ } else {
+ scope[t] = funct;
+ }
+ }
+ }
+
+ function doOption() {
+ var nt = state.tokens.next;
+ var body = nt.body.split(",").map(function (s) { return s.trim(); });
+ var predef = {};
+
+ if (nt.type === "globals") {
+ body.forEach(function (g) {
+ g = g.split(":");
+ var key = g[0];
+ var val = g[1];
+
+ if (key.charAt(0) === "-") {
+ key = key.slice(1);
+ val = false;
+
+ JSHINT.blacklist[key] = key;
+ updatePredefined();
+ } else {
+ predef[key] = (val === "true");
+ }
+ });
+
+ combine(predefined, predef);
+
+ for (var key in predef) {
+ if (_.has(predef, key)) {
+ declared[key] = nt;
+ }
+ }
+ }
+
+ if (nt.type === "exported") {
+ body.forEach(function (e) {
+ exported[e] = true;
+ });
+ }
+
+ if (nt.type === "members") {
+ membersOnly = membersOnly || {};
+
+ body.forEach(function (m) {
+ var ch1 = m.charAt(0);
+ var ch2 = m.charAt(m.length - 1);
+
+ if (ch1 === ch2 && (ch1 === "\"" || ch1 === "'")) {
+ m = m
+ .substr(1, m.length - 2)
+ .replace("\\b", "\b")
+ .replace("\\t", "\t")
+ .replace("\\n", "\n")
+ .replace("\\v", "\v")
+ .replace("\\f", "\f")
+ .replace("\\r", "\r")
+ .replace("\\\\", "\\")
+ .replace("\\\"", "\"");
+ }
+
+ membersOnly[m] = false;
+ });
+ }
+
+ var numvals = [
+ "maxstatements",
+ "maxparams",
+ "maxdepth",
+ "maxcomplexity",
+ "maxerr",
+ "maxlen",
+ "indent"
+ ];
+
+ if (nt.type === "jshint" || nt.type === "jslint") {
+ body.forEach(function (g) {
+ g = g.split(":");
+ var key = (g[0] || "").trim();
+ var val = (g[1] || "").trim();
+
+ if (!checkOption(key, nt)) {
+ return;
+ }
+
+ if (numvals.indexOf(key) >= 0) {
+
+ // GH988 - numeric options can be disabled by setting them to `false`
+ if (val !== "false") {
+ val = +val;
+
+ if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) {
+ error("E032", nt, g[1].trim());
+ return;
+ }
+
+ if (key === "indent") {
+ state.option["(explicitIndent)"] = true;
+ }
+ state.option[key] = val;
+ } else {
+ if (key === "indent") {
+ state.option["(explicitIndent)"] = false;
+ } else {
+ state.option[key] = false;
+ }
+ }
+
+ return;
+ }
+
+ if (key === "validthis") {
+ // `validthis` is valid only within a function scope.
+ if (funct["(global)"]) {
+ error("E009");
+ } else {
+ if (val === "true" || val === "false") {
+ state.option.validthis = (val === "true");
+ } else {
+ error("E002", nt);
+ }
+ }
+ return;
+ }
+
+ if (key === "quotmark") {
+ switch (val) {
+ case "true":
+ case "false":
+ state.option.quotmark = (val === "true");
+ break;
+ case "double":
+ case "single":
+ state.option.quotmark = val;
+ break;
+ default:
+ error("E002", nt);
+ }
+ return;
+ }
+
+ if (key === "unused") {
+ switch (val) {
+ case "true":
+ state.option.unused = true;
+ break;
+ case "false":
+ state.option.unused = false;
+ break;
+ case "vars":
+ case "strict":
+ state.option.unused = val;
+ break;
+ default:
+ error("E002", nt);
+ }
+ return;
+ }
+
+ if (key === "latedef") {
+ switch (val) {
+ case "true":
+ state.option.latedef = true;
+ break;
+ case "false":
+ state.option.latedef = false;
+ break;
+ case "nofunc":
+ state.option.latedef = "nofunc";
+ break;
+ default:
+ error("E002", nt);
+ }
+ return;
+ }
+
+ var match = /^([+-])(W\d{3})$/g.exec(key);
+ if (match) {
+ // ignore for -W..., unignore for +W...
+ state.ignored[match[2]] = (match[1] === "-");
+ return;
+ }
+
+ var tn;
+ if (val === "true" || val === "false") {
+ if (nt.type === "jslint") {
+ tn = renamedOptions[key] || key;
+ state.option[tn] = (val === "true");
+
+ if (invertedOptions[tn] !== undefined) {
+ state.option[tn] = !state.option[tn];
+ }
+ } else {
+ state.option[key] = (val === "true");
+ }
+
+ if (key === "newcap") {
+ state.option["(explicitNewcap)"] = true;
+ }
+ return;
+ }
+
+ error("E002", nt);
+ });
+
+ assume();
+ }
+ }
+
+ // We need a peek function. If it has an argument, it peeks that much farther
+ // ahead. It is used to distinguish
+ // for ( var i in ...
+ // from
+ // for ( var i = ...
+
+ function peek(p) {
+ var i = p || 0, j = 0, t;
+
+ while (j <= i) {
+ t = lookahead[j];
+ if (!t) {
+ t = lookahead[j] = lex.token();
+ }
+ j += 1;
+ }
+ return t;
+ }
+
+ // Produce the next token. It looks for programming errors.
+
+ function advance(id, t) {
+ switch (state.tokens.curr.id) {
+ case "(number)":
+ if (state.tokens.next.id === ".") {
+ warning("W005", state.tokens.curr);
+ }
+ break;
+ case "-":
+ if (state.tokens.next.id === "-" || state.tokens.next.id === "--") {
+ warning("W006");
+ }
+ break;
+ case "+":
+ if (state.tokens.next.id === "+" || state.tokens.next.id === "++") {
+ warning("W007");
+ }
+ break;
+ }
+
+ if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) {
+ anonname = state.tokens.curr.value;
+ }
+
+ if (id && state.tokens.next.id !== id) {
+ if (t) {
+ if (state.tokens.next.id === "(end)") {
+ error("E019", t, t.id);
+ } else {
+ error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value);
+ }
+ } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) {
+ warning("W116", state.tokens.next, id, state.tokens.next.value);
+ }
+ }
+
+ state.tokens.prev = state.tokens.curr;
+ state.tokens.curr = state.tokens.next;
+ for (;;) {
+ state.tokens.next = lookahead.shift() || lex.token();
+
+ if (!state.tokens.next) { // No more tokens left, give up
+ quit("E041", state.tokens.curr.line);
+ }
+
+ if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") {
+ return;
+ }
+
+ if (state.tokens.next.check) {
+ state.tokens.next.check();
+ }
+
+ if (state.tokens.next.isSpecial) {
+ doOption();
+ } else {
+ if (state.tokens.next.id !== "(endline)") {
+ break;
+ }
+ }
+ }
+ }
+
+ // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it
+ // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
+ // like .nud except that it is only used on the first token of a statement.
+ // Having .fud makes it much easier to define statement-oriented languages like
+ // JavaScript. I retained Pratt's nomenclature.
+
+ // .nud Null denotation
+ // .fud First null denotation
+ // .led Left denotation
+ // lbp Left binding power
+ // rbp Right binding power
+
+ // They are elements of the parsing method called Top Down Operator Precedence.
+
+ function expression(rbp, initial) {
+ var left, isArray = false, isObject = false, isLetExpr = false;
+
+ // if current expression is a let expression
+ if (!initial && state.tokens.next.value === "let" && peek(0).value === "(") {
+ if (!state.option.inMoz(true)) {
+ warning("W118", state.tokens.next, "let expressions");
+ }
+ isLetExpr = true;
+ // create a new block scope we use only for the current expression
+ funct["(blockscope)"].stack();
+ advance("let");
+ advance("(");
+ state.syntax["let"].fud.call(state.syntax["let"].fud, false);
+ advance(")");
+ }
+
+ if (state.tokens.next.id === "(end)")
+ error("E006", state.tokens.curr);
+
+ advance();
+
+ if (initial) {
+ anonname = "anonymous";
+ funct["(verb)"] = state.tokens.curr.value;
+ }
+
+ if (initial === true && state.tokens.curr.fud) {
+ left = state.tokens.curr.fud();
+ } else {
+ if (state.tokens.curr.nud) {
+ left = state.tokens.curr.nud();
+ } else {
+ error("E030", state.tokens.curr, state.tokens.curr.id);
+ }
+
+ var end_of_expr = state.tokens.next.identifier &&
+ !state.tokens.curr.led &&
+ state.tokens.curr.line !== state.tokens.next.line;
+ while (rbp < state.tokens.next.lbp && !end_of_expr) {
+ isArray = state.tokens.curr.value === "Array";
+ isObject = state.tokens.curr.value === "Object";
+
+ // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object()
+ // Line breaks in IfStatement heads exist to satisfy the checkJSHint
+ // "Line too long." error.
+ if (left && (left.value || (left.first && left.first.value))) {
+ // If the left.value is not "new", or the left.first.value is a "."
+ // then safely assume that this is not "new Array()" and possibly
+ // not "new Object()"...
+ if (left.value !== "new" ||
+ (left.first && left.first.value && left.first.value === ".")) {
+ isArray = false;
+ // ...In the case of Object, if the left.value and state.tokens.curr.value
+ // are not equal, then safely assume that this not "new Object()"
+ if (left.value !== state.tokens.curr.value) {
+ isObject = false;
+ }
+ }
+ }
+
+ advance();
+
+ if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") {
+ warning("W009", state.tokens.curr);
+ }
+
+ if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") {
+ warning("W010", state.tokens.curr);
+ }
+
+ if (left && state.tokens.curr.led) {
+ left = state.tokens.curr.led(left);
+ } else {
+ error("E033", state.tokens.curr, state.tokens.curr.id);
+ }
+ }
+ }
+ if (isLetExpr) {
+ funct["(blockscope)"].unstack();
+ }
+ return left;
+ }
+
+
+// Functions for conformance of style.
+
+ function adjacent(left, right) {
+ left = left || state.tokens.curr;
+ right = right || state.tokens.next;
+ if (state.option.white) {
+ if (left.character !== right.from && left.line === right.line) {
+ left.from += (left.character - left.from);
+ warning("W011", left, left.value);
+ }
+ }
+ }
+
+ function nobreak(left, right) {
+ left = left || state.tokens.curr;
+ right = right || state.tokens.next;
+ if (state.option.white && (left.character !== right.from || left.line !== right.line)) {
+ warning("W012", right, right.value);
+ }
+ }
+
+ function nospace(left, right) {
+ left = left || state.tokens.curr;
+ right = right || state.tokens.next;
+ if (state.option.white && !left.comment) {
+ if (left.line === right.line) {
+ adjacent(left, right);
+ }
+ }
+ }
+
+ function nonadjacent(left, right) {
+ if (state.option.white) {
+ left = left || state.tokens.curr;
+ right = right || state.tokens.next;
+
+ if (left.value === ";" && right.value === ";") {
+ return;
+ }
+
+ if (left.line === right.line && left.character === right.from) {
+ left.from += (left.character - left.from);
+ warning("W013", left, left.value);
+ }
+ }
+ }
+
+ function nobreaknonadjacent(left, right) {
+ left = left || state.tokens.curr;
+ right = right || state.tokens.next;
+ if (!state.option.laxbreak && left.line !== right.line) {
+ warning("W014", right, right.id);
+ } else if (state.option.white) {
+ left = left || state.tokens.curr;
+ right = right || state.tokens.next;
+ if (left.character === right.from) {
+ left.from += (left.character - left.from);
+ warning("W013", left, left.value);
+ }
+ }
+ }
+
+ function indentation(bias) {
+ if (!state.option.white && !state.option["(explicitIndent)"]) {
+ return;
+ }
+
+ if (state.tokens.next.id === "(end)") {
+ return;
+ }
+
+ var i = indent + (bias || 0);
+ if (state.tokens.next.from !== i) {
+ warning("W015", state.tokens.next, state.tokens.next.value, i, state.tokens.next.from);
+ }
+ }
+
+ function nolinebreak(t) {
+ t = t || state.tokens.curr;
+ if (t.line !== state.tokens.next.line) {
+ warning("E022", t, t.value);
+ }
+ }
+
+
+ function comma(opts) {
+ opts = opts || {};
+
+ if (!opts.peek) {
+ if (state.tokens.curr.line !== state.tokens.next.line) {
+ if (!state.option.laxcomma) {
+ if (comma.first) {
+ warning("I001");
+ comma.first = false;
+ }
+ warning("W014", state.tokens.curr, state.tokens.next.value);
+ }
+ } else if (!state.tokens.curr.comment &&
+ state.tokens.curr.character !== state.tokens.next.from && state.option.white) {
+ state.tokens.curr.from += (state.tokens.curr.character - state.tokens.curr.from);
+ warning("W011", state.tokens.curr, state.tokens.curr.value);
+ }
+
+ advance(",");
+ }
+
+ // TODO: This is a temporary solution to fight against false-positives in
+ // arrays and objects with trailing commas (see GH-363). The best solution
+ // would be to extract all whitespace rules out of parser.
+
+ if (state.tokens.next.value !== "]" && state.tokens.next.value !== "}") {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ }
+
+ if (state.tokens.next.identifier && !(opts.property && state.option.inES5())) {
+ // Keywords that cannot follow a comma operator.
+ switch (state.tokens.next.value) {
+ case "break":
+ case "case":
+ case "catch":
+ case "continue":
+ case "default":
+ case "do":
+ case "else":
+ case "finally":
+ case "for":
+ case "if":
+ case "in":
+ case "instanceof":
+ case "return":
+ case "yield":
+ case "switch":
+ case "throw":
+ case "try":
+ case "var":
+ case "let":
+ case "while":
+ case "with":
+ error("E024", state.tokens.next, state.tokens.next.value);
+ return false;
+ }
+ }
+
+ if (state.tokens.next.type === "(punctuator)") {
+ switch (state.tokens.next.value) {
+ case "}":
+ case "]":
+ case ",":
+ if (opts.allowTrailing) {
+ return true;
+ }
+
+ /* falls through */
+ case ")":
+ error("E024", state.tokens.next, state.tokens.next.value);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Functional constructors for making the symbols that will be inherited by
+ // tokens.
+
+ function symbol(s, p) {
+ var x = state.syntax[s];
+ if (!x || typeof x !== "object") {
+ state.syntax[s] = x = {
+ id: s,
+ lbp: p,
+ value: s
+ };
+ }
+ return x;
+ }
+
+ function delim(s) {
+ return symbol(s, 0);
+ }
+
+ function stmt(s, f) {
+ var x = delim(s);
+ x.identifier = x.reserved = true;
+ x.fud = f;
+ return x;
+ }
+
+ function blockstmt(s, f) {
+ var x = stmt(s, f);
+ x.block = true;
+ return x;
+ }
+
+ function reserveName(x) {
+ var c = x.id.charAt(0);
+ if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) {
+ x.identifier = x.reserved = true;
+ }
+ return x;
+ }
+
+ function prefix(s, f) {
+ var x = symbol(s, 150);
+ reserveName(x);
+ x.nud = (typeof f === "function") ? f : function () {
+ this.right = expression(150);
+ this.arity = "unary";
+ if (this.id === "++" || this.id === "--") {
+ if (state.option.plusplus) {
+ warning("W016", this, this.id);
+ } else if ((!this.right.identifier || isReserved(this.right)) &&
+ this.right.id !== "." && this.right.id !== "[") {
+ warning("W017", this);
+ }
+ }
+ return this;
+ };
+ return x;
+ }
+
+ function type(s, f) {
+ var x = delim(s);
+ x.type = s;
+ x.nud = f;
+ return x;
+ }
+
+ function reserve(name, func) {
+ var x = type(name, func);
+ x.identifier = true;
+ x.reserved = true;
+ return x;
+ }
+
+ function FutureReservedWord(name, meta) {
+ var x = type(name, (meta && meta.nud) || function () {
+ return this;
+ });
+
+ meta = meta || {};
+ meta.isFutureReservedWord = true;
+
+ x.value = name;
+ x.identifier = true;
+ x.reserved = true;
+ x.meta = meta;
+
+ return x;
+ }
+
+ function reservevar(s, v) {
+ return reserve(s, function () {
+ if (typeof v === "function") {
+ v(this);
+ }
+ return this;
+ });
+ }
+
+ function infix(s, f, p, w) {
+ var x = symbol(s, p);
+ reserveName(x);
+ x.led = function (left) {
+ if (!w) {
+ nobreaknonadjacent(state.tokens.prev, state.tokens.curr);
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ }
+ if (s === "in" && left.id === "!") {
+ warning("W018", left, "!");
+ }
+ if (typeof f === "function") {
+ return f(left, this);
+ } else {
+ this.left = left;
+ this.right = expression(p);
+ return this;
+ }
+ };
+ return x;
+ }
+
+
+ function application(s) {
+ var x = symbol(s, 42);
+
+ x.led = function (left) {
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "arrow function syntax (=>)");
+ }
+
+ nobreaknonadjacent(state.tokens.prev, state.tokens.curr);
+ nonadjacent(state.tokens.curr, state.tokens.next);
+
+ this.left = left;
+ this.right = doFunction(undefined, undefined, false, left);
+ return this;
+ };
+ return x;
+ }
+
+ function relation(s, f) {
+ var x = symbol(s, 100);
+
+ x.led = function (left) {
+ nobreaknonadjacent(state.tokens.prev, state.tokens.curr);
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ var right = expression(100);
+
+ if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) {
+ warning("W019", this);
+ } else if (f) {
+ f.apply(this, [left, right]);
+ }
+
+ if (!left || !right) {
+ quit("E041", state.tokens.curr.line);
+ }
+
+ if (left.id === "!") {
+ warning("W018", left, "!");
+ }
+
+ if (right.id === "!") {
+ warning("W018", right, "!");
+ }
+
+ this.left = left;
+ this.right = right;
+ return this;
+ };
+ return x;
+ }
+
+ function isPoorRelation(node) {
+ return node &&
+ ((node.type === "(number)" && +node.value === 0) ||
+ (node.type === "(string)" && node.value === "") ||
+ (node.type === "null" && !state.option.eqnull) ||
+ node.type === "true" ||
+ node.type === "false" ||
+ node.type === "undefined");
+ }
+
+ function assignop(s) {
+ symbol(s, 20).exps = true;
+
+ return infix(s, function (left, that) {
+ that.left = left;
+
+ if (left) {
+ if (predefined[left.value] === false &&
+ scope[left.value]["(global)"] === true) {
+ warning("W020", left);
+ } else if (left["function"]) {
+ warning("W021", left, left.value);
+ }
+
+ if (funct[left.value] === "const") {
+ error("E013", left, left.value);
+ }
+
+ if (left.id === ".") {
+ if (!left.left) {
+ warning("E031", that);
+ } else if (left.left.value === "arguments" && !state.directive["use strict"]) {
+ warning("E031", that);
+ }
+
+ that.right = expression(19);
+ return that;
+ } else if (left.id === "[") {
+ if (state.tokens.curr.left.first) {
+ state.tokens.curr.left.first.forEach(function (t) {
+ if (funct[t.value] === "const") {
+ error("E013", t, t.value);
+ }
+ });
+ } else if (!left.left) {
+ warning("E031", that);
+ } else if (left.left.value === "arguments" && !state.directive["use strict"]) {
+ warning("E031", that);
+ }
+ that.right = expression(19);
+ return that;
+ } else if (left.identifier && !isReserved(left)) {
+ if (funct[left.value] === "exception") {
+ warning("W022", left);
+ }
+ that.right = expression(19);
+ return that;
+ }
+
+ if (left === state.syntax["function"]) {
+ warning("W023", state.tokens.curr);
+ }
+ }
+
+ error("E031", that);
+ }, 20);
+ }
+
+
+ function bitwise(s, f, p) {
+ var x = symbol(s, p);
+ reserveName(x);
+ x.led = (typeof f === "function") ? f : function (left) {
+ if (state.option.bitwise) {
+ warning("W016", this, this.id);
+ }
+ this.left = left;
+ this.right = expression(p);
+ return this;
+ };
+ return x;
+ }
+
+
+ function bitwiseassignop(s) {
+ symbol(s, 20).exps = true;
+ return infix(s, function (left, that) {
+ if (state.option.bitwise) {
+ warning("W016", that, that.id);
+ }
+ nonadjacent(state.tokens.prev, state.tokens.curr);
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (left) {
+ if (left.id === "." || left.id === "[" ||
+ (left.identifier && !isReserved(left))) {
+ expression(19);
+ return that;
+ }
+ if (left === state.syntax["function"]) {
+ warning("W023", state.tokens.curr);
+ }
+ return that;
+ }
+ error("E031", that);
+ }, 20);
+ }
+
+
+ function suffix(s) {
+ var x = symbol(s, 150);
+
+ x.led = function (left) {
+ if (state.option.plusplus) {
+ warning("W016", this, this.id);
+ } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") {
+ warning("W017", this);
+ }
+
+ this.left = left;
+ return this;
+ };
+ return x;
+ }
+
+ // fnparam means that this identifier is being defined as a function
+ // argument (see identifier())
+ // prop means that this identifier is that of an object property
+
+ function optionalidentifier(fnparam, prop) {
+ if (!state.tokens.next.identifier) {
+ return;
+ }
+
+ advance();
+
+ var curr = state.tokens.curr;
+ var meta = curr.meta || {};
+ var val = state.tokens.curr.value;
+
+ if (!isReserved(curr)) {
+ return val;
+ }
+
+ if (prop) {
+ if (state.option.inES5() || meta.isFutureReservedWord) {
+ return val;
+ }
+ }
+
+ if (fnparam && val === "undefined") {
+ return val;
+ }
+
+ // Display an info message about reserved words as properties
+ // and ES5 but do it only once.
+ if (prop && !api.getCache("displayed:I002")) {
+ api.setCache("displayed:I002", true);
+ warning("I002");
+ }
+
+ warning("W024", state.tokens.curr, state.tokens.curr.id);
+ return val;
+ }
+
+ // fnparam means that this identifier is being defined as a function
+ // argument
+ // prop means that this identifier is that of an object property
+ function identifier(fnparam, prop) {
+ var i = optionalidentifier(fnparam, prop);
+ if (i) {
+ return i;
+ }
+ if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") {
+ warning("W025");
+ } else {
+ error("E030", state.tokens.next, state.tokens.next.value);
+ }
+ }
+
+
+ function reachable(s) {
+ var i = 0, t;
+ if (state.tokens.next.id !== ";" || noreach) {
+ return;
+ }
+ for (;;) {
+ t = peek(i);
+ if (t.reach) {
+ return;
+ }
+ if (t.id !== "(endline)") {
+ if (t.id === "function") {
+ if (!state.option.latedef) {
+ break;
+ }
+
+ warning("W026", t);
+ break;
+ }
+
+ warning("W027", t, t.value, s);
+ break;
+ }
+ i += 1;
+ }
+ }
+
+
+ function statement(noindent) {
+ var values;
+ var i = indent, r, s = scope, t = state.tokens.next;
+
+ if (t.id === ";") {
+ advance(";");
+ return;
+ }
+
+ // Is this a labelled statement?
+ var res = isReserved(t);
+
+ // We're being more tolerant here: if someone uses
+ // a FutureReservedWord as a label, we warn but proceed
+ // anyway.
+
+ if (res && t.meta && t.meta.isFutureReservedWord && peek().id === ":") {
+ warning("W024", t, t.id);
+ res = false;
+ }
+
+ // detect a destructuring assignment
+ if (_.has(["[", "{"], t.value)) {
+ if (lookupBlockType().isDestAssign) {
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "destructuring expression");
+ }
+ values = destructuringExpression();
+ values.forEach(function (tok) {
+ isundef(funct, "W117", tok.token, tok.id);
+ });
+ advance("=");
+ destructuringExpressionMatch(values, expression(5, true));
+ advance(";");
+ return;
+ }
+ }
+ if (t.identifier && !res && peek().id === ":") {
+ advance();
+ advance(":");
+ scope = Object.create(s);
+ addlabel(t.value, "label");
+
+ if (!state.tokens.next.labelled && state.tokens.next.value !== "{") {
+ warning("W028", state.tokens.next, t.value, state.tokens.next.value);
+ }
+
+ state.tokens.next.label = t.value;
+ t = state.tokens.next;
+ }
+
+ // Is it a lonely block?
+
+ if (t.id === "{") {
+ // Is it a switch case block?
+ //
+ // switch (foo) {
+ // case bar: { <= here.
+ // ...
+ // }
+ // }
+ var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":");
+ block(true, true, false, false, iscase);
+ return;
+ }
+
+ // Parse the statement.
+
+ if (!noindent) {
+ indentation();
+ }
+ r = expression(0, true);
+
+ // Look for the final semicolon.
+
+ if (!t.block) {
+ if (!state.option.expr && (!r || !r.exps)) {
+ warning("W030", state.tokens.curr);
+ } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") {
+ warning("W031", t);
+ }
+
+ if (state.tokens.next.id !== ";") {
+ if (!state.option.asi) {
+ // If this is the last statement in a block that ends on
+ // the same line *and* option lastsemic is on, ignore the warning.
+ // Otherwise, complain about missing semicolon.
+ if (!state.option.lastsemic || state.tokens.next.id !== "}" ||
+ state.tokens.next.line !== state.tokens.curr.line) {
+ warningAt("W033", state.tokens.curr.line, state.tokens.curr.character);
+ }
+ }
+ } else {
+ adjacent(state.tokens.curr, state.tokens.next);
+ advance(";");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ }
+ }
+
+ // Restore the indentation.
+
+ indent = i;
+ scope = s;
+ return r;
+ }
+
+
+ function statements(startLine) {
+ var a = [], p;
+
+ while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") {
+ if (state.tokens.next.id === ";") {
+ p = peek();
+
+ if (!p || (p.id !== "(" && p.id !== "[")) {
+ warning("W032");
+ }
+
+ advance(";");
+ } else {
+ a.push(statement(startLine === state.tokens.next.line));
+ }
+ }
+ return a;
+ }
+
+
+ /*
+ * read all directives
+ * recognizes a simple form of asi, but always
+ * warns, if it is used
+ */
+ function directives() {
+ var i, p, pn;
+
+ for (;;) {
+ if (state.tokens.next.id === "(string)") {
+ p = peek(0);
+ if (p.id === "(endline)") {
+ i = 1;
+ do {
+ pn = peek(i);
+ i = i + 1;
+ } while (pn.id === "(endline)");
+
+ if (pn.id !== ";") {
+ if (pn.id !== "(string)" && pn.id !== "(number)" &&
+ pn.id !== "(regexp)" && pn.identifier !== true &&
+ pn.id !== "}") {
+ break;
+ }
+ warning("W033", state.tokens.next);
+ } else {
+ p = pn;
+ }
+ } else if (p.id === "}") {
+ // Directive with no other statements, warn about missing semicolon
+ warning("W033", p);
+ } else if (p.id !== ";") {
+ break;
+ }
+
+ indentation();
+ advance();
+ if (state.directive[state.tokens.curr.value]) {
+ warning("W034", state.tokens.curr, state.tokens.curr.value);
+ }
+
+ if (state.tokens.curr.value === "use strict") {
+ if (!state.option["(explicitNewcap)"])
+ state.option.newcap = true;
+ state.option.undef = true;
+ }
+
+ // there's no directive negation, so always set to true
+ state.directive[state.tokens.curr.value] = true;
+
+ if (p.id === ";") {
+ advance(";");
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+
+ /*
+ * Parses a single block. A block is a sequence of statements wrapped in
+ * braces.
+ *
+ * ordinary - true for everything but function bodies and try blocks.
+ * stmt - true if block can be a single statement (e.g. in if/for/while).
+ * isfunc - true if block is a function body
+ * isfatarrow -
+ * iscase - true if block is a switch case block
+ */
+ function block(ordinary, stmt, isfunc, isfatarrow, iscase) {
+ var a,
+ b = inblock,
+ old_indent = indent,
+ m,
+ s = scope,
+ t,
+ line,
+ d;
+
+ inblock = ordinary;
+
+ if (!ordinary || !state.option.funcscope)
+ scope = Object.create(scope);
+
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ t = state.tokens.next;
+
+ var metrics = funct["(metrics)"];
+ metrics.nestedBlockDepth += 1;
+ metrics.verifyMaxNestedBlockDepthPerFunction();
+
+ if (state.tokens.next.id === "{") {
+ advance("{");
+
+ // create a new block scope
+ funct["(blockscope)"].stack();
+
+ line = state.tokens.curr.line;
+ if (state.tokens.next.id !== "}") {
+ indent += state.option.indent;
+ while (!ordinary && state.tokens.next.from > indent) {
+ indent += state.option.indent;
+ }
+
+ if (isfunc) {
+ m = {};
+ for (d in state.directive) {
+ if (_.has(state.directive, d)) {
+ m[d] = state.directive[d];
+ }
+ }
+ directives();
+
+ if (state.option.strict && funct["(context)"]["(global)"]) {
+ if (!m["use strict"] && !state.directive["use strict"]) {
+ warning("E007");
+ }
+ }
+ }
+
+ a = statements(line);
+
+ metrics.statementCount += a.length;
+
+ if (isfunc) {
+ state.directive = m;
+ }
+
+ indent -= state.option.indent;
+ if (line !== state.tokens.next.line) {
+ indentation();
+ }
+ } else if (line !== state.tokens.next.line) {
+ indentation();
+ }
+ advance("}", t);
+
+ funct["(blockscope)"].unstack();
+
+ indent = old_indent;
+ } else if (!ordinary) {
+ if (isfunc) {
+ m = {};
+ if (stmt && !isfatarrow && !state.option.inMoz(true)) {
+ error("W118", state.tokens.curr, "function closure expressions");
+ }
+
+ if (!stmt) {
+ for (d in state.directive) {
+ if (_.has(state.directive, d)) {
+ m[d] = state.directive[d];
+ }
+ }
+ }
+ expression(5);
+
+ if (state.option.strict && funct["(context)"]["(global)"]) {
+ if (!m["use strict"] && !state.directive["use strict"]) {
+ warning("E007");
+ }
+ }
+ } else {
+ error("E021", state.tokens.next, "{", state.tokens.next.value);
+ }
+ } else {
+
+ // check to avoid let declaration not within a block
+ funct["(nolet)"] = true;
+
+ if (!stmt || state.option.curly) {
+ warning("W116", state.tokens.next, "{", state.tokens.next.value);
+ }
+
+ noreach = true;
+ indent += state.option.indent;
+ // test indentation only if statement is in new line
+ a = [statement(state.tokens.next.line === state.tokens.curr.line)];
+ indent -= state.option.indent;
+ noreach = false;
+
+ delete funct["(nolet)"];
+ }
+ // If it is a "break" in switch case, don't clear and let it propagate out.
+ if (!(iscase && funct["(verb)"] === "break")) funct["(verb)"] = null;
+
+ if (!ordinary || !state.option.funcscope) scope = s;
+ inblock = b;
+ if (ordinary && state.option.noempty && (!a || a.length === 0)) {
+ warning("W035");
+ }
+ metrics.nestedBlockDepth -= 1;
+ return a;
+ }
+
+
+ function countMember(m) {
+ if (membersOnly && typeof membersOnly[m] !== "boolean") {
+ warning("W036", state.tokens.curr, m);
+ }
+ if (typeof member[m] === "number") {
+ member[m] += 1;
+ } else {
+ member[m] = 1;
+ }
+ }
+
+
+ function note_implied(tkn) {
+ var name = tkn.value, line = tkn.line, a = implied[name];
+ if (typeof a === "function") {
+ a = false;
+ }
+
+ if (!a) {
+ a = [line];
+ implied[name] = a;
+ } else if (a[a.length - 1] !== line) {
+ a.push(line);
+ }
+ }
+
+
+ // Build the syntax table by declaring the syntactic elements of the language.
+
+ type("(number)", function () {
+ return this;
+ });
+
+ type("(string)", function () {
+ return this;
+ });
+
+ state.syntax["(identifier)"] = {
+ type: "(identifier)",
+ lbp: 0,
+ identifier: true,
+ nud: function () {
+ var v = this.value,
+ s = scope[v],
+ f;
+
+ if (typeof s === "function") {
+ // Protection against accidental inheritance.
+ s = undefined;
+ } else if (typeof s === "boolean") {
+ f = funct;
+ funct = functions[0];
+ addlabel(v, "var");
+ s = funct;
+ funct = f;
+ }
+ var block;
+ if (_.has(funct, "(blockscope)")) {
+ block = funct["(blockscope)"].getlabel(v);
+ }
+
+ // The name is in scope and defined in the current function.
+ if (funct === s || block) {
+ // Change 'unused' to 'var', and reject labels.
+ // the name is in a block scope
+ switch (block ? block[v]["(type)"] : funct[v]) {
+ case "unused":
+ if (block) block[v]["(type)"] = "var";
+ else funct[v] = "var";
+ break;
+ case "unction":
+ if (block) block[v]["(type)"] = "function";
+ else funct[v] = "function";
+ this["function"] = true;
+ break;
+ case "function":
+ this["function"] = true;
+ break;
+ case "label":
+ warning("W037", state.tokens.curr, v);
+ break;
+ }
+ } else if (funct["(global)"]) {
+ // The name is not defined in the function. If we are in the global
+ // scope, then we have an undefined variable.
+ //
+ // Operators typeof and delete do not raise runtime errors even if
+ // the base object of a reference is null so no need to display warning
+ // if we're inside of typeof or delete.
+
+ if (typeof predefined[v] !== "boolean") {
+ // Attempting to subscript a null reference will throw an
+ // error, even within the typeof and delete operators
+ if (!(anonname === "typeof" || anonname === "delete") ||
+ (state.tokens.next && (state.tokens.next.value === "." ||
+ state.tokens.next.value === "["))) {
+
+ // if we're in a list comprehension, variables are declared
+ // locally and used before being defined. So we check
+ // the presence of the given variable in the comp array
+ // before declaring it undefined.
+
+ if (!funct["(comparray)"].check(v)) {
+ isundef(funct, "W117", state.tokens.curr, v);
+ }
+ }
+ }
+
+ note_implied(state.tokens.curr);
+ } else {
+ // If the name is already defined in the current
+ // function, but not as outer, then there is a scope error.
+
+ switch (funct[v]) {
+ case "closure":
+ case "function":
+ case "var":
+ case "unused":
+ warning("W038", state.tokens.curr, v);
+ break;
+ case "label":
+ warning("W037", state.tokens.curr, v);
+ break;
+ case "outer":
+ case "global":
+ break;
+ default:
+ // If the name is defined in an outer function, make an outer entry,
+ // and if it was unused, make it var.
+ if (s === true) {
+ funct[v] = true;
+ } else if (s === null) {
+ warning("W039", state.tokens.curr, v);
+ note_implied(state.tokens.curr);
+ } else if (typeof s !== "object") {
+ // Operators typeof and delete do not raise runtime errors even
+ // if the base object of a reference is null so no need to
+ //
+ // display warning if we're inside of typeof or delete.
+ // Attempting to subscript a null reference will throw an
+ // error, even within the typeof and delete operators
+ if (!(anonname === "typeof" || anonname === "delete") ||
+ (state.tokens.next &&
+ (state.tokens.next.value === "." || state.tokens.next.value === "["))) {
+
+ isundef(funct, "W117", state.tokens.curr, v);
+ }
+ funct[v] = true;
+ note_implied(state.tokens.curr);
+ } else {
+ switch (s[v]) {
+ case "function":
+ case "unction":
+ this["function"] = true;
+ s[v] = "closure";
+ funct[v] = s["(global)"] ? "global" : "outer";
+ break;
+ case "var":
+ case "unused":
+ s[v] = "closure";
+ funct[v] = s["(global)"] ? "global" : "outer";
+ break;
+ case "closure":
+ funct[v] = s["(global)"] ? "global" : "outer";
+ break;
+ case "label":
+ warning("W037", state.tokens.curr, v);
+ }
+ }
+ }
+ }
+ return this;
+ },
+ led: function () {
+ error("E033", state.tokens.next, state.tokens.next.value);
+ }
+ };
+
+ type("(regexp)", function () {
+ return this;
+ });
+
+ // ECMAScript parser
+
+ delim("(endline)");
+ delim("(begin)");
+ delim("(end)").reach = true;
+ delim("(error)").reach = true;
+ delim("}").reach = true;
+ delim(")");
+ delim("]");
+ delim("\"").reach = true;
+ delim("'").reach = true;
+ delim(";");
+ delim(":").reach = true;
+ delim("#");
+
+ reserve("else");
+ reserve("case").reach = true;
+ reserve("catch");
+ reserve("default").reach = true;
+ reserve("finally");
+ reservevar("arguments", function (x) {
+ if (state.directive["use strict"] && funct["(global)"]) {
+ warning("E008", x);
+ }
+ });
+ reservevar("eval");
+ reservevar("false");
+ reservevar("Infinity");
+ reservevar("null");
+ reservevar("this", function (x) {
+ if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] &&
+ funct["(name)"].charAt(0) > "Z") || funct["(global)"])) {
+ warning("W040", x);
+ }
+ });
+ reservevar("true");
+ reservevar("undefined");
+
+ assignop("=", "assign", 20);
+ assignop("+=", "assignadd", 20);
+ assignop("-=", "assignsub", 20);
+ assignop("*=", "assignmult", 20);
+ assignop("/=", "assigndiv", 20).nud = function () {
+ error("E014");
+ };
+ assignop("%=", "assignmod", 20);
+
+ bitwiseassignop("&=", "assignbitand", 20);
+ bitwiseassignop("|=", "assignbitor", 20);
+ bitwiseassignop("^=", "assignbitxor", 20);
+ bitwiseassignop("<<=", "assignshiftleft", 20);
+ bitwiseassignop(">>=", "assignshiftright", 20);
+ bitwiseassignop(">>>=", "assignshiftrightunsigned", 20);
+ infix(",", function (left, that) {
+ var expr;
+ that.exprs = [left];
+ if (!comma({peek: true})) {
+ return that;
+ }
+ while (true) {
+ if (!(expr = expression(5))) {
+ break;
+ }
+ that.exprs.push(expr);
+ if (state.tokens.next.value !== "," || !comma()) {
+ break;
+ }
+ }
+ return that;
+ }, 5, true);
+ infix("?", function (left, that) {
+ that.left = left;
+ that.right = expression(10);
+ advance(":");
+ that["else"] = expression(10);
+ return that;
+ }, 30);
+
+ infix("||", "or", 40);
+ infix("&&", "and", 50);
+ bitwise("|", "bitor", 70);
+ bitwise("^", "bitxor", 80);
+ bitwise("&", "bitand", 90);
+ relation("==", function (left, right) {
+ var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null");
+
+ if (!eqnull && state.option.eqeqeq)
+ warning("W116", this, "===", "==");
+ else if (isPoorRelation(left))
+ warning("W041", this, "===", left.value);
+ else if (isPoorRelation(right))
+ warning("W041", this, "===", right.value);
+
+ return this;
+ });
+ relation("===");
+ relation("!=", function (left, right) {
+ var eqnull = state.option.eqnull &&
+ (left.value === "null" || right.value === "null");
+
+ if (!eqnull && state.option.eqeqeq) {
+ warning("W116", this, "!==", "!=");
+ } else if (isPoorRelation(left)) {
+ warning("W041", this, "!==", left.value);
+ } else if (isPoorRelation(right)) {
+ warning("W041", this, "!==", right.value);
+ }
+ return this;
+ });
+ relation("!==");
+ relation("<");
+ relation(">");
+ relation("<=");
+ relation(">=");
+ bitwise("<<", "shiftleft", 120);
+ bitwise(">>", "shiftright", 120);
+ bitwise(">>>", "shiftrightunsigned", 120);
+ infix("in", "in", 120);
+ infix("instanceof", "instanceof", 120);
+ infix("+", function (left, that) {
+ var right = expression(130);
+ if (left && right && left.id === "(string)" && right.id === "(string)") {
+ left.value += right.value;
+ left.character = right.character;
+ if (!state.option.scripturl && reg.javascriptURL.test(left.value)) {
+ warning("W050", left);
+ }
+ return left;
+ }
+ that.left = left;
+ that.right = right;
+ return that;
+ }, 130);
+ prefix("+", "num");
+ prefix("+++", function () {
+ warning("W007");
+ this.right = expression(150);
+ this.arity = "unary";
+ return this;
+ });
+ infix("+++", function (left) {
+ warning("W007");
+ this.left = left;
+ this.right = expression(130);
+ return this;
+ }, 130);
+ infix("-", "sub", 130);
+ prefix("-", "neg");
+ prefix("---", function () {
+ warning("W006");
+ this.right = expression(150);
+ this.arity = "unary";
+ return this;
+ });
+ infix("---", function (left) {
+ warning("W006");
+ this.left = left;
+ this.right = expression(130);
+ return this;
+ }, 130);
+ infix("*", "mult", 140);
+ infix("/", "div", 140);
+ infix("%", "mod", 140);
+
+ suffix("++", "postinc");
+ prefix("++", "preinc");
+ state.syntax["++"].exps = true;
+
+ suffix("--", "postdec");
+ prefix("--", "predec");
+ state.syntax["--"].exps = true;
+ prefix("delete", function () {
+ var p = expression(5);
+ if (!p || (p.id !== "." && p.id !== "[")) {
+ warning("W051");
+ }
+ this.first = p;
+ return this;
+ }).exps = true;
+
+ prefix("~", function () {
+ if (state.option.bitwise) {
+ warning("W052", this, "~");
+ }
+ expression(150);
+ return this;
+ });
+
+ prefix("...", function () {
+ if (!state.option.inESNext()) {
+ warning("W104", this, "spread/rest operator");
+ }
+ if (!state.tokens.next.identifier) {
+ error("E030", state.tokens.next, state.tokens.next.value);
+ }
+ expression(150);
+ return this;
+ });
+
+ prefix("!", function () {
+ this.right = expression(150);
+ this.arity = "unary";
+
+ if (!this.right) { // '!' followed by nothing? Give up.
+ quit("E041", this.line || 0);
+ }
+
+ if (bang[this.right.id] === true) {
+ warning("W018", this, "!");
+ }
+ return this;
+ });
+
+ prefix("typeof", "typeof");
+ prefix("new", function () {
+ var c = expression(155), i;
+ if (c && c.id !== "function") {
+ if (c.identifier) {
+ c["new"] = true;
+ switch (c.value) {
+ case "Number":
+ case "String":
+ case "Boolean":
+ case "Math":
+ case "JSON":
+ warning("W053", state.tokens.prev, c.value);
+ break;
+ case "Function":
+ if (!state.option.evil) {
+ warning("W054");
+ }
+ break;
+ case "Date":
+ case "RegExp":
+ break;
+ default:
+ if (c.id !== "function") {
+ i = c.value.substr(0, 1);
+ if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) {
+ warning("W055", state.tokens.curr);
+ }
+ }
+ }
+ } else {
+ if (c.id !== "." && c.id !== "[" && c.id !== "(") {
+ warning("W056", state.tokens.curr);
+ }
+ }
+ } else {
+ if (!state.option.supernew)
+ warning("W057", this);
+ }
+ adjacent(state.tokens.curr, state.tokens.next);
+ if (state.tokens.next.id !== "(" && !state.option.supernew) {
+ warning("W058", state.tokens.curr, state.tokens.curr.value);
+ }
+ this.first = c;
+ return this;
+ });
+ state.syntax["new"].exps = true;
+
+ prefix("void").exps = true;
+
+ infix(".", function (left, that) {
+ adjacent(state.tokens.prev, state.tokens.curr);
+ nobreak();
+ var m = identifier(false, true);
+
+ if (typeof m === "string") {
+ countMember(m);
+ }
+
+ that.left = left;
+ that.right = m;
+
+ if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") {
+ warning("W001");
+ }
+
+ if (left && left.value === "arguments" && (m === "callee" || m === "caller")) {
+ if (state.option.noarg)
+ warning("W059", left, m);
+ else if (state.directive["use strict"])
+ error("E008");
+ } else if (!state.option.evil && left && left.value === "document" &&
+ (m === "write" || m === "writeln")) {
+ warning("W060", left);
+ }
+
+ if (!state.option.evil && (m === "eval" || m === "execScript")) {
+ warning("W061");
+ }
+
+ return that;
+ }, 160, true);
+
+ infix("(", function (left, that) {
+ if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") {
+ nobreak(state.tokens.prev, state.tokens.curr);
+ }
+
+ nospace();
+ if (state.option.immed && left && !left.immed && left.id === "function") {
+ warning("W062");
+ }
+
+ var n = 0;
+ var p = [];
+
+ if (left) {
+ if (left.type === "(identifier)") {
+ if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
+ if ("Number String Boolean Date Object".indexOf(left.value) === -1) {
+ if (left.value === "Math") {
+ warning("W063", left);
+ } else if (state.option.newcap) {
+ warning("W064", left);
+ }
+ }
+ }
+ }
+ }
+
+ if (state.tokens.next.id !== ")") {
+ for (;;) {
+ p[p.length] = expression(10);
+ n += 1;
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+
+ advance(")");
+ nospace(state.tokens.prev, state.tokens.curr);
+
+ if (typeof left === "object") {
+ if (left.value === "parseInt" && n === 1) {
+ warning("W065", state.tokens.curr);
+ }
+ if (!state.option.evil) {
+ if (left.value === "eval" || left.value === "Function" ||
+ left.value === "execScript") {
+ warning("W061", left);
+
+ if (p[0] && [0].id === "(string)") {
+ addInternalSrc(left, p[0].value);
+ }
+ } else if (p[0] && p[0].id === "(string)" &&
+ (left.value === "setTimeout" ||
+ left.value === "setInterval")) {
+ warning("W066", left);
+ addInternalSrc(left, p[0].value);
+
+ // window.setTimeout/setInterval
+ } else if (p[0] && p[0].id === "(string)" &&
+ left.value === "." &&
+ left.left.value === "window" &&
+ (left.right === "setTimeout" ||
+ left.right === "setInterval")) {
+ warning("W066", left);
+ addInternalSrc(left, p[0].value);
+ }
+ }
+ if (!left.identifier && left.id !== "." && left.id !== "[" &&
+ left.id !== "(" && left.id !== "&&" && left.id !== "||" &&
+ left.id !== "?") {
+ warning("W067", left);
+ }
+ }
+
+ that.left = left;
+ return that;
+ }, 155, true).exps = true;
+
+ prefix("(", function () {
+ nospace();
+ var bracket, brackets = [];
+ var pn, pn1, i = 0;
+
+ do {
+ pn = peek(i);
+ i += 1;
+ pn1 = peek(i);
+ i += 1;
+ } while (pn.value !== ")" && pn1.value !== "=>" && pn1.value !== ";" && pn1.type !== "(end)");
+
+ if (state.tokens.next.id === "function") {
+ state.tokens.next.immed = true;
+ }
+
+ var exprs = [];
+
+ if (state.tokens.next.id !== ")") {
+ for (;;) {
+ if (pn1.value === "=>" && state.tokens.next.value === "{") {
+ bracket = state.tokens.next;
+ bracket.left = destructuringExpression();
+ brackets.push(bracket);
+ for (var t in bracket.left) {
+ exprs.push(bracket.left[t].token);
+ }
+ } else {
+ exprs.push(expression(5));
+ }
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+
+ advance(")", this);
+ nospace(state.tokens.prev, state.tokens.curr);
+ if (state.option.immed && exprs[0] && exprs[0].id === "function") {
+ if (state.tokens.next.id !== "(" &&
+ (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) {
+ warning("W068", this);
+ }
+ }
+
+ if (state.tokens.next.value === "=>") {
+ return exprs;
+ }
+ if (!exprs.length) {
+ return;
+ }
+ exprs[exprs.length - 1].paren = true;
+ if (exprs.length > 1) {
+ return Object.create(state.syntax[","], { exprs: { value: exprs } });
+ }
+ return exprs[0];
+ });
+
+ application("=>");
+
+ infix("[", function (left, that) {
+ nobreak(state.tokens.prev, state.tokens.curr);
+ nospace();
+ var e = expression(5), s;
+ if (e && e.type === "(string)") {
+ if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) {
+ warning("W061", that);
+ }
+
+ countMember(e.value);
+ if (!state.option.sub && reg.identifier.test(e.value)) {
+ s = state.syntax[e.value];
+ if (!s || !isReserved(s)) {
+ warning("W069", state.tokens.prev, e.value);
+ }
+ }
+ }
+ advance("]", that);
+
+ if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") {
+ warning("W001");
+ }
+
+ nospace(state.tokens.prev, state.tokens.curr);
+ that.left = left;
+ that.right = e;
+ return that;
+ }, 160, true);
+
+ function comprehensiveArrayExpression() {
+ var res = {};
+ res.exps = true;
+ funct["(comparray)"].stack();
+
+ res.right = expression(5);
+ advance("for");
+ if (state.tokens.next.value === "each") {
+ advance("each");
+ if (!state.option.inMoz(true)) {
+ warning("W118", state.tokens.curr, "for each");
+ }
+ }
+ advance("(");
+ funct["(comparray)"].setState("define");
+ res.left = expression(5);
+ advance(")");
+ if (state.tokens.next.value === "if") {
+ advance("if");
+ advance("(");
+ funct["(comparray)"].setState("filter");
+ res.filter = expression(5);
+ advance(")");
+ }
+ advance("]");
+ funct["(comparray)"].unstack();
+ return res;
+ }
+
+ prefix("[", function () {
+ var blocktype = lookupBlockType(true);
+ if (blocktype.isCompArray) {
+ if (!state.option.inMoz(true)) {
+ warning("W118", state.tokens.curr, "array comprehension");
+ }
+ return comprehensiveArrayExpression();
+ } else if (blocktype.isDestAssign && !state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "destructuring assignment");
+ }
+ var b = state.tokens.curr.line !== state.tokens.next.line;
+ this.first = [];
+ if (b) {
+ indent += state.option.indent;
+ if (state.tokens.next.from === indent + state.option.indent) {
+ indent += state.option.indent;
+ }
+ }
+ while (state.tokens.next.id !== "(end)") {
+ while (state.tokens.next.id === ",") {
+ if (!state.option.inES5())
+ warning("W070");
+ advance(",");
+ }
+ if (state.tokens.next.id === "]") {
+ break;
+ }
+ if (b && state.tokens.curr.line !== state.tokens.next.line) {
+ indentation();
+ }
+ this.first.push(expression(10));
+ if (state.tokens.next.id === ",") {
+ comma({ allowTrailing: true });
+ if (state.tokens.next.id === "]" && !state.option.inES5(true)) {
+ warning("W070", state.tokens.curr);
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (b) {
+ indent -= state.option.indent;
+ indentation();
+ }
+ advance("]", this);
+ return this;
+ }, 160);
+
+
+ function property_name() {
+ var id = optionalidentifier(false, true);
+
+ if (!id) {
+ if (state.tokens.next.id === "(string)") {
+ id = state.tokens.next.value;
+ advance();
+ } else if (state.tokens.next.id === "(number)") {
+ id = state.tokens.next.value.toString();
+ advance();
+ }
+ }
+
+ if (id === "hasOwnProperty") {
+ warning("W001");
+ }
+
+ return id;
+ }
+
+
+ function functionparams(parsed) {
+ var curr, next;
+ var params = [];
+ var ident;
+ var tokens = [];
+ var t;
+
+ if (parsed) {
+ if (parsed instanceof Array) {
+ for (var i in parsed) {
+ curr = parsed[i];
+ if (_.contains(["{", "["], curr.id)) {
+ for (t in curr.left) {
+ t = tokens[t];
+ if (t.id) {
+ params.push(t.id);
+ addlabel(t.id, "unused", t.token);
+ }
+ }
+ } else if (curr.value === "...") {
+ if (!state.option.inESNext()) {
+ warning("W104", curr, "spread/rest operator");
+ }
+ continue;
+ } else {
+ addlabel(curr.value, "unused", curr);
+ }
+ }
+ return params;
+ } else {
+ if (parsed.identifier === true) {
+ addlabel(parsed.value, "unused", parsed);
+ return [parsed];
+ }
+ }
+ }
+
+ next = state.tokens.next;
+
+ advance("(");
+ nospace();
+
+ if (state.tokens.next.id === ")") {
+ advance(")");
+ return;
+ }
+
+ for (;;) {
+ if (_.contains(["{", "["], state.tokens.next.id)) {
+ tokens = destructuringExpression();
+ for (t in tokens) {
+ t = tokens[t];
+ if (t.id) {
+ params.push(t.id);
+ addlabel(t.id, "unused", t.token);
+ }
+ }
+ } else if (state.tokens.next.value === "...") {
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.next, "spread/rest operator");
+ }
+ advance("...");
+ nospace();
+ ident = identifier(true);
+ params.push(ident);
+ addlabel(ident, "unused", state.tokens.curr);
+ } else {
+ ident = identifier(true);
+ params.push(ident);
+ addlabel(ident, "unused", state.tokens.curr);
+ }
+ if (state.tokens.next.id === ",") {
+ comma();
+ } else {
+ advance(")", next);
+ nospace(state.tokens.prev, state.tokens.curr);
+ return params;
+ }
+ }
+ }
+
+
+ function doFunction(name, statement, generator, fatarrowparams) {
+ var f;
+ var oldOption = state.option;
+ var oldIgnored = state.ignored;
+ var oldScope = scope;
+
+ state.option = Object.create(state.option);
+ state.ignored = Object.create(state.ignored);
+ scope = Object.create(scope);
+
+ funct = {
+ "(name)" : name || "\"" + anonname + "\"",
+ "(line)" : state.tokens.next.line,
+ "(character)" : state.tokens.next.character,
+ "(context)" : funct,
+ "(breakage)" : 0,
+ "(loopage)" : 0,
+ "(metrics)" : createMetrics(state.tokens.next),
+ "(scope)" : scope,
+ "(statement)" : statement,
+ "(tokens)" : {},
+ "(blockscope)": funct["(blockscope)"],
+ "(comparray)" : funct["(comparray)"]
+ };
+
+ if (generator) {
+ funct["(generator)"] = true;
+ }
+
+ f = funct;
+ state.tokens.curr.funct = funct;
+
+ functions.push(funct);
+
+ if (name) {
+ addlabel(name, "function");
+ }
+
+ funct["(params)"] = functionparams(fatarrowparams);
+
+ funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]);
+
+ block(false, true, true, fatarrowparams ? true:false);
+
+ if (generator && funct["(generator)"] !== "yielded") {
+ error("E047", state.tokens.curr);
+ }
+
+ funct["(metrics)"].verifyMaxStatementsPerFunction();
+ funct["(metrics)"].verifyMaxComplexityPerFunction();
+ funct["(unusedOption)"] = state.option.unused;
+
+ scope = oldScope;
+ state.option = oldOption;
+ state.ignored = oldIgnored;
+ funct["(last)"] = state.tokens.curr.line;
+ funct["(lastcharacter)"] = state.tokens.curr.character;
+ funct = funct["(context)"];
+
+ return f;
+ }
+
+ function createMetrics(functionStartToken) {
+ return {
+ statementCount: 0,
+ nestedBlockDepth: -1,
+ ComplexityCount: 1,
+ verifyMaxStatementsPerFunction: function () {
+ if (state.option.maxstatements &&
+ this.statementCount > state.option.maxstatements) {
+ warning("W071", functionStartToken, this.statementCount);
+ }
+ },
+
+ verifyMaxParametersPerFunction: function (params) {
+ params = params || [];
+
+ if (state.option.maxparams && params.length > state.option.maxparams) {
+ warning("W072", functionStartToken, params.length);
+ }
+ },
+
+ verifyMaxNestedBlockDepthPerFunction: function () {
+ if (state.option.maxdepth &&
+ this.nestedBlockDepth > 0 &&
+ this.nestedBlockDepth === state.option.maxdepth + 1) {
+ warning("W073", null, this.nestedBlockDepth);
+ }
+ },
+
+ verifyMaxComplexityPerFunction: function () {
+ var max = state.option.maxcomplexity;
+ var cc = this.ComplexityCount;
+ if (max && cc > max) {
+ warning("W074", functionStartToken, cc);
+ }
+ }
+ };
+ }
+
+ function increaseComplexityCount() {
+ funct["(metrics)"].ComplexityCount += 1;
+ }
+
+ // Parse assignments that were found instead of conditionals.
+ // For example: if (a = 1) { ... }
+
+ function checkCondAssignment(expr) {
+ var id = expr.id;
+ if (id === ",") {
+ expr = expr.exprs[expr.exprs.length - 1];
+ id = expr.id;
+ }
+ switch (id) {
+ case "=":
+ case "+=":
+ case "-=":
+ case "*=":
+ case "%=":
+ case "&=":
+ case "|=":
+ case "^=":
+ case "/=":
+ if (!expr.paren && !state.option.boss) {
+ warning("W084");
+ }
+ }
+ }
+
+
+ (function (x) {
+ x.nud = function (isclassdef) {
+ var b, f, i, p, t, g;
+ var props = {}; // All properties, including accessors
+ var tag = "";
+
+ function saveProperty(name, tkn) {
+ if (props[name] && _.has(props, name))
+ warning("W075", state.tokens.next, i);
+ else
+ props[name] = {};
+
+ props[name].basic = true;
+ props[name].basictkn = tkn;
+ }
+
+ function saveSetter(name, tkn) {
+ if (props[name] && _.has(props, name)) {
+ if (props[name].basic || props[name].setter)
+ warning("W075", state.tokens.next, i);
+ } else {
+ props[name] = {};
+ }
+
+ props[name].setter = true;
+ props[name].setterToken = tkn;
+ }
+
+ function saveGetter(name) {
+ if (props[name] && _.has(props, name)) {
+ if (props[name].basic || props[name].getter)
+ warning("W075", state.tokens.next, i);
+ } else {
+ props[name] = {};
+ }
+
+ props[name].getter = true;
+ props[name].getterToken = state.tokens.curr;
+ }
+
+ b = state.tokens.curr.line !== state.tokens.next.line;
+ if (b) {
+ indent += state.option.indent;
+ if (state.tokens.next.from === indent + state.option.indent) {
+ indent += state.option.indent;
+ }
+ }
+
+ for (;;) {
+ if (state.tokens.next.id === "}") {
+ break;
+ }
+
+ if (b) {
+ indentation();
+ }
+
+ if (isclassdef && state.tokens.next.value === "static") {
+ advance("static");
+ tag = "static ";
+ }
+
+ if (state.tokens.next.value === "get" && peek().id !== ":") {
+ advance("get");
+
+ if (!state.option.inES5(!isclassdef)) {
+ error("E034");
+ }
+
+ i = property_name();
+ if (!i) {
+ error("E035");
+ }
+
+ // It is a Syntax Error if PropName of MethodDefinition is
+ // "constructor" and SpecialMethod of MethodDefinition is true.
+ if (isclassdef && i === "constructor") {
+ error("E049", state.tokens.next, "class getter method", i);
+ }
+
+ saveGetter(tag + i);
+ t = state.tokens.next;
+ adjacent(state.tokens.curr, state.tokens.next);
+ f = doFunction();
+ p = f["(params)"];
+
+ if (p) {
+ warning("W076", t, p[0], i);
+ }
+
+ adjacent(state.tokens.curr, state.tokens.next);
+ } else if (state.tokens.next.value === "set" && peek().id !== ":") {
+ advance("set");
+
+ if (!state.option.inES5(!isclassdef)) {
+ error("E034");
+ }
+
+ i = property_name();
+ if (!i) {
+ error("E035");
+ }
+
+ // It is a Syntax Error if PropName of MethodDefinition is
+ // "constructor" and SpecialMethod of MethodDefinition is true.
+ if (isclassdef && i === "constructor") {
+ error("E049", state.tokens.next, "class setter method", i);
+ }
+
+ saveSetter(tag + i, state.tokens.next);
+ t = state.tokens.next;
+ adjacent(state.tokens.curr, state.tokens.next);
+ f = doFunction();
+ p = f["(params)"];
+
+ if (!p || p.length !== 1) {
+ warning("W077", t, i);
+ }
+ } else {
+ g = false;
+ if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") {
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.next, "generator functions");
+ }
+ advance("*");
+ g = true;
+ }
+ i = property_name();
+ saveProperty(tag + i, state.tokens.next);
+
+ if (typeof i !== "string") {
+ break;
+ }
+
+ if (state.tokens.next.value === "(") {
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "concise methods");
+ }
+ doFunction(i, undefined, g);
+ } else if (!isclassdef) {
+ advance(":");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ expression(10);
+ }
+ }
+ // It is a Syntax Error if PropName of MethodDefinition is "prototype".
+ if (isclassdef && i === "prototype") {
+ error("E049", state.tokens.next, "class method", i);
+ }
+
+ countMember(i);
+ if (isclassdef) {
+ tag = "";
+ continue;
+ }
+ if (state.tokens.next.id === ",") {
+ comma({ allowTrailing: true, property: true });
+ if (state.tokens.next.id === ",") {
+ warning("W070", state.tokens.curr);
+ } else if (state.tokens.next.id === "}" && !state.option.inES5(true)) {
+ warning("W070", state.tokens.curr);
+ }
+ } else {
+ break;
+ }
+ }
+ if (b) {
+ indent -= state.option.indent;
+ indentation();
+ }
+ advance("}", this);
+
+ // Check for lonely setters if in the ES5 mode.
+ if (state.option.inES5()) {
+ for (var name in props) {
+ if (_.has(props, name) && props[name].setter && !props[name].getter) {
+ warning("W078", props[name].setterToken);
+ }
+ }
+ }
+ return this;
+ };
+ x.fud = function () {
+ error("E036", state.tokens.curr);
+ };
+ }(delim("{")));
+
+ function destructuringExpression() {
+ var id, ids;
+ var identifiers = [];
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "destructuring expression");
+ }
+ var nextInnerDE = function () {
+ var ident;
+ if (_.contains(["[", "{"], state.tokens.next.value)) {
+ ids = destructuringExpression();
+ for (var id in ids) {
+ id = ids[id];
+ identifiers.push({ id: id.id, token: id.token });
+ }
+ } else if (state.tokens.next.value === ",") {
+ identifiers.push({ id: null, token: state.tokens.curr });
+ } else {
+ ident = identifier();
+ if (ident)
+ identifiers.push({ id: ident, token: state.tokens.curr });
+ }
+ };
+ if (state.tokens.next.value === "[") {
+ advance("[");
+ nextInnerDE();
+ while (state.tokens.next.value !== "]") {
+ advance(",");
+ nextInnerDE();
+ }
+ advance("]");
+ } else if (state.tokens.next.value === "{") {
+ advance("{");
+ id = identifier();
+ if (state.tokens.next.value === ":") {
+ advance(":");
+ nextInnerDE();
+ } else {
+ identifiers.push({ id: id, token: state.tokens.curr });
+ }
+ while (state.tokens.next.value !== "}") {
+ advance(",");
+ id = identifier();
+ if (state.tokens.next.value === ":") {
+ advance(":");
+ nextInnerDE();
+ } else {
+ identifiers.push({ id: id, token: state.tokens.curr });
+ }
+ }
+ advance("}");
+ }
+ return identifiers;
+ }
+ function destructuringExpressionMatch(tokens, value) {
+ if (value.first) {
+ _.zip(tokens, value.first).forEach(function (val) {
+ var token = val[0];
+ var value = val[1];
+ if (token && value) {
+ token.first = value;
+ } else if (token && token.first && !value) {
+ warning("W080", token.first, token.first.value);
+ } /* else {
+ XXX value is discarded: wouldn't it need a warning ?
+ } */
+ });
+ }
+ }
+
+ var conststatement = stmt("const", function (prefix) {
+ var tokens, value;
+ // state variable to know if it is a lone identifier, or a destructuring statement.
+ var lone;
+
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "const");
+ }
+
+ this.first = [];
+ for (;;) {
+ var names = [];
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (_.contains(["{", "["], state.tokens.next.value)) {
+ tokens = destructuringExpression();
+ lone = false;
+ } else {
+ tokens = [ { id: identifier(), token: state.tokens.curr } ];
+ lone = true;
+ }
+ for (var t in tokens) {
+ t = tokens[t];
+ if (funct[t.id] === "const") {
+ warning("E011", null, t.id);
+ }
+ if (funct["(global)"] && predefined[t.id] === false) {
+ warning("W079", t.token, t.id);
+ }
+ if (t.id) {
+ addlabel(t.id, "const");
+ names.push(t.token);
+ }
+ }
+ if (prefix) {
+ break;
+ }
+
+ this.first = this.first.concat(names);
+
+ if (state.tokens.next.id !== "=") {
+ warning("E012", state.tokens.curr, state.tokens.curr.value);
+ }
+
+ if (state.tokens.next.id === "=") {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ advance("=");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (state.tokens.next.id === "undefined") {
+ warning("W080", state.tokens.prev, state.tokens.prev.value);
+ }
+ if (peek(0).id === "=" && state.tokens.next.identifier) {
+ error("E037", state.tokens.next, state.tokens.next.value);
+ }
+ value = expression(5);
+ if (lone) {
+ tokens[0].first = value;
+ } else {
+ destructuringExpressionMatch(names, value);
+ }
+ }
+
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ return this;
+ });
+ conststatement.exps = true;
+ var varstatement = stmt("var", function (prefix) {
+ // JavaScript does not have block scope. It only has function scope. So,
+ // declaring a variable in a block can have unexpected consequences.
+ var tokens, lone, value;
+
+ if (funct["(onevar)"] && state.option.onevar) {
+ warning("W081");
+ } else if (!funct["(global)"]) {
+ funct["(onevar)"] = true;
+ }
+
+ this.first = [];
+ for (;;) {
+ var names = [];
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (_.contains(["{", "["], state.tokens.next.value)) {
+ tokens = destructuringExpression();
+ lone = false;
+ } else {
+ tokens = [ { id: identifier(), token: state.tokens.curr } ];
+ lone = true;
+ }
+ for (var t in tokens) {
+ t = tokens[t];
+ if (state.option.inESNext() && funct[t.id] === "const") {
+ warning("E011", null, t.id);
+ }
+ if (funct["(global)"] && predefined[t.id] === false) {
+ warning("W079", t.token, t.id);
+ }
+ if (t.id) {
+ addlabel(t.id, "unused", t.token);
+ names.push(t.token);
+ }
+ }
+ if (prefix) {
+ break;
+ }
+
+ this.first = this.first.concat(names);
+
+ if (state.tokens.next.id === "=") {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ advance("=");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (state.tokens.next.id === "undefined") {
+ warning("W080", state.tokens.prev, state.tokens.prev.value);
+ }
+ if (peek(0).id === "=" && state.tokens.next.identifier) {
+ error("E038", state.tokens.next, state.tokens.next.value);
+ }
+ value = expression(5);
+ if (lone) {
+ tokens[0].first = value;
+ } else {
+ destructuringExpressionMatch(names, value);
+ }
+ }
+
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ return this;
+ });
+ varstatement.exps = true;
+ var letstatement = stmt("let", function (prefix) {
+ var tokens, lone, value, letblock;
+
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "let");
+ }
+
+ if (state.tokens.next.value === "(") {
+ if (!state.option.inMoz(true)) {
+ warning("W118", state.tokens.next, "let block");
+ }
+ advance("(");
+ funct["(blockscope)"].stack();
+ letblock = true;
+ } else if (funct["(nolet)"]) {
+ error("E048", state.tokens.curr);
+ }
+
+ if (funct["(onevar)"] && state.option.onevar) {
+ warning("W081");
+ } else if (!funct["(global)"]) {
+ funct["(onevar)"] = true;
+ }
+
+ this.first = [];
+ for (;;) {
+ var names = [];
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (_.contains(["{", "["], state.tokens.next.value)) {
+ tokens = destructuringExpression();
+ lone = false;
+ } else {
+ tokens = [ { id: identifier(), token: state.tokens.curr.value } ];
+ lone = true;
+ }
+ for (var t in tokens) {
+ t = tokens[t];
+ if (state.option.inESNext() && funct[t.id] === "const") {
+ warning("E011", null, t.id);
+ }
+ if (funct["(global)"] && predefined[t.id] === false) {
+ warning("W079", t.token, t.id);
+ }
+ if (t.id && !funct["(nolet)"]) {
+ addlabel(t.id, "unused", t.token, true);
+ names.push(t.token);
+ }
+ }
+ if (prefix) {
+ break;
+ }
+
+ this.first = this.first.concat(names);
+
+ if (state.tokens.next.id === "=") {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ advance("=");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ if (state.tokens.next.id === "undefined") {
+ warning("W080", state.tokens.prev, state.tokens.prev.value);
+ }
+ if (peek(0).id === "=" && state.tokens.next.identifier) {
+ error("E037", state.tokens.next, state.tokens.next.value);
+ }
+ value = expression(5);
+ if (lone) {
+ tokens[0].first = value;
+ } else {
+ destructuringExpressionMatch(names, value);
+ }
+ }
+
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ if (letblock) {
+ advance(")");
+ block(true, true);
+ this.block = true;
+ funct["(blockscope)"].unstack();
+ }
+
+ return this;
+ });
+ letstatement.exps = true;
+
+ blockstmt("class", function () {
+ return classdef.call(this, true);
+ });
+
+ function classdef(stmt) {
+ /*jshint validthis:true */
+ if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "class");
+ }
+ if (stmt) {
+ // BindingIdentifier
+ this.name = identifier();
+ addlabel(this.name, "unused", state.tokens.curr);
+ } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") {
+ // BindingIdentifier(opt)
+ this.name = identifier();
+ }
+ classtail(this);
+ return this;
+ }
+
+ function classtail(c) {
+ var strictness = state.directive["use strict"];
+
+ // ClassHeritage(opt)
+ if (state.tokens.next.value === "extends") {
+ advance("extends");
+ c.heritage = expression(10);
+ }
+
+ // A ClassBody is always strict code.
+ state.directive["use strict"] = true;
+ advance("{");
+ // ClassBody(opt)
+ c.body = state.syntax["{"].nud(true);
+ state.directive["use strict"] = strictness;
+ }
+
+ blockstmt("function", function () {
+ var generator = false;
+ if (state.tokens.next.value === "*") {
+ advance("*");
+ if (state.option.inESNext(true)) {
+ generator = true;
+ } else {
+ warning("W119", state.tokens.curr, "function*");
+ }
+ }
+ if (inblock) {
+ warning("W082", state.tokens.curr);
+
+ }
+ var i = identifier();
+ if (funct[i] === "const") {
+ warning("E011", null, i);
+ }
+ adjacent(state.tokens.curr, state.tokens.next);
+ addlabel(i, "unction", state.tokens.curr);
+
+ doFunction(i, { statement: true }, generator);
+ if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) {
+ error("E039");
+ }
+ return this;
+ });
+
+ prefix("function", function () {
+ var generator = false;
+ if (state.tokens.next.value === "*") {
+ if (!state.option.inESNext()) {
+ warning("W119", state.tokens.curr, "function*");
+ }
+ advance("*");
+ generator = true;
+ }
+ var i = optionalidentifier();
+ if (i || state.option.gcl) {
+ adjacent(state.tokens.curr, state.tokens.next);
+ } else {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ }
+ doFunction(i, undefined, generator);
+ if (!state.option.loopfunc && funct["(loopage)"]) {
+ warning("W083");
+ }
+ return this;
+ });
+
+ blockstmt("if", function () {
+ var t = state.tokens.next;
+ increaseComplexityCount();
+ state.condition = true;
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ checkCondAssignment(expression(0));
+ advance(")", t);
+ state.condition = false;
+ nospace(state.tokens.prev, state.tokens.curr);
+ block(true, true);
+ if (state.tokens.next.id === "else") {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ advance("else");
+ if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") {
+ statement(true);
+ } else {
+ block(true, true);
+ }
+ }
+ return this;
+ });
+
+ blockstmt("try", function () {
+ var b;
+
+ function doCatch() {
+ var oldScope = scope;
+ var e;
+
+ advance("catch");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ advance("(");
+
+ scope = Object.create(oldScope);
+
+ e = state.tokens.next.value;
+ if (state.tokens.next.type !== "(identifier)") {
+ e = null;
+ warning("E030", state.tokens.next, e);
+ }
+
+ advance();
+
+ funct = {
+ "(name)" : "(catch)",
+ "(line)" : state.tokens.next.line,
+ "(character)": state.tokens.next.character,
+ "(context)" : funct,
+ "(breakage)" : funct["(breakage)"],
+ "(loopage)" : funct["(loopage)"],
+ "(scope)" : scope,
+ "(statement)": false,
+ "(metrics)" : createMetrics(state.tokens.next),
+ "(catch)" : true,
+ "(tokens)" : {},
+ "(blockscope)": funct["(blockscope)"],
+ "(comparray)": funct["(comparray)"]
+ };
+
+ if (e) {
+ addlabel(e, "exception");
+ }
+
+ if (state.tokens.next.value === "if") {
+ if (!state.option.inMoz(true)) {
+ warning("W118", state.tokens.curr, "catch filter");
+ }
+ advance("if");
+ expression(0);
+ }
+
+ advance(")");
+
+ state.tokens.curr.funct = funct;
+ functions.push(funct);
+
+ block(false);
+
+ scope = oldScope;
+
+ funct["(last)"] = state.tokens.curr.line;
+ funct["(lastcharacter)"] = state.tokens.curr.character;
+ funct = funct["(context)"];
+ }
+
+ block(false);
+
+ while (state.tokens.next.id === "catch") {
+ increaseComplexityCount();
+ if (b && (!state.option.inMoz(true))) {
+ warning("W118", state.tokens.next, "multiple catch blocks");
+ }
+ doCatch();
+ b = true;
+ }
+
+ if (state.tokens.next.id === "finally") {
+ advance("finally");
+ block(false);
+ return;
+ }
+
+ if (!b) {
+ error("E021", state.tokens.next, "catch", state.tokens.next.value);
+ }
+
+ return this;
+ });
+
+ blockstmt("while", function () {
+ var t = state.tokens.next;
+ funct["(breakage)"] += 1;
+ funct["(loopage)"] += 1;
+ increaseComplexityCount();
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ checkCondAssignment(expression(0));
+ advance(")", t);
+ nospace(state.tokens.prev, state.tokens.curr);
+ block(true, true);
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ return this;
+ }).labelled = true;
+
+ blockstmt("with", function () {
+ var t = state.tokens.next;
+ if (state.directive["use strict"]) {
+ error("E010", state.tokens.curr);
+ } else if (!state.option.withstmt) {
+ warning("W085", state.tokens.curr);
+ }
+
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ expression(0);
+ advance(")", t);
+ nospace(state.tokens.prev, state.tokens.curr);
+ block(true, true);
+
+ return this;
+ });
+
+ blockstmt("switch", function () {
+ var t = state.tokens.next,
+ g = false;
+ funct["(breakage)"] += 1;
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+ checkCondAssignment(expression(0));
+ advance(")", t);
+ nospace(state.tokens.prev, state.tokens.curr);
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ t = state.tokens.next;
+ advance("{");
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ indent += state.option.indent;
+ this.cases = [];
+
+ for (;;) {
+ switch (state.tokens.next.id) {
+ case "case":
+ switch (funct["(verb)"]) {
+ case "yield":
+ case "break":
+ case "case":
+ case "continue":
+ case "return":
+ case "switch":
+ case "throw":
+ break;
+ default:
+ // You can tell JSHint that you don't use break intentionally by
+ // adding a comment /* falls through */ on a line just before
+ // the next `case`.
+ if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) {
+ warning("W086", state.tokens.curr, "case");
+ }
+ }
+ indentation(-state.option.indent);
+ advance("case");
+ this.cases.push(expression(20));
+ increaseComplexityCount();
+ g = true;
+ advance(":");
+ funct["(verb)"] = "case";
+ break;
+ case "default":
+ switch (funct["(verb)"]) {
+ case "yield":
+ case "break":
+ case "continue":
+ case "return":
+ case "throw":
+ break;
+ default:
+ // Do not display a warning if 'default' is the first statement or if
+ // there is a special /* falls through */ comment.
+ if (this.cases.length) {
+ if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) {
+ warning("W086", state.tokens.curr, "default");
+ }
+ }
+ }
+ indentation(-state.option.indent);
+ advance("default");
+ g = true;
+ advance(":");
+ break;
+ case "}":
+ indent -= state.option.indent;
+ indentation();
+ advance("}", t);
+ funct["(breakage)"] -= 1;
+ funct["(verb)"] = undefined;
+ return;
+ case "(end)":
+ error("E023", state.tokens.next, "}");
+ return;
+ default:
+ if (g) {
+ switch (state.tokens.curr.id) {
+ case ",":
+ error("E040");
+ return;
+ case ":":
+ g = false;
+ statements();
+ break;
+ default:
+ error("E025", state.tokens.curr);
+ return;
+ }
+ } else {
+ if (state.tokens.curr.id === ":") {
+ advance(":");
+ error("E024", state.tokens.curr, ":");
+ statements();
+ } else {
+ error("E021", state.tokens.next, "case", state.tokens.next.value);
+ return;
+ }
+ }
+ }
+ }
+ }).labelled = true;
+
+ stmt("debugger", function () {
+ if (!state.option.debug) {
+ warning("W087");
+ }
+ return this;
+ }).exps = true;
+
+ (function () {
+ var x = stmt("do", function () {
+ funct["(breakage)"] += 1;
+ funct["(loopage)"] += 1;
+ increaseComplexityCount();
+
+ this.first = block(true, true);
+ advance("while");
+ var t = state.tokens.next;
+ nonadjacent(state.tokens.curr, t);
+ advance("(");
+ nospace();
+ checkCondAssignment(expression(0));
+ advance(")", t);
+ nospace(state.tokens.prev, state.tokens.curr);
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ return this;
+ });
+ x.labelled = true;
+ x.exps = true;
+ }());
+
+ blockstmt("for", function () {
+ var s, t = state.tokens.next;
+ var letscope = false;
+ var foreachtok = null;
+
+ if (t.value === "each") {
+ foreachtok = t;
+ advance("each");
+ if (!state.option.inMoz(true)) {
+ warning("W118", state.tokens.curr, "for each");
+ }
+ }
+
+ funct["(breakage)"] += 1;
+ funct["(loopage)"] += 1;
+ increaseComplexityCount();
+ advance("(");
+ nonadjacent(this, t);
+ nospace();
+
+ // what kind of for(…) statement it is? for(…of…)? for(…in…)? for(…;…;…)?
+ var nextop; // contains the token of the "in" or "of" operator
+ var i = 0;
+ var inof = ["in", "of"];
+ do {
+ nextop = peek(i);
+ ++i;
+ } while (!_.contains(inof, nextop.value) && nextop.value !== ";" &&
+ nextop.type !== "(end)");
+
+ // if we're in a for (… in|of …) statement
+ if (_.contains(inof, nextop.value)) {
+ if (!state.option.inESNext() && nextop.value === "of") {
+ error("W104", nextop, "for of");
+ }
+ if (state.tokens.next.id === "var") {
+ advance("var");
+ state.syntax["var"].fud.call(state.syntax["var"].fud, true);
+ } else if (state.tokens.next.id === "let") {
+ advance("let");
+ // create a new block scope
+ letscope = true;
+ funct["(blockscope)"].stack();
+ state.syntax["let"].fud.call(state.syntax["let"].fud, true);
+ } else {
+ switch (funct[state.tokens.next.value]) {
+ case "unused":
+ funct[state.tokens.next.value] = "var";
+ break;
+ case "var":
+ break;
+ default:
+ if (!funct["(blockscope)"].getlabel(state.tokens.next.value))
+ warning("W088", state.tokens.next, state.tokens.next.value);
+ }
+ advance();
+ }
+ advance(nextop.value);
+ expression(20);
+ advance(")", t);
+ s = block(true, true);
+ if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" ||
+ s[0].value !== "if")) {
+ warning("W089", this);
+ }
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+ } else {
+ if (foreachtok) {
+ error("E045", foreachtok);
+ }
+ if (state.tokens.next.id !== ";") {
+ if (state.tokens.next.id === "var") {
+ advance("var");
+ state.syntax["var"].fud.call(state.syntax["var"].fud);
+ } else if (state.tokens.next.id === "let") {
+ advance("let");
+ // create a new block scope
+ letscope = true;
+ funct["(blockscope)"].stack();
+ state.syntax["let"].fud.call(state.syntax["let"].fud);
+ } else {
+ for (;;) {
+ expression(0, "for");
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+ }
+ nolinebreak(state.tokens.curr);
+ advance(";");
+ if (state.tokens.next.id !== ";") {
+ checkCondAssignment(expression(0));
+ }
+ nolinebreak(state.tokens.curr);
+ advance(";");
+ if (state.tokens.next.id === ";") {
+ error("E021", state.tokens.next, ")", ";");
+ }
+ if (state.tokens.next.id !== ")") {
+ for (;;) {
+ expression(0, "for");
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ comma();
+ }
+ }
+ advance(")", t);
+ nospace(state.tokens.prev, state.tokens.curr);
+ block(true, true);
+ funct["(breakage)"] -= 1;
+ funct["(loopage)"] -= 1;
+
+ }
+ // unstack loop blockscope
+ if (letscope) {
+ funct["(blockscope)"].unstack();
+ }
+ return this;
+ }).labelled = true;
+
+
+ stmt("break", function () {
+ var v = state.tokens.next.value;
+
+ if (funct["(breakage)"] === 0)
+ warning("W052", state.tokens.next, this.value);
+
+ if (!state.option.asi)
+ nolinebreak(this);
+
+ if (state.tokens.next.id !== ";") {
+ if (state.tokens.curr.line === state.tokens.next.line) {
+ if (funct[v] !== "label") {
+ warning("W090", state.tokens.next, v);
+ } else if (scope[v] !== funct) {
+ warning("W091", state.tokens.next, v);
+ }
+ this.first = state.tokens.next;
+ advance();
+ }
+ }
+ reachable("break");
+ return this;
+ }).exps = true;
+
+
+ stmt("continue", function () {
+ var v = state.tokens.next.value;
+
+ if (funct["(breakage)"] === 0)
+ warning("W052", state.tokens.next, this.value);
+
+ if (!state.option.asi)
+ nolinebreak(this);
+
+ if (state.tokens.next.id !== ";") {
+ if (state.tokens.curr.line === state.tokens.next.line) {
+ if (funct[v] !== "label") {
+ warning("W090", state.tokens.next, v);
+ } else if (scope[v] !== funct) {
+ warning("W091", state.tokens.next, v);
+ }
+ this.first = state.tokens.next;
+ advance();
+ }
+ } else if (!funct["(loopage)"]) {
+ warning("W052", state.tokens.next, this.value);
+ }
+ reachable("continue");
+ return this;
+ }).exps = true;
+
+
+ stmt("return", function () {
+ if (this.line === state.tokens.next.line) {
+ if (state.tokens.next.id === "(regexp)")
+ warning("W092");
+
+ if (state.tokens.next.id !== ";" && !state.tokens.next.reach) {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ this.first = expression(0);
+
+ if (this.first &&
+ this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) {
+ warningAt("W093", this.first.line, this.first.character);
+ }
+ }
+ } else {
+ if (state.tokens.next.type === "(punctuator)" &&
+ ["[", "{", "+", "-"].indexOf(state.tokens.next.value) > -1) {
+ nolinebreak(this); // always warn (Line breaking error)
+ }
+ }
+ reachable("return");
+ return this;
+ }).exps = true;
+
+ stmt("yield", function () {
+ if (state.option.inESNext(true) && funct["(generator)"] !== true) {
+ error("E046", state.tokens.curr, "yield");
+ } else if (!state.option.inESNext()) {
+ warning("W104", state.tokens.curr, "yield");
+ }
+ funct["(generator)"] = "yielded";
+ if (this.line === state.tokens.next.line) {
+ if (state.tokens.next.id === "(regexp)")
+ warning("W092");
+
+ if (state.tokens.next.id !== ";" && !state.tokens.next.reach) {
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ this.first = expression(0);
+
+ if (this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) {
+ warningAt("W093", this.first.line, this.first.character);
+ }
+ }
+ } else if (!state.option.asi) {
+ nolinebreak(this); // always warn (Line breaking error)
+ }
+ return this;
+ }).exps = true;
+
+
+ stmt("throw", function () {
+ nolinebreak(this);
+ nonadjacent(state.tokens.curr, state.tokens.next);
+ this.first = expression(20);
+ reachable("throw");
+ return this;
+ }).exps = true;
+
+ // Future Reserved Words
+
+ FutureReservedWord("abstract");
+ FutureReservedWord("boolean");
+ FutureReservedWord("byte");
+ FutureReservedWord("char");
+ FutureReservedWord("class", { es5: true, nud: classdef });
+ FutureReservedWord("double");
+ FutureReservedWord("enum", { es5: true });
+ FutureReservedWord("export", { es5: true });
+ FutureReservedWord("extends", { es5: true });
+ FutureReservedWord("final");
+ FutureReservedWord("float");
+ FutureReservedWord("goto");
+ FutureReservedWord("implements", { es5: true, strictOnly: true });
+ FutureReservedWord("import", { es5: true });
+ FutureReservedWord("int");
+ FutureReservedWord("interface", { es5: true, strictOnly: true });
+ FutureReservedWord("long");
+ FutureReservedWord("native");
+ FutureReservedWord("package", { es5: true, strictOnly: true });
+ FutureReservedWord("private", { es5: true, strictOnly: true });
+ FutureReservedWord("protected", { es5: true, strictOnly: true });
+ FutureReservedWord("public", { es5: true, strictOnly: true });
+ FutureReservedWord("short");
+ FutureReservedWord("static", { es5: true, strictOnly: true });
+ FutureReservedWord("super", { es5: true });
+ FutureReservedWord("synchronized");
+ FutureReservedWord("throws");
+ FutureReservedWord("transient");
+ FutureReservedWord("volatile");
+
+ // this function is used to determine wether a squarebracket or a curlybracket
+ // expression is a comprehension array, destructuring assignment or a json value.
+
+ var lookupBlockType = function () {
+ var pn, pn1;
+ var i = 0;
+ var bracketStack = 0;
+ var ret = {};
+ if (_.contains(["[", "{"], state.tokens.curr.value))
+ bracketStack += 1;
+ if (_.contains(["[", "{"], state.tokens.next.value))
+ bracketStack += 1;
+ if (_.contains(["]", "}"], state.tokens.next.value))
+ bracketStack -= 1;
+ do {
+ pn = peek(i);
+ pn1 = peek(i + 1);
+ i = i + 1;
+ if (_.contains(["[", "{"], pn.value)) {
+ bracketStack += 1;
+ } else if (_.contains(["]", "}"], pn.value)) {
+ bracketStack -= 1;
+ }
+ if (pn.identifier && pn.value === "for" && bracketStack === 1) {
+ ret.isCompArray = true;
+ ret.notJson = true;
+ break;
+ }
+ if (_.contains(["}", "]"], pn.value) && pn1.value === "=") {
+ ret.isDestAssign = true;
+ ret.notJson = true;
+ break;
+ }
+ if (pn.value === ";") {
+ ret.isBlock = true;
+ ret.notJson = true;
+ }
+ } while (bracketStack > 0 && pn.id !== "(end)" && i < 15);
+ return ret;
+ };
+
+ // Check whether this function has been reached for a destructuring assign with undeclared values
+ function destructuringAssignOrJsonValue() {
+ // lookup for the assignment (esnext only)
+ // if it has semicolons, it is a block, so go parse it as a block
+ // or it's not a block, but there are assignments, check for undeclared variables
+
+ var block = lookupBlockType();
+ if (block.notJson) {
+ if (!state.option.inESNext() && block.isDestAssign) {
+ warning("W104", state.tokens.curr, "destructuring assignment");
+ }
+ statements();
+ // otherwise parse json value
+ } else {
+ state.option.laxbreak = true;
+ state.jsonMode = true;
+ jsonValue();
+ }
+ }
+
+ // array comprehension parsing function
+ // parses and defines the three states of the list comprehension in order
+ // to avoid defining global variables, but keeping them to the list comprehension scope
+ // only. The order of the states are as follows:
+ // * "use" which will be the returned iterative part of the list comprehension
+ // * "define" which will define the variables local to the list comprehension
+ // * "filter" which will help filter out values
+
+ var arrayComprehension = function () {
+ var CompArray = function () {
+ this.mode = "use";
+ this.variables = [];
+ };
+ var _carrays = [];
+ var _current;
+ function declare(v) {
+ var l = _current.variables.filter(function (elt) {
+ // if it has, change its undef state
+ if (elt.value === v) {
+ elt.undef = false;
+ return v;
+ }
+ }).length;
+ return l !== 0;
+ }
+ function use(v) {
+ var l = _current.variables.filter(function (elt) {
+ // and if it has been defined
+ if (elt.value === v && !elt.undef) {
+ if (elt.unused === true) {
+ elt.unused = false;
+ }
+ return v;
+ }
+ }).length;
+ // otherwise we warn about it
+ return (l === 0);
+ }
+ return {stack: function () {
+ _current = new CompArray();
+ _carrays.push(_current);
+ },
+ unstack: function () {
+ _current.variables.filter(function (v) {
+ if (v.unused)
+ warning("W098", v.token, v.value);
+ if (v.undef)
+ isundef(v.funct, "W117", v.token, v.value);
+ });
+ _carrays.splice(_carrays[_carrays.length - 1], 1);
+ _current = _carrays[_carrays.length - 1];
+ },
+ setState: function (s) {
+ if (_.contains(["use", "define", "filter"], s))
+ _current.mode = s;
+ },
+ check: function (v) {
+ // When we are in "use" state of the list comp, we enqueue that var
+ if (_current && _current.mode === "use") {
+ _current.variables.push({funct: funct,
+ token: state.tokens.curr,
+ value: v,
+ undef: true,
+ unused: false});
+ return true;
+ // When we are in "define" state of the list comp,
+ } else if (_current && _current.mode === "define") {
+ // check if the variable has been used previously
+ if (!declare(v)) {
+ _current.variables.push({funct: funct,
+ token: state.tokens.curr,
+ value: v,
+ undef: false,
+ unused: true});
+ }
+ return true;
+ // When we are in "filter" state,
+ } else if (_current && _current.mode === "filter") {
+ // we check whether current variable has been declared
+ if (use(v)) {
+ // if not we warn about it
+ isundef(funct, "W117", state.tokens.curr, v);
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+ };
+
+
+ // Parse JSON
+
+ function jsonValue() {
+
+ function jsonObject() {
+ var o = {}, t = state.tokens.next;
+ advance("{");
+ if (state.tokens.next.id !== "}") {
+ for (;;) {
+ if (state.tokens.next.id === "(end)") {
+ error("E026", state.tokens.next, t.line);
+ } else if (state.tokens.next.id === "}") {
+ warning("W094", state.tokens.curr);
+ break;
+ } else if (state.tokens.next.id === ",") {
+ error("E028", state.tokens.next);
+ } else if (state.tokens.next.id !== "(string)") {
+ warning("W095", state.tokens.next, state.tokens.next.value);
+ }
+ if (o[state.tokens.next.value] === true) {
+ warning("W075", state.tokens.next, state.tokens.next.value);
+ } else if ((state.tokens.next.value === "__proto__" &&
+ !state.option.proto) || (state.tokens.next.value === "__iterator__" &&
+ !state.option.iterator)) {
+ warning("W096", state.tokens.next, state.tokens.next.value);
+ } else {
+ o[state.tokens.next.value] = true;
+ }
+ advance();
+ advance(":");
+ jsonValue();
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ advance(",");
+ }
+ }
+ advance("}");
+ }
+
+ function jsonArray() {
+ var t = state.tokens.next;
+ advance("[");
+ if (state.tokens.next.id !== "]") {
+ for (;;) {
+ if (state.tokens.next.id === "(end)") {
+ error("E027", state.tokens.next, t.line);
+ } else if (state.tokens.next.id === "]") {
+ warning("W094", state.tokens.curr);
+ break;
+ } else if (state.tokens.next.id === ",") {
+ error("E028", state.tokens.next);
+ }
+ jsonValue();
+ if (state.tokens.next.id !== ",") {
+ break;
+ }
+ advance(",");
+ }
+ }
+ advance("]");
+ }
+
+ switch (state.tokens.next.id) {
+ case "{":
+ jsonObject();
+ break;
+ case "[":
+ jsonArray();
+ break;
+ case "true":
+ case "false":
+ case "null":
+ case "(number)":
+ case "(string)":
+ advance();
+ break;
+ case "-":
+ advance("-");
+ if (state.tokens.curr.character !== state.tokens.next.from) {
+ warning("W011", state.tokens.curr);
+ }
+ adjacent(state.tokens.curr, state.tokens.next);
+ advance("(number)");
+ break;
+ default:
+ error("E003", state.tokens.next);
+ }
+ }
+
+ var blockScope = function () {
+ var _current = {};
+ var _variables = [_current];
+
+ function _checkBlockLabels() {
+ for (var t in _current) {
+ if (_current[t]["(type)"] === "unused") {
+ if (state.option.unused) {
+ var tkn = _current[t]["(token)"];
+ var line = tkn.line;
+ var chr = tkn.character;
+ warningAt("W098", line, chr, t);
+ }
+ }
+ }
+ }
+
+ return {
+ stack: function () {
+ _current = {};
+ _variables.push(_current);
+ },
+
+ unstack: function () {
+ _checkBlockLabels();
+ _variables.splice(_variables.length - 1, 1);
+ _current = _.last(_variables);
+ },
+
+ getlabel: function (l) {
+ for (var i = _variables.length - 1 ; i >= 0; --i) {
+ if (_.has(_variables[i], l)) {
+ return _variables[i];
+ }
+ }
+ },
+
+ current: {
+ has: function (t) {
+ return _.has(_current, t);
+ },
+ add: function (t, type, tok) {
+ _current[t] = { "(type)" : type,
+ "(token)": tok };
+ }
+ }
+ };
+ };
+
+ // The actual JSHINT function itself.
+ var itself = function (s, o, g) {
+ var a, i, k, x;
+ var optionKeys;
+ var newOptionObj = {};
+ var newIgnoredObj = {};
+
+ state.reset();
+
+ if (o && o.scope) {
+ JSHINT.scope = o.scope;
+ } else {
+ JSHINT.errors = [];
+ JSHINT.undefs = [];
+ JSHINT.internals = [];
+ JSHINT.blacklist = {};
+ JSHINT.scope = "(main)";
+ }
+
+ predefined = Object.create(null);
+ combine(predefined, vars.ecmaIdentifiers);
+ combine(predefined, vars.reservedVars);
+
+ combine(predefined, g || {});
+
+ declared = Object.create(null);
+ exported = Object.create(null);
+
+ if (o) {
+ a = o.predef;
+ if (a) {
+ if (!Array.isArray(a) && typeof a === "object") {
+ a = Object.keys(a);
+ }
+
+ a.forEach(function (item) {
+ var slice, prop;
+
+ if (item[0] === "-") {
+ slice = item.slice(1);
+ JSHINT.blacklist[slice] = slice;
+ } else {
+ prop = Object.getOwnPropertyDescriptor(o.predef, item);
+ predefined[item] = prop ? prop.value : false;
+ }
+ });
+ }
+
+ optionKeys = Object.keys(o);
+ for (x = 0; x < optionKeys.length; x++) {
+ if (/^-W\d{3}$/g.test(optionKeys[x])) {
+ newIgnoredObj[optionKeys[x].slice(1)] = true;
+ } else {
+ newOptionObj[optionKeys[x]] = o[optionKeys[x]];
+
+ if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false)
+ newOptionObj["(explicitNewcap)"] = true;
+
+ if (optionKeys[x] === "indent")
+ newOptionObj["(explicitIndent)"] = o[optionKeys[x]] === false ? false : true;
+ }
+ }
+ }
+
+ state.option = newOptionObj;
+ state.ignored = newIgnoredObj;
+
+ state.option.indent = state.option.indent || 4;
+ state.option.maxerr = state.option.maxerr || 50;
+
+ indent = 1;
+ global = Object.create(predefined);
+ scope = global;
+ funct = {
+ "(global)": true,
+ "(name)": "(global)",
+ "(scope)": scope,
+ "(breakage)": 0,
+ "(loopage)": 0,
+ "(tokens)": {},
+ "(metrics)": createMetrics(state.tokens.next),
+ "(blockscope)": blockScope(),
+ "(comparray)": arrayComprehension()
+ };
+ functions = [funct];
+ urls = [];
+ stack = null;
+ member = {};
+ membersOnly = null;
+ implied = {};
+ inblock = false;
+ lookahead = [];
+ warnings = 0;
+ unuseds = [];
+
+ if (!isString(s) && !Array.isArray(s)) {
+ errorAt("E004", 0);
+ return false;
+ }
+
+ api = {
+ get isJSON() {
+ return state.jsonMode;
+ },
+
+ getOption: function (name) {
+ return state.option[name] || null;
+ },
+
+ getCache: function (name) {
+ return state.cache[name];
+ },
+
+ setCache: function (name, value) {
+ state.cache[name] = value;
+ },
+
+ warn: function (code, data) {
+ warningAt.apply(null, [ code, data.line, data.char ].concat(data.data));
+ },
+
+ on: function (names, listener) {
+ names.split(" ").forEach(function (name) {
+ emitter.on(name, listener);
+ }.bind(this));
+ }
+ };
+
+ emitter.removeAllListeners();
+ (extraModules || []).forEach(function (func) {
+ func(api);
+ });
+
+ state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"];
+
+ lex = new Lexer(s);
+
+ lex.on("warning", function (ev) {
+ warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data));
+ });
+
+ lex.on("error", function (ev) {
+ errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data));
+ });
+
+ lex.on("fatal", function (ev) {
+ quit("E041", ev.line, ev.from);
+ });
+
+ lex.on("Identifier", function (ev) {
+ emitter.emit("Identifier", ev);
+ });
+
+ lex.on("String", function (ev) {
+ emitter.emit("String", ev);
+ });
+
+ lex.on("Number", function (ev) {
+ emitter.emit("Number", ev);
+ });
+
+ lex.start();
+
+ // Check options
+ for (var name in o) {
+ if (_.has(o, name)) {
+ checkOption(name, state.tokens.curr);
+ }
+ }
+
+ assume();
+
+ // combine the passed globals after we've assumed all our options
+ combine(predefined, g || {});
+
+ //reset values
+ comma.first = true;
+
+ try {
+ advance();
+ switch (state.tokens.next.id) {
+ case "{":
+ case "[":
+ destructuringAssignOrJsonValue();
+ break;
+ default:
+ directives();
+
+ if (state.directive["use strict"]) {
+ if (!state.option.globalstrict && !state.option.node) {
+ warning("W097", state.tokens.prev);
+ }
+ }
+
+ statements();
+ }
+ advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined);
+ funct["(blockscope)"].unstack();
+
+ var markDefined = function (name, context) {
+ do {
+ if (typeof context[name] === "string") {
+ // JSHINT marks unused variables as 'unused' and
+ // unused function declaration as 'unction'. This
+ // code changes such instances back 'var' and
+ // 'closure' so that the code in JSHINT.data()
+ // doesn't think they're unused.
+
+ if (context[name] === "unused")
+ context[name] = "var";
+ else if (context[name] === "unction")
+ context[name] = "closure";
+
+ return true;
+ }
+
+ context = context["(context)"];
+ } while (context);
+
+ return false;
+ };
+
+ var clearImplied = function (name, line) {
+ if (!implied[name])
+ return;
+
+ var newImplied = [];
+ for (var i = 0; i < implied[name].length; i += 1) {
+ if (implied[name][i] !== line)
+ newImplied.push(implied[name][i]);
+ }
+
+ if (newImplied.length === 0)
+ delete implied[name];
+ else
+ implied[name] = newImplied;
+ };
+
+ var warnUnused = function (name, tkn, type, unused_opt) {
+ var line = tkn.line;
+ var chr = tkn.character;
+
+ if (unused_opt === undefined) {
+ unused_opt = state.option.unused;
+ }
+
+ if (unused_opt === true) {
+ unused_opt = "last-param";
+ }
+
+ var warnable_types = {
+ "vars": ["var"],
+ "last-param": ["var", "param"],
+ "strict": ["var", "param", "last-param"]
+ };
+
+ if (unused_opt) {
+ if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) {
+ warningAt("W098", line, chr, name);
+ }
+ }
+
+ unuseds.push({
+ name: name,
+ line: line,
+ character: chr
+ });
+ };
+
+ var checkUnused = function (func, key) {
+ var type = func[key];
+ var tkn = func["(tokens)"][key];
+
+ if (key.charAt(0) === "(")
+ return;
+
+ if (type !== "unused" && type !== "unction")
+ return;
+
+ // Params are checked separately from other variables.
+ if (func["(params)"] && func["(params)"].indexOf(key) !== -1)
+ return;
+
+ // Variable is in global scope and defined as exported.
+ if (func["(global)"] && _.has(exported, key)) {
+ return;
+ }
+
+ warnUnused(key, tkn, "var");
+ };
+
+ // Check queued 'x is not defined' instances to see if they're still undefined.
+ for (i = 0; i < JSHINT.undefs.length; i += 1) {
+ k = JSHINT.undefs[i].slice(0);
+
+ if (markDefined(k[2].value, k[0])) {
+ clearImplied(k[2].value, k[2].line);
+ } else if (state.option.undef) {
+ warning.apply(warning, k.slice(1));
+ }
+ }
+
+ functions.forEach(function (func) {
+ if (func["(unusedOption)"] === false) {
+ return;
+ }
+
+ for (var key in func) {
+ if (_.has(func, key)) {
+ checkUnused(func, key);
+ }
+ }
+
+ if (!func["(params)"])
+ return;
+
+ var params = func["(params)"].slice();
+ var param = params.pop();
+ var type, unused_opt;
+
+ while (param) {
+ type = func[param];
+ unused_opt = func["(unusedOption)"] || state.option.unused;
+ unused_opt = unused_opt === true ? "last-param" : unused_opt;
+
+ // 'undefined' is a special case for (function (window, undefined) { ... })();
+ // patterns.
+
+ if (param === "undefined")
+ return;
+
+ if (type === "unused" || type === "unction") {
+ warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]);
+ } else if (unused_opt === "last-param") {
+ return;
+ }
+
+ param = params.pop();
+ }
+ });
+
+ for (var key in declared) {
+ if (_.has(declared, key) && !_.has(global, key)) {
+ warnUnused(key, declared[key], "var");
+ }
+ }
+
+ } catch (err) {
+ if (err && err.name === "JSHintError") {
+ var nt = state.tokens.next || {};
+ JSHINT.errors.push({
+ scope : "(main)",
+ raw : err.raw,
+ reason : err.message,
+ line : err.line || nt.line,
+ character : err.character || nt.from
+ }, null);
+ } else {
+ throw err;
+ }
+ }
+
+ // Loop over the listed "internals", and check them as well.
+
+ if (JSHINT.scope === "(main)") {
+ o = o || {};
+
+ for (i = 0; i < JSHINT.internals.length; i += 1) {
+ k = JSHINT.internals[i];
+ o.scope = k.elem;
+ itself(k.value, o, g);
+ }
+ }
+
+ return JSHINT.errors.length === 0;
+ };
+
+ // Modules.
+ itself.addModule = function (func) {
+ extraModules.push(func);
+ };
+
+ itself.addModule(style.register);
+
+ // Data summary.
+ itself.data = function () {
+ var data = {
+ functions: [],
+ options: state.option
+ };
+ var implieds = [];
+ var members = [];
+ var fu, f, i, j, n, globals;
+
+ if (itself.errors.length) {
+ data.errors = itself.errors;
+ }
+
+ if (state.jsonMode) {
+ data.json = true;
+ }
+
+ for (n in implied) {
+ if (_.has(implied, n)) {
+ implieds.push({
+ name: n,
+ line: implied[n]
+ });
+ }
+ }
+
+ if (implieds.length > 0) {
+ data.implieds = implieds;
+ }
+
+ if (urls.length > 0) {
+ data.urls = urls;
+ }
+
+ globals = Object.keys(scope);
+ if (globals.length > 0) {
+ data.globals = globals;
+ }
+
+ for (i = 1; i < functions.length; i += 1) {
+ f = functions[i];
+ fu = {};
+
+ for (j = 0; j < functionicity.length; j += 1) {
+ fu[functionicity[j]] = [];
+ }
+
+ for (j = 0; j < functionicity.length; j += 1) {
+ if (fu[functionicity[j]].length === 0) {
+ delete fu[functionicity[j]];
+ }
+ }
+
+ fu.name = f["(name)"];
+ fu.param = f["(params)"];
+ fu.line = f["(line)"];
+ fu.character = f["(character)"];
+ fu.last = f["(last)"];
+ fu.lastcharacter = f["(lastcharacter)"];
+ data.functions.push(fu);
+ }
+
+ if (unuseds.length > 0) {
+ data.unused = unuseds;
+ }
+
+ members = [];
+ for (n in member) {
+ if (typeof member[n] === "number") {
+ data.member = member;
+ break;
+ }
+ }
+
+ return data;
+ };
+
+ itself.jshint = itself;
+
+ return itself;
+}());
+
+// Make JSHINT a Node module, if possible.
+if (typeof exports === "object" && exports) {
+ exports.JSHINT = JSHINT;
+}
+
+})()
+},{"events":2,"../shared/vars.js":3,"./lex.js":10,"./reg.js":6,"./state.js":4,"../shared/messages.js":12,"./style.js":5,"console-browserify":7,"underscore":11}],12:[function(require,module,exports){
+(function(){"use strict";
+
+var _ = require("underscore");
+
+var errors = {
+ // JSHint options
+ E001: "Bad option: '{a}'.",
+ E002: "Bad option value.",
+
+ // JSHint input
+ E003: "Expected a JSON value.",
+ E004: "Input is neither a string nor an array of strings.",
+ E005: "Input is empty.",
+ E006: "Unexpected early end of program.",
+
+ // Strict mode
+ E007: "Missing \"use strict\" statement.",
+ E008: "Strict violation.",
+ E009: "Option 'validthis' can't be used in a global scope.",
+ E010: "'with' is not allowed in strict mode.",
+
+ // Constants
+ E011: "const '{a}' has already been declared.",
+ E012: "const '{a}' is initialized to 'undefined'.",
+ E013: "Attempting to override '{a}' which is a constant.",
+
+ // Regular expressions
+ E014: "A regular expression literal can be confused with '/='.",
+ E015: "Unclosed regular expression.",
+ E016: "Invalid regular expression.",
+
+ // Tokens
+ E017: "Unclosed comment.",
+ E018: "Unbegun comment.",
+ E019: "Unmatched '{a}'.",
+ E020: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
+ E021: "Expected '{a}' and instead saw '{b}'.",
+ E022: "Line breaking error '{a}'.",
+ E023: "Missing '{a}'.",
+ E024: "Unexpected '{a}'.",
+ E025: "Missing ':' on a case clause.",
+ E026: "Missing '}' to match '{' from line {a}.",
+ E027: "Missing ']' to match '[' form line {a}.",
+ E028: "Illegal comma.",
+ E029: "Unclosed string.",
+
+ // Everything else
+ E030: "Expected an identifier and instead saw '{a}'.",
+ E031: "Bad assignment.", // FIXME: Rephrase
+ E032: "Expected a small integer or 'false' and instead saw '{a}'.",
+ E033: "Expected an operator and instead saw '{a}'.",
+ E034: "get/set are ES5 features.",
+ E035: "Missing property name.",
+ E036: "Expected to see a statement and instead saw a block.",
+ E037: "Constant {a} was not declared correctly.",
+ E038: "Variable {a} was not declared correctly.",
+ E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.",
+ E040: "Each value should have its own case label.",
+ E041: "Unrecoverable syntax error.",
+ E042: "Stopping.",
+ E043: "Too many errors.",
+ E044: "'{a}' is already defined and can't be redefined.",
+ E045: "Invalid for each loop.",
+ E046: "A yield statement shall be within a generator function (with syntax: `function*`)",
+ E047: "A generator function shall contain a yield statement.",
+ E048: "Let declaration not directly within block.",
+ E049: "A {a} cannot be named '{b}'."
+};
+
+var warnings = {
+ W001: "'hasOwnProperty' is a really bad name.",
+ W002: "Value of '{a}' may be overwritten in IE.",
+ W003: "'{a}' was used before it was defined.",
+ W004: "'{a}' is already defined.",
+ W005: "A dot following a number can be confused with a decimal point.",
+ W006: "Confusing minuses.",
+ W007: "Confusing pluses.",
+ W008: "A leading decimal point can be confused with a dot: '{a}'.",
+ W009: "The array literal notation [] is preferrable.",
+ W010: "The object literal notation {} is preferrable.",
+ W011: "Unexpected space after '{a}'.",
+ W012: "Unexpected space before '{a}'.",
+ W013: "Missing space after '{a}'.",
+ W014: "Bad line breaking before '{a}'.",
+ W015: "Expected '{a}' to have an indentation at {b} instead at {c}.",
+ W016: "Unexpected use of '{a}'.",
+ W017: "Bad operand.",
+ W018: "Confusing use of '{a}'.",
+ W019: "Use the isNaN function to compare with NaN.",
+ W020: "Read only.",
+ W021: "'{a}' is a function.",
+ W022: "Do not assign to the exception parameter.",
+ W023: "Expected an identifier in an assignment and instead saw a function invocation.",
+ W024: "Expected an identifier and instead saw '{a}' (a reserved word).",
+ W025: "Missing name in function declaration.",
+ W026: "Inner functions should be listed at the top of the outer function.",
+ W027: "Unreachable '{a}' after '{b}'.",
+ W028: "Label '{a}' on {b} statement.",
+ W030: "Expected an assignment or function call and instead saw an expression.",
+ W031: "Do not use 'new' for side effects.",
+ W032: "Unnecessary semicolon.",
+ W033: "Missing semicolon.",
+ W034: "Unnecessary directive \"{a}\".",
+ W035: "Empty block.",
+ W036: "Unexpected /*member '{a}'.",
+ W037: "'{a}' is a statement label.",
+ W038: "'{a}' used out of scope.",
+ W039: "'{a}' is not allowed.",
+ W040: "Possible strict violation.",
+ W041: "Use '{a}' to compare with '{b}'.",
+ W042: "Avoid EOL escaping.",
+ W043: "Bad escaping of EOL. Use option multistr if needed.",
+ W044: "Bad or unnecessary escaping.",
+ W045: "Bad number '{a}'.",
+ W046: "Don't use extra leading zeros '{a}'.",
+ W047: "A trailing decimal point can be confused with a dot: '{a}'.",
+ W048: "Unexpected control character in regular expression.",
+ W049: "Unexpected escaped character '{a}' in regular expression.",
+ W050: "JavaScript URL.",
+ W051: "Variables should not be deleted.",
+ W052: "Unexpected '{a}'.",
+ W053: "Do not use {a} as a constructor.",
+ W054: "The Function constructor is a form of eval.",
+ W055: "A constructor name should start with an uppercase letter.",
+ W056: "Bad constructor.",
+ W057: "Weird construction. Is 'new' unnecessary?",
+ W058: "Missing '()' invoking a constructor.",
+ W059: "Avoid arguments.{a}.",
+ W060: "document.write can be a form of eval.",
+ W061: "eval can be harmful.",
+ W062: "Wrap an immediate function invocation in parens " +
+ "to assist the reader in understanding that the expression " +
+ "is the result of a function, and not the function itself.",
+ W063: "Math is not a function.",
+ W064: "Missing 'new' prefix when invoking a constructor.",
+ W065: "Missing radix parameter.",
+ W066: "Implied eval. Consider passing a function instead of a string.",
+ W067: "Bad invocation.",
+ W068: "Wrapping non-IIFE function literals in parens is unnecessary.",
+ W069: "['{a}'] is better written in dot notation.",
+ W070: "Extra comma. (it breaks older versions of IE)",
+ W071: "This function has too many statements. ({a})",
+ W072: "This function has too many parameters. ({a})",
+ W073: "Blocks are nested too deeply. ({a})",
+ W074: "This function's cyclomatic complexity is too high. ({a})",
+ W075: "Duplicate key '{a}'.",
+ W076: "Unexpected parameter '{a}' in get {b} function.",
+ W077: "Expected a single parameter in set {a} function.",
+ W078: "Setter is defined without getter.",
+ W079: "Redefinition of '{a}'.",
+ W080: "It's not necessary to initialize '{a}' to 'undefined'.",
+ W081: "Too many var statements.",
+ W082: "Function declarations should not be placed in blocks. " +
+ "Use a function expression or move the statement to the top of " +
+ "the outer function.",
+ W083: "Don't make functions within a loop.",
+ W084: "Expected a conditional expression and instead saw an assignment.",
+ W085: "Don't use 'with'.",
+ W086: "Expected a 'break' statement before '{a}'.",
+ W087: "Forgotten 'debugger' statement?",
+ W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.",
+ W089: "The body of a for in should be wrapped in an if statement to filter " +
+ "unwanted properties from the prototype.",
+ W090: "'{a}' is not a statement label.",
+ W091: "'{a}' is out of scope.",
+ W092: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.",
+ W093: "Did you mean to return a conditional instead of an assignment?",
+ W094: "Unexpected comma.",
+ W095: "Expected a string and instead saw {a}.",
+ W096: "The '{a}' key may produce unexpected results.",
+ W097: "Use the function form of \"use strict\".",
+ W098: "'{a}' is defined but never used.",
+ W099: "Mixed spaces and tabs.",
+ W100: "This character may get silently deleted by one or more browsers.",
+ W101: "Line is too long.",
+ W102: "Trailing whitespace.",
+ W103: "The '{a}' property is deprecated.",
+ W104: "'{a}' is only available in JavaScript 1.7.",
+ W105: "Unexpected {a} in '{b}'.",
+ W106: "Identifier '{a}' is not in camel case.",
+ W107: "Script URL.",
+ W108: "Strings must use doublequote.",
+ W109: "Strings must use singlequote.",
+ W110: "Mixed double and single quotes.",
+ W112: "Unclosed string.",
+ W113: "Control character in string: {a}.",
+ W114: "Avoid {a}.",
+ W115: "Octal literals are not allowed in strict mode.",
+ W116: "Expected '{a}' and instead saw '{b}'.",
+ W117: "'{a}' is not defined.",
+ W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).",
+ W119: "'{a}' is only available in ES6 (use esnext option)."
+};
+
+var info = {
+ I001: "Comma warnings can be turned off with 'laxcomma'.",
+ I002: "Reserved words as properties can be used under the 'es5' option.",
+ I003: "ES5 option is now set per default"
+};
+
+exports.errors = {};
+exports.warnings = {};
+exports.info = {};
+
+_.each(errors, function (desc, code) {
+ exports.errors[code] = { code: code, desc: desc };
+});
+
+_.each(warnings, function (desc, code) {
+ exports.warnings[code] = { code: code, desc: desc };
+});
+
+_.each(info, function (desc, code) {
+ exports.info[code] = { code: code, desc: desc };
+});
+
+})()
+},{"underscore":11}],8:[function(require,module,exports){
+var events = require('events');
+
+exports.isArray = isArray;
+exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'};
+exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'};
+
+
+exports.print = function () {};
+exports.puts = function () {};
+exports.debug = function() {};
+
+exports.inspect = function(obj, showHidden, depth, colors) {
+ var seen = [];
+
+ var stylize = function(str, styleType) {
+ // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+ var styles =
+ { 'bold' : [1, 22],
+ 'italic' : [3, 23],
+ 'underline' : [4, 24],
+ 'inverse' : [7, 27],
+ 'white' : [37, 39],
+ 'grey' : [90, 39],
+ 'black' : [30, 39],
+ 'blue' : [34, 39],
+ 'cyan' : [36, 39],
+ 'green' : [32, 39],
+ 'magenta' : [35, 39],
+ 'red' : [31, 39],
+ 'yellow' : [33, 39] };
+
+ var style =
+ { 'special': 'cyan',
+ 'number': 'blue',
+ 'boolean': 'yellow',
+ 'undefined': 'grey',
+ 'null': 'bold',
+ 'string': 'green',
+ 'date': 'magenta',
+ // "name": intentionally not styling
+ 'regexp': 'red' }[styleType];
+
+ if (style) {
+ return '\033[' + styles[style][0] + 'm' + str +
+ '\033[' + styles[style][1] + 'm';
+ } else {
+ return str;
+ }
+ };
+ if (! colors) {
+ stylize = function(str, styleType) { return str; };
+ }
+
+ function format(value, recurseTimes) {
+ // Provide a hook for user-specified inspect functions.
+ // Check that value is an object with an inspect function on it
+ if (value && typeof value.inspect === 'function' &&
+ // Filter out the util module, it's inspect function is special
+ value !== exports &&
+ // Also filter out any prototype objects using the circular check.
+ !(value.constructor && value.constructor.prototype === value)) {
+ return value.inspect(recurseTimes);
+ }
+
+ // Primitive types cannot have properties
+ switch (typeof value) {
+ case 'undefined':
+ return stylize('undefined', 'undefined');
+
+ case 'string':
+ var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
+ .replace(/'/g, "\\'")
+ .replace(/\\"/g, '"') + '\'';
+ return stylize(simple, 'string');
+
+ case 'number':
+ return stylize('' + value, 'number');
+
+ case 'boolean':
+ return stylize('' + value, 'boolean');
+ }
+ // For some reason typeof null is "object", so special case here.
+ if (value === null) {
+ return stylize('null', 'null');
+ }
+
+ // Look up the keys of the object.
+ var visible_keys = Object_keys(value);
+ var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys;
+
+ // Functions without properties can be shortcutted.
+ if (typeof value === 'function' && keys.length === 0) {
+ if (isRegExp(value)) {
+ return stylize('' + value, 'regexp');
+ } else {
+ var name = value.name ? ': ' + value.name : '';
+ return stylize('[Function' + name + ']', 'special');
+ }
+ }
+
+ // Dates without properties can be shortcutted
+ if (isDate(value) && keys.length === 0) {
+ return stylize(value.toUTCString(), 'date');
+ }
+
+ var base, type, braces;
+ // Determine the object type
+ if (isArray(value)) {
+ type = 'Array';
+ braces = ['[', ']'];
+ } else {
+ type = 'Object';
+ braces = ['{', '}'];
+ }
+
+ // Make functions say that they are functions
+ if (typeof value === 'function') {
+ var n = value.name ? ': ' + value.name : '';
+ base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
+ } else {
+ base = '';
+ }
+
+ // Make dates with properties first say the date
+ if (isDate(value)) {
+ base = ' ' + value.toUTCString();
+ }
+
+ if (keys.length === 0) {
+ return braces[0] + base + braces[1];
+ }
+
+ if (recurseTimes < 0) {
+ if (isRegExp(value)) {
+ return stylize('' + value, 'regexp');
+ } else {
+ return stylize('[Object]', 'special');
+ }
+ }
+
+ seen.push(value);
+
+ var output = keys.map(function(key) {
+ var name, str;
+ if (value.__lookupGetter__) {
+ if (value.__lookupGetter__(key)) {
+ if (value.__lookupSetter__(key)) {
+ str = stylize('[Getter/Setter]', 'special');
+ } else {
+ str = stylize('[Getter]', 'special');
+ }
+ } else {
+ if (value.__lookupSetter__(key)) {
+ str = stylize('[Setter]', 'special');
+ }
+ }
+ }
+ if (visible_keys.indexOf(key) < 0) {
+ name = '[' + key + ']';
+ }
+ if (!str) {
+ if (seen.indexOf(value[key]) < 0) {
+ if (recurseTimes === null) {
+ str = format(value[key]);
+ } else {
+ str = format(value[key], recurseTimes - 1);
+ }
+ if (str.indexOf('\n') > -1) {
+ if (isArray(value)) {
+ str = str.split('\n').map(function(line) {
+ return ' ' + line;
+ }).join('\n').substr(2);
+ } else {
+ str = '\n' + str.split('\n').map(function(line) {
+ return ' ' + line;
+ }).join('\n');
+ }
+ }
+ } else {
+ str = stylize('[Circular]', 'special');
+ }
+ }
+ if (typeof name === 'undefined') {
+ if (type === 'Array' && key.match(/^\d+$/)) {
+ return str;
+ }
+ name = JSON.stringify('' + key);
+ if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
+ name = name.substr(1, name.length - 2);
+ name = stylize(name, 'name');
+ } else {
+ name = name.replace(/'/g, "\\'")
+ .replace(/\\"/g, '"')
+ .replace(/(^"|"$)/g, "'");
+ name = stylize(name, 'string');
+ }
+ }
+
+ return name + ': ' + str;
+ });
+
+ seen.pop();
+
+ var numLinesEst = 0;
+ var length = output.reduce(function(prev, cur) {
+ numLinesEst++;
+ if (cur.indexOf('\n') >= 0) numLinesEst++;
+ return prev + cur.length + 1;
+ }, 0);
+
+ if (length > 50) {
+ output = braces[0] +
+ (base === '' ? '' : base + '\n ') +
+ ' ' +
+ output.join(',\n ') +
+ ' ' +
+ braces[1];
+
+ } else {
+ output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+ }
+
+ return output;
+ }
+ return format(obj, (typeof depth === 'undefined' ? 2 : depth));
+};
+
+
+function isArray(ar) {
+ return ar instanceof Array ||
+ Array.isArray(ar) ||
+ (ar && ar !== Object.prototype && isArray(ar.__proto__));
+}
+
+
+function isRegExp(re) {
+ return re instanceof RegExp ||
+ (typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]');
+}
+
+
+function isDate(d) {
+ if (d instanceof Date) return true;
+ if (typeof d !== 'object') return false;
+ var properties = Date.prototype && Object_getOwnPropertyNames(Date.prototype);
+ var proto = d.__proto__ && Object_getOwnPropertyNames(d.__proto__);
+ return JSON.stringify(proto) === JSON.stringify(properties);
+}
+
+function pad(n) {
+ return n < 10 ? '0' + n.toString(10) : n.toString(10);
+}
+
+var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+ 'Oct', 'Nov', 'Dec'];
+
+// 26 Feb 16:19:34
+function timestamp() {
+ var d = new Date();
+ var time = [pad(d.getHours()),
+ pad(d.getMinutes()),
+ pad(d.getSeconds())].join(':');
+ return [d.getDate(), months[d.getMonth()], time].join(' ');
+}
+
+exports.log = function (msg) {};
+
+exports.pump = null;
+
+var Object_keys = Object.keys || function (obj) {
+ var res = [];
+ for (var key in obj) res.push(key);
+ return res;
+};
+
+var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) {
+ var res = [];
+ for (var key in obj) {
+ if (Object.hasOwnProperty.call(obj, key)) res.push(key);
+ }
+ return res;
+};
+
+var Object_create = Object.create || function (prototype, properties) {
+ // from es5-shim
+ var object;
+ if (prototype === null) {
+ object = { '__proto__' : null };
+ }
+ else {
+ if (typeof prototype !== 'object') {
+ throw new TypeError(
+ 'typeof prototype[' + (typeof prototype) + '] != \'object\''
+ );
+ }
+ var Type = function () {};
+ Type.prototype = prototype;
+ object = new Type();
+ object.__proto__ = prototype;
+ }
+ if (typeof properties !== 'undefined' && Object.defineProperties) {
+ Object.defineProperties(object, properties);
+ }
+ return object;
+};
+
+exports.inherits = function(ctor, superCtor) {
+ ctor.super_ = superCtor;
+ ctor.prototype = Object_create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+};
+
+var formatRegExp = /%[sdj%]/g;
+exports.format = function(f) {
+ if (typeof f !== 'string') {
+ var objects = [];
+ for (var i = 0; i < arguments.length; i++) {
+ objects.push(exports.inspect(arguments[i]));
+ }
+ return objects.join(' ');
+ }
+
+ var i = 1;
+ var args = arguments;
+ var len = args.length;
+ var str = String(f).replace(formatRegExp, function(x) {
+ if (x === '%%') return '%';
+ if (i >= len) return x;
+ switch (x) {
+ case '%s': return String(args[i++]);
+ case '%d': return Number(args[i++]);
+ case '%j': return JSON.stringify(args[i++]);
+ default:
+ return x;
+ }
+ });
+ for(var x = args[i]; i < len; x = args[++i]){
+ if (x === null || typeof x !== 'object') {
+ str += ' ' + x;
+ } else {
+ str += ' ' + exports.inspect(x);
+ }
+ }
+ return str;
+};
+
+},{"events":2}],9:[function(require,module,exports){
+(function(){// UTILITY
+var util = require('util');
+var Buffer = require("buffer").Buffer;
+var pSlice = Array.prototype.slice;
+
+function objectKeys(object) {
+ if (Object.keys) return Object.keys(object);
+ var result = [];
+ for (var name in object) {
+ if (Object.prototype.hasOwnProperty.call(object, name)) {
+ result.push(name);
+ }
+ }
+ return result;
+}
+
+// 1. The assert module provides functions that throw
+// AssertionError's when particular conditions are not met. The
+// assert module must conform to the following interface.
+
+var assert = module.exports = ok;
+
+// 2. The AssertionError is defined in assert.
+// new assert.AssertionError({ message: message,
+// actual: actual,
+// expected: expected })
+
+assert.AssertionError = function AssertionError(options) {
+ this.name = 'AssertionError';
+ this.message = options.message;
+ this.actual = options.actual;
+ this.expected = options.expected;
+ this.operator = options.operator;
+ var stackStartFunction = options.stackStartFunction || fail;
+
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, stackStartFunction);
+ }
+};
+util.inherits(assert.AssertionError, Error);
+
+function replacer(key, value) {
+ if (value === undefined) {
+ return '' + value;
+ }
+ if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) {
+ return value.toString();
+ }
+ if (typeof value === 'function' || value instanceof RegExp) {
+ return value.toString();
+ }
+ return value;
+}
+
+function truncate(s, n) {
+ if (typeof s == 'string') {
+ return s.length < n ? s : s.slice(0, n);
+ } else {
+ return s;
+ }
+}
+
+assert.AssertionError.prototype.toString = function() {
+ if (this.message) {
+ return [this.name + ':', this.message].join(' ');
+ } else {
+ return [
+ this.name + ':',
+ truncate(JSON.stringify(this.actual, replacer), 128),
+ this.operator,
+ truncate(JSON.stringify(this.expected, replacer), 128)
+ ].join(' ');
+ }
+};
+
+// assert.AssertionError instanceof Error
+
+assert.AssertionError.__proto__ = Error.prototype;
+
+// At present only the three keys mentioned above are used and
+// understood by the spec. Implementations or sub modules can pass
+// other keys to the AssertionError's constructor - they will be
+// ignored.
+
+// 3. All of the following functions must throw an AssertionError
+// when a corresponding condition is not met, with a message that
+// may be undefined if not provided. All assertion methods provide
+// both the actual and expected values to the assertion error for
+// display purposes.
+
+function fail(actual, expected, message, operator, stackStartFunction) {
+ throw new assert.AssertionError({
+ message: message,
+ actual: actual,
+ expected: expected,
+ operator: operator,
+ stackStartFunction: stackStartFunction
+ });
+}
+
+// EXTENSION! allows for well behaved errors defined elsewhere.
+assert.fail = fail;
+
+// 4. Pure assertion tests whether a value is truthy, as determined
+// by !!guard.
+// assert.ok(guard, message_opt);
+// This statement is equivalent to assert.equal(true, guard,
+// message_opt);. To test strictly for the value true, use
+// assert.strictEqual(true, guard, message_opt);.
+
+function ok(value, message) {
+ if (!!!value) fail(value, true, message, '==', assert.ok);
+}
+assert.ok = ok;
+
+// 5. The equality assertion tests shallow, coercive equality with
+// ==.
+// assert.equal(actual, expected, message_opt);
+
+assert.equal = function equal(actual, expected, message) {
+ if (actual != expected) fail(actual, expected, message, '==', assert.equal);
+};
+
+// 6. The non-equality assertion tests for whether two objects are not equal
+// with != assert.notEqual(actual, expected, message_opt);
+
+assert.notEqual = function notEqual(actual, expected, message) {
+ if (actual == expected) {
+ fail(actual, expected, message, '!=', assert.notEqual);
+ }
+};
+
+// 7. The equivalence assertion tests a deep equality relation.
+// assert.deepEqual(actual, expected, message_opt);
+
+assert.deepEqual = function deepEqual(actual, expected, message) {
+ if (!_deepEqual(actual, expected)) {
+ fail(actual, expected, message, 'deepEqual', assert.deepEqual);
+ }
+};
+
+function _deepEqual(actual, expected) {
+ // 7.1. All identical values are equivalent, as determined by ===.
+ if (actual === expected) {
+ return true;
+
+ } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
+ if (actual.length != expected.length) return false;
+
+ for (var i = 0; i < actual.length; i++) {
+ if (actual[i] !== expected[i]) return false;
+ }
+
+ return true;
+
+ // 7.2. If the expected value is a Date object, the actual value is
+ // equivalent if it is also a Date object that refers to the same time.
+ } else if (actual instanceof Date && expected instanceof Date) {
+ return actual.getTime() === expected.getTime();
+
+ // 7.3. Other pairs that do not both pass typeof value == 'object',
+ // equivalence is determined by ==.
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
+ return actual == expected;
+
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
+ // determined by having the same number of owned properties (as verified
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
+ // (although not necessarily the same order), equivalent values for every
+ // corresponding key, and an identical 'prototype' property. Note: this
+ // accounts for both named and indexed properties on Arrays.
+ } else {
+ return objEquiv(actual, expected);
+ }
+}
+
+function isUndefinedOrNull(value) {
+ return value === null || value === undefined;
+}
+
+function isArguments(object) {
+ return Object.prototype.toString.call(object) == '[object Arguments]';
+}
+
+function objEquiv(a, b) {
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
+ return false;
+ // an identical 'prototype' property.
+ if (a.prototype !== b.prototype) return false;
+ //~~~I've managed to break Object.keys through screwy arguments passing.
+ // Converting to array solves the problem.
+ if (isArguments(a)) {
+ if (!isArguments(b)) {
+ return false;
+ }
+ a = pSlice.call(a);
+ b = pSlice.call(b);
+ return _deepEqual(a, b);
+ }
+ try {
+ var ka = objectKeys(a),
+ kb = objectKeys(b),
+ key, i;
+ } catch (e) {//happens when one is a string literal and the other isn't
+ return false;
+ }
+ // having the same number of owned properties (keys incorporates
+ // hasOwnProperty)
+ if (ka.length != kb.length)
+ return false;
+ //the same set of keys (although not necessarily the same order),
+ ka.sort();
+ kb.sort();
+ //~~~cheap key test
+ for (i = ka.length - 1; i >= 0; i--) {
+ if (ka[i] != kb[i])
+ return false;
+ }
+ //equivalent values for every corresponding key, and
+ //~~~possibly expensive deep test
+ for (i = ka.length - 1; i >= 0; i--) {
+ key = ka[i];
+ if (!_deepEqual(a[key], b[key])) return false;
+ }
+ return true;
+}
+
+// 8. The non-equivalence assertion tests for any deep inequality.
+// assert.notDeepEqual(actual, expected, message_opt);
+
+assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
+ if (_deepEqual(actual, expected)) {
+ fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
+ }
+};
+
+// 9. The strict equality assertion tests strict equality, as determined by ===.
+// assert.strictEqual(actual, expected, message_opt);
+
+assert.strictEqual = function strictEqual(actual, expected, message) {
+ if (actual !== expected) {
+ fail(actual, expected, message, '===', assert.strictEqual);
+ }
+};
+
+// 10. The strict non-equality assertion tests for strict inequality, as
+// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
+
+assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
+ if (actual === expected) {
+ fail(actual, expected, message, '!==', assert.notStrictEqual);
+ }
+};
+
+function expectedException(actual, expected) {
+ if (!actual || !expected) {
+ return false;
+ }
+
+ if (expected instanceof RegExp) {
+ return expected.test(actual);
+ } else if (actual instanceof expected) {
+ return true;
+ } else if (expected.call({}, actual) === true) {
+ return true;
+ }
+
+ return false;
+}
+
+function _throws(shouldThrow, block, expected, message) {
+ var actual;
+
+ if (typeof expected === 'string') {
+ message = expected;
+ expected = null;
+ }
+
+ try {
+ block();
+ } catch (e) {
+ actual = e;
+ }
+
+ message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
+ (message ? ' ' + message : '.');
+
+ if (shouldThrow && !actual) {
+ fail('Missing expected exception' + message);
+ }
+
+ if (!shouldThrow && expectedException(actual, expected)) {
+ fail('Got unwanted exception' + message);
+ }
+
+ if ((shouldThrow && actual && expected &&
+ !expectedException(actual, expected)) || (!shouldThrow && actual)) {
+ throw actual;
+ }
+}
+
+// 11. Expected to throw an error:
+// assert.throws(block, Error_opt, message_opt);
+
+assert.throws = function(block, /*optional*/error, /*optional*/message) {
+ _throws.apply(this, [true].concat(pSlice.call(arguments)));
+};
+
+// EXTENSION! This is annoying to write outside this module.
+assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
+ _throws.apply(this, [false].concat(pSlice.call(arguments)));
+};
+
+assert.ifError = function(err) { if (err) {throw err;}};
+
+})()
+},{"util":8,"buffer":13}],11:[function(require,module,exports){
+(function(){// Underscore.js 1.4.4
+// http://underscorejs.org
+// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore may be freely distributed under the MIT license.
+
+(function() {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var root = this;
+
+ // Save the previous value of the `_` variable.
+ var previousUnderscore = root._;
+
+ // Establish the object that gets returned to break out of a loop iteration.
+ var breaker = {};
+
+ // Save bytes in the minified (but not gzipped) version:
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+ // Create quick reference variables for speed access to core prototypes.
+ var push = ArrayProto.push,
+ slice = ArrayProto.slice,
+ concat = ArrayProto.concat,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty;
+
+ // All **ECMAScript 5** native function implementations that we hope to use
+ // are declared here.
+ var
+ nativeForEach = ArrayProto.forEach,
+ nativeMap = ArrayProto.map,
+ nativeReduce = ArrayProto.reduce,
+ nativeReduceRight = ArrayProto.reduceRight,
+ nativeFilter = ArrayProto.filter,
+ nativeEvery = ArrayProto.every,
+ nativeSome = ArrayProto.some,
+ nativeIndexOf = ArrayProto.indexOf,
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys,
+ nativeBind = FuncProto.bind;
+
+ // Create a safe reference to the Underscore object for use below.
+ var _ = function(obj) {
+ if (obj instanceof _) return obj;
+ if (!(this instanceof _)) return new _(obj);
+ this._wrapped = obj;
+ };
+
+ // Export the Underscore object for **Node.js**, with
+ // backwards-compatibility for the old `require()` API. If we're in
+ // the browser, add `_` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode.
+ if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
+ }
+ exports._ = _;
+ } else {
+ root._ = _;
+ }
+
+ // Current version.
+ _.VERSION = '1.4.4';
+
+ // Collection Functions
+ // --------------------
+
+ // The cornerstone, an `each` implementation, aka `forEach`.
+ // Handles objects with the built-in `forEach`, arrays, and raw objects.
+ // Delegates to **ECMAScript 5**'s native `forEach` if available.
+ var each = _.each = _.forEach = function(obj, iterator, context) {
+ if (obj == null) return;
+ if (nativeForEach && obj.forEach === nativeForEach) {
+ obj.forEach(iterator, context);
+ } else if (obj.length === +obj.length) {
+ for (var i = 0, l = obj.length; i < l; i++) {
+ if (iterator.call(context, obj[i], i, obj) === breaker) return;
+ }
+ } else {
+ for (var key in obj) {
+ if (_.has(obj, key)) {
+ if (iterator.call(context, obj[key], key, obj) === breaker) return;
+ }
+ }
+ }
+ };
+
+ // Return the results of applying the iterator to each element.
+ // Delegates to **ECMAScript 5**'s native `map` if available.
+ _.map = _.collect = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+ each(obj, function(value, index, list) {
+ results[results.length] = iterator.call(context, value, index, list);
+ });
+ return results;
+ };
+
+ var reduceError = 'Reduce of empty array with no initial value';
+
+ // **Reduce** builds up a single result from a list of values, aka `inject`,
+ // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+ _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduce && obj.reduce === nativeReduce) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+ }
+ each(obj, function(value, index, list) {
+ if (!initial) {
+ memo = value;
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, value, index, list);
+ }
+ });
+ if (!initial) throw new TypeError(reduceError);
+ return memo;
+ };
+
+ // The right-associative version of reduce, also known as `foldr`.
+ // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+ _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ }
+ var length = obj.length;
+ if (length !== +length) {
+ var keys = _.keys(obj);
+ length = keys.length;
+ }
+ each(obj, function(value, index, list) {
+ index = keys ? keys[--length] : --length;
+ if (!initial) {
+ memo = obj[index];
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, obj[index], index, list);
+ }
+ });
+ if (!initial) throw new TypeError(reduceError);
+ return memo;
+ };
+
+ // Return the first value which passes a truth test. Aliased as `detect`.
+ _.find = _.detect = function(obj, iterator, context) {
+ var result;
+ any(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) {
+ result = value;
+ return true;
+ }
+ });
+ return result;
+ };
+
+ // Return all the elements that pass a truth test.
+ // Delegates to **ECMAScript 5**'s native `filter` if available.
+ // Aliased as `select`.
+ _.filter = _.select = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+ each(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ };
+
+ // Return all the elements for which a truth test fails.
+ _.reject = function(obj, iterator, context) {
+ return _.filter(obj, function(value, index, list) {
+ return !iterator.call(context, value, index, list);
+ }, context);
+ };
+
+ // Determine whether all of the elements match a truth test.
+ // Delegates to **ECMAScript 5**'s native `every` if available.
+ // Aliased as `all`.
+ _.every = _.all = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
+ var result = true;
+ if (obj == null) return result;
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+ each(obj, function(value, index, list) {
+ if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+ });
+ return !!result;
+ };
+
+ // Determine if at least one element in the object matches a truth test.
+ // Delegates to **ECMAScript 5**'s native `some` if available.
+ // Aliased as `any`.
+ var any = _.some = _.any = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
+ var result = false;
+ if (obj == null) return result;
+ if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+ each(obj, function(value, index, list) {
+ if (result || (result = iterator.call(context, value, index, list))) return breaker;
+ });
+ return !!result;
+ };
+
+ // Determine if the array or object contains a given value (using `===`).
+ // Aliased as `include`.
+ _.contains = _.include = function(obj, target) {
+ if (obj == null) return false;
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ return any(obj, function(value) {
+ return value === target;
+ });
+ };
+
+ // Invoke a method (with arguments) on every item in a collection.
+ _.invoke = function(obj, method) {
+ var args = slice.call(arguments, 2);
+ var isFunc = _.isFunction(method);
+ return _.map(obj, function(value) {
+ return (isFunc ? method : value[method]).apply(value, args);
+ });
+ };
+
+ // Convenience version of a common use case of `map`: fetching a property.
+ _.pluck = function(obj, key) {
+ return _.map(obj, function(value){ return value[key]; });
+ };
+
+ // Convenience version of a common use case of `filter`: selecting only objects
+ // containing specific `key:value` pairs.
+ _.where = function(obj, attrs, first) {
+ if (_.isEmpty(attrs)) return first ? null : [];
+ return _[first ? 'find' : 'filter'](obj, function(value) {
+ for (var key in attrs) {
+ if (attrs[key] !== value[key]) return false;
+ }
+ return true;
+ });
+ };
+
+ // Convenience version of a common use case of `find`: getting the first object
+ // containing specific `key:value` pairs.
+ _.findWhere = function(obj, attrs) {
+ return _.where(obj, attrs, true);
+ };
+
+ // Return the maximum element or (element-based computation).
+ // Can't optimize arrays of integers longer than 65,535 elements.
+ // See: https://bugs.webkit.org/show_bug.cgi?id=80797
+ _.max = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.max.apply(Math, obj);
+ }
+ if (!iterator && _.isEmpty(obj)) return -Infinity;
+ var result = {computed : -Infinity, value: -Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed >= result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Return the minimum element (or element-based computation).
+ _.min = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.min.apply(Math, obj);
+ }
+ if (!iterator && _.isEmpty(obj)) return Infinity;
+ var result = {computed : Infinity, value: Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed < result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Shuffle an array.
+ _.shuffle = function(obj) {
+ var rand;
+ var index = 0;
+ var shuffled = [];
+ each(obj, function(value) {
+ rand = _.random(index++);
+ shuffled[index - 1] = shuffled[rand];
+ shuffled[rand] = value;
+ });
+ return shuffled;
+ };
+
+ // An internal function to generate lookup iterators.
+ var lookupIterator = function(value) {
+ return _.isFunction(value) ? value : function(obj){ return obj[value]; };
+ };
+
+ // Sort the object's values by a criterion produced by an iterator.
+ _.sortBy = function(obj, value, context) {
+ var iterator = lookupIterator(value);
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value : value,
+ index : index,
+ criteria : iterator.call(context, value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria;
+ var b = right.criteria;
+ if (a !== b) {
+ if (a > b || a === void 0) return 1;
+ if (a < b || b === void 0) return -1;
+ }
+ return left.index < right.index ? -1 : 1;
+ }), 'value');
+ };
+
+ // An internal function used for aggregate "group by" operations.
+ var group = function(obj, value, context, behavior) {
+ var result = {};
+ var iterator = lookupIterator(value || _.identity);
+ each(obj, function(value, index) {
+ var key = iterator.call(context, value, index, obj);
+ behavior(result, key, value);
+ });
+ return result;
+ };
+
+ // Groups the object's values by a criterion. Pass either a string attribute
+ // to group by, or a function that returns the criterion.
+ _.groupBy = function(obj, value, context) {
+ return group(obj, value, context, function(result, key, value) {
+ (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
+ });
+ };
+
+ // Counts instances of an object that group by a certain criterion. Pass
+ // either a string attribute to count by, or a function that returns the
+ // criterion.
+ _.countBy = function(obj, value, context) {
+ return group(obj, value, context, function(result, key) {
+ if (!_.has(result, key)) result[key] = 0;
+ result[key]++;
+ });
+ };
+
+ // Use a comparator function to figure out the smallest index at which
+ // an object should be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iterator, context) {
+ iterator = iterator == null ? _.identity : lookupIterator(iterator);
+ var value = iterator.call(context, obj);
+ var low = 0, high = array.length;
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
+ }
+ return low;
+ };
+
+ // Safely convert anything iterable into a real, live array.
+ _.toArray = function(obj) {
+ if (!obj) return [];
+ if (_.isArray(obj)) return slice.call(obj);
+ if (obj.length === +obj.length) return _.map(obj, _.identity);
+ return _.values(obj);
+ };
+
+ // Return the number of elements in an object.
+ _.size = function(obj) {
+ if (obj == null) return 0;
+ return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
+ };
+
+ // Array Functions
+ // ---------------
+
+ // Get the first element of an array. Passing **n** will return the first N
+ // values in the array. Aliased as `head` and `take`. The **guard** check
+ // allows it to work with `_.map`.
+ _.first = _.head = _.take = function(array, n, guard) {
+ if (array == null) return void 0;
+ return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+ };
+
+ // Returns everything but the last entry of the array. Especially useful on
+ // the arguments object. Passing **n** will return all the values in
+ // the array, excluding the last N. The **guard** check allows it to work with
+ // `_.map`.
+ _.initial = function(array, n, guard) {
+ return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+ };
+
+ // Get the last element of an array. Passing **n** will return the last N
+ // values in the array. The **guard** check allows it to work with `_.map`.
+ _.last = function(array, n, guard) {
+ if (array == null) return void 0;
+ if ((n != null) && !guard) {
+ return slice.call(array, Math.max(array.length - n, 0));
+ } else {
+ return array[array.length - 1];
+ }
+ };
+
+ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+ // Especially useful on the arguments object. Passing an **n** will return
+ // the rest N values in the array. The **guard**
+ // check allows it to work with `_.map`.
+ _.rest = _.tail = _.drop = function(array, n, guard) {
+ return slice.call(array, (n == null) || guard ? 1 : n);
+ };
+
+ // Trim out all falsy values from an array.
+ _.compact = function(array) {
+ return _.filter(array, _.identity);
+ };
+
+ // Internal implementation of a recursive `flatten` function.
+ var flatten = function(input, shallow, output) {
+ each(input, function(value) {
+ if (_.isArray(value)) {
+ shallow ? push.apply(output, value) : flatten(value, shallow, output);
+ } else {
+ output.push(value);
+ }
+ });
+ return output;
+ };
+
+ // Return a completely flattened version of an array.
+ _.flatten = function(array, shallow) {
+ return flatten(array, shallow, []);
+ };
+
+ // Return a version of the array that does not contain the specified value(s).
+ _.without = function(array) {
+ return _.difference(array, slice.call(arguments, 1));
+ };
+
+ // Produce a duplicate-free version of the array. If the array has already
+ // been sorted, you have the option of using a faster algorithm.
+ // Aliased as `unique`.
+ _.uniq = _.unique = function(array, isSorted, iterator, context) {
+ if (_.isFunction(isSorted)) {
+ context = iterator;
+ iterator = isSorted;
+ isSorted = false;
+ }
+ var initial = iterator ? _.map(array, iterator, context) : array;
+ var results = [];
+ var seen = [];
+ each(initial, function(value, index) {
+ if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
+ seen.push(value);
+ results.push(array[index]);
+ }
+ });
+ return results;
+ };
+
+ // Produce an array that contains the union: each distinct element from all of
+ // the passed-in arrays.
+ _.union = function() {
+ return _.uniq(concat.apply(ArrayProto, arguments));
+ };
+
+ // Produce an array that contains every item shared between all the
+ // passed-in arrays.
+ _.intersection = function(array) {
+ var rest = slice.call(arguments, 1);
+ return _.filter(_.uniq(array), function(item) {
+ return _.every(rest, function(other) {
+ return _.indexOf(other, item) >= 0;
+ });
+ });
+ };
+
+ // Take the difference between one array and a number of other arrays.
+ // Only the elements present in just the first array will remain.
+ _.difference = function(array) {
+ var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
+ return _.filter(array, function(value){ return !_.contains(rest, value); });
+ };
+
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ var args = slice.call(arguments);
+ var length = _.max(_.pluck(args, 'length'));
+ var results = new Array(length);
+ for (var i = 0; i < length; i++) {
+ results[i] = _.pluck(args, "" + i);
+ }
+ return results;
+ };
+
+ // Converts lists into objects. Pass either a single array of `[key, value]`
+ // pairs, or two parallel arrays of the same length -- one of keys, and one of
+ // the corresponding values.
+ _.object = function(list, values) {
+ if (list == null) return {};
+ var result = {};
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (values) {
+ result[list[i]] = values[i];
+ } else {
+ result[list[i][0]] = list[i][1];
+ }
+ }
+ return result;
+ };
+
+ // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+ // we need this function. Return the position of the first occurrence of an
+ // item in an array, or -1 if the item is not included in the array.
+ // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+ // If the array is large and already in sort order, pass `true`
+ // for **isSorted** to use binary search.
+ _.indexOf = function(array, item, isSorted) {
+ if (array == null) return -1;
+ var i = 0, l = array.length;
+ if (isSorted) {
+ if (typeof isSorted == 'number') {
+ i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
+ } else {
+ i = _.sortedIndex(array, item);
+ return array[i] === item ? i : -1;
+ }
+ }
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
+ for (; i < l; i++) if (array[i] === item) return i;
+ return -1;
+ };
+
+ // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+ _.lastIndexOf = function(array, item, from) {
+ if (array == null) return -1;
+ var hasIndex = from != null;
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
+ return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
+ }
+ var i = (hasIndex ? from : array.length);
+ while (i--) if (array[i] === item) return i;
+ return -1;
+ };
+
+ // Generate an integer Array containing an arithmetic progression. A port of
+ // the native Python `range()` function. See
+ // [the Python documentation](http://docs.python.org/library/functions.html#range).
+ _.range = function(start, stop, step) {
+ if (arguments.length <= 1) {
+ stop = start || 0;
+ start = 0;
+ }
+ step = arguments[2] || 1;
+
+ var len = Math.max(Math.ceil((stop - start) / step), 0);
+ var idx = 0;
+ var range = new Array(len);
+
+ while(idx < len) {
+ range[idx++] = start;
+ start += step;
+ }
+
+ return range;
+ };
+
+ // Function (ahem) Functions
+ // ------------------
+
+ // Create a function bound to a given object (assigning `this`, and arguments,
+ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
+ // available.
+ _.bind = function(func, context) {
+ if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ var args = slice.call(arguments, 2);
+ return function() {
+ return func.apply(context, args.concat(slice.call(arguments)));
+ };
+ };
+
+ // Partially apply a function by creating a version that has had some of its
+ // arguments pre-filled, without changing its dynamic `this` context.
+ _.partial = function(func) {
+ var args = slice.call(arguments, 1);
+ return function() {
+ return func.apply(this, args.concat(slice.call(arguments)));
+ };
+ };
+
+ // Bind all of an object's methods to that object. Useful for ensuring that
+ // all callbacks defined on an object belong to it.
+ _.bindAll = function(obj) {
+ var funcs = slice.call(arguments, 1);
+ if (funcs.length === 0) funcs = _.functions(obj);
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ return obj;
+ };
+
+ // Memoize an expensive function by storing its results.
+ _.memoize = function(func, hasher) {
+ var memo = {};
+ hasher || (hasher = _.identity);
+ return function() {
+ var key = hasher.apply(this, arguments);
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+ };
+ };
+
+ // Delays a function for the given number of milliseconds, and then calls
+ // it with the arguments supplied.
+ _.delay = function(func, wait) {
+ var args = slice.call(arguments, 2);
+ return setTimeout(function(){ return func.apply(null, args); }, wait);
+ };
+
+ // Defers a function, scheduling it to run after the current call stack has
+ // cleared.
+ _.defer = function(func) {
+ return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+ };
+
+ // Returns a function, that, when invoked, will only be triggered at most once
+ // during a given window of time.
+ _.throttle = function(func, wait) {
+ var context, args, timeout, result;
+ var previous = 0;
+ var later = function() {
+ previous = new Date;
+ timeout = null;
+ result = func.apply(context, args);
+ };
+ return function() {
+ var now = new Date;
+ var remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0) {
+ clearTimeout(timeout);
+ timeout = null;
+ previous = now;
+ result = func.apply(context, args);
+ } else if (!timeout) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+ };
+
+ // Returns a function, that, as long as it continues to be invoked, will not
+ // be triggered. The function will be called after it stops being called for
+ // N milliseconds. If `immediate` is passed, trigger the function on the
+ // leading edge, instead of the trailing.
+ _.debounce = function(func, wait, immediate) {
+ var timeout, result;
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) result = func.apply(context, args);
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) result = func.apply(context, args);
+ return result;
+ };
+ };
+
+ // Returns a function that will be executed at most one time, no matter how
+ // often you call it. Useful for lazy initialization.
+ _.once = function(func) {
+ var ran = false, memo;
+ return function() {
+ if (ran) return memo;
+ ran = true;
+ memo = func.apply(this, arguments);
+ func = null;
+ return memo;
+ };
+ };
+
+ // Returns the first function passed as an argument to the second,
+ // allowing you to adjust arguments, run code before and after, and
+ // conditionally execute the original function.
+ _.wrap = function(func, wrapper) {
+ return function() {
+ var args = [func];
+ push.apply(args, arguments);
+ return wrapper.apply(this, args);
+ };
+ };
+
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var funcs = arguments;
+ return function() {
+ var args = arguments;
+ for (var i = funcs.length - 1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+ return args[0];
+ };
+ };
+
+ // Returns a function that will only be executed after being called N times.
+ _.after = function(times, func) {
+ if (times <= 0) return func();
+ return function() {
+ if (--times < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ };
+
+ // Object Functions
+ // ----------------
+
+ // Retrieve the names of an object's properties.
+ // Delegates to **ECMAScript 5**'s native `Object.keys`
+ _.keys = nativeKeys || function(obj) {
+ if (obj !== Object(obj)) throw new TypeError('Invalid object');
+ var keys = [];
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+ return keys;
+ };
+
+ // Retrieve the values of an object's properties.
+ _.values = function(obj) {
+ var values = [];
+ for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
+ return values;
+ };
+
+ // Convert an object into a list of `[key, value]` pairs.
+ _.pairs = function(obj) {
+ var pairs = [];
+ for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
+ return pairs;
+ };
+
+ // Invert the keys and values of an object. The values must be serializable.
+ _.invert = function(obj) {
+ var result = {};
+ for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
+ return result;
+ };
+
+ // Return a sorted list of the function names available on the object.
+ // Aliased as `methods`
+ _.functions = _.methods = function(obj) {
+ var names = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
+ }
+ return names.sort();
+ };
+
+ // Extend a given object with all the properties in passed-in object(s).
+ _.extend = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ if (source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+ }
+ });
+ return obj;
+ };
+
+ // Return a copy of the object only containing the whitelisted properties.
+ _.pick = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ each(keys, function(key) {
+ if (key in obj) copy[key] = obj[key];
+ });
+ return copy;
+ };
+
+ // Return a copy of the object without the blacklisted properties.
+ _.omit = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ for (var key in obj) {
+ if (!_.contains(keys, key)) copy[key] = obj[key];
+ }
+ return copy;
+ };
+
+ // Fill in a given object with default properties.
+ _.defaults = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ if (source) {
+ for (var prop in source) {
+ if (obj[prop] == null) obj[prop] = source[prop];
+ }
+ }
+ });
+ return obj;
+ };
+
+ // Create a (shallow-cloned) duplicate of an object.
+ _.clone = function(obj) {
+ if (!_.isObject(obj)) return obj;
+ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+ };
+
+ // Invokes interceptor with the obj, and then returns obj.
+ // The primary purpose of this method is to "tap into" a method chain, in
+ // order to perform operations on intermediate results within the chain.
+ _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
+ };
+
+ // Internal recursive comparison function for `isEqual`.
+ var eq = function(a, b, aStack, bStack) {
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+ if (a === b) return a !== 0 || 1 / a == 1 / b;
+ // A strict comparison is necessary because `null == undefined`.
+ if (a == null || b == null) return a === b;
+ // Unwrap any wrapped objects.
+ if (a instanceof _) a = a._wrapped;
+ if (b instanceof _) b = b._wrapped;
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className != toString.call(b)) return false;
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return a == String(b);
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') return false;
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] == a) return bStack[length] == b;
+ }
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+ var size = 0, result = true;
+ // Recursively compare objects and arrays.
+ if (className == '[object Array]') {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ size = a.length;
+ result = size == b.length;
+ if (result) {
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (size--) {
+ if (!(result = eq(a[size], b[size], aStack, bStack))) break;
+ }
+ }
+ } else {
+ // Objects with different constructors are not equivalent, but `Object`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
+ return false;
+ }
+ // Deep compare objects.
+ for (var key in a) {
+ if (_.has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (_.has(b, key) && !(size--)) break;
+ }
+ result = !size;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+ return result;
+ };
+
+ // Perform a deep comparison to check if two objects are equal.
+ _.isEqual = function(a, b) {
+ return eq(a, b, [], []);
+ };
+
+ // Is a given array, string, or object empty?
+ // An "empty" object has no enumerable own-properties.
+ _.isEmpty = function(obj) {
+ if (obj == null) return true;
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+ for (var key in obj) if (_.has(obj, key)) return false;
+ return true;
+ };
+
+ // Is a given value a DOM element?
+ _.isElement = function(obj) {
+ return !!(obj && obj.nodeType === 1);
+ };
+
+ // Is a given value an array?
+ // Delegates to ECMA5's native Array.isArray
+ _.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) == '[object Array]';
+ };
+
+ // Is a given variable an object?
+ _.isObject = function(obj) {
+ return obj === Object(obj);
+ };
+
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
+ each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
+ _['is' + name] = function(obj) {
+ return toString.call(obj) == '[object ' + name + ']';
+ };
+ });
+
+ // Define a fallback version of the method in browsers (ahem, IE), where
+ // there isn't any inspectable "Arguments" type.
+ if (!_.isArguments(arguments)) {
+ _.isArguments = function(obj) {
+ return !!(obj && _.has(obj, 'callee'));
+ };
+ }
+
+ // Optimize `isFunction` if appropriate.
+ if (typeof (/./) !== 'function') {
+ _.isFunction = function(obj) {
+ return typeof obj === 'function';
+ };
+ }
+
+ // Is a given object a finite number?
+ _.isFinite = function(obj) {
+ return isFinite(obj) && !isNaN(parseFloat(obj));
+ };
+
+ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
+ _.isNaN = function(obj) {
+ return _.isNumber(obj) && obj != +obj;
+ };
+
+ // Is a given value a boolean?
+ _.isBoolean = function(obj) {
+ return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+ };
+
+ // Is a given value equal to null?
+ _.isNull = function(obj) {
+ return obj === null;
+ };
+
+ // Is a given variable undefined?
+ _.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ // Shortcut function for checking if an object has a given property directly
+ // on itself (in other words, not on a prototype).
+ _.has = function(obj, key) {
+ return hasOwnProperty.call(obj, key);
+ };
+
+ // Utility Functions
+ // -----------------
+
+ // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+ // previous owner. Returns a reference to the Underscore object.
+ _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ };
+
+ // Keep the identity function around for default iterators.
+ _.identity = function(value) {
+ return value;
+ };
+
+ // Run a function **n** times.
+ _.times = function(n, iterator, context) {
+ var accum = Array(n);
+ for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
+ return accum;
+ };
+
+ // Return a random integer between min and max (inclusive).
+ _.random = function(min, max) {
+ if (max == null) {
+ max = min;
+ min = 0;
+ }
+ return min + Math.floor(Math.random() * (max - min + 1));
+ };
+
+ // List of HTML entities for escaping.
+ var entityMap = {
+ escape: {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#x27;',
+ '/': '&#x2F;'
+ }
+ };
+ entityMap.unescape = _.invert(entityMap.escape);
+
+ // Regexes containing the keys and values listed immediately above.
+ var entityRegexes = {
+ escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
+ unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
+ };
+
+ // Functions for escaping and unescaping strings to/from HTML interpolation.
+ _.each(['escape', 'unescape'], function(method) {
+ _[method] = function(string) {
+ if (string == null) return '';
+ return ('' + string).replace(entityRegexes[method], function(match) {
+ return entityMap[method][match];
+ });
+ };
+ });
+
+ // If the value of the named property is a function then invoke it;
+ // otherwise, return it.
+ _.result = function(object, property) {
+ if (object == null) return null;
+ var value = object[property];
+ return _.isFunction(value) ? value.call(object) : value;
+ };
+
+ // Add your own custom functions to the Underscore object.
+ _.mixin = function(obj) {
+ each(_.functions(obj), function(name){
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result.call(this, func.apply(_, args));
+ };
+ });
+ };
+
+ // Generate a unique integer id (unique within the entire client session).
+ // Useful for temporary DOM ids.
+ var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = ++idCounter + '';
+ return prefix ? prefix + id : id;
+ };
+
+ // By default, Underscore uses ERB-style template delimiters, change the
+ // following template settings to use alternative delimiters.
+ _.templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
+ };
+
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /(.)^/;
+
+ // Certain characters need to be escaped so that they can be put into a
+ // string literal.
+ var escapes = {
+ "'": "'",
+ '\\': '\\',
+ '\r': 'r',
+ '\n': 'n',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+
+ // JavaScript micro-templating, similar to John Resig's implementation.
+ // Underscore templating handles arbitrary delimiters, preserves whitespace,
+ // and correctly escapes quotes within interpolated code.
+ _.template = function(text, data, settings) {
+ var render;
+ settings = _.defaults({}, settings, _.templateSettings);
+
+ // Combine delimiters into one regular expression via alternation.
+ var matcher = new RegExp([
+ (settings.escape || noMatch).source,
+ (settings.interpolate || noMatch).source,
+ (settings.evaluate || noMatch).source
+ ].join('|') + '|$', 'g');
+
+ // Compile the template source, escaping string literals appropriately.
+ var index = 0;
+ var source = "__p+='";
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+ source += text.slice(index, offset)
+ .replace(escaper, function(match) { return '\\' + escapes[match]; });
+
+ if (escape) {
+ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
+ }
+ if (interpolate) {
+ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
+ }
+ if (evaluate) {
+ source += "';\n" + evaluate + "\n__p+='";
+ }
+ index = offset + match.length;
+ return match;
+ });
+ source += "';\n";
+
+ // If a variable is not specified, place data values in local scope.
+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+
+ source = "var __t,__p='',__j=Array.prototype.join," +
+ "print=function(){__p+=__j.call(arguments,'');};\n" +
+ source + "return __p;\n";
+
+ try {
+ render = new Function(settings.variable || 'obj', '_', source);
+ } catch (e) {
+ e.source = source;
+ throw e;
+ }
+
+ if (data) return render(data, _);
+ var template = function(data) {
+ return render.call(this, data, _);
+ };
+
+ // Provide the compiled function source as a convenience for precompilation.
+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
+
+ return template;
+ };
+
+ // Add a "chain" function, which will delegate to the wrapper.
+ _.chain = function(obj) {
+ return _(obj).chain();
+ };
+
+ // OOP
+ // ---------------
+ // If Underscore is called as a function, it returns a wrapped object that
+ // can be used OO-style. This wrapper holds altered versions of all the
+ // underscore functions. Wrapped objects may be chained.
+
+ // Helper function to continue chaining intermediate results.
+ var result = function(obj) {
+ return this._chain ? _(obj).chain() : obj;
+ };
+
+ // Add all of the Underscore functions to the wrapper object.
+ _.mixin(_);
+
+ // Add all mutator Array functions to the wrapper.
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ var obj = this._wrapped;
+ method.apply(obj, arguments);
+ if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
+ return result.call(this, obj);
+ };
+ });
+
+ // Add all accessor Array functions to the wrapper.
+ each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ return result.call(this, method.apply(this._wrapped, arguments));
+ };
+ });
+
+ _.extend(_.prototype, {
+
+ // Start chaining a wrapped Underscore object.
+ chain: function() {
+ this._chain = true;
+ return this;
+ },
+
+ // Extracts the result from a wrapped and chained object.
+ value: function() {
+ return this._wrapped;
+ }
+
+ });
+
+}).call(this);
+
+})()
+},{}],14:[function(require,module,exports){
+exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) {
+ var e, m,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ nBits = -7,
+ i = isBE ? 0 : (nBytes - 1),
+ d = isBE ? 1 : -1,
+ s = buffer[offset + i];
+
+ i += d;
+
+ e = s & ((1 << (-nBits)) - 1);
+ s >>= (-nBits);
+ nBits += eLen;
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ m = e & ((1 << (-nBits)) - 1);
+ e >>= (-nBits);
+ nBits += mLen;
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ if (e === 0) {
+ e = 1 - eBias;
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
+ } else {
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
+};
+
+exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) {
+ var e, m, c,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
+ i = isBE ? (nBytes - 1) : 0,
+ d = isBE ? -1 : 1,
+ s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
+
+ value = Math.abs(value);
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0;
+ e = eMax;
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2);
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--;
+ c *= 2;
+ }
+ if (e + eBias >= 1) {
+ value += rt / c;
+ } else {
+ value += rt * Math.pow(2, 1 - eBias);
+ }
+ if (value * c >= 2) {
+ e++;
+ c /= 2;
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0;
+ e = eMax;
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen);
+ e = e + eBias;
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
+
+ e = (e << mLen) | m;
+ eLen += mLen;
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
+
+ buffer[offset + i - d] |= s * 128;
+};
+
+},{}],13:[function(require,module,exports){
+(function(){function SlowBuffer (size) {
+ this.length = size;
+};
+
+var assert = require('assert');
+
+exports.INSPECT_MAX_BYTES = 50;
+
+
+function toHex(n) {
+ if (n < 16) return '0' + n.toString(16);
+ return n.toString(16);
+}
+
+function utf8ToBytes(str) {
+ var byteArray = [];
+ for (var i = 0; i < str.length; i++)
+ if (str.charCodeAt(i) <= 0x7F)
+ byteArray.push(str.charCodeAt(i));
+ else {
+ var h = encodeURIComponent(str.charAt(i)).substr(1).split('%');
+ for (var j = 0; j < h.length; j++)
+ byteArray.push(parseInt(h[j], 16));
+ }
+
+ return byteArray;
+}
+
+function asciiToBytes(str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++ )
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push( str.charCodeAt(i) & 0xFF );
+
+ return byteArray;
+}
+
+function base64ToBytes(str) {
+ return require("base64-js").toByteArray(str);
+}
+
+SlowBuffer.byteLength = function (str, encoding) {
+ switch (encoding || "utf8") {
+ case 'hex':
+ return str.length / 2;
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8ToBytes(str).length;
+
+ case 'ascii':
+ case 'binary':
+ return str.length;
+
+ case 'base64':
+ return base64ToBytes(str).length;
+
+ default:
+ throw new Error('Unknown encoding');
+ }
+};
+
+function blitBuffer(src, dst, offset, length) {
+ var pos, i = 0;
+ while (i < length) {
+ if ((i+offset >= dst.length) || (i >= src.length))
+ break;
+
+ dst[i + offset] = src[i];
+ i++;
+ }
+ return i;
+}
+
+SlowBuffer.prototype.utf8Write = function (string, offset, length) {
+ var bytes, pos;
+ return SlowBuffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length);
+};
+
+SlowBuffer.prototype.asciiWrite = function (string, offset, length) {
+ var bytes, pos;
+ return SlowBuffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length);
+};
+
+SlowBuffer.prototype.binaryWrite = SlowBuffer.prototype.asciiWrite;
+
+SlowBuffer.prototype.base64Write = function (string, offset, length) {
+ var bytes, pos;
+ return SlowBuffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length);
+};
+
+SlowBuffer.prototype.base64Slice = function (start, end) {
+ var bytes = Array.prototype.slice.apply(this, arguments)
+ return require("base64-js").fromByteArray(bytes);
+}
+
+function decodeUtf8Char(str) {
+ try {
+ return decodeURIComponent(str);
+ } catch (err) {
+ return String.fromCharCode(0xFFFD); // UTF 8 invalid char
+ }
+}
+
+SlowBuffer.prototype.utf8Slice = function () {
+ var bytes = Array.prototype.slice.apply(this, arguments);
+ var res = "";
+ var tmp = "";
+ var i = 0;
+ while (i < bytes.length) {
+ if (bytes[i] <= 0x7F) {
+ res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]);
+ tmp = "";
+ } else
+ tmp += "%" + bytes[i].toString(16);
+
+ i++;
+ }
+
+ return res + decodeUtf8Char(tmp);
+}
+
+SlowBuffer.prototype.asciiSlice = function () {
+ var bytes = Array.prototype.slice.apply(this, arguments);
+ var ret = "";
+ for (var i = 0; i < bytes.length; i++)
+ ret += String.fromCharCode(bytes[i]);
+ return ret;
+}
+
+SlowBuffer.prototype.binarySlice = SlowBuffer.prototype.asciiSlice;
+
+SlowBuffer.prototype.inspect = function() {
+ var out = [],
+ len = this.length;
+ for (var i = 0; i < len; i++) {
+ out[i] = toHex(this[i]);
+ if (i == exports.INSPECT_MAX_BYTES) {
+ out[i + 1] = '...';
+ break;
+ }
+ }
+ return '<SlowBuffer ' + out.join(' ') + '>';
+};
+
+
+SlowBuffer.prototype.hexSlice = function(start, end) {
+ var len = this.length;
+
+ if (!start || start < 0) start = 0;
+ if (!end || end < 0 || end > len) end = len;
+
+ var out = '';
+ for (var i = start; i < end; i++) {
+ out += toHex(this[i]);
+ }
+ return out;
+};
+
+
+SlowBuffer.prototype.toString = function(encoding, start, end) {
+ encoding = String(encoding || 'utf8').toLowerCase();
+ start = +start || 0;
+ if (typeof end == 'undefined') end = this.length;
+
+ // Fastpath empty strings
+ if (+end == start) {
+ return '';
+ }
+
+ switch (encoding) {
+ case 'hex':
+ return this.hexSlice(start, end);
+
+ case 'utf8':
+ case 'utf-8':
+ return this.utf8Slice(start, end);
+
+ case 'ascii':
+ return this.asciiSlice(start, end);
+
+ case 'binary':
+ return this.binarySlice(start, end);
+
+ case 'base64':
+ return this.base64Slice(start, end);
+
+ case 'ucs2':
+ case 'ucs-2':
+ return this.ucs2Slice(start, end);
+
+ default:
+ throw new Error('Unknown encoding');
+ }
+};
+
+
+SlowBuffer.prototype.hexWrite = function(string, offset, length) {
+ offset = +offset || 0;
+ var remaining = this.length - offset;
+ if (!length) {
+ length = remaining;
+ } else {
+ length = +length;
+ if (length > remaining) {
+ length = remaining;
+ }
+ }
+
+ // must be an even number of digits
+ var strLen = string.length;
+ if (strLen % 2) {
+ throw new Error('Invalid hex string');
+ }
+ if (length > strLen / 2) {
+ length = strLen / 2;
+ }
+ for (var i = 0; i < length; i++) {
+ var byte = parseInt(string.substr(i * 2, 2), 16);
+ if (isNaN(byte)) throw new Error('Invalid hex string');
+ this[offset + i] = byte;
+ }
+ SlowBuffer._charsWritten = i * 2;
+ return i;
+};
+
+
+SlowBuffer.prototype.write = function(string, offset, length, encoding) {
+ // Support both (string, offset, length, encoding)
+ // and the legacy (string, encoding, offset, length)
+ if (isFinite(offset)) {
+ if (!isFinite(length)) {
+ encoding = length;
+ length = undefined;
+ }
+ } else { // legacy
+ var swap = encoding;
+ encoding = offset;
+ offset = length;
+ length = swap;
+ }
+
+ offset = +offset || 0;
+ var remaining = this.length - offset;
+ if (!length) {
+ length = remaining;
+ } else {
+ length = +length;
+ if (length > remaining) {
+ length = remaining;
+ }
+ }
+ encoding = String(encoding || 'utf8').toLowerCase();
+
+ switch (encoding) {
+ case 'hex':
+ return this.hexWrite(string, offset, length);
+
+ case 'utf8':
+ case 'utf-8':
+ return this.utf8Write(string, offset, length);
+
+ case 'ascii':
+ return this.asciiWrite(string, offset, length);
+
+ case 'binary':
+ return this.binaryWrite(string, offset, length);
+
+ case 'base64':
+ return this.base64Write(string, offset, length);
+
+ case 'ucs2':
+ case 'ucs-2':
+ return this.ucs2Write(string, offset, length);
+
+ default:
+ throw new Error('Unknown encoding');
+ }
+};
+
+
+// slice(start, end)
+SlowBuffer.prototype.slice = function(start, end) {
+ if (end === undefined) end = this.length;
+
+ if (end > this.length) {
+ throw new Error('oob');
+ }
+ if (start > end) {
+ throw new Error('oob');
+ }
+
+ return new Buffer(this, end - start, +start);
+};
+
+SlowBuffer.prototype.copy = function(target, targetstart, sourcestart, sourceend) {
+ var temp = [];
+ for (var i=sourcestart; i<sourceend; i++) {
+ assert.ok(typeof this[i] !== 'undefined', "copying undefined buffer bytes!");
+ temp.push(this[i]);
+ }
+
+ for (var i=targetstart; i<targetstart+temp.length; i++) {
+ target[i] = temp[i-targetstart];
+ }
+};
+
+SlowBuffer.prototype.fill = function(value, start, end) {
+ if (end > this.length) {
+ throw new Error('oob');
+ }
+ if (start > end) {
+ throw new Error('oob');
+ }
+
+ for (var i = start; i < end; i++) {
+ this[i] = value;
+ }
+}
+
+function coerce(length) {
+ // Coerce length to a number (possibly NaN), round up
+ // in case it's fractional (e.g. 123.456) then do a
+ // double negate to coerce a NaN to 0. Easy, right?
+ length = ~~Math.ceil(+length);
+ return length < 0 ? 0 : length;
+}
+
+
+// Buffer
+
+function Buffer(subject, encoding, offset) {
+ if (!(this instanceof Buffer)) {
+ return new Buffer(subject, encoding, offset);
+ }
+
+ var type;
+
+ // Are we slicing?
+ if (typeof offset === 'number') {
+ this.length = coerce(encoding);
+ this.parent = subject;
+ this.offset = offset;
+ } else {
+ // Find the length
+ switch (type = typeof subject) {
+ case 'number':
+ this.length = coerce(subject);
+ break;
+
+ case 'string':
+ this.length = Buffer.byteLength(subject, encoding);
+ break;
+
+ case 'object': // Assume object is an array
+ this.length = coerce(subject.length);
+ break;
+
+ default:
+ throw new Error('First argument needs to be a number, ' +
+ 'array or string.');
+ }
+
+ if (this.length > Buffer.poolSize) {
+ // Big buffer, just alloc one.
+ this.parent = new SlowBuffer(this.length);
+ this.offset = 0;
+
+ } else {
+ // Small buffer.
+ if (!pool || pool.length - pool.used < this.length) allocPool();
+ this.parent = pool;
+ this.offset = pool.used;
+ pool.used += this.length;
+ }
+
+ // Treat array-ish objects as a byte array.
+ if (isArrayIsh(subject)) {
+ for (var i = 0; i < this.length; i++) {
+ if (subject instanceof Buffer) {
+ this.parent[i + this.offset] = subject.readUInt8(i);
+ }
+ else {
+ this.parent[i + this.offset] = subject[i];
+ }
+ }
+ } else if (type == 'string') {
+ // We are a string
+ this.length = this.write(subject, 0, encoding);
+ }
+ }
+
+}
+
+function isArrayIsh(subject) {
+ return Array.isArray(subject) || Buffer.isBuffer(subject) ||
+ subject && typeof subject === 'object' &&
+ typeof subject.length === 'number';
+}
+
+exports.SlowBuffer = SlowBuffer;
+exports.Buffer = Buffer;
+
+Buffer.poolSize = 8 * 1024;
+var pool;
+
+function allocPool() {
+ pool = new SlowBuffer(Buffer.poolSize);
+ pool.used = 0;
+}
+
+
+// Static methods
+Buffer.isBuffer = function isBuffer(b) {
+ return b instanceof Buffer || b instanceof SlowBuffer;
+};
+
+Buffer.concat = function (list, totalLength) {
+ if (!Array.isArray(list)) {
+ throw new Error("Usage: Buffer.concat(list, [totalLength])\n \
+ list should be an Array.");
+ }
+
+ if (list.length === 0) {
+ return new Buffer(0);
+ } else if (list.length === 1) {
+ return list[0];
+ }
+
+ if (typeof totalLength !== 'number') {
+ totalLength = 0;
+ for (var i = 0; i < list.length; i++) {
+ var buf = list[i];
+ totalLength += buf.length;
+ }
+ }
+
+ var buffer = new Buffer(totalLength);
+ var pos = 0;
+ for (var i = 0; i < list.length; i++) {
+ var buf = list[i];
+ buf.copy(buffer, pos);
+ pos += buf.length;
+ }
+ return buffer;
+};
+
+// Inspect
+Buffer.prototype.inspect = function inspect() {
+ var out = [],
+ len = this.length;
+
+ for (var i = 0; i < len; i++) {
+ out[i] = toHex(this.parent[i + this.offset]);
+ if (i == exports.INSPECT_MAX_BYTES) {
+ out[i + 1] = '...';
+ break;
+ }
+ }
+
+ return '<Buffer ' + out.join(' ') + '>';
+};
+
+
+Buffer.prototype.get = function get(i) {
+ if (i < 0 || i >= this.length) throw new Error('oob');
+ return this.parent[this.offset + i];
+};
+
+
+Buffer.prototype.set = function set(i, v) {
+ if (i < 0 || i >= this.length) throw new Error('oob');
+ return this.parent[this.offset + i] = v;
+};
+
+
+// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8')
+Buffer.prototype.write = function(string, offset, length, encoding) {
+ // Support both (string, offset, length, encoding)
+ // and the legacy (string, encoding, offset, length)
+ if (isFinite(offset)) {
+ if (!isFinite(length)) {
+ encoding = length;
+ length = undefined;
+ }
+ } else { // legacy
+ var swap = encoding;
+ encoding = offset;
+ offset = length;
+ length = swap;
+ }
+
+ offset = +offset || 0;
+ var remaining = this.length - offset;
+ if (!length) {
+ length = remaining;
+ } else {
+ length = +length;
+ if (length > remaining) {
+ length = remaining;
+ }
+ }
+ encoding = String(encoding || 'utf8').toLowerCase();
+
+ var ret;
+ switch (encoding) {
+ case 'hex':
+ ret = this.parent.hexWrite(string, this.offset + offset, length);
+ break;
+
+ case 'utf8':
+ case 'utf-8':
+ ret = this.parent.utf8Write(string, this.offset + offset, length);
+ break;
+
+ case 'ascii':
+ ret = this.parent.asciiWrite(string, this.offset + offset, length);
+ break;
+
+ case 'binary':
+ ret = this.parent.binaryWrite(string, this.offset + offset, length);
+ break;
+
+ case 'base64':
+ // Warning: maxLength not taken into account in base64Write
+ ret = this.parent.base64Write(string, this.offset + offset, length);
+ break;
+
+ case 'ucs2':
+ case 'ucs-2':
+ ret = this.parent.ucs2Write(string, this.offset + offset, length);
+ break;
+
+ default:
+ throw new Error('Unknown encoding');
+ }
+
+ Buffer._charsWritten = SlowBuffer._charsWritten;
+
+ return ret;
+};
+
+
+// toString(encoding, start=0, end=buffer.length)
+Buffer.prototype.toString = function(encoding, start, end) {
+ encoding = String(encoding || 'utf8').toLowerCase();
+
+ if (typeof start == 'undefined' || start < 0) {
+ start = 0;
+ } else if (start > this.length) {
+ start = this.length;
+ }
+
+ if (typeof end == 'undefined' || end > this.length) {
+ end = this.length;
+ } else if (end < 0) {
+ end = 0;
+ }
+
+ start = start + this.offset;
+ end = end + this.offset;
+
+ switch (encoding) {
+ case 'hex':
+ return this.parent.hexSlice(start, end);
+
+ case 'utf8':
+ case 'utf-8':
+ return this.parent.utf8Slice(start, end);
+
+ case 'ascii':
+ return this.parent.asciiSlice(start, end);
+
+ case 'binary':
+ return this.parent.binarySlice(start, end);
+
+ case 'base64':
+ return this.parent.base64Slice(start, end);
+
+ case 'ucs2':
+ case 'ucs-2':
+ return this.parent.ucs2Slice(start, end);
+
+ default:
+ throw new Error('Unknown encoding');
+ }
+};
+
+
+// byteLength
+Buffer.byteLength = SlowBuffer.byteLength;
+
+
+// fill(value, start=0, end=buffer.length)
+Buffer.prototype.fill = function fill(value, start, end) {
+ value || (value = 0);
+ start || (start = 0);
+ end || (end = this.length);
+
+ if (typeof value === 'string') {
+ value = value.charCodeAt(0);
+ }
+ if (!(typeof value === 'number') || isNaN(value)) {
+ throw new Error('value is not a number');
+ }
+
+ if (end < start) throw new Error('end < start');
+
+ // Fill 0 bytes; we're done
+ if (end === start) return 0;
+ if (this.length == 0) return 0;
+
+ if (start < 0 || start >= this.length) {
+ throw new Error('start out of bounds');
+ }
+
+ if (end < 0 || end > this.length) {
+ throw new Error('end out of bounds');
+ }
+
+ return this.parent.fill(value,
+ start + this.offset,
+ end + this.offset);
+};
+
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function(target, target_start, start, end) {
+ var source = this;
+ start || (start = 0);
+ end || (end = this.length);
+ target_start || (target_start = 0);
+
+ if (end < start) throw new Error('sourceEnd < sourceStart');
+
+ // Copy 0 bytes; we're done
+ if (end === start) return 0;
+ if (target.length == 0 || source.length == 0) return 0;
+
+ if (target_start < 0 || target_start >= target.length) {
+ throw new Error('targetStart out of bounds');
+ }
+
+ if (start < 0 || start >= source.length) {
+ throw new Error('sourceStart out of bounds');
+ }
+
+ if (end < 0 || end > source.length) {
+ throw new Error('sourceEnd out of bounds');
+ }
+
+ // Are we oob?
+ if (end > this.length) {
+ end = this.length;
+ }
+
+ if (target.length - target_start < end - start) {
+ end = target.length - target_start + start;
+ }
+
+ return this.parent.copy(target.parent,
+ target_start + target.offset,
+ start + this.offset,
+ end + this.offset);
+};
+
+
+// slice(start, end)
+Buffer.prototype.slice = function(start, end) {
+ if (end === undefined) end = this.length;
+ if (end > this.length) throw new Error('oob');
+ if (start > end) throw new Error('oob');
+
+ return new Buffer(this.parent, end - start, +start + this.offset);
+};
+
+
+// Legacy methods for backwards compatibility.
+
+Buffer.prototype.utf8Slice = function(start, end) {
+ return this.toString('utf8', start, end);
+};
+
+Buffer.prototype.binarySlice = function(start, end) {
+ return this.toString('binary', start, end);
+};
+
+Buffer.prototype.asciiSlice = function(start, end) {
+ return this.toString('ascii', start, end);
+};
+
+Buffer.prototype.utf8Write = function(string, offset) {
+ return this.write(string, offset, 'utf8');
+};
+
+Buffer.prototype.binaryWrite = function(string, offset) {
+ return this.write(string, offset, 'binary');
+};
+
+Buffer.prototype.asciiWrite = function(string, offset) {
+ return this.write(string, offset, 'ascii');
+};
+
+Buffer.prototype.readUInt8 = function(offset, noAssert) {
+ var buffer = this;
+
+ if (!noAssert) {
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ if (offset >= buffer.length) return;
+
+ return buffer.parent[buffer.offset + offset];
+};
+
+function readUInt16(buffer, offset, isBigEndian, noAssert) {
+ var val = 0;
+
+
+ if (!noAssert) {
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 1 < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ if (offset >= buffer.length) return 0;
+
+ if (isBigEndian) {
+ val = buffer.parent[buffer.offset + offset] << 8;
+ if (offset + 1 < buffer.length) {
+ val |= buffer.parent[buffer.offset + offset + 1];
+ }
+ } else {
+ val = buffer.parent[buffer.offset + offset];
+ if (offset + 1 < buffer.length) {
+ val |= buffer.parent[buffer.offset + offset + 1] << 8;
+ }
+ }
+
+ return val;
+}
+
+Buffer.prototype.readUInt16LE = function(offset, noAssert) {
+ return readUInt16(this, offset, false, noAssert);
+};
+
+Buffer.prototype.readUInt16BE = function(offset, noAssert) {
+ return readUInt16(this, offset, true, noAssert);
+};
+
+function readUInt32(buffer, offset, isBigEndian, noAssert) {
+ var val = 0;
+
+ if (!noAssert) {
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ if (offset >= buffer.length) return 0;
+
+ if (isBigEndian) {
+ if (offset + 1 < buffer.length)
+ val = buffer.parent[buffer.offset + offset + 1] << 16;
+ if (offset + 2 < buffer.length)
+ val |= buffer.parent[buffer.offset + offset + 2] << 8;
+ if (offset + 3 < buffer.length)
+ val |= buffer.parent[buffer.offset + offset + 3];
+ val = val + (buffer.parent[buffer.offset + offset] << 24 >>> 0);
+ } else {
+ if (offset + 2 < buffer.length)
+ val = buffer.parent[buffer.offset + offset + 2] << 16;
+ if (offset + 1 < buffer.length)
+ val |= buffer.parent[buffer.offset + offset + 1] << 8;
+ val |= buffer.parent[buffer.offset + offset];
+ if (offset + 3 < buffer.length)
+ val = val + (buffer.parent[buffer.offset + offset + 3] << 24 >>> 0);
+ }
+
+ return val;
+}
+
+Buffer.prototype.readUInt32LE = function(offset, noAssert) {
+ return readUInt32(this, offset, false, noAssert);
+};
+
+Buffer.prototype.readUInt32BE = function(offset, noAssert) {
+ return readUInt32(this, offset, true, noAssert);
+};
+
+
+/*
+ * Signed integer types, yay team! A reminder on how two's complement actually
+ * works. The first bit is the signed bit, i.e. tells us whether or not the
+ * number should be positive or negative. If the two's complement value is
+ * positive, then we're done, as it's equivalent to the unsigned representation.
+ *
+ * Now if the number is positive, you're pretty much done, you can just leverage
+ * the unsigned translations and return those. Unfortunately, negative numbers
+ * aren't quite that straightforward.
+ *
+ * At first glance, one might be inclined to use the traditional formula to
+ * translate binary numbers between the positive and negative values in two's
+ * complement. (Though it doesn't quite work for the most negative value)
+ * Mainly:
+ * - invert all the bits
+ * - add one to the result
+ *
+ * Of course, this doesn't quite work in Javascript. Take for example the value
+ * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of
+ * course, Javascript will do the following:
+ *
+ * > ~0xff80
+ * -65409
+ *
+ * Whoh there, Javascript, that's not quite right. But wait, according to
+ * Javascript that's perfectly correct. When Javascript ends up seeing the
+ * constant 0xff80, it has no notion that it is actually a signed number. It
+ * assumes that we've input the unsigned value 0xff80. Thus, when it does the
+ * binary negation, it casts it into a signed value, (positive 0xff80). Then
+ * when you perform binary negation on that, it turns it into a negative number.
+ *
+ * Instead, we're going to have to use the following general formula, that works
+ * in a rather Javascript friendly way. I'm glad we don't support this kind of
+ * weird numbering scheme in the kernel.
+ *
+ * (BIT-MAX - (unsigned)val + 1) * -1
+ *
+ * The astute observer, may think that this doesn't make sense for 8-bit numbers
+ * (really it isn't necessary for them). However, when you get 16-bit numbers,
+ * you do. Let's go back to our prior example and see how this will look:
+ *
+ * (0xffff - 0xff80 + 1) * -1
+ * (0x007f + 1) * -1
+ * (0x0080) * -1
+ */
+Buffer.prototype.readInt8 = function(offset, noAssert) {
+ var buffer = this;
+ var neg;
+
+ if (!noAssert) {
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ if (offset >= buffer.length) return;
+
+ neg = buffer.parent[buffer.offset + offset] & 0x80;
+ if (!neg) {
+ return (buffer.parent[buffer.offset + offset]);
+ }
+
+ return ((0xff - buffer.parent[buffer.offset + offset] + 1) * -1);
+};
+
+function readInt16(buffer, offset, isBigEndian, noAssert) {
+ var neg, val;
+
+ if (!noAssert) {
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 1 < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ val = readUInt16(buffer, offset, isBigEndian, noAssert);
+ neg = val & 0x8000;
+ if (!neg) {
+ return val;
+ }
+
+ return (0xffff - val + 1) * -1;
+}
+
+Buffer.prototype.readInt16LE = function(offset, noAssert) {
+ return readInt16(this, offset, false, noAssert);
+};
+
+Buffer.prototype.readInt16BE = function(offset, noAssert) {
+ return readInt16(this, offset, true, noAssert);
+};
+
+function readInt32(buffer, offset, isBigEndian, noAssert) {
+ var neg, val;
+
+ if (!noAssert) {
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ val = readUInt32(buffer, offset, isBigEndian, noAssert);
+ neg = val & 0x80000000;
+ if (!neg) {
+ return (val);
+ }
+
+ return (0xffffffff - val + 1) * -1;
+}
+
+Buffer.prototype.readInt32LE = function(offset, noAssert) {
+ return readInt32(this, offset, false, noAssert);
+};
+
+Buffer.prototype.readInt32BE = function(offset, noAssert) {
+ return readInt32(this, offset, true, noAssert);
+};
+
+function readFloat(buffer, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian,
+ 23, 4);
+}
+
+Buffer.prototype.readFloatLE = function(offset, noAssert) {
+ return readFloat(this, offset, false, noAssert);
+};
+
+Buffer.prototype.readFloatBE = function(offset, noAssert) {
+ return readFloat(this, offset, true, noAssert);
+};
+
+function readDouble(buffer, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset + 7 < buffer.length,
+ 'Trying to read beyond buffer length');
+ }
+
+ return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian,
+ 52, 8);
+}
+
+Buffer.prototype.readDoubleLE = function(offset, noAssert) {
+ return readDouble(this, offset, false, noAssert);
+};
+
+Buffer.prototype.readDoubleBE = function(offset, noAssert) {
+ return readDouble(this, offset, true, noAssert);
+};
+
+
+/*
+ * We have to make sure that the value is a valid integer. This means that it is
+ * non-negative. It has no fractional component and that it does not exceed the
+ * maximum allowed value.
+ *
+ * value The number to check for validity
+ *
+ * max The maximum value
+ */
+function verifuint(value, max) {
+ assert.ok(typeof (value) == 'number',
+ 'cannot write a non-number as a number');
+
+ assert.ok(value >= 0,
+ 'specified a negative value for writing an unsigned value');
+
+ assert.ok(value <= max, 'value is larger than maximum value for type');
+
+ assert.ok(Math.floor(value) === value, 'value has a fractional component');
+}
+
+Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
+ var buffer = this;
+
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset < buffer.length,
+ 'trying to write beyond buffer length');
+
+ verifuint(value, 0xff);
+ }
+
+ if (offset < buffer.length) {
+ buffer.parent[buffer.offset + offset] = value;
+ }
+};
+
+function writeUInt16(buffer, value, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 1 < buffer.length,
+ 'trying to write beyond buffer length');
+
+ verifuint(value, 0xffff);
+ }
+
+ for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) {
+ buffer.parent[buffer.offset + offset + i] =
+ (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>>
+ (isBigEndian ? 1 - i : i) * 8;
+ }
+
+}
+
+Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
+ writeUInt16(this, value, offset, false, noAssert);
+};
+
+Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
+ writeUInt16(this, value, offset, true, noAssert);
+};
+
+function writeUInt32(buffer, value, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'trying to write beyond buffer length');
+
+ verifuint(value, 0xffffffff);
+ }
+
+ for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) {
+ buffer.parent[buffer.offset + offset + i] =
+ (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff;
+ }
+}
+
+Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
+ writeUInt32(this, value, offset, false, noAssert);
+};
+
+Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
+ writeUInt32(this, value, offset, true, noAssert);
+};
+
+
+/*
+ * We now move onto our friends in the signed number category. Unlike unsigned
+ * numbers, we're going to have to worry a bit more about how we put values into
+ * arrays. Since we are only worrying about signed 32-bit values, we're in
+ * slightly better shape. Unfortunately, we really can't do our favorite binary
+ * & in this system. It really seems to do the wrong thing. For example:
+ *
+ * > -32 & 0xff
+ * 224
+ *
+ * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of
+ * this aren't treated as a signed number. Ultimately a bad thing.
+ *
+ * What we're going to want to do is basically create the unsigned equivalent of
+ * our representation and pass that off to the wuint* functions. To do that
+ * we're going to do the following:
+ *
+ * - if the value is positive
+ * we can pass it directly off to the equivalent wuint
+ * - if the value is negative
+ * we do the following computation:
+ * mb + val + 1, where
+ * mb is the maximum unsigned value in that byte size
+ * val is the Javascript negative integer
+ *
+ *
+ * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If
+ * you do out the computations:
+ *
+ * 0xffff - 128 + 1
+ * 0xffff - 127
+ * 0xff80
+ *
+ * You can then encode this value as the signed version. This is really rather
+ * hacky, but it should work and get the job done which is our goal here.
+ */
+
+/*
+ * A series of checks to make sure we actually have a signed 32-bit number
+ */
+function verifsint(value, max, min) {
+ assert.ok(typeof (value) == 'number',
+ 'cannot write a non-number as a number');
+
+ assert.ok(value <= max, 'value larger than maximum allowed value');
+
+ assert.ok(value >= min, 'value smaller than minimum allowed value');
+
+ assert.ok(Math.floor(value) === value, 'value has a fractional component');
+}
+
+function verifIEEE754(value, max, min) {
+ assert.ok(typeof (value) == 'number',
+ 'cannot write a non-number as a number');
+
+ assert.ok(value <= max, 'value larger than maximum allowed value');
+
+ assert.ok(value >= min, 'value smaller than minimum allowed value');
+}
+
+Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
+ var buffer = this;
+
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset < buffer.length,
+ 'Trying to write beyond buffer length');
+
+ verifsint(value, 0x7f, -0x80);
+ }
+
+ if (value >= 0) {
+ buffer.writeUInt8(value, offset, noAssert);
+ } else {
+ buffer.writeUInt8(0xff + value + 1, offset, noAssert);
+ }
+};
+
+function writeInt16(buffer, value, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 1 < buffer.length,
+ 'Trying to write beyond buffer length');
+
+ verifsint(value, 0x7fff, -0x8000);
+ }
+
+ if (value >= 0) {
+ writeUInt16(buffer, value, offset, isBigEndian, noAssert);
+ } else {
+ writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert);
+ }
+}
+
+Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
+ writeInt16(this, value, offset, false, noAssert);
+};
+
+Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
+ writeInt16(this, value, offset, true, noAssert);
+};
+
+function writeInt32(buffer, value, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to write beyond buffer length');
+
+ verifsint(value, 0x7fffffff, -0x80000000);
+ }
+
+ if (value >= 0) {
+ writeUInt32(buffer, value, offset, isBigEndian, noAssert);
+ } else {
+ writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert);
+ }
+}
+
+Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
+ writeInt32(this, value, offset, false, noAssert);
+};
+
+Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
+ writeInt32(this, value, offset, true, noAssert);
+};
+
+function writeFloat(buffer, value, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to write beyond buffer length');
+
+ verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38);
+ }
+
+ require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian,
+ 23, 4);
+}
+
+Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
+ writeFloat(this, value, offset, false, noAssert);
+};
+
+Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
+ writeFloat(this, value, offset, true, noAssert);
+};
+
+function writeDouble(buffer, value, offset, isBigEndian, noAssert) {
+ if (!noAssert) {
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(typeof (isBigEndian) === 'boolean',
+ 'missing or invalid endian');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 7 < buffer.length,
+ 'Trying to write beyond buffer length');
+
+ verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308);
+ }
+
+ require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian,
+ 52, 8);
+}
+
+Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
+ writeDouble(this, value, offset, false, noAssert);
+};
+
+Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
+ writeDouble(this, value, offset, true, noAssert);
+};
+
+SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8;
+SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE;
+SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE;
+SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE;
+SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE;
+SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8;
+SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE;
+SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE;
+SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE;
+SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE;
+SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE;
+SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE;
+SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE;
+SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE;
+SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8;
+SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE;
+SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE;
+SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE;
+SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE;
+SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8;
+SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE;
+SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE;
+SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE;
+SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE;
+SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE;
+SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE;
+SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE;
+SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE;
+
+})()
+},{"assert":9,"./buffer_ieee754":14,"base64-js":15}],15:[function(require,module,exports){
+(function (exports) {
+ 'use strict';
+
+ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+ function b64ToByteArray(b64) {
+ var i, j, l, tmp, placeHolders, arr;
+
+ if (b64.length % 4 > 0) {
+ throw 'Invalid string. Length must be a multiple of 4';
+ }
+
+ // the number of equal signs (place holders)
+ // if there are two placeholders, than the two characters before it
+ // represent one byte
+ // if there is only one, then the three characters before it represent 2 bytes
+ // this is just a cheap hack to not do indexOf twice
+ placeHolders = b64.indexOf('=');
+ placeHolders = placeHolders > 0 ? b64.length - placeHolders : 0;
+
+ // base64 is 4/3 + up to two characters of the original data
+ arr = [];//new Uint8Array(b64.length * 3 / 4 - placeHolders);
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ l = placeHolders > 0 ? b64.length - 4 : b64.length;
+
+ for (i = 0, j = 0; i < l; i += 4, j += 3) {
+ tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12) | (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]);
+ arr.push((tmp & 0xFF0000) >> 16);
+ arr.push((tmp & 0xFF00) >> 8);
+ arr.push(tmp & 0xFF);
+ }
+
+ if (placeHolders === 2) {
+ tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4);
+ arr.push(tmp & 0xFF);
+ } else if (placeHolders === 1) {
+ tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4) | (lookup.indexOf(b64[i + 2]) >> 2);
+ arr.push((tmp >> 8) & 0xFF);
+ arr.push(tmp & 0xFF);
+ }
+
+ return arr;
+ }
+
+ function uint8ToBase64(uint8) {
+ var i,
+ extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
+ output = "",
+ temp, length;
+
+ function tripletToBase64 (num) {
+ return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F];
+ };
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
+ temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
+ output += tripletToBase64(temp);
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ switch (extraBytes) {
+ case 1:
+ temp = uint8[uint8.length - 1];
+ output += lookup[temp >> 2];
+ output += lookup[(temp << 4) & 0x3F];
+ output += '==';
+ break;
+ case 2:
+ temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]);
+ output += lookup[temp >> 10];
+ output += lookup[(temp >> 4) & 0x3F];
+ output += lookup[(temp << 2) & 0x3F];
+ output += '=';
+ break;
+ }
+
+ return output;
+ }
+
+ module.exports.toByteArray = b64ToByteArray;
+ module.exports.fromByteArray = uint8ToBase64;
+}());
+
+},{}]},{},["E/GbHF"])
+;
+JSHINT = require('jshint').JSHINT;
+}());
diff --git a/dom/system/gonk/tests/marionette/ril_jshint/jshintrc b/dom/system/gonk/tests/marionette/ril_jshint/jshintrc
new file mode 100644
index 000000000..437fe1a6f
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/ril_jshint/jshintrc
@@ -0,0 +1,118 @@
+{
+ // JSHint Default Configuration File (as on JSHint website)
+ // See http://jshint.com/docs/ for more details
+
+ // Modify for RIL usage.
+
+ "maxerr" : 10000, // {int} Maximum error before stopping
+
+ // Enforcing
+ "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.)
+ "camelcase" : false, // true: Identifiers must be in camelCase
+ "curly" : false, // true: Require {} for every new block or scope
+ "eqeqeq" : false, // true: Require triple equals (===) for comparison
+ "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
+ "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
+ //"indent" : 2, // {int} Number of spaces to use for indentation
+ "latedef" : false, // true: Require variables/functions to be defined before being used
+ "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
+ "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
+ "noempty" : false, // true: Prohibit use of empty blocks
+ "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
+ "plusplus" : false, // true: Prohibit use of `++` & `--`
+ "quotmark" : false, // Quotation mark consistency:
+ // false : do nothing (default)
+ // true : ensure whatever is used is consistent
+ // "single" : require single quotes
+ // "double" : require double quotes
+ "undef" : false, // true: Require all non-global variables to be declared (prevents global leaks)
+ "unused" : false, // true: Require all defined variables be used
+ "strict" : false, // true: Requires all functions run in ES5 Strict Mode
+ "trailing" : false, // true: Prohibit trailing whitespaces
+ "maxparams" : false, // {int} Max number of formal params allowed per function
+ "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
+ "maxstatements" : false, // {int} Max number statements per function
+ "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
+ "maxlen" : false, // {int} Max number of characters per line
+
+ // Relaxing
+ "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
+ "boss" : false, // true: Tolerate assignments where comparisons would be expected
+ "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
+ "eqnull" : true, // true: Tolerate use of `== null`
+ "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
+ "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
+ "moz" : true, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
+ // (ex: `for each`, multiple try/catch, function expression…)
+ "evil" : false, // true: Tolerate use of `eval` and `new Function()`
+ "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
+ "funcscope" : false, // true: Tolerate defining variables inside control statements"
+ "globalstrict" : true, // true: Allow global "use strict" (also enables 'strict')
+ "iterator" : false, // true: Tolerate using the `__iterator__` property
+ "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
+ "laxbreak" : true, // true: Tolerate possibly unsafe line breakings
+ "laxcomma" : false, // true: Tolerate comma-first style coding
+ "loopfunc" : false, // true: Tolerate functions being defined in loops
+ "multistr" : false, // true: Tolerate multi-line strings
+ "proto" : true, // true: Tolerate using the `__proto__` property
+ "scripturl" : false, // true: Tolerate script-targeted URLs
+ "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
+ "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
+ "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
+ "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
+ "validthis" : true, // true: Tolerate using this in a non-constructor function
+
+ // Environments
+ "browser" : false, // Web Browser (window, document, etc)
+ "couch" : false, // CouchDB
+ "devel" : true, // Development/debugging (alert, confirm, etc)
+ "dojo" : false, // Dojo Toolkit
+ "jquery" : false, // jQuery
+ "mootools" : false, // MooTools
+ "node" : false, // Node.js
+ "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
+ "prototypejs" : false, // Prototype and Scriptaculous
+ "rhino" : false, // Rhino
+ "worker" : true, // Web Workers
+ "wsh" : false, // Windows Scripting Host
+ "yui" : false, // Yahoo User Interface
+
+ // Legacy
+ "nomen" : false, // true: Prohibit dangling `_` in variables
+ "onevar" : false, // true: Allow only one `var` statement per function
+ "passfail" : false, // true: Stop on first error
+ "white" : false, // true: Check against strict whitespace and indentation rules
+
+ // Custom Globals
+ "predef" : [ ], // additional predefined global variables
+
+ "globals": {
+ "ChromeWorker": false,
+ "Components": false,
+ "DOMRequestIpcHelper": false,
+ "ObjectWrapper": false,
+ "PhoneNumberUtils": false,
+ "RILNetworkInterface": false,
+ "Services": false,
+ "Uint8Array": false,
+ "WAP": false,
+ "XPCOMUtils": false,
+ "cpmm": false,
+ "dump": false,
+ "gAudioManager": false,
+ "gMessageManager": false,
+ "gMobileMessageDatabaseService": false,
+ "gMobileMessageService": false,
+ "gNetworkManager": false,
+ "gPowerManagerService": false,
+ "gSettingsService": false,
+ "gSmsService": false,
+ "gSystemMessenger": false,
+ "gSystemWorkerManager": false,
+ "gTimeService": false,
+ "gUUIDGenerator": false,
+ "ppmm": true,
+
+ "__end_guardian_for_easy_sorting__": false
+ }
+}
diff --git a/dom/system/gonk/tests/marionette/test_all_network_info.js b/dom/system/gonk/tests/marionette/test_all_network_info.js
new file mode 100644
index 000000000..5225ab6d6
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_all_network_info.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+var networkManager =
+ Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
+ok(networkManager,
+ "networkManager.constructor is " + networkManager.constructor);
+
+var wifiManager = window.navigator.mozWifiManager;
+ok(wifiManager, "wifiManager.constructor is " + wifiManager.constructor);
+
+function setEmulatorAPN() {
+ let apn = [
+ [{"carrier":"T-Mobile US",
+ "apn":"epc.tmobile.com",
+ "mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc",
+ "types":["default","supl","mms","ims","dun", "fota"]}]
+ ];
+
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, apn);
+}
+
+function ensureWifiEnabled(aEnabled) {
+ if (wifiManager.enabled === aEnabled) {
+ log('Already ' + (aEnabled ? 'enabled' : 'disabled'));
+ return Promise.resolve();
+ }
+ return requestWifiEnabled(aEnabled);
+}
+
+function requestWifiEnabled(aEnabled) {
+ let promises = [];
+
+ promises.push(waitForTargetEvent(wifiManager, aEnabled ? 'enabled' : 'disabled',
+ function() {
+ return wifiManager.enabled === aEnabled ? true : false;
+ }));
+ promises.push(setSettings(SETTINGS_KEY_WIFI_ENABLED, aEnabled));
+
+ return Promise.all(promises);
+}
+
+// Test initial State
+function verifyInitialState() {
+ log("= verifyInitialState =");
+
+ // Data and wifi should be off before starting any test.
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then(value => {
+ is(value, false, "Data must be off");
+ })
+ .then(() => ensureWifiEnabled(false));
+}
+
+function testAllNetworkInfo(aAnyConnected) {
+ log("= testAllNetworkInfo = " + aAnyConnected);
+
+ let allNetworkInfo = networkManager.allNetworkInfo;
+ ok(allNetworkInfo, "NetworkManager.allNetworkInfo");
+
+ let count = Object.keys(allNetworkInfo).length;
+ ok(count > 0, "NetworkManager.allNetworkInfo count");
+
+ let connected = false;
+ for (let networkId in allNetworkInfo) {
+ if (allNetworkInfo.hasOwnProperty(networkId)) {
+ let networkInfo = allNetworkInfo[networkId];
+ if (networkInfo.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
+ connected = true;
+ break;
+ }
+ }
+ }
+
+ is(aAnyConnected, connected, "NetworkManager.allNetworkInfo any connected");
+}
+
+// Start test
+startTestBase(function() {
+
+ let origApnSettings, origWifiEnabled;
+ return Promise.resolve()
+ .then(() => {
+ origWifiEnabled = wifiManager.enabled;
+ })
+ .then(() => verifyInitialState())
+ .then(() => getSettings(SETTINGS_KEY_DATA_APN_SETTINGS))
+ .then(value => {
+ origApnSettings = value;
+ })
+ .then(() => setEmulatorAPN())
+ .then(() => setDataEnabledAndWait(true))
+ .then(() => testAllNetworkInfo(true))
+ .then(() => setDataEnabledAndWait(false))
+ .then(() => testAllNetworkInfo(false))
+ // Restore original apn settings and wifi state.
+ .then(() => {
+ if (origApnSettings) {
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, origApnSettings);
+ }
+ })
+ .then(() => ensureWifiEnabled(origWifiEnabled));
+});
diff --git a/dom/system/gonk/tests/marionette/test_data_connection.js b/dom/system/gonk/tests/marionette/test_data_connection.js
new file mode 100644
index 000000000..5a53b1e5f
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_data_connection.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+function setEmulatorAPN() {
+ let apn = [
+ [{"carrier":"T-Mobile US",
+ "apn":"epc.tmobile.com",
+ "mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc",
+ "types":["default","supl","mms","ims","dun", "fota"]}]
+ ];
+
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, apn);
+}
+
+// Test initial State
+function testInitialState() {
+ log("= testInitialState =");
+
+ // Data should be off before starting any test.
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then(value => {
+ is(value, false, "Data must be off");
+ });
+}
+
+// Test default data Connection
+function testDefaultDataConnection() {
+ log("= testDefaultDataConnection =");
+
+ // Enable default data
+ return setDataEnabledAndWait(true)
+ // Disable default data
+ .then(() => setDataEnabledAndWait(false));
+}
+
+// Test non default data connection
+function testNonDefaultDataConnection() {
+ log("= testNonDefaultDataConnection =");
+
+ function doTestNonDefaultDataConnection(type) {
+ log("doTestNonDefaultDataConnection: " + type);
+
+ return setupDataCallAndWait(type)
+ .then(() => deactivateDataCallAndWait(type));
+ }
+
+ let currentApn;
+ return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS)
+ .then(value => {
+ currentApn = value;
+ })
+ .then(setEmulatorAPN)
+ .then(() => doTestNonDefaultDataConnection(NETWORK_TYPE_MOBILE_MMS))
+ .then(() => doTestNonDefaultDataConnection(NETWORK_TYPE_MOBILE_SUPL))
+ .then(() => doTestNonDefaultDataConnection(NETWORK_TYPE_MOBILE_IMS))
+ .then(() => doTestNonDefaultDataConnection(NETWORK_TYPE_MOBILE_DUN))
+ .then(() => doTestNonDefaultDataConnection(NETWORK_TYPE_MOBILE_FOTA))
+ // Restore APN settings
+ .then(() => setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, currentApn));
+}
+
+// Start test
+startTestBase(function() {
+ return testInitialState()
+ .then(() => testDefaultDataConnection())
+ .then(() => testNonDefaultDataConnection());
+});
diff --git a/dom/system/gonk/tests/marionette/test_data_connection_proxy.js b/dom/system/gonk/tests/marionette/test_data_connection_proxy.js
new file mode 100644
index 000000000..a99187538
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_data_connection_proxy.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+const HTTP_PROXY = "10.0.2.200";
+const HTTP_PROXY_PORT = "8080";
+const MANUAL_PROXY_CONFIGURATION = 1;
+
+// Test initial State
+function verifyInitialState() {
+ log("= verifyInitialState =");
+
+ // Data should be off before starting any test.
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then(value => {
+ is(value, false, "Data must be off");
+ });
+}
+
+function setTestApn() {
+ let apn = [
+ [ {"carrier": "T-Mobile US",
+ "apn": "epc.tmobile.com",
+ "proxy": HTTP_PROXY,
+ "port": HTTP_PROXY_PORT,
+ "mmsc": "http://mms.msg.eng.t-mobile.com/mms/wapenc",
+ "types": ["default","supl","mms"]} ]
+ ];
+
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, apn);
+}
+
+function waitForHttpProxyVerified(aShouldBeSet) {
+ let TIME_OUT_VALUE = 20000;
+
+ return new Promise(function(aResolve, aReject) {
+ try {
+ waitFor(aResolve, () => {
+ let proxyType = SpecialPowers.getIntPref("network.proxy.type");
+ let httpProxy = SpecialPowers.getCharPref("network.proxy.http");
+ let sslProxy = SpecialPowers.getCharPref("network.proxy.ssl");
+ let httpProxyPort = SpecialPowers.getIntPref("network.proxy.http_port");
+ let sslProxyPort = SpecialPowers.getIntPref("network.proxy.ssl_port");
+
+ if ((aShouldBeSet &&
+ proxyType == MANUAL_PROXY_CONFIGURATION &&
+ httpProxy == HTTP_PROXY &&
+ sslProxy == HTTP_PROXY &&
+ httpProxyPort == HTTP_PROXY_PORT &&
+ sslProxyPort == HTTP_PROXY_PORT) ||
+ (!aShouldBeSet && proxyType != MANUAL_PROXY_CONFIGURATION &&
+ !httpProxy && !sslProxy && !httpProxyPort && !sslProxyPort)) {
+ return true;
+ }
+
+ return false;
+ }, TIME_OUT_VALUE);
+ } catch(aError) {
+ // Timed out.
+ aReject(aError);
+ }
+ });
+}
+
+function testDefaultDataHttpProxy() {
+ log("= testDefaultDataHttpProxy =");
+
+ return setDataEnabledAndWait(true)
+ .then(() => waitForHttpProxyVerified(true))
+ .then(() => setDataEnabledAndWait(false))
+ .then(() => waitForHttpProxyVerified(false));
+}
+
+function testNonDefaultDataHttpProxy(aType) {
+ log("= testNonDefaultDataHttpProxy - " + aType + " =");
+
+ return setupDataCallAndWait(aType)
+ // Http proxy should not be set for non-default data connections.
+ .then(() => waitForHttpProxyVerified(false))
+ .then(() => deactivateDataCallAndWait(aType));
+}
+
+// Start test
+startTestBase(function() {
+ let origApnSettings;
+ return verifyInitialState()
+ .then(() => getSettings(SETTINGS_KEY_DATA_APN_SETTINGS))
+ .then(value => {
+ origApnSettings = value;
+ })
+ .then(() => setTestApn())
+ .then(() => testDefaultDataHttpProxy())
+ .then(() => testNonDefaultDataHttpProxy(NETWORK_TYPE_MOBILE_MMS))
+ .then(() => testNonDefaultDataHttpProxy(NETWORK_TYPE_MOBILE_SUPL))
+ // Restore APN settings
+ .then(() => setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, origApnSettings));
+});
diff --git a/dom/system/gonk/tests/marionette/test_dsds_numRadioInterfaces.js b/dom/system/gonk/tests/marionette/test_dsds_numRadioInterfaces.js
new file mode 100644
index 000000000..e178b8b65
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_dsds_numRadioInterfaces.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_CONTEXT = "chrome";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+
+const NS_RIL_CONTRACTID = "@mozilla.org/ril;1";
+
+const PROP_RO_MOZ_RIL_NUMCLIENTS = "ro.moz.ril.numclients";
+
+const PREF_RIL_NUM_RADIO_INTERFACES = "ril.numRadioInterfaces";
+
+ok(libcutils, "libcutils is available");
+
+var propNum = (function() {
+ try {
+ let numString = libcutils.property_get(PROP_RO_MOZ_RIL_NUMCLIENTS, "1");
+ let num = parseInt(numString, 10);
+ if (num >= 0) {
+ return num;
+ }
+ } catch (e) {}
+})();
+
+log("Retrieved '" + PROP_RO_MOZ_RIL_NUMCLIENTS + "' = " + propNum);
+ok(propNum, PROP_RO_MOZ_RIL_NUMCLIENTS);
+
+var prefNum = Services.prefs.getIntPref(PREF_RIL_NUM_RADIO_INTERFACES);
+log("Retrieved '" + PREF_RIL_NUM_RADIO_INTERFACES + "' = " + prefNum);
+
+var ril = Cc[NS_RIL_CONTRACTID].getService(Ci.nsIRadioInterfaceLayer);
+ok(ril, "ril.constructor is " + ril.constructor);
+
+var ifaceNum = ril.numRadioInterfaces;
+log("Retrieved 'nsIRadioInterfaceLayer.numRadioInterfaces' = " + ifaceNum);
+
+is(propNum, prefNum);
+is(propNum, ifaceNum);
+
+finish();
diff --git a/dom/system/gonk/tests/marionette/test_fakevolume.js b/dom/system/gonk/tests/marionette/test_fakevolume.js
new file mode 100644
index 000000000..173f9ac11
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_fakevolume.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 10000;
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+var volumeService = Cc["@mozilla.org/telephony/volume-service;1"].getService(Ci.nsIVolumeService);
+ok(volumeService, "Should have volume service");
+
+var volName = "fake";
+var mountPoint = "/data/fake/storage";
+volumeService.createFakeVolume(volName, mountPoint);
+
+var vol = volumeService.getVolumeByName(volName);
+ok(vol, "volume shouldn't be null");
+
+is(volName, vol.name, "name");
+is(mountPoint, vol.mountPoint, "moutnPoint");
+is(Ci.nsIVolume.STATE_MOUNTED, vol.state, "state");
+
+ok(vol.mountGeneration > 0, "mount generation should not be zero");
+
+finish();
diff --git a/dom/system/gonk/tests/marionette/test_geolocation.js b/dom/system/gonk/tests/marionette/test_geolocation.js
new file mode 100644
index 000000000..201c8b3e3
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_geolocation.js
@@ -0,0 +1,117 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 10000;
+
+var geolocation = window.navigator.geolocation;
+ok(geolocation);
+
+var sample = [];
+var result = [];
+var wpid;
+
+/**
+ * Grant special power to get the geolocation
+ */
+SpecialPowers.addPermission("geolocation", true, document);
+
+/**
+ * Disable wifi geolocation provider
+ */
+wifiUri = SpecialPowers.getCharPref("geo.wifi.uri");
+SpecialPowers.setCharPref("geo.wifi.uri", "http://mochi.test:8888/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs?action=stop-responding");
+
+/**
+ * Helper that compares the geolocation against the web API.
+ */
+function verifyLocation() {
+
+ log("Sample:" + sample.join(','));
+ log("Result:" + result.join(','));
+
+ for (i in sample) {
+ is(sample.pop(), result.pop());
+ }
+
+ window.setTimeout(cleanup, 0);
+}
+
+/**
+ * Test story begins here.
+ */
+function setup() {
+ log("Providing initial setup: set geographic position watcher.");
+
+
+ wpid = geolocation.watchPosition(function(position) {
+ log("Position changes: (" + position.coords.latitude + "/" + position.coords.longitude + ")");
+ result.push(""+position.coords.latitude + "/" + position.coords.longitude);
+ });
+
+ lat = 0;
+ lon = 0;
+
+ cmd = "geo fix " + lon + " " + lat;
+ sample.push(lat+"/"+lon);
+
+ runEmulatorCmd(cmd, function(result) {
+ window.setTimeout(movePosition_1, 0);
+ });
+}
+
+function movePosition_1() {
+ log("Geolocation changes. Move to Position 1.");
+
+ lat = 25;
+ lon = 121.56499833333334;
+
+ cmd = "geo fix " + lon + " " + lat;
+ sample.push(lat+"/"+lon);
+
+ runEmulatorCmd(cmd, function(result) {
+ window.setTimeout(movePosition_2, 0);
+ });
+}
+
+function movePosition_2() {
+ log("Geolocation changes to a negative longitude. Move to Position 2.");
+
+ lat = 37.393;
+ lon = -122.08199833333335;
+
+ cmd = "geo fix " + lon + " " + lat;
+ sample.push(lat+"/"+lon);
+
+ runEmulatorCmd(cmd, function(result) {
+ window.setTimeout(movePosition_3, 0);
+ });
+}
+
+function movePosition_3() {
+ log("Geolocation changes with WatchPosition. Move to Position 3.");
+
+ lat = -22;
+ lon = -43;
+
+ cmd = "geo fix " + lon + " " + lat;
+ sample.push(lat+"/"+lon);
+
+ geolocation.getCurrentPosition(function(position) {
+ log("getCurrentPosition: Expected location: ("+lat+"/"+lon+"); Current location: (" + position.coords.latitude + "/" + position.coords.longitude + ")");
+ is(lat, position.coords.latitude);
+ is(lon, position.coords.longitude);
+ });
+
+ runEmulatorCmd(cmd, function(result) {
+ window.setTimeout(verifyLocation, 0);
+ });
+}
+
+function cleanup() {
+ geolocation.clearWatch(wpid);
+ SpecialPowers.removePermission("geolocation", document);
+ SpecialPowers.setCharPref("geo.wifi.uri", wifiUri);
+ finish();
+}
+
+setup();
diff --git a/dom/system/gonk/tests/marionette/test_multiple_data_connection.js b/dom/system/gonk/tests/marionette/test_multiple_data_connection.js
new file mode 100644
index 000000000..24abd4451
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_multiple_data_connection.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+// Must sync with hardware/ril/reference-ril/reference-ril.c
+const MAX_DATA_CONTEXTS = 4;
+
+function setEmulatorAPN() {
+ // Use different apn for each network type.
+ let apn = [[ { "carrier":"T-Mobile US",
+ "apn":"epc1.tmobile.com",
+ "types":["default"] },
+ { "carrier":"T-Mobile US",
+ "apn":"epc2.tmobile.com",
+ "mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc",
+ "types":["mms"] },
+ { "carrier":"T-Mobile US",
+ "apn":"epc3.tmobile.com",
+ "types":["supl"] },
+ { "carrier":"T-Mobile US",
+ "apn":"epc4.tmobile.com",
+ "types":["ims"] },
+ { "carrier":"T-Mobile US",
+ "apn":"epc5.tmobile.com",
+ "types":["dun"] },
+ { "carrier":"T-Mobile US",
+ "apn":"epc6.tmobile.com",
+ "types":["fota"] }]];
+
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, apn);
+}
+
+// Test initial State
+function testInitialState() {
+ log("= testInitialState =");
+
+ // Data should be off before starting any test.
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then(value => {
+ is(value, false, "Data must be off");
+ });
+}
+
+function testSetupConcurrentDataCalls() {
+ log("= testSetupConcurrentDataCalls =");
+
+ let promise = Promise.resolve();
+ // Skip default mobile type.
+ for (let i = 1; i < MAX_DATA_CONTEXTS; i++) {
+ let type = networkTypes[i];
+ promise = promise.then(() => setupDataCallAndWait(type));
+ }
+ return promise;
+}
+
+function testDeactivateConcurrentDataCalls() {
+ log("= testDeactivateConcurrentDataCalls =");
+
+ let promise = Promise.resolve();
+ // Skip default mobile type.
+ for (let i = 1; i < MAX_DATA_CONTEXTS; i++) {
+ let type = networkTypes[i];
+ promise = promise.then(() => deactivateDataCallAndWait(type));
+ }
+ return promise;
+}
+
+// Start test
+startTestBase(function() {
+
+ let origApnSettings;
+ return testInitialState()
+ .then(() => getSettings(SETTINGS_KEY_DATA_APN_SETTINGS))
+ .then(value => {
+ origApnSettings = value;
+ })
+ .then(() => setEmulatorAPN())
+ .then(() => setDataEnabledAndWait(true))
+ .then(() => testSetupConcurrentDataCalls())
+ .then(() => testDeactivateConcurrentDataCalls())
+ .then(() => setDataEnabledAndWait(false))
+ .then(() => {
+ if (origApnSettings) {
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, origApnSettings);
+ }
+ });
+});
diff --git a/dom/system/gonk/tests/marionette/test_network_active_changed.js b/dom/system/gonk/tests/marionette/test_network_active_changed.js
new file mode 100644
index 000000000..5886f37ed
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_network_active_changed.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+var networkManager =
+ Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
+ok(networkManager,
+ "networkManager.constructor is " + networkManager.constructor);
+
+function testInitialState() {
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then((enabled) => {
+ is(enabled, false, "data should be off by default");
+ is(networkManager.activeNetworkInfo, null,
+ "networkManager.activeNetworkInfo should be null by default");
+ });
+}
+
+function testActiveNetworkChangedBySwitchingDataCall(aDataCallEnabled) {
+ log("Test active network by switching dataCallEnabled to " + aDataCallEnabled);
+
+ let promises = [];
+ promises.push(waitForObserverEvent(TOPIC_NETWORK_ACTIVE_CHANGED));
+ promises.push(setSettings(SETTINGS_KEY_DATA_ENABLED, aDataCallEnabled));
+
+ return Promise.all(promises).then(function(results) {
+ let subject = results[0];
+
+ if (aDataCallEnabled) {
+ ok(subject instanceof Ci.nsINetworkInfo,
+ "subject should be an instance of nsINetworkInfo");
+ ok(subject instanceof Ci.nsIRilNetworkInfo,
+ "subject should be an instance of nsIRilNetworkInfo");
+ is(subject.type, NETWORK_TYPE_MOBILE,
+ "subject.type should be NETWORK_TYPE_MOBILE");
+ }
+
+ is(subject, networkManager.activeNetworkInfo,
+ "subject should be equal with networkManager.activeNetworkInfo");
+ });
+}
+
+// Start test
+startTestBase(function() {
+ return testInitialState()
+ // Test active network changed by enabling data call.
+ .then(() => testActiveNetworkChangedBySwitchingDataCall(true))
+ // Test active network changed by disabling data call.
+ .then(() => testActiveNetworkChangedBySwitchingDataCall(false));
+});
diff --git a/dom/system/gonk/tests/marionette/test_network_interface_list_service.js b/dom/system/gonk/tests/marionette/test_network_interface_list_service.js
new file mode 100644
index 000000000..549940fa5
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_network_interface_list_service.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+function getNetworkInfo(aType) {
+ let networkListService =
+ Cc["@mozilla.org/network/interface-list-service;1"].
+ getService(Ci.nsINetworkInterfaceListService);
+ // Get all available interfaces
+ let networkList = networkListService.getDataInterfaceList(0);
+
+ // Try to get nsINetworkInterface for aType.
+ let numberOfInterface = networkList.getNumberOfInterface();
+ for (let i = 0; i < numberOfInterface; i++) {
+ let info = networkList.getInterfaceInfo(i);
+ if (info.type === aType) {
+ return info;
+ }
+ }
+
+ return null;
+}
+
+// Test getDataInterfaceList by enabling/disabling mobile data.
+function testGetDataInterfaceList(aMobileDataEnabled) {
+ log("Test getDataInterfaceList with mobile data " +
+ aMobileDataEnabled ? "enabled" : "disabled");
+
+ return setDataEnabledAndWait(aMobileDataEnabled)
+ .then(() => getNetworkInfo(NETWORK_TYPE_MOBILE))
+ .then((networkInfo) => {
+ if (!networkInfo) {
+ ok(false, "Should get an valid nsINetworkInfo for mobile");
+ return;
+ }
+
+ ok(networkInfo instanceof Ci.nsINetworkInfo,
+ "networkInfo should be an instance of nsINetworkInfo");
+
+ let ipAddresses = {};
+ let prefixs = {};
+ let numOfGateways = {};
+ let numOfDnses = {};
+ let numOfIpAddresses = networkInfo.getAddresses(ipAddresses, prefixs);
+ let gateways = networkInfo.getGateways(numOfGateways);
+ let dnses = networkInfo.getDnses(numOfDnses);
+
+ if (aMobileDataEnabled) {
+ // Mobile data is enabled.
+ is(networkInfo.state, NETWORK_STATE_CONNECTED, "check state");
+ ok(numOfIpAddresses > 0, "check number of ipAddresses");
+ ok(ipAddresses.value.length > 0, "check ipAddresses.length");
+ ok(prefixs.value.length > 0, "check prefixs.length");
+ ok(numOfGateways.value > 0, "check number of gateways");
+ ok(prefixs.value.length > 0, "check prefixs.length");
+ ok(gateways.length > 0, "check gateways.length");
+ ok(numOfDnses.value > 0, "check number of dnses");
+ ok(dnses.length > 0, "check dnses.length");
+ } else {
+ // Mobile data is disabled.
+ is(networkInfo.state, NETWORK_STATE_DISCONNECTED, "check state");
+ is(numOfIpAddresses, 0, "check number of ipAddresses");
+ is(ipAddresses.value.length, 0, "check ipAddresses.length");
+ is(prefixs.value.length, 0, "check prefixs.length");
+ is(numOfGateways.value, 0, "check number of gateways");
+ is(prefixs.value.length, 0, "check prefixs.length");
+ is(gateways.length, 0, "check gateways.length");
+ is(numOfDnses.value, 0, "check number of dnses");
+ is(dnses.length, 0, "check dnses.length");
+ }
+ });
+}
+
+// Start test
+startTestBase(function() {
+ return Promise.resolve()
+ // Test initial State
+ .then(() => {
+ log("Test initial state");
+
+ // Data should be off before starting any test.
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then(value => {
+ is(value, false, "Mobile data must be off");
+ });
+ })
+
+ // Test getDataInterfaceList with mobile data enabled.
+ .then(() => testGetDataInterfaceList(true))
+
+ // Test getDataInterfaceList with mobile data disabled.
+ .then(() => testGetDataInterfaceList(false));
+});
diff --git a/dom/system/gonk/tests/marionette/test_network_interface_mtu.js b/dom/system/gonk/tests/marionette/test_network_interface_mtu.js
new file mode 100644
index 000000000..679efe2ed
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_network_interface_mtu.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+const TEST_MTU1 = "1410";
+const TEST_MTU2 = "1440";
+
+function setEmulatorAPN() {
+ let apn = [
+ [ { "carrier":"T-Mobile US",
+ "apn":"epc1.tmobile.com",
+ "types":["default"],
+ "mtu": TEST_MTU1 },
+ { "carrier":"T-Mobile US",
+ "apn":"epc2.tmobile.com",
+ "mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc",
+ "types":["supl","mms","ims","dun", "fota"],
+ "mtu": TEST_MTU2 } ]
+ ];
+
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, apn);
+}
+
+function verifyInitialState() {
+ // Data should be off before starting any test.
+ return getSettings(SETTINGS_KEY_DATA_ENABLED)
+ .then(value => {
+ is(value, false, "Data must be off");
+ });
+}
+
+function verifyMtu(aInterfaceName, aMtu) {
+ return runEmulatorShellCmdSafe(['ip', 'link', 'show', 'dev', aInterfaceName])
+ .then(aLines => {
+ // Sample output:
+ //
+ // 4: rmnet0: <BROADCAST,MULTICAST> mtu 1410 qdisc pfifo_fast state DOWN mode DEFAULT qlen 1000
+ // link/ether 52:54:00:12:34:58 brd ff:ff:ff:ff:ff:ff
+ //
+ let mtu;
+ aLines.some(function (aLine) {
+ let tokens = aLine.trim().split(/\s+/);
+ let mtuIndex = tokens.indexOf('mtu');
+ if (mtuIndex < 0 || mtuIndex + 1 >= tokens.length) {
+ return false;
+ }
+
+ mtu = tokens[mtuIndex + 1];
+ return true;
+ });
+
+ is(mtu, aMtu, aInterfaceName + "'s mtu.");
+ });
+}
+
+function testDefaultDataCallMtu() {
+ log("= testDefaultDataCallMtu =");
+
+ return setDataEnabledAndWait(true)
+ .then(aNetworkInfo => verifyMtu(aNetworkInfo.name, TEST_MTU1))
+ .then(() => setDataEnabledAndWait(false));
+}
+
+function testNonDefaultDataCallMtu() {
+ log("= testNonDefaultDataCallMtu =");
+
+ function doTestNonDefaultDataCallMtu(aType) {
+ log("doTestNonDefaultDataCallMtu: " + aType);
+
+ return setupDataCallAndWait(aType)
+ .then(aNetworkInfo => verifyMtu(aNetworkInfo.name, TEST_MTU2))
+ .then(() => deactivateDataCallAndWait(aType));
+ }
+
+ return doTestNonDefaultDataCallMtu(NETWORK_TYPE_MOBILE_MMS)
+ .then(() => doTestNonDefaultDataCallMtu(NETWORK_TYPE_MOBILE_SUPL))
+ .then(() => doTestNonDefaultDataCallMtu(NETWORK_TYPE_MOBILE_IMS))
+ .then(() => doTestNonDefaultDataCallMtu(NETWORK_TYPE_MOBILE_DUN))
+ .then(() => doTestNonDefaultDataCallMtu(NETWORK_TYPE_MOBILE_FOTA));
+}
+
+// Start test
+startTestBase(function() {
+ let origApnSettings;
+ return verifyInitialState()
+ .then(() => getSettings(SETTINGS_KEY_DATA_APN_SETTINGS))
+ .then(value => {
+ origApnSettings = value;
+ })
+ .then(() => setEmulatorAPN())
+ .then(() => testDefaultDataCallMtu())
+ .then(() => testNonDefaultDataCallMtu())
+ .then(() => {
+ if (origApnSettings) {
+ return setSettings(SETTINGS_KEY_DATA_APN_SETTINGS, origApnSettings);
+ }
+ });
+});
diff --git a/dom/system/gonk/tests/marionette/test_ril_code_quality.py b/dom/system/gonk/tests/marionette/test_ril_code_quality.py
new file mode 100644
index 000000000..d741d8a2e
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_ril_code_quality.py
@@ -0,0 +1,371 @@
+"""
+The test performs the static code analysis check by JSHint.
+
+Target js files:
+- RadioInterfaceLayer.js
+- ril_worker.js
+- ril_consts.js
+
+If the js file contains the line of 'importScript()' (Ex: ril_worker.js), the
+test will perform a special merge step before excuting JSHint.
+
+Ex: Script A
+--------------------------------
+importScripts('Script B')
+...
+--------------------------------
+
+We merge these two scripts into one by the following way.
+
+--------------------------------
+[Script B (ex: ril_consts.js)]
+(function(){ [Script A (ex: ril_worker.js)]
+})();
+--------------------------------
+
+Script A (ril_worker.js) runs global strict mode.
+Script B (ril_consts.js) not.
+
+The above merge way ensures the correct scope of 'strict mode.'
+"""
+
+import bisect
+import inspect
+import os
+import os.path
+import re
+import unicodedata
+
+from marionette_harness import MarionetteTestCase
+
+
+class StringUtility:
+
+ """A collection of some string utilities."""
+
+ @staticmethod
+ def find_match_lines(lines, pattern):
+ """Return a list of lines that contains given pattern."""
+ return [line for line in lines if pattern in line]
+
+ @staticmethod
+ def remove_non_ascii(data):
+ """Remove non ascii characters in data and return it as new string."""
+ if type(data).__name__ == 'unicode':
+ data = unicodedata.normalize(
+ 'NFKD', data).encode('ascii', 'ignore')
+ return data
+
+ @staticmethod
+ def auto_close(lines):
+ """Ensure every line ends with '\n'."""
+ if lines and not lines[-1].endswith('\n'):
+ lines[-1] += '\n'
+ return lines
+
+ @staticmethod
+ def auto_wrap_strict_mode(lines):
+ """Wrap by function scope if lines contain 'use strict'."""
+ if StringUtility.find_match_lines(lines, 'use strict'):
+ lines[0] = '(function(){' + lines[0]
+ lines.append('})();\n')
+ return lines
+
+ @staticmethod
+ def get_imported_list(lines):
+ """Get a list of imported items."""
+ return [item
+ for line in StringUtility.find_match_lines(lines, 'importScripts')
+ for item in StringUtility._get_imported_list_from_line(line)]
+
+ @staticmethod
+ def _get_imported_list_from_line(line):
+ """Extract all items from 'importScripts(...)'.
+
+ importScripts("ril_consts.js", "systemlibs.js")
+ => ['ril_consts', 'systemlibs.js']
+
+ """
+ pattern = re.compile(r'\s*importScripts\((.*)\)')
+ m = pattern.match(line)
+ if not m:
+ raise Exception('Parse importScripts error.')
+ return [name.translate(None, '\' "') for name in m.group(1).split(',')]
+
+
+class ResourceUriFileReader:
+
+ """Handle the process of reading the source code from system."""
+
+ URI_PREFIX = 'resource://gre/'
+ URI_PATH = {
+ 'RadioInterfaceLayer.js': 'components/RadioInterfaceLayer.js',
+ 'ril_worker.js': 'modules/ril_worker.js',
+ 'ril_consts.js': 'modules/ril_consts.js',
+ 'systemlibs.js': 'modules/systemlibs.js',
+ 'worker_buf.js': 'modules/workers/worker_buf.js',
+ }
+
+ CODE_OPEN_CHANNEL_BY_URI = '''
+ var Cc = Components.classes;
+ var Ci = Components.interfaces;
+ var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+ global.uri = '%(uri)s';
+ global.channel = ios.newChannel2(global.uri,
+ null,
+ null,
+ null, // aLoadingNode
+ secMan.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ '''
+
+ CODE_GET_SPEC = '''
+ return global.channel.URI.spec;
+ '''
+
+ CODE_READ_CONTENT = '''
+ var Cc = Components.classes;
+ var Ci = Components.interfaces;
+
+ var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
+ var inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
+
+ var jaruri = global.channel.URI.QueryInterface(Ci.nsIJARURI);
+ var file = jaruri.JARFile.QueryInterface(Ci.nsIFileURL).file;
+ var entry = jaruri.JAREntry;
+ zipReader.open(file);
+ inputStream.init(zipReader.getInputStream(entry));
+ var content = inputStream.read(inputStream.available());
+ inputStream.close();
+ zipReader.close();
+ return content;
+ '''
+
+ @classmethod
+ def get_uri(cls, filename):
+ """Convert filename to URI in system."""
+ if filename.startswith(cls.URI_PREFIX):
+ return filename
+ else:
+ return cls.URI_PREFIX + cls.URI_PATH[filename]
+
+ def __init__(self, marionette):
+ self.runjs = lambda x: marionette.execute_script(x,
+ new_sandbox=False,
+ sandbox='system')
+
+ def read_file(self, filename):
+ """Read file and return the contents as string."""
+ content = self._read_uri(self.get_uri(filename))
+ content = content.replace('"use strict";', '')
+ return StringUtility.remove_non_ascii(content)
+
+ def _read_uri(self, uri):
+ """Read URI in system and return the contents as string."""
+ # Open the uri as a channel.
+ self.runjs(self.CODE_OPEN_CHANNEL_BY_URI % {'uri': uri})
+
+ # Make sure spec is a jar uri, and not recursive.
+ # Ex: 'jar:file:///system/b2g/omni.ja!/modules/ril_worker.js'
+ #
+ # For simplicity, we don't handle other special cases in this test.
+ # If B2G build system changes in the future, such as put the jar in
+ # another jar, the test case will fail.
+ spec = self.runjs(self.CODE_GET_SPEC)
+ if (not spec.startswith('jar:file://')) or (spec.count('jar:') != 1):
+ raise Exception('URI resolve error')
+
+ # Read the content from channel.
+ content = self.runjs(self.CODE_READ_CONTENT)
+ return content
+
+
+class JSHintEngine:
+
+ """Invoke jshint script on system."""
+
+ CODE_INIT_JSHINT = '''
+ %(script)s;
+ global.JSHINT = JSHINT;
+ global.options = JSON.parse(%(config_string)s);
+ global.globals = global.options.globals;
+ delete global.options.globals;
+ '''
+
+ CODE_RUN_JSHINT = '''
+ global.script = %(code)s;
+ return global.JSHINT(global.script, global.options, global.globals);
+ '''
+
+ CODE_GET_JSHINT_ERROR = '''
+ return global.JSHINT.errors;
+ '''
+
+ def __init__(self, marionette, script, config):
+ # Remove single line comment in config.
+ config = '\n'.join([line.partition('//')[0]
+ for line in config.splitlines()])
+
+ # Set global (JSHINT, options, global) in js environment.
+ self.runjs = lambda x: marionette.execute_script(x,
+ new_sandbox=False,
+ sandbox='system')
+ self.runjs(self.CODE_INIT_JSHINT %
+ {'script': script, 'config_string': repr(config)})
+
+ def run(self, code, filename=''):
+ """Excute JShint check for the given code."""
+ check_pass = self.runjs(self.CODE_RUN_JSHINT % {'code': repr(code)})
+ errors = self.runjs(self.CODE_GET_JSHINT_ERROR)
+ return check_pass, self._get_error_messages(errors, filename)
+
+ def _get_error_messages(self, errors, filename=''):
+ """
+ Convert an error object to a list of readable string.
+
+ [{"a": null, "c": null, "code": "W033", "d": null, "character": 6,
+ "evidence": "var a", "raw": "Missing semicolon.",
+ "reason": "Missing semicolon.", "b": null, "scope": "(main)", "line": 1,
+ "id": "(error)"}]
+ => line 1, col 6, Missing semicolon.
+
+ """
+ LINE, COL, REASON = u'line', u'character', u'reason'
+ return ["%s: line %s, col %s, %s" %
+ (filename, error[LINE], error[COL], error[REASON])
+ for error in errors if error]
+
+
+class Linter:
+
+ """Handle the linting related process."""
+
+ def __init__(self, code_reader, jshint, reporter=None):
+ """Set the linter with code_reader, jshint engine, and reporter.
+
+ Should have following functionality.
+ - code_reader.read_file(filename)
+ - jshint.run(code, filename)
+ - reporter([...])
+
+ """
+ self.code_reader = code_reader
+ self.jshint = jshint
+ if reporter is None:
+ self.reporter = lambda x: '\n'.join(x)
+ else:
+ self.reporter = reporter
+
+ def lint_file(self, filename):
+ """Lint the file and return (pass, error_message)."""
+ # Get code contents.
+ code = self.code_reader.read_file(filename)
+ lines = code.splitlines()
+ import_list = StringUtility.get_imported_list(lines)
+ if not import_list:
+ check_pass, error_message = self.jshint.run(code, filename)
+ else:
+ newlines, info = self._merge_multiple_codes(filename, import_list)
+ # Each line of |newlines| contains '\n'.
+ check_pass, error_message = self.jshint.run(''.join(newlines))
+ error_message = self._convert_merged_result(error_message, info)
+ # Only keep errors for this file.
+ error_message = [line for line in error_message
+ if line.startswith(filename)]
+ check_pass = (len(error_message) == 0)
+ return check_pass, self.reporter(error_message)
+
+ def _merge_multiple_codes(self, filename, import_list):
+ """Merge multiple codes from filename and import_list."""
+ dirname, filename = os.path.split(filename)
+ dst_line = 1
+ dst_results = []
+ info = []
+
+ # Put the imported script first, and then the original script.
+ for f in import_list + [filename]:
+ filepath = os.path.join(dirname, f)
+
+ # Maintain a mapping table.
+ # New line number after merge => original file and line number.
+ info.append((dst_line, filepath, 1))
+ try:
+ code = self.code_reader.read_file(filepath)
+ lines = code.splitlines(True) # Keep '\n'.
+ src_results = StringUtility.auto_wrap_strict_mode(
+ StringUtility.auto_close(lines))
+ dst_results.extend(src_results)
+ dst_line += len(src_results)
+ except:
+ info.pop()
+ return dst_results, info
+
+ def _convert_merged_result(self, error_lines, line_info):
+ pattern = re.compile(r'(.*): line (\d+),(.*)')
+ start_line = [info[0] for info in line_info]
+ new_result_lines = []
+ for line in error_lines:
+ m = pattern.match(line)
+ if not m:
+ continue
+
+ line_number, remain = int(m.group(2)), m.group(3)
+
+ # [1, 2, 7, 8]
+ # ^ for 7, pos = 3
+ # ^ for 6, pos = 2
+ pos = bisect.bisect_right(start_line, line_number)
+ dst_line, name, src_line = line_info[pos - 1]
+ real_line_number = line_number - dst_line + src_line
+ new_result_lines.append(
+ "%s: line %s,%s" % (name, real_line_number, remain))
+ return new_result_lines
+
+
+class TestRILCodeQuality(MarionetteTestCase):
+
+ JSHINT_PATH = 'ril_jshint/jshint.js'
+ JSHINTRC_PATH = 'ril_jshint/jshintrc'
+
+ def _read_local_file(self, filepath):
+ """Read file content from local (folder of this test case)."""
+ test_dir = os.path.dirname(inspect.getfile(TestRILCodeQuality))
+ return open(os.path.join(test_dir, filepath)).read()
+
+ def _get_extended_error_message(self, error_message):
+ return '\n'.join(['See errors below and more information in Bug 880643',
+ '\n'.join(error_message),
+ 'See errors above and more information in Bug 880643'])
+
+ def _check(self, filename):
+ check_pass, error_message = self.linter.lint_file(filename)
+ self.assertTrue(check_pass, error_message)
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.linter = Linter(
+ ResourceUriFileReader(self.marionette),
+ JSHintEngine(self.marionette,
+ self._read_local_file(self.JSHINT_PATH),
+ self._read_local_file(self.JSHINTRC_PATH)),
+ self._get_extended_error_message)
+
+ def tearDown(self):
+ MarionetteTestCase.tearDown(self)
+
+ def test_RadioInterfaceLayer(self):
+ self._check('RadioInterfaceLayer.js')
+
+ # Bug 936504. Disable the test for 'ril_worker.js'. It sometimes runs very
+ # slow and causes the timeout fail on try server.
+ #def test_ril_worker(self):
+ # self._check('ril_worker.js')
+
+ def test_ril_consts(self):
+ self._check('ril_consts.js')
+
+ def test_worker_buf(self):
+ self._check('worker_buf.js')
diff --git a/dom/system/gonk/tests/marionette/test_screen_state.js b/dom/system/gonk/tests/marionette/test_screen_state.js
new file mode 100644
index 000000000..2281412d5
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_screen_state.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 10000;
+
+var Services = SpecialPowers.Services;
+
+function testScreenState(on, expected, msg) {
+ // send event to RadioInterface
+ Services.obs.notifyObservers(null, 'screen-state-changed', on);
+ // maybe rild/qemu needs some time to process the event
+ window.setTimeout(function() {
+ runEmulatorCmd('gsm report creg', function(result) {
+ is(result.pop(), 'OK', '\'gsm report creg\' successful');
+ ok(result.indexOf(expected) !== -1, msg);
+ runNextTest();
+ })}, 1000);
+}
+
+function testScreenStateDisabled() {
+ testScreenState('off', '+CREG: 1', 'screen is disabled');
+}
+
+function testScreenStateEnabled() {
+ testScreenState('on', '+CREG: 2', 'screen is enabled');
+}
+
+var tests = [
+ testScreenStateDisabled,
+ testScreenStateEnabled
+];
+
+function runNextTest() {
+ let test = tests.shift();
+ if (!test) {
+ cleanUp();
+ return;
+ }
+
+ test();
+}
+
+function cleanUp() {
+ finish();
+}
+
+runNextTest();
diff --git a/dom/system/gonk/tests/marionette/test_timezone_changes.js b/dom/system/gonk/tests/marionette/test_timezone_changes.js
new file mode 100644
index 000000000..11dbaec5a
--- /dev/null
+++ b/dom/system/gonk/tests/marionette/test_timezone_changes.js
@@ -0,0 +1,135 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+function init() {
+ let promises = [];
+
+ /*
+ * The initial timezone of the emulator could be anywhere, depends the host
+ * machine. Ensure resetting it to UTC before testing.
+ */
+ promises.push(runEmulatorCmdSafe('gsm timezone 0'));
+ promises.push(new Promise((aResolve, aReject) => {
+ waitFor(aResolve, () => {
+ return new Date().getTimezoneOffset() === 0;
+ });
+ }));
+
+ return Promise.all(promises);
+}
+
+function paddingZeros(aNumber, aLength) {
+ let str = '' + aNumber;
+ while (str.length < aLength) {
+ str = '0' + str;
+ }
+
+ return str;
+}
+
+function verifyDate(aTestDate, aUTCOffsetDate) {
+ // Verify basic properties.
+ is(aUTCOffsetDate.getUTCFullYear(), aTestDate.getFullYear(), 'year');
+ is(aUTCOffsetDate.getUTCMonth(), aTestDate.getMonth(), 'month');
+ is(aUTCOffsetDate.getUTCDate(), aTestDate.getDate(), 'date');
+ is(aUTCOffsetDate.getUTCHours(), aTestDate.getHours(), 'hours');
+ is(aUTCOffsetDate.getUTCMinutes(), aTestDate.getMinutes(), 'minutes');
+ is(aUTCOffsetDate.getUTCMilliseconds(), aTestDate.getMilliseconds(), 'milliseconds');
+
+ // Ensure toLocaleString also uses correct timezone.
+ // It uses ICU's timezone instead of the offset calculated from gecko prtime.
+ let expectedDateString =
+ paddingZeros(aUTCOffsetDate.getUTCMonth() + 1, 2) + '/' +
+ paddingZeros(aUTCOffsetDate.getUTCDate(), 2);
+ let dateString = aTestDate.toLocaleString('en-US', {
+ month: '2-digit',
+ day: '2-digit',
+ });
+ let expectedTimeString =
+ paddingZeros(aUTCOffsetDate.getUTCHours(), 2) + ':' +
+ paddingZeros(aUTCOffsetDate.getUTCMinutes(), 2);
+ let timeString = aTestDate.toLocaleString('en-US', {
+ hour12: false,
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+
+ is(expectedDateString, dateString, 'dateString');
+ is(expectedTimeString, timeString, 'timeString');
+}
+
+function waitForTimezoneUpdate(aTzOffset,
+ aTestDateInMillis = 86400000, // Use 'UTC 00:00:00, 2nd of Jan, 1970' by default.
+ aTransTzOffset, aTransTestDateInMillis) {
+ return new Promise(function(aResolve, aReject) {
+ window.addEventListener('moztimechange', function onevent(aEvent) {
+ // Since there could be multiple duplicate moztimechange event, wait until
+ // timezone is actually changed to expected value before removing the
+ // listener.
+ let testDate = new Date(aTestDateInMillis);
+ if (testDate.getTimezoneOffset() === aTzOffset) {
+ window.removeEventListener('moztimechange', onevent);
+
+ // The UTC time of offsetDate is the same as the expected local time of
+ // testDate. We'll use it to verify the values.
+ let offsetDate = new Date(aTestDateInMillis - aTzOffset * 60 * 1000);
+ verifyDate(testDate, offsetDate);
+
+ // Verify transition time if given.
+ if (aTransTzOffset !== undefined) {
+ testDate = new Date(aTransTestDateInMillis);
+ is(testDate.getTimezoneOffset(), aTransTzOffset);
+
+ // Verify transition date.
+ offsetDate = new Date(aTransTestDateInMillis - aTransTzOffset * 60 * 1000);
+ verifyDate(testDate, offsetDate);
+ }
+
+ aResolve(aEvent);
+ }
+ });
+ });
+}
+
+function testChangeNitzTimezone(aTzDiff) {
+ let promises = [];
+
+ // aTzOffset should be the expected value for getTimezoneOffset().
+ // Note that getTimezoneOffset() is not so straightforward,
+ // it values (UTC - localtime), so UTC+08:00 returns -480.
+ promises.push(waitForTimezoneUpdate(-aTzDiff * 15));
+ promises.push(runEmulatorCmdSafe('gsm timezone ' + aTzDiff));
+
+ return Promise.all(promises);
+}
+
+function testChangeOlsonTimezone(aOlsonTz, aTzOffset, aTestDateInMillis,
+ aTransTzOffset, aTransTestDateInMillis) {
+ let promises = [];
+
+ promises.push(waitForTimezoneUpdate(aTzOffset, aTestDateInMillis,
+ aTransTzOffset, aTransTestDateInMillis));
+ promises.push(setSettings('time.timezone', aOlsonTz));
+
+ return Promise.all(promises);
+}
+
+// Start test
+startTestBase(function() {
+ return init()
+ .then(() => testChangeNitzTimezone(36)) // UTC+09:00
+ .then(() => testChangeOlsonTimezone('America/New_York',
+ 300, 1446357600000, // 2015/11/01 02:00 UTC-04:00 => 01:00 UTC-05:00 (EST)
+ 240, 1425798000000)) // 2015/03/08 02:00 UTC-05:00 => 03:00 UTC-04:00 (EDT)
+ .then(() => testChangeNitzTimezone(-22)) // UTC-05:30
+ .then(() => testChangeNitzTimezone(51)) // UTC+12:45
+ .then(() => testChangeOlsonTimezone('Australia/Adelaide',
+ -570, 1428165000000, // 2015/04/05 03:00 UTC+10:30 => 02:00 UTC+09:30 (ACST)
+ -630, 1443889800000)) // 2015/10/04 02:00 UTC+09:30 => 03:00 UTC+10:30 (ACDT)
+ .then(() => testChangeNitzTimezone(-38)) // UTC-09:30
+ .then(() => testChangeNitzTimezone(0)) // UTC
+ .then(() => runEmulatorCmdSafe('gsm timezone auto'));
+});