diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index a2a7fbc8..e9d1838a 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -69,6 +69,12 @@ var ElasticTabstopsLite = require("ace/ext/elastic_tabstops_lite").ElasticTabsto var IncrementalSearch = require("ace/incremental_search").IncrementalSearch; + +var workerModule = require("ace/worker/worker_client"); +if (location.href.indexOf("noworker" !== -1)) { + workerModule.WorkerClient = workerModule.UIWorkerClient; +} + /*********** create editor ***************************/ var container = document.getElementById("editor-container"); diff --git a/demo/kitchen-sink/require.js b/demo/kitchen-sink/require.js index 9199e0fb..2109a251 100644 --- a/demo/kitchen-sink/require.js +++ b/demo/kitchen-sink/require.js @@ -1,5 +1,5 @@ /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -12,7 +12,7 @@ var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.1.5', + version = '2.1.6', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, @@ -22,7 +22,7 @@ var requirejs, require, define; hasOwn = op.hasOwnProperty, ap = Array.prototype, apsp = ap.splice, - isBrowser = !!(typeof window !== 'undefined' && navigator && document), + isBrowser = !!(typeof window !== 'undefined' && navigator && window.document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined', //PS3 indicates loaded and complete, but need to wait for complete //specifically. Sequence is 'loading', 'loaded', execution, @@ -134,6 +134,10 @@ var requirejs, require, define; return document.getElementsByTagName('script'); } + function defaultOnError(err) { + throw err; + } + //Allow getting a global that expressed in //dot notation, like 'a.b.c'. function getGlobal(value) { @@ -500,7 +504,12 @@ var requirejs, require, define; fn(defined[id]); } } else { - getModule(depMap).on(name, fn); + mod = getModule(depMap); + if (mod.error && name === 'error') { + fn(mod.error); + } else { + mod.on(name, fn); + } } } @@ -571,7 +580,13 @@ var requirejs, require, define; id: mod.map.id, uri: mod.map.url, config: function () { - return (config.config && getOwn(config.config, mod.map.id)) || {}; + var c, + pkg = getOwn(config.pkgs, mod.map.id); + // For packages, only support config targeted + // at the main module. + c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) : + getOwn(config.config, mod.map.id); + return c || {}; }, exports: defined[mod.map.id] }); @@ -840,8 +855,13 @@ var requirejs, require, define; if (this.depCount < 1 && !this.defined) { if (isFunction(factory)) { //If there is an error listener, favor passing - //to that instead of throwing an error. - if (this.events.error) { + //to that instead of throwing an error. However, + //only do it for define()'d modules. require + //errbacks should not be called for failures in + //their callbacks (#699). However if a global + //onError is set, use that. + if ((this.events.error && this.map.isDefine) || + req.onError !== defaultOnError) { try { exports = context.execCb(id, factory, depExports, exports); } catch (e) { @@ -869,8 +889,8 @@ var requirejs, require, define; if (err) { err.requireMap = this.map; - err.requireModules = [this.map.id]; - err.requireType = 'define'; + err.requireModules = this.map.isDefine ? [this.map.id] : null; + err.requireType = this.map.isDefine ? 'define' : 'require'; return onError((this.error = err)); } @@ -1093,7 +1113,7 @@ var requirejs, require, define; })); if (this.errback) { - on(depMap, 'error', this.errback); + on(depMap, 'error', bind(this, this.errback)); } } @@ -1605,7 +1625,7 @@ var requirejs, require, define; }, /** - * Executes a module callack function. Broken out as a separate function + * Executes a module callback function. Broken out as a separate function * solely to allow the build system to sequence the files in the built * layer in the right sequence. * @@ -1643,7 +1663,7 @@ var requirejs, require, define; onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { - return onError(makeError('scripterror', 'Script error', evt, [data.id])); + return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); } } }; @@ -1772,9 +1792,7 @@ var requirejs, require, define; * function. Intercept/override it if you want custom error handling. * @param {Error} err the error object. */ - req.onError = function (err) { - throw err; - }; + req.onError = defaultOnError; /** * Does the request to load a module for the browser case. @@ -1906,24 +1924,31 @@ var requirejs, require, define; //baseUrl, if it is not already set. dataMain = script.getAttribute('data-main'); if (dataMain) { + //Preserve dataMain in case it is a path (i.e. contains '?') + mainScript = dataMain; + //Set final baseUrl if there is not already an explicit one. if (!cfg.baseUrl) { //Pull off the directory of data-main for use as the //baseUrl. - src = dataMain.split('/'); + src = mainScript.split('/'); mainScript = src.pop(); subPath = src.length ? src.join('/') + '/' : './'; cfg.baseUrl = subPath; - dataMain = mainScript; } - //Strip off any trailing .js since dataMain is now + //Strip off any trailing .js since mainScript is now //like a module name. - dataMain = dataMain.replace(jsSuffixRegExp, ''); + mainScript = mainScript.replace(jsSuffixRegExp, ''); + + //If mainScript is still a path, fall back to dataMain + if (req.jsExtRegExp.test(mainScript)) { + mainScript = dataMain; + } //Put the data-main script in the files to load. - cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; + cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; return true; } diff --git a/lib/ace/mode/coffee/helpers.js b/lib/ace/mode/coffee/helpers.js index 0ea2f3fe..3208602f 100644 --- a/lib/ace/mode/coffee/helpers.js +++ b/lib/ace/mode/coffee/helpers.js @@ -25,7 +25,7 @@ */ define(function(require, exports, module) { -// Generated by CoffeeScript 1.6.2 +// Generated by CoffeeScript 1.6.3 var buildLocationData, extend, flatten, last, repeat, _ref; @@ -214,11 +214,11 @@ define(function(require, exports, module) { }; exports.throwSyntaxError = function(message, location) { - var error, _ref1, _ref2; - if ((_ref1 = location.last_line) == null) { + var error; + if (location.last_line == null) { location.last_line = location.first_line; } - if ((_ref2 = location.last_column) == null) { + if (location.last_column == null) { location.last_column = location.first_column; } error = new SyntaxError(message); @@ -226,11 +226,13 @@ define(function(require, exports, module) { throw error; }; - exports.prettyErrorMessage = function(error, fileName, code, useColors) { + exports.prettyErrorMessage = function(error, filename, code, useColors) { var codeLine, colorize, end, first_column, first_line, last_column, last_line, marker, message, start, _ref1; if (!error.location) { return error.stack || ("" + error); } + filename = error.filename || filename; + code = error.code || code; _ref1 = error.location, first_line = _ref1.first_line, first_column = _ref1.first_column, last_line = _ref1.last_line, last_column = _ref1.last_column; codeLine = code.split('\n')[first_line]; start = first_column; @@ -243,7 +245,7 @@ define(function(require, exports, module) { codeLine = codeLine.slice(0, start) + colorize(codeLine.slice(start, end)) + codeLine.slice(end); marker = colorize(marker); } - message = "" + fileName + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + error.message + "\n" + codeLine + "\n" + marker; + message = "" + filename + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + error.message + "\n" + codeLine + "\n" + marker; return message; }; diff --git a/lib/ace/mode/coffee/lexer.js b/lib/ace/mode/coffee/lexer.js index e493b09d..ea7497b7 100644 --- a/lib/ace/mode/coffee/lexer.js +++ b/lib/ace/mode/coffee/lexer.js @@ -25,14 +25,14 @@ */ define(function(require, exports, module) { -// Generated by CoffeeScript 1.6.2 +// Generated by CoffeeScript 1.6.3 - var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, starts, throwSyntaxError, _ref, _ref1, + var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; _ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES; - _ref1 = require('./helpers'), count = _ref1.count, starts = _ref1.starts, compact = _ref1.compact, last = _ref1.last, invertLiterate = _ref1.invertLiterate, locationDataToString = _ref1.locationDataToString, throwSyntaxError = _ref1.throwSyntaxError; + _ref1 = require('./helpers'), count = _ref1.count, starts = _ref1.starts, compact = _ref1.compact, last = _ref1.last, repeat = _ref1.repeat, invertLiterate = _ref1.invertLiterate, locationDataToString = _ref1.locationDataToString, throwSyntaxError = _ref1.throwSyntaxError; exports.Lexer = Lexer = (function() { function Lexer() {} @@ -182,10 +182,10 @@ define(function(require, exports, module) { } lexedLength = number.length; if (octalLiteral = /^0o([0-7]+)/.exec(number)) { - number = '0x' + (parseInt(octalLiteral[1], 8)).toString(16); + number = '0x' + parseInt(octalLiteral[1], 8).toString(16); } if (binaryLiteral = /^0b([01]+)/.exec(number)) { - number = '0x' + (parseInt(binaryLiteral[1], 2)).toString(16); + number = '0x' + parseInt(binaryLiteral[1], 2).toString(16); } this.token('NUMBER', number, 0, lexedLength); return lexedLength; @@ -255,7 +255,7 @@ define(function(require, exports, module) { if (here) { this.token('HERECOMMENT', this.sanitizeHeredoc(here, { herecomment: true, - indent: Array(this.indent + 1).join(' ') + indent: repeat(' ', this.indent) }), 0, comment.length); } return comment.length; @@ -737,7 +737,7 @@ define(function(require, exports, module) { column = this.chunkColumn; if (lineCount > 0) { lines = string.split('\n'); - column = (last(lines)).length; + column = last(lines).length; } else { column += string.length; } @@ -903,9 +903,9 @@ define(function(require, exports, module) { BOOL = ['TRUE', 'FALSE']; - NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--', ']']; + NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--']; - NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING'); + NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING', ']'); CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']; diff --git a/lib/ace/mode/coffee/nodes.js b/lib/ace/mode/coffee/nodes.js index ef26ecf1..dbab75a8 100644 --- a/lib/ace/mode/coffee/nodes.js +++ b/lib/ace/mode/coffee/nodes.js @@ -25,7 +25,7 @@ */ define(function(require, exports, module) { -// Generated by CoffeeScript 1.6.2 +// Generated by CoffeeScript 1.6.3 var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, CodeFragment, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, last, locationDataToString, merge, multident, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1, _ref2, _ref3, __hasProp = {}.hasOwnProperty, @@ -71,7 +71,7 @@ define(function(require, exports, module) { } CodeFragment.prototype.toString = function() { - return "" + this.code + [this.locationData ? ": " + locationDataToString(this.locationData) : void 0]; + return "" + this.code + (this.locationData ? ": " + locationDataToString(this.locationData) : ''); }; return CodeFragment; @@ -503,6 +503,8 @@ define(function(require, exports, module) { fragments.push(this.makeCode(scope.assignedVariables().join(",\n" + (this.tab + TAB)))); } fragments.push(this.makeCode(";\n" + (this.spaced ? '\n' : ''))); + } else if (fragments.length && post.length) { + fragments.push(this.makeCode("\n")); } } return fragments.concat(post); @@ -662,7 +664,7 @@ define(function(require, exports, module) { Return.prototype.compileNode = function(o) { var answer; answer = []; - answer.push(this.makeCode(this.tab + ("return" + [this.expression ? " " : void 0]))); + answer.push(this.makeCode(this.tab + ("return" + (this.expression ? " " : "")))); if (this.expression) { answer = answer.concat(this.expression.compileToFragments(o, LEVEL_PAREN)); } @@ -801,17 +803,16 @@ define(function(require, exports, module) { }; Value.prototype.unfoldSoak = function(o) { - var _ref4, - _this = this; - return (_ref4 = this.unfoldedSoak) != null ? _ref4 : this.unfoldedSoak = (function() { - var fst, i, ifn, prop, ref, snd, _i, _len, _ref5, _ref6; + var _this = this; + return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (function() { + var fst, i, ifn, prop, ref, snd, _i, _len, _ref4, _ref5; if (ifn = _this.base.unfoldSoak(o)) { - (_ref5 = ifn.body.properties).push.apply(_ref5, _this.properties); + (_ref4 = ifn.body.properties).push.apply(_ref4, _this.properties); return ifn; } - _ref6 = _this.properties; - for (i = _i = 0, _len = _ref6.length; _i < _len; i = ++_i) { - prop = _ref6[i]; + _ref5 = _this.properties; + for (i = _i = 0, _len = _ref5.length; _i < _len; i = ++_i) { + prop = _ref5[i]; if (!prop.soak) { continue; } @@ -1336,7 +1337,7 @@ define(function(require, exports, module) { } answer.push.apply(answer, fragments); } - if ((fragmentsToText(answer)).indexOf('\n') >= 0) { + if (fragmentsToText(answer).indexOf('\n') >= 0) { answer.unshift(this.makeCode("[\n" + o.indent)); answer.push(this.makeCode("\n" + this.tab + "]")); } else { @@ -2334,7 +2335,7 @@ define(function(require, exports, module) { Op.prototype.compileExistence = function(o) { var fst, ref; - if (this.first.isComplex()) { + if (!o.isExistentialEquals && this.first.isComplex()) { ref = new Literal(o.scope.freeVariable('ref')); fst = new Parens(new Assign(ref, this.first)); } else { @@ -2438,7 +2439,7 @@ define(function(require, exports, module) { var fragments, ref, sub, _ref4; _ref4 = this.object.cache(o, LEVEL_LIST), sub = _ref4[0], ref = _ref4[1]; fragments = [].concat(this.makeCode(utility('indexOf') + ".call("), this.array.compileToFragments(o, LEVEL_LIST), this.makeCode(", "), ref, this.makeCode(") " + (this.negated ? '< 0' : '>= 0'))); - if ((fragmentsToText(sub)) === (fragmentsToText(ref))) { + if (fragmentsToText(sub) === fragmentsToText(ref)) { return fragments; } fragments = sub.concat(this.makeCode(', '), fragments); @@ -2610,6 +2611,9 @@ define(function(require, exports, module) { if (this.range && this.pattern) { this.name.error('cannot pattern match over range loops'); } + if (this.own && !this.object) { + this.index.error('cannot use own with for-in'); + } this.returns = false; } diff --git a/lib/ace/mode/coffee/rewriter.js b/lib/ace/mode/coffee/rewriter.js index a25ff29f..009d17d0 100644 --- a/lib/ace/mode/coffee/rewriter.js +++ b/lib/ace/mode/coffee/rewriter.js @@ -25,9 +25,9 @@ */ define(function(require, exports, module) { -// Generated by CoffeeScript 1.6.2 +// Generated by CoffeeScript 1.6.3 - var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref, + var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __slice = [].slice; @@ -300,7 +300,7 @@ define(function(require, exports, module) { startImplicitCall(i + 1); return forward(2); } - if (this.matchTags(i, IMPLICIT_FUNC, 'INDENT', null, ':') && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) { + if (__indexOf.call(IMPLICIT_FUNC, tag) >= 0 && this.matchTags(i + 1, 'INDENT', null, ':') && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) { startImplicitCall(i + 1); stack.push(['INDENT', i + 2]); return forward(3); @@ -385,8 +385,8 @@ define(function(require, exports, module) { var action, condition, indent, outdent, starter; starter = indent = outdent = null; condition = function(token, i) { - var _ref; - return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && (starter !== 'IF' && starter !== 'THEN')); + var _ref, _ref1; + return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref1 = token[0]) === 'CATCH' || _ref1 === 'FINALLY') && (starter === '->' || starter === '=>')); }; action = function(token, i) { return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent); @@ -501,8 +501,6 @@ define(function(require, exports, module) { IMPLICIT_UNSPACED_CALL = ['+', '-']; - IMPLICIT_BLOCK = ['->', '=>', '{', '[', ',']; - IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR']; SINGLE_LINERS = ['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN']; diff --git a/lib/ace/mode/coffee/scope.js b/lib/ace/mode/coffee/scope.js index e0b47928..5834963b 100644 --- a/lib/ace/mode/coffee/scope.js +++ b/lib/ace/mode/coffee/scope.js @@ -25,7 +25,7 @@ */ define(function(require, exports, module) { -// Generated by CoffeeScript 1.6.2 +// Generated by CoffeeScript 1.6.3 var Scope, extend, last, _ref; diff --git a/lib/ace/mode/javascript/jshint.js b/lib/ace/mode/javascript/jshint.js index 64dc933d..45091639 100644 --- a/lib/ace/mode/javascript/jshint.js +++ b/lib/ace/mode/javascript/jshint.js @@ -1,11 +1,4237 @@ -define(function(require, exports, module) { +define(function() { +var require = function (file, cwd) { + var resolved = require.resolve(file, cwd || '/'); + var mod = require.modules[resolved]; + if (!mod) throw new Error( + 'Failed to resolve module ' + file + ', tried ' + resolved + ); + var cached = require.cache[resolved]; + var res = cached? cached.exports : mod(); + return res; +}; + +require.paths = []; +require.modules = {}; +require.cache = {}; +require.extensions = [".js",".coffee",".json"]; + +require._core = { + 'assert': true, + 'events': true, + 'fs': true, + 'path': true, + 'vm': true +}; + +require.resolve = (function () { + return function (x, cwd) { + if (!cwd) cwd = '/'; + + if (require._core[x]) return x; + var path = require.modules.path(); + cwd = path.resolve('/', cwd); + var y = cwd || '/'; + + if (x.match(/^(?:\.\.?\/|\/)/)) { + var m = loadAsFileSync(path.resolve(y, x)) + || loadAsDirectorySync(path.resolve(y, x)); + if (m) return m; + } + + var n = loadNodeModulesSync(x, y); + if (n) return n; + + throw new Error("Cannot find module '" + x + "'"); + + function loadAsFileSync (x) { + x = path.normalize(x); + if (require.modules[x]) { + return x; + } + + for (var i = 0; i < require.extensions.length; i++) { + var ext = require.extensions[i]; + if (require.modules[x + ext]) return x + ext; + } + } + + function loadAsDirectorySync (x) { + x = x.replace(/\/+$/, ''); + var pkgfile = path.normalize(x + '/package.json'); + if (require.modules[pkgfile]) { + var pkg = require.modules[pkgfile](); + var b = pkg.browserify; + if (typeof b === 'object' && b.main) { + var m = loadAsFileSync(path.resolve(x, b.main)); + if (m) return m; + } + else if (typeof b === 'string') { + var m = loadAsFileSync(path.resolve(x, b)); + if (m) return m; + } + else if (pkg.main) { + var m = loadAsFileSync(path.resolve(x, pkg.main)); + if (m) return m; + } + } + + return loadAsFileSync(x + '/index'); + } + + function loadNodeModulesSync (x, start) { + var dirs = nodeModulesPathsSync(start); + for (var i = 0; i < dirs.length; i++) { + var dir = dirs[i]; + var m = loadAsFileSync(dir + '/' + x); + if (m) return m; + var n = loadAsDirectorySync(dir + '/' + x); + if (n) return n; + } + + var m = loadAsFileSync(x); + if (m) return m; + } + + function nodeModulesPathsSync (start) { + var parts; + if (start === '/') parts = [ '' ]; + else parts = path.normalize(start).split('/'); + + var dirs = []; + for (var i = parts.length - 1; i >= 0; i--) { + if (parts[i] === 'node_modules') continue; + var dir = parts.slice(0, i + 1).join('/') + '/node_modules'; + dirs.push(dir); + } + + return dirs; + } + }; +})(); + +require.alias = function (from, to) { + var path = require.modules.path(); + var res = null; + try { + res = require.resolve(from + '/package.json', '/'); + } + catch (err) { + res = require.resolve(from, '/'); + } + var basedir = path.dirname(res); + + var keys = (Object.keys || function (obj) { + var res = []; + for (var key in obj) res.push(key); + return res; + })(require.modules); + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (key.slice(0, basedir.length + 1) === basedir + '/') { + var f = key.slice(basedir.length); + require.modules[to + f] = require.modules[basedir + f]; + } + else if (key === basedir) { + require.modules[to] = require.modules[basedir]; + } + } +}; + +(function () { + var process = {}; + var global = typeof window !== 'undefined' ? window : {}; + var definedProcess = false; + + require.define = function (filename, fn) { + if (!definedProcess && require.modules.__browserify_process) { + process = require.modules.__browserify_process(); + definedProcess = true; + } + + var dirname = require._core[filename] + ? '' + : require.modules.path().dirname(filename) + ; + + var require_ = function (file) { + var requiredModule = require(file, dirname); + var cached = require.cache[require.resolve(file, dirname)]; + + if (cached && cached.parent === null) { + cached.parent = module_; + } + + return requiredModule; + }; + require_.resolve = function (name) { + return require.resolve(name, dirname); + }; + require_.modules = require.modules; + require_.define = require.define; + require_.cache = require.cache; + var module_ = { + id : filename, + filename: filename, + exports : {}, + loaded : false, + parent: null + }; + + require.modules[filename] = function () { + require.cache[filename] = module_; + fn.call( + module_.exports, + require_, + module_, + module_.exports, + dirname, + filename, + process, + global + ); + module_.loaded = true; + return module_.exports; + }; + }; +})(); + + +require.define("path",function(require,module,exports,__dirname,__filename,process,global) { +function filter (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + if (fn(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length; i >= 0; i--) { + var last = parts[i]; + if (last == '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Regex to split a filename into [*, dir, basename, ext] +// posix version +var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { +var resolvedPath = '', + resolvedAbsolute = false; + +for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) + ? arguments[i] + : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; +} + +// At this point the path should be resolved to a full absolute path, but +// handle relative paths to be safe (might happen when process.cwd() fails) + +// Normalize the path +resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { +var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.slice(-1) === '/'; + +// Normalize the path +path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + return p && typeof p === 'string'; + }).join('/')); +}; + + +exports.dirname = function(path) { + var dir = splitPathRe.exec(path)[1] || ''; + var isWindows = false; + if (!dir) { + // No dirname + return '.'; + } else if (dir.length === 1 || + (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) { + // It is just a slash or a drive letter with a slash + return dir; + } else { + // It is a full dirname, strip trailing slash + return dir.substring(0, dir.length - 1); + } +}; + + +exports.basename = function(path, ext) { + var f = splitPathRe.exec(path)[2] || ''; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPathRe.exec(path)[3] || ''; +}; + +//@ sourceURL=path +}); + +require.define("__browserify_process",function(require,module,exports,__dirname,__filename,process,global) { +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 window.setImmediate; + } + + if (canPost) { + var queue = []; + window.addEventListener('message', function (ev) { + if (ev.source === window && ev.data === 'browserify-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('browserify-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +process.binding = function (name) { + if (name === 'evals') return (require)('vm') + else throw new Error('No such module. (Possibly not yet loaded)') +}; + +(function () { + var cwd = '/'; + var path; + process.cwd = function () { return cwd }; + process.chdir = function (dir) { + if (!path) path = require('path'); + cwd = path.resolve(dir, cwd); + }; +})(); + +//@ sourceURL=__browserify_process +}); + +require.define("/node_modules/underscore/package.json",function(require,module,exports,__dirname,__filename,process,global) { +module.exports = {"main":"underscore.js"} +//@ sourceURL=/node_modules/underscore/package.json +}); + +require.define("/node_modules/underscore/underscore.js",function(require,module,exports,__dirname,__filename,process,global) { +// Underscore.js 1.4.0 +// http://underscorejs.org +// (c) 2009-2012 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, + unshift = ArrayProto.unshift, + 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.0'; + + // 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 (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 (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; + }; + + // **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 (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('Reduce of empty array with no initial value'); + 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 (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return arguments.length > 2 ? 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('Reduce of empty array with no initial value'); + 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 (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) { + var results = []; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // 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 (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 (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) { + var found = false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + found = any(obj, function(value) { + return value === target; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (_.isFunction(method) ? 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 + // with specific `key:value` pairs. + _.where = function(obj, attrs) { + if (_.isEmpty(attrs)) return []; + return _.filter(obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return 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}; + 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}; + 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); + 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, value) { + 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 (obj.length === +obj.length) return slice.call(obj); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + 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) { + 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 ((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, function(value){ return !!value; }); + }; + + // 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) { + 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) { + 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) { + 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, fromIndex) { + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item, fromIndex); + var i = (fromIndex != null ? fromIndex : 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 + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function bind(func, context) { + var bound, args; + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // 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, throttling, more, result; + var whenDone = _.debounce(function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) { + result = func.apply(context, args); + } + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + throttling = true; + result = func.apply(context, args); + } + whenDone(); + 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) { + 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) { + 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 _.isNumber(obj) && isFinite(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) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + (0 | Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + 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) { + 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]; }); + source += + escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" : + interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" : + evaluate ? "';\n" + evaluate + "\n__p+='" : ''; + index = offset + match.length; + }); + 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 { + var 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); + +//@ sourceURL=/node_modules/underscore/underscore.js +}); + +require.define("events",function(require,module,exports,__dirname,__filename,process,global) { +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]' + } +; + +// 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 = list.indexOf(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) { + // 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]; +}; + +//@ sourceURL=events +}); + +require.define("/src/shared/vars.js",function(require,module,exports,__dirname,__filename,process,global) { +"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, + 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, + 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, + XPathNamespace : false, + XPathNSResolver : false, + XPathResult : false +}; + +exports.devel = { + alert : false, + confirm: false, + console: false, + Debug : false, + opera : false, + prompt : false +}; + +exports.worker = { + importScripts: true, + postMessage : true, + self : true +}; + +// Widely adopted global names that are not part of ECMAScript standard +exports.nonstandard = { + escape : false, + unescape: false +}; + +// Globals provided by popular JavaScript environments. + +exports.couch = { + "require" : false, + respond : false, + getRow : false, + emit : false, + send : false, + start : false, + sum : false, + log : false, + exports : false, + module : false, + provides : false +}; + +exports.node = { + __filename : false, + __dirname : false, + Buffer : false, + 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 +}; + +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 +}; + + +//@ sourceURL=/src/shared/vars.js +}); + +require.define("/src/shared/messages.js",function(require,module,exports,__dirname,__filename,process,global) { +"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 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." +}; + +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.", + W029: "Label '{a}' looks like a javascript url.", + 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 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: "Assignment in conditional expression", + 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.", +}; + +var info = { + I001: "Comma warnings can be turned off with 'laxcomma'." +}; + +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 }; +}); + +//@ sourceURL=/src/shared/messages.js +}); + +require.define("/src/stable/lex.js",function(require,module,exports,__dirname,__filename,process,global) { +/* + * 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 +} + +/* + * 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)); + }, + + /* + * 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; + } + + /* 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: "<<=" + }; + } + + // 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.character; + + // 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; + } + + 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.input(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 () { + var quote = this.peek(); + + // String must start with a quote. + if (quote !== "\"" && quote !== "'") { + return null; + } + + // In JSON strings must always use double quotes. + if (state.jsonMode && quote !== "\"") { + this.trigger("warning", { + code: "W108", + line: this.line, + character: this.char // +1? + }); + } + + 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. + + if (!state.option.multistr) { + this.trigger("warning", { + code: "W043", + line: this.line, + character: this.char + }); + } else if (state.jsonMode) { + this.trigger("warning", { + code: "W042", + line: this.line, + character: this.char + }); + } + } + + // 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: [ "" ] + }); + } + + // Special treatment for some escaped characters. + + if (char === "\\") { + this.skip(); + char = this.peek(); + + switch (char) { + case "'": + if (state.jsonMode) { + this.trigger("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\'" ] + }); + } + 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(), 10); + if (n >= 0 && n <= 7 && state.directive["use strict"]) { + this.trigger("warning", { + code: "W115", + line: this.line, + character: this.char + }); + } + break; + case "u": + char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16)); + jump = 5; + break; + case "v": + if (state.jsonMode) { + this.trigger("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\v" ] + }); + } + + char = "\v"; + break; + case "x": + var x = parseInt(this.input.substr(1, 2), 16); + + if (state.jsonMode) { + this.trigger("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\x-" ] + }); + } + + 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(/(\/\/)? \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 () { + 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 (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(); + + 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 () { + var token; + + // 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 (!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; + } + + return obj; + }.bind(this); + + for (;;) { + if (!this.input.length) { + return create(this.nextLine() ? "(endline)" : "(end)", ""); + } + + token = this.next(); + + 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.trigger("String", { + line: this.line, + char: this.character, + from: this.from, + value: token.value, + quote: token.quote + }); + + return create("(string)", token.value); + case Token.Identifier: + this.trigger("Identifier", { + line: this.line, + char: this.character, + 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 ] + }); + } + + if (state.jsonMode && token.base === 16) { + this.trigger("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "0x-" ] + }); + } + + if (state.directive["use strict"] && token.base === 8) { + this.trigger("warning", { + code: "W115", + line: this.line, + character: this.char + }); + } + + this.trigger("Number", { + line: this.line, + char: this.character, + 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.character, + from: this.from + }; + } + + break; + + case "": + break; + + default: + return create("(punctuator)", token.value); + } + } + } +}; + +exports.Lexer = Lexer; +//@ sourceURL=/src/stable/lex.js +}); + +require.define("/src/stable/reg.js",function(require,module,exports,__dirname,__filename,process,global) { +/* + * Regular expressions. Some of these are stupidly long. + */ + +/*jshint maxlen:1000 */ + +"use string"; + +// Unsafe comment or string (ax) +exports.unsafeString = + /@cc|<\/?|script|\]\s*\]|<\s*!|</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*$/; +//@ sourceURL=/src/stable/reg.js +}); + +require.define("/src/stable/state.js",function(require,module,exports,__dirname,__filename,process,global) { +"use strict"; + +var state = { + syntax: {}, + + reset: function () { + this.tokens = { + prev: null, + next: null, + curr: null + }, + + this.option = {}; + this.directive = {}; + this.jsonMode = false; + this.lines = []; + this.tab = ""; + this.cache = {}; // Node.JS doesn't have Map. Sniff. + } +}; + +exports.state = state; +//@ sourceURL=/src/stable/state.js +}); + +require.define("/src/stable/style.js",function(require,module,exports,__dirname,__filename,process,global) { +"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 + }); + } + }); +}; +//@ sourceURL=/src/stable/style.js +}); + +require.define("/src/stable/jshint.js",function(require,module,exports,__dirname,__filename,process,global) { /*! * 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. - * - * JSHint is a derivative work of JSLint: + * MIT license that JSLint is. It stops evil-doers everywhere: * * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) * @@ -31,198 +4257,16 @@ define(function(require, exports, module) { * */ -/* - JSHINT is a global function. It takes two parameters. +/*jshint quotmark:double */ - var myResult = JSHINT(source, option); - - The first parameter is either a string or an array of strings. If it is a - string, it will be split on '\n' or '\r'. If it is an array of strings, it - is assumed that each string represents one line. The source can be a - JavaScript text or a JSON text. - - The second parameter is an optional object of options which control the - operation of JSHINT. Most of the options are booleans: They are all - optional and have a default value of false. One of the options, predef, - can be an array of names, which will be used to declare global variables, - or an object whose keys are used as global names, with a boolean value - that determines if they are assignable. - - If it checks out, JSHINT returns true. Otherwise, it returns false. - - If false, you can inspect JSHINT.errors to find out the problems. - JSHINT.errors is an array of objects containing these members: - - { - line : The line (relative to 1) at which the lint was found - character : The character (relative to 1) at which the lint was found - reason : The problem - evidence : The text line in which the problem occurred - raw : The raw message before the details were inserted - a : The first detail - b : The second detail - c : The third detail - d : The fourth detail - } - - If a fatal error was found, a null will be the last element of the - JSHINT.errors array. - - You can request a data structure which contains JSHint's results. - - var myData = JSHINT.data(); - - It returns a structure with this form: - - { - errors: [ - { - line: NUMBER, - character: NUMBER, - reason: STRING, - evidence: STRING - } - ], - functions: [ - name: STRING, - line: NUMBER, - character: NUMBER, - last: NUMBER, - lastcharacter: NUMBER, - param: [ - STRING - ], - closure: [ - STRING - ], - var: [ - STRING - ], - exception: [ - STRING - ], - outer: [ - STRING - ], - unused: [ - STRING - ], - global: [ - STRING - ], - label: [ - STRING - ] - ], - globals: [ - STRING - ], - member: { - STRING: NUMBER - }, - unused: [ - { - name: STRING, - line: NUMBER - } - ], - implieds: [ - { - name: STRING, - line: NUMBER - } - ], - urls: [ - STRING - ], - json: BOOLEAN - } - - Empty arrays will not be included. - -*/ - -/*jshint - evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true, - undef: true, maxlen: 100, indent: 4, quotmark: double, unused: true -*/ - -/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)", - "(breakage)", "(character)", "(context)", "(error)", "(explicitNewcap)", "(global)", - "(identifier)", "(last)", "(lastcharacter)", "(line)", "(loopage)", "(metrics)", - "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "(tokens)", "(catch)", - "*", "+", "++", "-", "--", "\/", "<", "<=", "==", - "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax, - __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio, - Autocompleter, Asset, Boolean, Builder, Buffer, Browser, Blob, COM, CScript, Canvas, - CustomAnimation, Class, Control, ComplexityCount, Chain, Color, Cookie, Core, DataView, Date, - Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMEvent, DOMReady, DOMParser, - Drag, E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event, - Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form, - FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey, - HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement, - HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement, - HTMLDivElement, HTMLDListElement, HTMLFieldSetElement, - HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement, - HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement, - HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement, - HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement, - HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement, - HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement, - HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement, - HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement, - HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement, - HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement, - HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement, - Iframe, IframeShim, Image, importScripts, Int16Array, Int32Array, Int8Array, - Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E, - MAX_VALUE, MIN_VALUE, Map, Mask, Math, MenuItem, MessageChannel, MessageEvent, MessagePort, - MoveAnimation, MooTools, MutationObserver, NaN, Native, NEGATIVE_INFINITY, Node, NodeFilter, - Number, Object, ObjectRange, - Option, Options, OverText, PI, POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype, - RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation, Set, - SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion, - ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller, - Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables, - SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template, - Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL, - VBArray, WeakMap, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XMLSerializer, - XPathEvaluator, XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult, - "\\", a, abs, addEventListener, address, alert, apply, applicationCache, arguments, arity, - asi, atob, b, basic, basicToken, bitwise, blacklist, block, blur, boolOptions, boss, - browser, btoa, c, call, callee, caller, camelcase, cases, charAt, charCodeAt, character, - clearInterval, clearTimeout, close, closed, closure, comment, complexityCount, condition, - confirm, console, constructor, content, couch, create, css, curly, d, data, datalist, dd, debug, - decodeURI, decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document, - dojo, dijit, dojox, define, else, emit, encodeURI, encodeURIComponent, elem, - eqeq, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil, - ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus, forEach, - forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions, - g, gc, getComputedStyle, getRow, getter, getterToken, GLOBAL, global, globals, globalstrict, - hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include, - indent, indexOf, init, ins, internals, instanceOf, isAlpha, isApplicationRunning, isArray, - isDigit, isFinite, isNaN, iterator, java, join, jshint, - JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastcharacter, lastsemic, laxbreak, - laxcomma, latedef, lbp, led, left, length, line, load, loadClass, localStorage, location, - log, loopfunc, m, match, max, maxcomplexity, maxdepth, maxerr, maxlen, maxstatements, maxparams, - member, message, meta, module, moveBy, moveTo, mootools, multistr, name, navigator, new, newcap, - nestedBlockDepth, noarg, node, noempty, nomen, nonew, nonstandard, nud, onbeforeunload, onblur, - onerror, onevar, onecase, onfocus, onload, onresize, onunload, open, openDatabase, openURL, - opener, opera, options, outer, param, parent, parseFloat, parseInt, passfail, plusplus, - postMessage, pop, predef, print, process, prompt, proto, prototype, prototypejs, provides, push, - quit, quotmark, range, raw, reach, reason, regexp, readFile, readUrl, regexdash, - removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, resolvePath, - resumeUpdates, respond, rhino, right, runCommand, scroll, scope, screen, scripturl, scrollBy, - scrollTo, scrollbar, search, seal, self, send, serialize, sessionStorage, setInterval, setTimeout, - setter, setterToken, shift, slice, smarttabs, sort, spawn, split, statement, statementCount, stack, - status, start, strict, sub, substr, supernew, shadow, supplant, sum, sync, test, toLowerCase, - toString, toUpperCase, toint32, token, tokens, top, trailing, type, typeOf, Uint16Array, - Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis, value, valueOf, var, vars, - version, verifyMaxParametersPerFunction, verifyMaxStatementsPerFunction, - verifyMaxComplexityPerFunction, verifyMaxNestedBlockDepthPerFunction, WebSocket, withstmt, white, - window, windows, Worker, worker, wsh, yui, YUI, Y, YUI_config*/ - -/*global exports: false */ +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 build the application inside a function so that we produce only a single // global variable. That function will be invoked immediately, and its return @@ -253,685 +4297,213 @@ var JSHINT = (function () { // 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 - es5 : true, // if ES5 syntax should be allowed - esnext : true, // if es.next 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 - 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. - latedef : true, // if the use before definition should not be tolerated - 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 + 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 + es5 : true, // if ES5 syntax should be allowed + esnext : true, // if es.next 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 + 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. + latedef : true, // if the use before definition should not be tolerated + 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 - onecase : true, // if one case switch statements should be allowed - passfail : true, // if the scan should stop on first error - plusplus : true, // if increment/decrement should not be allowed - proto : true, // if the `__proto__` property should be allowed + // 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 + 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 - regexdash : true, // if unescaped first/last dash (-) inside brackets - // should be tolerated - regexp : true, // if the . should not be allowed in regexp literals - rhino : true, // if the Rhino environment globals should be predefined - undef : true, // if variables should be declared before used - unused : true, // if variables should be always 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 + // predefined + rhino : true, // if the Rhino environment globals should be predefined + undef : true, // if variables should be declared before used + unused : true, // if variables should be always 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, + 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 + maxdepth : false, // {int} max nested block depth per function + maxparams : false, // {int} max params per function maxcomplexity: false // {int} max cyclomatic complexity per function }, // 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, + 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 + eqeqeq : true, + onevar : 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" - }, - - - // browser contains a set of global names which are commonly provided by a - // web browser environment. - 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, - 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, - top : false, - Uint16Array : false, - Uint32Array : false, - Uint8Array : false, - WebSocket : false, - window : false, - Worker : false, - XMLHttpRequest : false, - XMLSerializer : false, - XPathEvaluator : false, - XPathException : false, - XPathExpression : false, - XPathNamespace : false, - XPathNSResolver : false, - XPathResult : false - }, - - couch = { - "require" : false, - respond : false, - getRow : false, - emit : false, - send : false, - start : false, - sum : false, - log : false, - exports : false, - module : false, - provides : false + eqeq : "eqeqeq", + vars : "onevar", + windows: "wsh" }, declared, // Globals that were declared using /*global ... */ syntax. - - devel = { - alert : false, - confirm : false, - console : false, - Debug : false, - opera : false, - prompt : false - }, - - dojo = { - dojo : false, - dijit : false, - dojox : false, - define : false, - "require" : false - }, - - funct, // The current function + exported, // Variables that are used outside of the current file. functionicity = [ "closure", "exception", "global", "label", "outer", "unused", "var" ], - functions, // All of the functions + funct, // The current function + functions, // All of the functions - global, // The global scope - implied, // Implied globals + global, // The global scope + ignored, // Ignored warnings + implied, // Implied globals inblock, indent, - jsonmode, - - jquery = { - "$" : false, - jQuery : false - }, - - lines, lookahead, + lex, member, membersOnly, - - 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 - }, - - nexttoken, - - node = { - __filename : false, - __dirname : false, - Buffer : false, - console : false, - exports : true, // In Node it is ok to exports = module.exports = foo(); - GLOBAL : false, - global : false, - module : false, - process : false, - require : false, - setTimeout : false, - clearTimeout : false, - setInterval : false, - clearInterval : false - }, - noreach, - option, predefined, // Global variables defined by option - prereg, - prevtoken, - 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 - }, - - quotmark, - - 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 - }, - - scope, // The current scope + scope, // The current scope stack, - - // standard contains the global names that are provided by the - // ECMAScript standard. - standard = { - 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, - Map : false, - Math : false, - NaN : 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 - }, - - // widely adopted global names that are not part of ECMAScript standard - nonstandard = { - escape : false, - unescape : false - }, - - directive, - syntax = {}, - tab, - token, unuseds, urls, useESNextSyntax, warnings, - worker = { - importScripts : true, - postMessage : true, - self : true - }, - - wsh = { - ActiveXObject : true, - Enumerator : true, - GetObject : true, - ScriptEngine : true, - ScriptEngineBuildVersion : true, - ScriptEngineMajorVersion : true, - ScriptEngineMinorVersion : true, - VBArray : true, - WSH : true, - WScript : true, - XDomainRequest : true - }, - - yui = { - YUI : false, - Y : false, - YUI_config : false - }; - // Regular expressions. Some of these are stupidly long. - var ax, cx, tx, nx, nxg, lx, ix, jx, ft; - (function () { - /*jshint maxlen:300 */ - - // unsafe comment or string - ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i; - - // unsafe characters that are silently deleted by one or more browsers - cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; - - // token - tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/=(?!(\S*\/[gim]?))|\/(\*(jshint|jslint|members?|global)?|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/; - - // characters in strings that need escapement - nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; - nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - - // star slash - lx = /\*\//; - - // identifier - ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; - - // javascript url - jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; - - // catches /* falls through */ comments - ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; - }()); - - function F() {} // Used by Object.create - - function is_own(object, name) { - // The object.hasOwnProperty method fails when the property under consideration - // is named 'hasOwnProperty'. So we have to use this more convoluted form. - return Object.prototype.hasOwnProperty.call(object, name); - } + extraModules = [], + emitter = new events.EventEmitter(); function checkOption(name, t) { - if (valOptions[name] === undefined && boolOptions[name] === undefined) { - warning("Bad option: '" + 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" || renamedOptions[name] === undefined) { + error("E001", t, name); + return false; + } + } + + return true; } function isString(obj) { return Object.prototype.toString.call(obj) === "[object String]"; } - // Provide critical ES5 functions to ES3. - - if (typeof Array.isArray !== "function") { - Array.isArray = function (o) { - return Object.prototype.toString.apply(o) === "[object Array]"; - }; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = function (fn, scope) { - var len = this.length; - - for (var i = 0; i < len; i++) { - fn.call(scope || this, this[i], i, this); - } - }; - } - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { - if (this === null || this === undefined) { - throw new TypeError(); - } - - var t = new Object(this); - var len = t.length >>> 0; - - if (len === 0) { - return -1; - } - - var n = 0; - if (arguments.length > 0) { - n = Number(arguments[1]); - if (n != n) { // shortcut for verifying if it's NaN - n = 0; - } else if (n !== 0 && n != Infinity && n != -Infinity) { - n = (n > 0 || -1) * Math.floor(Math.abs(n)); - } - } - - if (n >= len) { - return -1; - } - - var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); - for (; k < len; k++) { - if (k in t && t[k] === searchElement) { - return k; - } - } - - return -1; - }; - } - - if (typeof Object.create !== "function") { - Object.create = function (o) { - F.prototype = o; - return new F(); - }; - } - - if (typeof Object.keys !== "function") { - Object.keys = function (o) { - var a = [], k; - for (k in o) { - if (is_own(o, k)) { - a.push(k); - } - } - return a; - }; - } - - // Non standard methods - - function isAlpha(str) { - return (str >= "a" && str <= "z\uffff") || - (str >= "A" && str <= "Z\uffff"); - } - - function isDigit(str) { - return (str >= "0" && str <= "9"); - } - - function isIdentifier(token, value) { - if (!token) + function isIdentifier(tkn, value) { + if (!tkn) return false; - if (!token.identifier || token.value !== value) + 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.es5 && !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]; @@ -942,7 +4514,7 @@ var JSHINT = (function () { function combine(t, o) { var n; for (n in o) { - if (is_own(o, n) && !is_own(JSHINT.blacklist, n)) { + if (_.has(o, n) && !_.has(JSHINT.blacklist, n)) { t[n] = o[n]; } } @@ -955,72 +4527,72 @@ var JSHINT = (function () { } function assume() { - if (option.couch) { - combine(predefined, couch); + if (state.option.couch) { + combine(predefined, vars.couch); } - if (option.rhino) { - combine(predefined, rhino); + if (state.option.rhino) { + combine(predefined, vars.rhino); } - if (option.prototypejs) { - combine(predefined, prototypejs); + if (state.option.prototypejs) { + combine(predefined, vars.prototypejs); } - if (option.node) { - combine(predefined, node); - option.globalstrict = true; + if (state.option.node) { + combine(predefined, vars.node); } - if (option.devel) { - combine(predefined, devel); + if (state.option.devel) { + combine(predefined, vars.devel); } - if (option.dojo) { - combine(predefined, dojo); + if (state.option.dojo) { + combine(predefined, vars.dojo); } - if (option.browser) { - combine(predefined, browser); + if (state.option.browser) { + combine(predefined, vars.browser); } - if (option.nonstandard) { - combine(predefined, nonstandard); + if (state.option.nonstandard) { + combine(predefined, vars.nonstandard); } - if (option.jquery) { - combine(predefined, jquery); + if (state.option.jquery) { + combine(predefined, vars.jquery); } - if (option.mootools) { - combine(predefined, mootools); + if (state.option.mootools) { + combine(predefined, vars.mootools); } - if (option.worker) { - combine(predefined, worker); + if (state.option.worker) { + combine(predefined, vars.worker); } - if (option.wsh) { - combine(predefined, wsh); + if (state.option.wsh) { + combine(predefined, vars.wsh); } - if (option.esnext) { + if (state.option.esnext) { useESNextSyntax(); } - if (option.globalstrict && option.strict !== false) { - option.strict = true; + if (state.option.globalstrict && state.option.strict !== false) { + state.option.strict = true; } - if (option.yui) { - combine(predefined, yui); + if (state.option.yui) { + combine(predefined, vars.yui); } } // Produce an error warning. - function quit(message, line, chr) { - var percentage = Math.floor((line / lines.length) * 100); + function quit(code, line, chr) { + var percentage = Math.floor((line / state.lines.length) * 100); + var message = messages.errors[code].desc; throw { name: "JSHintError", @@ -1031,22 +4603,38 @@ var JSHINT = (function () { }; } - function isundef(scope, m, t, a) { - return JSHINT.undefs.push([scope, m, t, a]); + function isundef(scope, code, token, a) { + return JSHINT.undefs.push([scope, code, token, a]); } - function warning(m, t, a, b, c, d) { - var ch, l, w; - t = t || nexttoken; - if (t.id === "(end)") { // `~ - t = token; + function warning(code, t, a, b, c, d) { + var ch, l, w, msg; + + if (/^W\d{3}$/.test(code)) { + if (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: m, - evidence: lines[l - 1] || "", + raw: msg.desc, + code: msg.code, + evidence: state.lines[l - 1] || "", line: l, character: ch, scope: JSHINT.scope, @@ -1055,15 +4643,19 @@ var JSHINT = (function () { c: c, d: d }; - w.reason = supplant(m, w); + + w.reason = supplant(msg.desc, w); JSHINT.errors.push(w); - if (option.passfail) { - quit("Stopping. ", l, ch); + + if (state.option.passfail) { + quit("E042", l, ch); } + warnings += 1; - if (warnings >= option.maxerr) { - quit("Too many errors.", l, ch); + if (warnings >= state.option.maxerr) { + quit("E043", l, ch); } + return w; } @@ -1097,790 +4689,40 @@ var JSHINT = (function () { return i; } - -// lexical analysis and token construction - - var lex = (function lex() { - var character, from, line, s; - -// Private lex methods - - function nextLine() { - var at, - match, - tw; // trailing whitespace check - - if (line >= lines.length) - return false; - - character = 1; - s = lines[line]; - line += 1; - - // If smarttabs option is used check for spaces followed by tabs only. - // Otherwise check for any occurence of mixed tabs and spaces. - // Tabs and one space followed by block comment is allowed. - if (option.smarttabs) { - // negative look-behind for "//" - match = s.match(/(\/\/)? \t/); - at = match && !match[1] ? 0 : -1; - } else { - at = s.search(/ \t|\t [^\*]/); - } - - if (at >= 0) - warningAt("Mixed spaces and tabs.", line, at + 1); - - s = s.replace(/\t/g, tab); - at = s.search(cx); - - if (at >= 0) - warningAt("Unsafe character.", line, at); - - if (option.maxlen && option.maxlen < s.length) - warningAt("Line too long.", line, s.length); - - // Check for trailing whitespaces - tw = option.trailing && s.match(/^(.*?)\s+$/); - if (tw && !/^\s+$/.test(s)) { - warningAt("Trailing whitespace.", line, tw[1].length + 1); - } - return true; - } - -// Produce a token object. The token inherits from a syntax symbol. - - function it(type, value) { - var i, t; - - function checkName(name) { - if (!option.proto && name === "__proto__") { - warningAt("The '{a}' property is deprecated.", line, from, name); - return; - } - - if (!option.iterator && name === "__iterator__") { - warningAt("'{a}' is only available in JavaScript 1.7.", line, from, name); - return; - } - - // Check for dangling underscores unless we're in Node - // environment and this identifier represents built-in - // Node globals with underscores. - - var hasDangling = /^(_+.*|.*_+)$/.test(name); - - if (option.nomen && hasDangling && name !== "_") { - if (option.node && token.id !== "." && /^(__dirname|__filename)$/.test(name)) - return; - - warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", name); - return; - } - - // Check for non-camelcase names. Names like MY_VAR and - // _myVar are okay though. - - if (option.camelcase) { - if (name.replace(/^_+/, "").indexOf("_") > -1 && !name.match(/^[A-Z0-9_]*$/)) { - warningAt("Identifier '{a}' is not in camel case.", line, from, value); - } - } - } - - if (type === "(color)" || type === "(range)") { - t = {type: type}; - } else if (type === "(punctuator)" || - (type === "(identifier)" && is_own(syntax, value))) { - t = syntax[value] || syntax["(error)"]; - } else { - t = syntax[type]; - } - - t = Object.create(t); - - if (type === "(string)" || type === "(range)") { - if (!option.scripturl && jx.test(value)) { - warningAt("Script URL.", line, from); - } - } - - if (type === "(identifier)") { - t.identifier = true; - checkName(value); - } - - t.value = value; - t.line = line; - t.character = character; - t.from = from; - i = t.id; - if (i !== "(endline)") { - prereg = i && - (("(,=:[!&|?{};".indexOf(i.charAt(i.length - 1)) >= 0) || - i === "return" || - i === "case"); - } - return t; - } - - // Public lex methods - return { - init: function (source) { - if (typeof source === "string") { - lines = source - .replace(/\r\n/g, "\n") - .replace(/\r/g, "\n") - .split("\n"); - } else { - lines = source; - } - - // 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] = ""; - - line = 0; - nextLine(); - from = 1; - }, - - range: function (begin, end) { - var c, value = ""; - from = character; - if (s.charAt(0) !== begin) { - errorAt("Expected '{a}' and instead saw '{b}'.", - line, character, begin, s.charAt(0)); - } - for (;;) { - s = s.slice(1); - character += 1; - c = s.charAt(0); - switch (c) { - case "": - errorAt("Missing '{a}'.", line, character, c); - break; - case end: - s = s.slice(1); - character += 1; - return it("(range)", value); - case "\\": - warningAt("Unexpected '{a}'.", line, character, c); - } - value += c; - } - - }, - - - // token -- this is called by advance to get the next token - token: function () { - var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange, n; - - function match(x) { - var r = x.exec(s), r1; - - if (r) { - l = r[0].length; - r1 = r[1]; - c = r1.charAt(0); - s = s.substr(l); - from = character + l - r1.length; - character += l; - return r1; - } - } - - function string(x) { - var c, j, r = "", allowNewLine = false; - - if (jsonmode && x !== "\"") { - warningAt("Strings must use doublequote.", - line, character); - } - - if (option.quotmark) { - if (option.quotmark === "single" && x !== "'") { - warningAt("Strings must use singlequote.", - line, character); - } else if (option.quotmark === "double" && x !== "\"") { - warningAt("Strings must use doublequote.", - line, character); - } else if (option.quotmark === true) { - quotmark = quotmark || x; - if (quotmark !== x) { - warningAt("Mixed double and single quotes.", - line, character); - } - } - } - - function esc(n) { - var i = parseInt(s.substr(j + 1, n), 16); - j += n; - if (i >= 32 && i <= 126 && - i !== 34 && i !== 92 && i !== 39) { - warningAt("Unnecessary escapement.", line, character); - } - character += n; - c = String.fromCharCode(i); - } - - j = 0; - -unclosedString: - for (;;) { - while (j >= s.length) { - j = 0; - - var cl = line, cf = from; - if (!nextLine()) { - errorAt("Unclosed string.", cl, cf); - break unclosedString; - } - - if (allowNewLine) { - allowNewLine = false; - } else { - warningAt("Unclosed string.", cl, cf); - } - } - - c = s.charAt(j); - if (c === x) { - character += 1; - s = s.substr(j + 1); - return it("(string)", r, x); - } - - if (c < " ") { - if (c === "\n" || c === "\r") { - break; - } - warningAt("Control character in string: {a}.", - line, character + j, s.slice(0, j)); - } else if (c === "\\") { - j += 1; - character += 1; - c = s.charAt(j); - n = s.charAt(j + 1); - switch (c) { - case "\\": - case "\"": - case "/": - break; - case "\'": - if (jsonmode) { - warningAt("Avoid \\'.", line, character); - } - break; - case "b": - c = "\b"; - break; - case "f": - c = "\f"; - break; - case "n": - c = "\n"; - break; - case "r": - c = "\r"; - break; - case "t": - c = "\t"; - break; - case "0": - c = "\0"; - // Octal literals fail in strict mode - // check if the number is between 00 and 07 - // where 'n' is the token next to 'c' - if (n >= 0 && n <= 7 && directive["use strict"]) { - warningAt( - "Octal literals are not allowed in strict mode.", - line, character); - } - break; - case "u": - esc(4); - break; - case "v": - if (jsonmode) { - warningAt("Avoid \\v.", line, character); - } - c = "\v"; - break; - case "x": - if (jsonmode) { - warningAt("Avoid \\x-.", line, character); - } - esc(2); - break; - case "": - // last character is escape character - // always allow new line if escaped, but show - // warning if option is not set - allowNewLine = true; - if (option.multistr) { - if (jsonmode) { - warningAt("Avoid EOL escapement.", line, character); - } - c = ""; - character -= 1; - break; - } - warningAt("Bad escapement of EOL. Use option multistr if needed.", - line, character); - break; - case "!": - if (s.charAt(j - 2) === "<") - break; - /*falls through*/ - default: - warningAt("Bad escapement.", line, character); - } - } - r += c; - character += 1; - j += 1; - } - } - - for (;;) { - if (!s) { - return it(nextLine() ? "(endline)" : "(end)", ""); - } - - t = match(tx); - - if (!t) { - t = ""; - c = ""; - while (s && s < "!") { - s = s.substr(1); - } - if (s) { - errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); - s = ""; - } - } else { - - // identifier - - if (isAlpha(c) || c === "_" || c === "$") { - return it("(identifier)", t); - } - - // number - - if (isDigit(c)) { - if (!isFinite(Number(t))) { - warningAt("Bad number '{a}'.", - line, character, t); - } - if (isAlpha(s.substr(0, 1))) { - warningAt("Missing space after '{a}'.", - line, character, t); - } - if (c === "0") { - d = t.substr(1, 1); - if (isDigit(d)) { - if (token.id !== ".") { - warningAt("Don't use extra leading zeros '{a}'.", - line, character, t); - } - } else if (jsonmode && (d === "x" || d === "X")) { - warningAt("Avoid 0x-. '{a}'.", - line, character, t); - } - } - if (t.substr(t.length - 1) === ".") { - warningAt( -"A trailing decimal point can be confused with a dot '{a}'.", line, character, t); - } - return it("(number)", t); - } - switch (t) { - - // string - - case "\"": - case "'": - return string(t); - - // // comment - - case "//": - s = ""; - token.comment = true; - break; - - // /* comment - - case "/*": - for (;;) { - i = s.search(lx); - if (i >= 0) { - break; - } - if (!nextLine()) { - errorAt("Unclosed comment.", line, character); - } - } - s = s.substr(i + 2); - token.comment = true; - break; - - // /*members /*jshint /*global - - case "/*members": - case "/*member": - case "/*jshint": - case "/*jslint": - case "/*global": - case "*/": - return { - value: t, - type: "special", - line: line, - character: character, - from: from - }; - - case "": - break; - // / - case "/": - if (s.charAt(0) === "=") { - errorAt("A regular expression literal can be confused with '/='.", - line, from); - } - - if (prereg) { - depth = 0; - captures = 0; - l = 0; - for (;;) { - b = true; - c = s.charAt(l); - l += 1; - switch (c) { - case "": - errorAt("Unclosed regular expression.", line, from); - return quit("Stopping.", line, from); - case "/": - if (depth > 0) { - warningAt("{a} unterminated regular expression " + - "group(s).", line, from + l, depth); - } - c = s.substr(0, l - 1); - q = { - g: true, - i: true, - m: true - }; - while (q[s.charAt(l)] === true) { - q[s.charAt(l)] = false; - l += 1; - } - character += l; - s = s.substr(l); - q = s.charAt(0); - if (q === "/" || q === "*") { - errorAt("Confusing regular expression.", - line, from); - } - return it("(regexp)", c); - case "\\": - c = s.charAt(l); - if (c < " ") { - warningAt( -"Unexpected control character in regular expression.", line, from + l); - } else if (c === "<") { - warningAt( -"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); - } - l += 1; - break; - case "(": - depth += 1; - b = false; - if (s.charAt(l) === "?") { - l += 1; - switch (s.charAt(l)) { - case ":": - case "=": - case "!": - l += 1; - break; - default: - warningAt( -"Expected '{a}' and instead saw '{b}'.", line, from + l, ":", s.charAt(l)); - } - } else { - captures += 1; - } - break; - case "|": - b = false; - break; - case ")": - if (depth === 0) { - warningAt("Unescaped '{a}'.", - line, from + l, ")"); - } else { - depth -= 1; - } - break; - case " ": - q = 1; - while (s.charAt(l) === " ") { - l += 1; - q += 1; - } - if (q > 1) { - warningAt( -"Spaces are hard to count. Use {{a}}.", line, from + l, q); - } - break; - case "[": - c = s.charAt(l); - if (c === "^") { - l += 1; - if (s.charAt(l) === "]") { - errorAt("Unescaped '{a}'.", - line, from + l, "^"); - } - } - if (c === "]") { - warningAt("Empty class.", line, - from + l - 1); - } - isLiteral = false; - isInRange = false; -klass: - do { - c = s.charAt(l); - l += 1; - switch (c) { - case "[": - case "^": - warningAt("Unescaped '{a}'.", - line, from + l, c); - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - case "-": - if (isLiteral && !isInRange) { - isLiteral = false; - isInRange = true; - } else if (isInRange) { - isInRange = false; - } else if (s.charAt(l) === "]") { - isInRange = true; - } else { - if (option.regexdash !== (l === 2 || (l === 3 && - s.charAt(1) === "^"))) { - warningAt("Unescaped '{a}'.", - line, from + l - 1, "-"); - } - isLiteral = true; - } - break; - case "]": - if (isInRange && !option.regexdash) { - warningAt("Unescaped '{a}'.", - line, from + l - 1, "-"); - } - break klass; - case "\\": - c = s.charAt(l); - if (c < " ") { - warningAt( -"Unexpected control character in regular expression.", line, from + l); - } else if (c === "<") { - warningAt( -"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); - } - l += 1; - - // \w, \s and \d are never part of a character range - if (/[wsd]/i.test(c)) { - if (isInRange) { - warningAt("Unescaped '{a}'.", - line, from + l, "-"); - isInRange = false; - } - isLiteral = false; - } else if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - case "/": - warningAt("Unescaped '{a}'.", - line, from + l - 1, "/"); - - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - case "<": - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - default: - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - } - } while (c); - break; - case ".": - if (option.regexp) { - warningAt("Insecure '{a}'.", line, - from + l, c); - } - break; - case "]": - case "?": - case "{": - case "}": - case "+": - case "*": - warningAt("Unescaped '{a}'.", line, - from + l, c); - } - if (b) { - switch (s.charAt(l)) { - case "?": - case "+": - case "*": - l += 1; - if (s.charAt(l) === "?") { - l += 1; - } - break; - case "{": - l += 1; - c = s.charAt(l); - if (c < "0" || c > "9") { - warningAt( -"Expected a number and instead saw '{a}'.", line, from + l, c); - break; // No reason to continue checking numbers. - } - l += 1; - low = +c; - for (;;) { - c = s.charAt(l); - if (c < "0" || c > "9") { - break; - } - l += 1; - low = +c + (low * 10); - } - high = low; - if (c === ",") { - l += 1; - high = Infinity; - c = s.charAt(l); - if (c >= "0" && c <= "9") { - l += 1; - high = +c; - for (;;) { - c = s.charAt(l); - if (c < "0" || c > "9") { - break; - } - l += 1; - high = +c + (high * 10); - } - } - } - if (s.charAt(l) !== "}") { - warningAt( -"Expected '{a}' and instead saw '{b}'.", line, from + l, "}", c); - } else { - l += 1; - } - if (s.charAt(l) === "?") { - l += 1; - } - if (low > high) { - warningAt( -"'{a}' should not be greater than '{b}'.", line, from + l, low, high); - } - } - } - } - c = s.substr(0, l - 1); - character += l; - s = s.substr(l); - return it("(regexp)", c); - } - return it("(punctuator)", t); - - // punctuator - - case "#": - return it("(punctuator)", t); - default: - return it("(punctuator)", t); - } - } - } - } - }; - }()); - - - function addlabel(t, type, token) { - if (t === "hasOwnProperty") { - warning("'hasOwnProperty' is a really bad name."); - } - + function addlabel(t, type, tkn) { // Define t in the current function in the current scope. if (type === "exception") { - if (is_own(funct["(context)"], t)) { - if (funct[t] !== true && !option.node) { - warning("Value of '{a}' may be overwritten in IE.", nexttoken, t); + if (_.has(funct["(context)"], t)) { + if (funct[t] !== true && !state.option.node) { + warning("W002", state.tokens.next, t); } } } - if (is_own(funct, t) && !funct["(global)"]) { + if (_.has(funct, t) && !funct["(global)"]) { if (funct[t] === true) { - if (option.latedef) - warning("'{a}' was used before it was defined.", nexttoken, t); + if (state.option.latedef) + warning("W003", state.tokens.next, t); } else { - if (!option.shadow && type !== "exception") { - warning("'{a}' is already defined.", nexttoken, t); + if (!state.option.shadow && type !== "exception") { + warning("W004", state.tokens.next, t); } } } funct[t] = type; - if (token) { - funct["(tokens)"][t] = token; + if (tkn) { + funct["(tokens)"][t] = tkn; } if (funct["(global)"]) { global[t] = funct; - if (is_own(implied, t)) { - if (option.latedef) - warning("'{a}' was used before it was defined.", nexttoken, t); + if (_.has(implied, t)) { + if (state.option.latedef) { + warning("W003", state.tokens.next, t); + } + delete implied[t]; } } else { @@ -1888,179 +4730,169 @@ klass: } } - function doOption() { - var nt = nexttoken; - var o = nt.value; - var quotmarkValue = option.quotmark; + var nt = state.tokens.next; + var body = nt.body.split(",").map(function (s) { return s.trim(); }); var predef = {}; - var b, obj, filter, t, tn, v, minus; - switch (o) { - case "*/": - error("Unbegun comment."); - break; - case "/*members": - case "/*member": - o = "/*members"; - if (!membersOnly) { - membersOnly = {}; + 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; + } } - obj = membersOnly; - option.quotmark = false; - break; - case "/*jshint": - case "/*jslint": - obj = option; - filter = boolOptions; - break; - case "/*global": - obj = predef; - break; - default: - error("What?"); } - t = lex.token(); + if (nt.type === "exported") { + body.forEach(function (e) { + exported[e] = true; + }); + } - for (;;) { - minus = false; - var breakOuterLoop; - for (;;) { - if (t.type === "special" && t.value === "*/") { - breakOuterLoop = true; - break; - } - if (t.id !== "(endline)" && t.id !== ",") { - break; - } - t = lex.token(); - } - if (breakOuterLoop) - break; + if (nt.type === "members") { + membersOnly = membersOnly || {}; - if (o === "/*global" && t.value === "-") { - minus = true; - t = lex.token(); - } + body.forEach(function (m) { + var ch1 = m.charAt(0); + var ch2 = m.charAt(m.length - 1); - if (t.type !== "(string)" && t.type !== "(identifier)" && o !== "/*members") { - error("Bad option.", t); - } - - v = lex.token(); - if (v.id === ":") { - v = lex.token(); - - if (obj === membersOnly) { - error("Expected '{a}' and instead saw '{b}'.", t, "*/", ":"); + 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("\\\"", "\""); } - if (o === "/*jshint") { - checkOption(t.value, t); + 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; } - var numericVals = [ - "maxstatements", - "maxparams", - "maxdepth", - "maxcomplexity", - "maxerr", - "maxlen", - "indent" - ]; + if (numvals.indexOf(key) >= 0) { + val = +val; - if (numericVals.indexOf(t.value) > -1 && (o === "/*jshint" || o === "/*jslint")) { - b = +v.value; - - if (typeof b !== "number" || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { - error("Expected a small integer and instead saw '{a}'.", v, v.value); + if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) { + error("E032", nt, g[1].trim()); + return; } - if (t.value === "indent") - obj.white = true; + if (key === "indent") { + state.option["(explicitIndent)"] = true; + } - obj[t.value] = b; - } else if (t.value === "validthis") { + state.option[key] = val; + return; + } + + if (key === "validthis") { + // `validthis` is valid only within a function scope. if (funct["(global)"]) { - error("Option 'validthis' can't be used in a global scope."); + error("E009"); } else { - if (v.value === "true" || v.value === "false") - obj[t.value] = v.value === "true"; - else - error("Bad option value.", v); + if (val === "true" || val === "false") { + state.option.validthis = (val === "true"); + } else { + error("E002", nt); + } } - } else if (t.value === "quotmark" && (o === "/*jshint")) { - switch (v.value) { + return; + } + + if (key === "quotmark") { + switch (val) { case "true": - obj.quotmark = true; - break; case "false": - obj.quotmark = false; + state.option.quotmark = (val === "true"); break; case "double": case "single": - obj.quotmark = v.value; + state.option.quotmark = val; break; default: - error("Bad option value.", v); + error("E002", nt); } - } else if (v.value === "true" || v.value === "false") { - if (o === "/*jslint") { - tn = renamedOptions[t.value] || t.value; - obj[tn] = v.value === "true"; + return; + } + + if (/^-W\d{3}$/g.test(key)) { + ignored[key.slice(1)] = true; + 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) { - obj[tn] = !obj[tn]; + state.option[tn] = !state.option[tn]; } } else { - obj[t.value] = v.value === "true"; + state.option[key] = (val === "true"); } - if (t.value === "newcap") - obj["(explicitNewcap)"] = true; - } else { - error("Bad option value.", v); - } - t = lex.token(); - } else { - if (o === "/*jshint" || o === "/*jslint") { - error("Missing option value.", t); + if (key === "newcap") { + state.option["(explicitNewcap)"] = true; + } + return; } - obj[t.value] = false; + error("E002", nt); + }); - if (o === "/*global" && minus === true) { - JSHINT.blacklist[t.value] = t.value; - updatePredefined(); - } - - t = v; - } - } - - if (o === "/*members") { - option.quotmark = quotmarkValue; - } - - combine(predefined, predef); - - for (var key in predef) { - if (is_own(predef, key)) { - declared[key] = nt; - } - } - - if (filter) { 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 = ... + // 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; @@ -2075,59 +4907,60 @@ klass: return t; } - - -// Produce the next token. It looks for programming errors. + // Produce the next token. It looks for programming errors. function advance(id, t) { - switch (token.id) { + switch (state.tokens.curr.id) { case "(number)": - if (nexttoken.id === ".") { - warning("A dot following a number can be confused with a decimal point.", token); + if (state.tokens.next.id === ".") { + warning("W005", state.tokens.curr); } break; case "-": - if (nexttoken.id === "-" || nexttoken.id === "--") { - warning("Confusing minusses."); + if (state.tokens.next.id === "-" || state.tokens.next.id === "--") { + warning("W006"); } break; case "+": - if (nexttoken.id === "+" || nexttoken.id === "++") { - warning("Confusing plusses."); + if (state.tokens.next.id === "+" || state.tokens.next.id === "++") { + warning("W007"); } break; } - if (token.type === "(string)" || token.identifier) { - anonname = token.value; + if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) { + anonname = state.tokens.curr.value; } - if (id && nexttoken.id !== id) { + if (id && state.tokens.next.id !== id) { if (t) { - if (nexttoken.id === "(end)") { - warning("Unmatched '{a}'.", t, t.id); + if (state.tokens.next.id === "(end)") { + error("E019", t, t.id); } else { - warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", - nexttoken, id, t.id, t.line, nexttoken.value); + error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value); } - } else if (nexttoken.type !== "(identifier)" || - nexttoken.value !== id) { - warning("Expected '{a}' and instead saw '{b}'.", - nexttoken, id, nexttoken.value); + } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) { + warning("W116", state.tokens.next, id, state.tokens.next.value); } } - prevtoken = token; - token = nexttoken; + state.tokens.prev = state.tokens.curr; + state.tokens.curr = state.tokens.next; for (;;) { - nexttoken = lookahead.shift() || lex.token(); - if (nexttoken.id === "(end)" || nexttoken.id === "(error)") { + 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 (nexttoken.type === "special") { + + if (state.tokens.next.isSpecial) { doOption(); } else { - if (nexttoken.id !== "(endline)") { + if (state.tokens.next.id !== "(endline)") { break; } } @@ -2135,50 +4968,45 @@ klass: } -// 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. + // 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 + // .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. + // They are elements of the parsing method called Top Down Operator Precedence. function expression(rbp, initial) { var left, isArray = false, isObject = false; - if (nexttoken.id === "(end)") - error("Unexpected early end of program.", token); + if (state.tokens.next.id === "(end)") + error("E006", state.tokens.curr); advance(); + if (initial) { anonname = "anonymous"; - funct["(verb)"] = token.value; + funct["(verb)"] = state.tokens.curr.value; } - if (initial === true && token.fud) { - left = token.fud(); + + if (initial === true && state.tokens.curr.fud) { + left = state.tokens.curr.fud(); } else { - if (token.nud) { - left = token.nud(); + if (state.tokens.curr.nud) { + left = state.tokens.curr.nud(); } else { - if (nexttoken.type === "(number)" && token.id === ".") { - warning("A leading decimal point can be confused with a dot: '.{a}'.", - token, nexttoken.value); - advance(); - return token; - } else { - error("Expected an identifier and instead saw '{a}'.", - token, token.id); - } + error("E030", state.tokens.curr, state.tokens.curr.id); } - while (rbp < nexttoken.lbp) { - isArray = token.value === "Array"; - isObject = token.value === "Object"; + + while (rbp < state.tokens.next.lbp) { + 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 @@ -2190,24 +5018,28 @@ klass: if (left.value !== "new" || (left.first && left.first.value && left.first.value === ".")) { isArray = false; - // ...In the case of Object, if the left.value and token.value + // ...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 !== token.value) { + if (left.value !== state.tokens.curr.value) { isObject = false; } } } advance(); - if (isArray && token.id === "(" && nexttoken.id === ")") - warning("Use the array literal notation [].", token); - if (isObject && token.id === "(" && nexttoken.id === ")") - warning("Use the object literal notation {}.", token); - if (token.led) { - left = token.led(left); + + 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 (state.tokens.curr.led) { + left = state.tokens.curr.led(left); } else { - error("Expected an operator and instead saw '{a}'.", - token, token.id); + error("E033", state.tokens.curr, state.tokens.curr.id); } } } @@ -2218,28 +5050,28 @@ klass: // Functions for conformance of style. function adjacent(left, right) { - left = left || token; - right = right || nexttoken; - if (option.white) { + 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("Unexpected space after '{a}'.", left, left.value); + warning("W011", left, left.value); } } } function nobreak(left, right) { - left = left || token; - right = right || nexttoken; - if (option.white && (left.character !== right.from || left.line !== right.line)) { - warning("Unexpected space before '{a}'.", right, right.value); + 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 || token; - right = right || nexttoken; - if (option.white && !left.comment) { + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (state.option.white && !left.comment) { if (left.line === right.line) { adjacent(left, right); } @@ -2247,81 +5079,136 @@ klass: } function nonadjacent(left, right) { - if (option.white) { - left = left || token; - right = right || nexttoken; + 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("Missing space after '{a}'.", - left, left.value); + warning("W013", left, left.value); } } } function nobreaknonadjacent(left, right) { - left = left || token; - right = right || nexttoken; - if (!option.laxbreak && left.line !== right.line) { - warning("Bad line breaking before '{a}'.", right, right.id); - } else if (option.white) { - left = left || token; - right = right || nexttoken; + 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("Missing space after '{a}'.", - left, left.value); + warning("W013", left, left.value); } } } function indentation(bias) { - var i; - if (option.white && nexttoken.id !== "(end)") { - i = indent + (bias || 0); - if (nexttoken.from !== i) { - warning( -"Expected '{a}' to have an indentation at {b} instead at {c}.", - nexttoken, nexttoken.value, i, nexttoken.from); - } + 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 || token; - if (t.line !== nexttoken.line) { - warning("Line breaking error '{a}'.", t, t.value); + t = t || state.tokens.curr; + if (t.line !== state.tokens.next.line) { + warning("E022", t, t.value); } } - function comma() { - if (token.line !== nexttoken.line) { - if (!option.laxcomma) { + function comma(opts) { + opts = opts || {}; + + if (state.tokens.curr.line !== state.tokens.next.line) { + if (!state.option.laxcomma) { if (comma.first) { - warning("Comma warnings can be turned off with 'laxcomma'"); + warning("I001"); comma.first = false; } - warning("Bad line breaking before '{a}'.", token, nexttoken.id); + warning("W014", state.tokens.curr, state.tokens.next.id); } - } else if (!token.comment && token.character !== nexttoken.from && option.white) { - token.from += (token.character - token.from); - warning("Unexpected space after '{a}'.", token, token.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(","); - nonadjacent(token, nexttoken); + + // 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) { + // 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 "switch": + case "throw": + case "try": + case "var": + case "while": + case "with": + error("E024", state.tokens.next, state.tokens.next.value); + return; + } + } + + if (state.tokens.next.type === "(punctuator)") { + switch (state.tokens.next.value) { + case "}": + case "]": + case ",": + if (opts.allowTrailing) { + return; + } + + /* falls through */ + case ")": + error("E024", state.tokens.next, state.tokens.next.value); + } + } } - -// Functional constructors for making the symbols that will be inherited by -// tokens. + // Functional constructors for making the symbols that will be inherited by + // tokens. function symbol(s, p) { - var x = syntax[s]; + var x = state.syntax[s]; if (!x || typeof x !== "object") { - syntax[s] = x = { + state.syntax[s] = x = { id: s, lbp: p, value: s @@ -2330,12 +5217,10 @@ klass: return x; } - function delim(s) { return symbol(s, 0); } - function stmt(s, f) { var x = delim(s); x.identifier = x.reserved = true; @@ -2343,14 +5228,12 @@ klass: 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")) { @@ -2359,7 +5242,6 @@ klass: return x; } - function prefix(s, f) { var x = symbol(s, 150); reserveName(x); @@ -2367,11 +5249,11 @@ klass: this.right = expression(150); this.arity = "unary"; if (this.id === "++" || this.id === "--") { - if (option.plusplus) { - warning("Unexpected use of '{a}'.", this, this.id); - } else if ((!this.right.identifier || this.right.reserved) && + if (state.option.plusplus) { + warning("W016", this, this.id); + } else if ((!this.right.identifier || isReserved(this.right)) && this.right.id !== "." && this.right.id !== "[") { - warning("Bad operand.", this); + warning("W017", this); } } return this; @@ -2379,7 +5261,6 @@ klass: return x; } - function type(s, f) { var x = delim(s); x.type = s; @@ -2387,13 +5268,28 @@ klass: return x; } - - function reserve(s, f) { - var x = type(s, f); - x.identifier = x.reserved = true; + 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, 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 () { @@ -2404,17 +5300,16 @@ klass: }); } - function infix(s, f, p, w) { var x = symbol(s, p); reserveName(x); x.led = function (left) { if (!w) { - nobreaknonadjacent(prevtoken, token); - nonadjacent(token, nexttoken); + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); } if (s === "in" && left.id === "!") { - warning("Confusing use of '{a}'.", left, "!"); + warning("W018", left, "!"); } if (typeof f === "function") { return f(left, this); @@ -2427,25 +5322,32 @@ klass: return x; } - function relation(s, f) { var x = symbol(s, 100); + x.led = function (left) { - nobreaknonadjacent(prevtoken, token); - nonadjacent(token, nexttoken); + 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("Use the isNaN function to compare with NaN.", this); + warning("W019", this); } else if (f) { f.apply(this, [left, right]); } + + if (!left || !right) { + quit("E041", state.tokens.curr.line); + } + if (left.id === "!") { - warning("Confusing use of '{a}'.", left, "!"); + warning("W018", left, "!"); } + if (right.id === "!") { - warning("Confusing use of '{a}'.", right, "!"); + warning("W018", right, "!"); } + this.left = left; this.right = right; return this; @@ -2453,18 +5355,16 @@ klass: return x; } - function isPoorRelation(node) { return node && ((node.type === "(number)" && +node.value === 0) || (node.type === "(string)" && node.value === "") || - (node.type === "null" && !option.eqnull) || + (node.type === "null" && !state.option.eqnull) || node.type === "true" || node.type === "false" || node.type === "undefined"); } - function assignop(s) { symbol(s, 20).exps = true; @@ -2473,38 +5373,36 @@ klass: if (predefined[left.value] === false && scope[left.value]["(global)"] === true) { - warning("Read only.", left); + warning("W020", left); } else if (left["function"]) { - warning("'{a}' is a function.", left, left.value); + warning("W021", left, left.value); } if (left) { - if (option.esnext && funct[left.value] === "const") { - warning("Attempting to override '{a}' which is a constant", left, left.value); + if (state.option.esnext && funct[left.value] === "const") { + error("E013", left, left.value); } if (left.id === "." || left.id === "[") { if (!left.left || left.left.value === "arguments") { - warning("Bad assignment.", that); + warning("E031", that); } that.right = expression(19); return that; - } else if (left.identifier && !left.reserved) { + } else if (left.identifier && !isReserved(left)) { if (funct[left.value] === "exception") { - warning("Do not assign to the exception parameter.", left); + warning("W022", left); } that.right = expression(19); return that; } - if (left === syntax["function"]) { - warning( -"Expected an identifier in an assignment and instead saw a function invocation.", - token); + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); } } - error("Bad assignment.", that); + error("E031", that); }, 20); } @@ -2513,8 +5411,8 @@ klass: var x = symbol(s, p); reserveName(x); x.led = (typeof f === "function") ? f : function (left) { - if (option.bitwise) { - warning("Unexpected use of '{a}'.", this, this.id); + if (state.option.bitwise) { + warning("W016", this, this.id); } this.left = left; this.right = expression(p); @@ -2527,82 +5425,95 @@ klass: function bitwiseassignop(s) { symbol(s, 20).exps = true; return infix(s, function (left, that) { - if (option.bitwise) { - warning("Unexpected use of '{a}'.", that, that.id); + if (state.option.bitwise) { + warning("W016", that, that.id); } - nonadjacent(prevtoken, token); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); if (left) { if (left.id === "." || left.id === "[" || - (left.identifier && !left.reserved)) { + (left.identifier && !isReserved(left))) { expression(19); return that; } - if (left === syntax["function"]) { - warning( -"Expected an identifier in an assignment, and instead saw a function invocation.", - token); + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); } return that; } - error("Bad assignment.", that); + error("E031", that); }, 20); } function suffix(s) { var x = symbol(s, 150); + x.led = function (left) { - if (option.plusplus) { - warning("Unexpected use of '{a}'.", this, this.id); - } else if ((!left.identifier || left.reserved) && - left.id !== "." && left.id !== "[") { - warning("Bad operand.", this); + 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()) - function optionalidentifier(fnparam) { - if (nexttoken.identifier) { - advance(); - if (token.reserved && !option.es5) { - // `undefined` as a function param is a common pattern to protect - // against the case when somebody does `undefined = true` and - // help with minification. More info: https://gist.github.com/315916 - if (!fnparam || token.value !== "undefined") { - warning("Expected an identifier and instead saw '{a}' (a reserved word).", - token, token.id); - } - } - return token.value; + // 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.es5 || meta.isFutureReservedWord) { + return val; + } + } + + if (fnparam && val === "undefined") { + return val; + } + + warning("W024", state.tokens.curr, state.tokens.curr.id); + return val; } // fnparam means that this identifier is being defined as a function // argument - function identifier(fnparam) { - var i = optionalidentifier(fnparam); + // 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 (token.id === "function" && nexttoken.id === "(") { - warning("Missing name in function declaration."); + if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") { + warning("W025"); } else { - error("Expected an identifier and instead saw '{a}'.", - nexttoken, nexttoken.value); + error("E030", state.tokens.next, state.tokens.next.value); } } function reachable(s) { var i = 0, t; - if (nexttoken.id !== ";" || noreach) { + if (state.tokens.next.id !== ";" || noreach) { return; } for (;;) { @@ -2612,14 +5523,15 @@ klass: } if (t.id !== "(endline)") { if (t.id === "function") { - if (!option.latedef) { + if (!state.option.latedef) { break; } - warning( -"Inner functions should be listed at the top of the outer function.", t); + + warning("W026", t); break; } - warning("Unreachable '{a}' after '{b}'.", t, t.value, s); + + warning("W027", t, t.value, s); break; } i += 1; @@ -2628,7 +5540,7 @@ klass: function statement(noindent) { - var i = indent, r, s = scope, t = nexttoken; + var i = indent, r, s = scope, t = state.tokens.next; if (t.id === ";") { advance(";"); @@ -2636,23 +5548,33 @@ klass: } // Is this a labelled statement? + var res = isReserved(t); - if (t.identifier && !t.reserved && peek().id === ":") { + // 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) { + warning("W024", t, t.id); + res = false; + } + + if (t.identifier && !res && peek().id === ":") { advance(); advance(":"); scope = Object.create(s); addlabel(t.value, "label"); - if (!nexttoken.labelled && nexttoken.value !== "{") { - warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); + if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { + warning("W028", state.tokens.next, t.value, state.tokens.next.value); } - if (jx.test(t.value + ":")) { - warning("Label '{a}' looks like a javascript url.", t, t.value); + if (reg.javascriptURL.test(t.value + ":")) { + warning("W029", t, t.value); } - nexttoken.label = t.value; - t = nexttoken; + state.tokens.next.label = t.value; + t = state.tokens.next; } // Is it a lonely block? @@ -2672,35 +5594,34 @@ klass: // Look for the final semicolon. if (!t.block) { - if (!option.expr && (!r || !r.exps)) { - warning("Expected an assignment or function call and instead saw an expression.", - token); - } else if (option.nonew && r.id === "(" && r.left.id === "new") { - warning("Do not use 'new' for side effects.", t); + if (!state.option.expr && (!r || !r.exps)) { + warning("W030", state.tokens.curr); + } else if (state.option.nonew && r.id === "(" && r.left.id === "new") { + warning("W031", t); } - if (nexttoken.id === ",") { + if (state.tokens.next.id === ",") { return comma(); } - if (nexttoken.id !== ";") { - if (!option.asi) { + 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 (!option.lastsemic || nexttoken.id !== "}" || - nexttoken.line !== token.line) { - warningAt("Missing semicolon.", token.line, token.character); + 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(token, nexttoken); + adjacent(state.tokens.curr, state.tokens.next); advance(";"); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); } } -// Restore the indentation. + // Restore the indentation. indent = i; scope = s; @@ -2711,15 +5632,17 @@ klass: function statements(startLine) { var a = [], p; - while (!nexttoken.reach && nexttoken.id !== "(end)") { - if (nexttoken.id === ";") { + while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") { + if (state.tokens.next.id === ";") { p = peek(); - if (!p || p.id !== "(") { - warning("Unnecessary semicolon."); + + if (!p || (p.id !== "(" && p.id !== "[")) { + warning("W032"); } + advance(";"); } else { - a.push(statement(startLine === nexttoken.line)); + a.push(statement(startLine === state.tokens.next.line)); } } return a; @@ -2735,7 +5658,7 @@ klass: var i, p, pn; for (;;) { - if (nexttoken.id === "(string)") { + if (state.tokens.next.id === "(string)") { p = peek(0); if (p.id === "(endline)") { i = 1; @@ -2750,31 +5673,31 @@ klass: pn.id !== "}") { break; } - warning("Missing semicolon.", nexttoken); + warning("W033", state.tokens.next); } else { p = pn; } } else if (p.id === "}") { - // directive with no other statements, warn about missing semicolon - warning("Missing semicolon.", p); + // Directive with no other statements, warn about missing semicolon + warning("W033", p); } else if (p.id !== ";") { break; } indentation(); advance(); - if (directive[token.value]) { - warning("Unnecessary directive \"{a}\".", token, token.value); + if (state.directive[state.tokens.curr.value]) { + warning("W034", state.tokens.curr, state.tokens.curr.value); } - if (token.value === "use strict") { - if (!option["(explicitNewcap)"]) - option.newcap = true; - option.undef = true; + 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 - directive[token.value] = true; + state.directive[state.tokens.curr.value] = true; if (p.id === ";") { advance(";"); @@ -2806,37 +5729,37 @@ klass: inblock = ordinary; - if (!ordinary || !option.funcscope) + if (!ordinary || !state.option.funcscope) scope = Object.create(scope); - nonadjacent(token, nexttoken); - t = nexttoken; + nonadjacent(state.tokens.curr, state.tokens.next); + t = state.tokens.next; var metrics = funct["(metrics)"]; metrics.nestedBlockDepth += 1; metrics.verifyMaxNestedBlockDepthPerFunction(); - if (nexttoken.id === "{") { + if (state.tokens.next.id === "{") { advance("{"); - line = token.line; - if (nexttoken.id !== "}") { - indent += option.indent; - while (!ordinary && nexttoken.from > indent) { - indent += option.indent; + 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 directive) { - if (is_own(directive, d)) { - m[d] = directive[d]; + for (d in state.directive) { + if (_.has(state.directive, d)) { + m[d] = state.directive[d]; } } directives(); - if (option.strict && funct["(context)"]["(global)"]) { - if (!m["use strict"] && !directive["use strict"]) { - warning("Missing \"use strict\" statement."); + if (state.option.strict && funct["(context)"]["(global)"]) { + if (!m["use strict"] && !state.directive["use strict"]) { + warning("E007"); } } } @@ -2846,38 +5769,37 @@ klass: metrics.statementCount += a.length; if (isfunc) { - directive = m; + state.directive = m; } - indent -= option.indent; - if (line !== nexttoken.line) { + indent -= state.option.indent; + if (line !== state.tokens.next.line) { indentation(); } - } else if (line !== nexttoken.line) { + } else if (line !== state.tokens.next.line) { indentation(); } advance("}", t); indent = old_indent; } else if (!ordinary) { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, "{", nexttoken.value); + error("E021", state.tokens.next, "{", state.tokens.next.value); } else { - if (!stmt || option.curly) - warning("Expected '{a}' and instead saw '{b}'.", - nexttoken, "{", nexttoken.value); + if (!stmt || state.option.curly) { + warning("W116", state.tokens.next, "{", state.tokens.next.value); + } noreach = true; - indent += option.indent; + indent += state.option.indent; // test indentation only if statement is in new line - a = [statement(nexttoken.line === token.line)]; - indent -= option.indent; + a = [statement(state.tokens.next.line === state.tokens.curr.line)]; + indent -= state.option.indent; noreach = false; } funct["(verb)"] = null; - if (!ordinary || !option.funcscope) scope = s; + if (!ordinary || !state.option.funcscope) scope = s; inblock = b; - if (ordinary && option.noempty && (!a || a.length === 0)) { - warning("Empty block."); + if (ordinary && state.option.noempty && (!a || a.length === 0)) { + warning("W035"); } metrics.nestedBlockDepth -= 1; return a; @@ -2886,7 +5808,7 @@ klass: function countMember(m) { if (membersOnly && typeof membersOnly[m] !== "boolean") { - warning("Unexpected /*member '{a}'.", token, m); + warning("W036", state.tokens.curr, m); } if (typeof member[m] === "number") { member[m] += 1; @@ -2896,8 +5818,8 @@ klass: } - function note_implied(token) { - var name = token.value, line = token.line, a = implied[name]; + function note_implied(tkn) { + var name = tkn.value, line = tkn.line, a = implied[name]; if (typeof a === "function") { a = false; } @@ -2921,7 +5843,7 @@ klass: return this; }); - syntax["(identifier)"] = { + state.syntax["(identifier)"] = { type: "(identifier)", lbp: 0, identifier: true, @@ -2956,7 +5878,7 @@ klass: this["function"] = true; break; case "label": - warning("'{a}' is a statement label.", token, v); + warning("W037", state.tokens.curr, v); break; } } else if (funct["(global)"]) { @@ -2967,17 +5889,18 @@ klass: // the base object of a reference is null so no need to display warning // if we're inside of typeof or delete. - if (option.undef && typeof predefined[v] !== "boolean") { + 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") || - (nexttoken && (nexttoken.value === "." || nexttoken.value === "["))) { + (state.tokens.next && (state.tokens.next.value === "." || + state.tokens.next.value === "["))) { - isundef(funct, "'{a}' is not defined.", token, v); + isundef(funct, "W117", state.tokens.curr, v); } } - note_implied(token); + 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. @@ -2987,10 +5910,10 @@ klass: case "function": case "var": case "unused": - warning("'{a}' used out of scope.", token, v); + warning("W038", state.tokens.curr, v); break; case "label": - warning("'{a}' is a statement label.", token, v); + warning("W037", state.tokens.curr, v); break; case "outer": case "global": @@ -3001,24 +5924,23 @@ klass: if (s === true) { funct[v] = true; } else if (s === null) { - warning("'{a}' is not allowed.", token, v); - note_implied(token); + 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. - if (option.undef) { - // Attempting to subscript a null reference will throw an - // error, even within the typeof and delete operators - if (!(anonname === "typeof" || anonname === "delete") || - (nexttoken && - (nexttoken.value === "." || nexttoken.value === "["))) { + // 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, "'{a}' is not defined.", token, v); - } + isundef(funct, "W117", state.tokens.curr, v); } funct[v] = true; - note_implied(token); + note_implied(state.tokens.curr); } else { switch (s[v]) { case "function": @@ -3036,7 +5958,7 @@ klass: funct[v] = s["(global)"] ? "global" : "outer"; break; case "label": - warning("'{a}' is a statement label.", token, v); + warning("W037", state.tokens.curr, v); } } } @@ -3044,8 +5966,7 @@ klass: return this; }, led: function () { - error("Expected an operator and instead saw '{a}'.", - nexttoken, nexttoken.value); + error("E033", state.tokens.next, state.tokens.next.value); } }; @@ -3053,16 +5974,11 @@ klass: return this; }); - -// ECMAScript parser + // ECMAScript parser delim("(endline)"); delim("(begin)"); delim("(end)").reach = true; - delim(""); delim("(error)").reach = true; delim("}").reach = true; delim(")"); @@ -3073,15 +5989,15 @@ klass: delim(":").reach = true; delim(","); delim("#"); - delim("@"); + reserve("else"); reserve("case").reach = true; reserve("catch"); reserve("default").reach = true; reserve("finally"); reservevar("arguments", function (x) { - if (directive["use strict"] && funct["(global)"]) { - warning("Strict violation.", x); + if (state.directive["use strict"] && funct["(global)"]) { + warning("E008", x); } }); reservevar("eval"); @@ -3089,21 +6005,23 @@ klass: reservevar("Infinity"); reservevar("null"); reservevar("this", function (x) { - if (directive["use strict"] && !option.validthis && ((funct["(statement)"] && + if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] && funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { - warning("Possible strict violation.", x); + 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("A regular expression literal can be confused with '/='."); + error("E014"); }; assignop("%=", "assignmod", 20); + bitwiseassignop("&=", "assignbitand", 20); bitwiseassignop("|=", "assignbitor", 20); bitwiseassignop("^=", "assignbitxor", 20); @@ -3124,31 +6042,28 @@ klass: bitwise("^", "bitxor", 80); bitwise("&", "bitand", 90); relation("==", function (left, right) { - var eqnull = option.eqnull && (left.value === "null" || right.value === "null"); + var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); - if (!eqnull && option.eqeqeq) - warning("Expected '{a}' and instead saw '{b}'.", this, "===", "=="); + if (!eqnull && state.option.eqeqeq) + warning("W116", this, "===", "=="); else if (isPoorRelation(left)) - warning("Use '{a}' to compare with '{b}'.", this, "===", left.value); + warning("W041", this, "===", left.value); else if (isPoorRelation(right)) - warning("Use '{a}' to compare with '{b}'.", this, "===", right.value); + warning("W041", this, "===", right.value); return this; }); relation("==="); relation("!=", function (left, right) { - var eqnull = option.eqnull && + var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); - if (!eqnull && option.eqeqeq) { - warning("Expected '{a}' and instead saw '{b}'.", - this, "!==", "!="); + if (!eqnull && state.option.eqeqeq) { + warning("W116", this, "!==", "!="); } else if (isPoorRelation(left)) { - warning("Use '{a}' to compare with '{b}'.", - this, "!==", left.value); + warning("W041", this, "!==", left.value); } else if (isPoorRelation(right)) { - warning("Use '{a}' to compare with '{b}'.", - this, "!==", right.value); + warning("W041", this, "!==", right.value); } return this; }); @@ -3167,8 +6082,8 @@ klass: if (left && right && left.id === "(string)" && right.id === "(string)") { left.value += right.value; left.character = right.character; - if (!option.scripturl && jx.test(left.value)) { - warning("JavaScript URL.", left); + if (!state.option.scripturl && reg.javascriptURL.test(left.value)) { + warning("W050", left); } return left; } @@ -3178,13 +6093,13 @@ klass: }, 130); prefix("+", "num"); prefix("+++", function () { - warning("Confusing pluses."); + warning("W007"); this.right = expression(150); this.arity = "unary"; return this; }); infix("+++", function (left) { - warning("Confusing pluses."); + warning("W007"); this.left = left; this.right = expression(130); return this; @@ -3192,13 +6107,13 @@ klass: infix("-", "sub", 130); prefix("-", "neg"); prefix("---", function () { - warning("Confusing minuses."); + warning("W006"); this.right = expression(150); this.arity = "unary"; return this; }); infix("---", function (left) { - warning("Confusing minuses."); + warning("W006"); this.left = left; this.right = expression(130); return this; @@ -3209,23 +6124,23 @@ klass: suffix("++", "postinc"); prefix("++", "preinc"); - syntax["++"].exps = true; + state.syntax["++"].exps = true; suffix("--", "postdec"); prefix("--", "predec"); - syntax["--"].exps = true; + state.syntax["--"].exps = true; prefix("delete", function () { var p = expression(0); if (!p || (p.id !== "." && p.id !== "[")) { - warning("Variables should not be deleted."); + warning("W051"); } this.first = p; return this; }).exps = true; prefix("~", function () { - if (option.bitwise) { - warning("Unexpected '{a}'.", this, "~"); + if (state.option.bitwise) { + warning("W052", this, "~"); } expression(150); return this; @@ -3234,11 +6149,17 @@ klass: 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("Confusing use of '{a}'.", this, "!"); + warning("W018", this, "!"); } return this; }); + prefix("typeof", "typeof"); prefix("new", function () { var c = expression(155), i; @@ -3251,11 +6172,11 @@ klass: case "Boolean": case "Math": case "JSON": - warning("Do not use {a} as a constructor.", prevtoken, c.value); + warning("W053", state.tokens.prev, c.value); break; case "Function": - if (!option.evil) { - warning("The Function constructor is eval."); + if (!state.option.evil) { + warning("W054"); } break; case "Date": @@ -3264,102 +6185,113 @@ klass: default: if (c.id !== "function") { i = c.value.substr(0, 1); - if (option.newcap && (i < "A" || i > "Z") && !is_own(global, c.value)) { - warning("A constructor name should start with an uppercase letter.", - token); + 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("Bad constructor.", token); + warning("W056", state.tokens.curr); } } } else { - if (!option.supernew) - warning("Weird construction. Delete 'new'.", this); + if (!state.option.supernew) + warning("W057", this); } - adjacent(token, nexttoken); - if (nexttoken.id !== "(" && !option.supernew) { - warning("Missing '()' invoking a constructor.", - token, token.value); + 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; }); - syntax["new"].exps = true; + state.syntax["new"].exps = true; prefix("void").exps = true; infix(".", function (left, that) { - adjacent(prevtoken, token); + adjacent(state.tokens.prev, state.tokens.curr); nobreak(); - var m = identifier(); + 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 (option.noarg) - warning("Avoid arguments.{a}.", left, m); - else if (directive["use strict"]) - error("Strict violation."); - } else if (!option.evil && left && left.value === "document" && + 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("document.write can be a form of eval.", left); + warning("W060", left); } - if (!option.evil && (m === "eval" || m === "execScript")) { - warning("eval is evil."); + + if (!state.option.evil && (m === "eval" || m === "execScript")) { + warning("W061"); } + return that; }, 160, true); infix("(", function (left, that) { - if (prevtoken.id !== "}" && prevtoken.id !== ")") { - nobreak(prevtoken, token); + if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") { + nobreak(state.tokens.prev, state.tokens.curr); } + nospace(); - if (option.immed && !left.immed && left.id === "function") { - warning("Wrap an immediate function invocation in parentheses " + - "to assist the reader in understanding that the expression " + - "is the result of a function, and not the function itself."); + if (state.option.immed && !left.immed && left.id === "function") { + warning("W062"); } - var n = 0, - p = []; + + 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("Math is not a function.", left); - } else if (option.newcap) { - warning("Missing 'new' prefix when invoking a constructor.", left); + warning("W063", left); + } else if (state.option.newcap) { + warning("W064", left); } } } } } - if (nexttoken.id !== ")") { + + if (state.tokens.next.id !== ")") { for (;;) { p[p.length] = expression(10); n += 1; - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); } } + advance(")"); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); + if (typeof left === "object") { if (left.value === "parseInt" && n === 1) { - warning("Missing radix parameter.", token); + warning("W065", state.tokens.curr); } - if (!option.evil) { + if (!state.option.evil) { if (left.value === "eval" || left.value === "Function" || left.value === "execScript") { - warning("eval is evil.", left); + warning("W061", left); if (p[0] && [0].id === "(string)") { addInternalSrc(left, p[0].value); @@ -3367,8 +6299,7 @@ klass: } else if (p[0] && p[0].id === "(string)" && (left.value === "setTimeout" || left.value === "setInterval")) { - warning( - "Implied eval is evil. Pass a function instead of a string.", left); + warning("W066", left); addInternalSrc(left, p[0].value); // window.setTimeout/setInterval @@ -3377,91 +6308,107 @@ klass: left.left.value === "window" && (left.right === "setTimeout" || left.right === "setInterval")) { - warning( - "Implied eval is evil. Pass a function instead of a string.", left); + warning("W066", left); addInternalSrc(left, p[0].value); } } if (!left.identifier && left.id !== "." && left.id !== "[" && left.id !== "(" && left.id !== "&&" && left.id !== "||" && left.id !== "?") { - warning("Bad invocation.", left); + warning("W067", left); } } + that.left = left; return that; }, 155, true).exps = true; prefix("(", function () { nospace(); - if (nexttoken.id === "function") { - nexttoken.immed = true; + + if (state.tokens.next.id === "function") { + state.tokens.next.immed = true; } - var v = expression(0); - advance(")", this); - nospace(prevtoken, token); - if (option.immed && v.id === "function") { - if (nexttoken.id !== "(" && - (nexttoken.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { - warning( -"Do not wrap function literals in parens unless they are to be immediately invoked.", - this); + + var exprs = []; + + if (state.tokens.next.id !== ")") { + for (;;) { + exprs.push(expression(0)); + if (state.tokens.next.id !== ",") { + break; + } + comma(); } } - return v; + advance(")", this); + nospace(state.tokens.prev, state.tokens.curr); + if (state.option.immed && exprs[0].id === "function") { + if (state.tokens.next.id !== "(" && + (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { + warning("W068", this); + } + } + + return exprs[0]; }); infix("[", function (left, that) { - nobreak(prevtoken, token); + nobreak(state.tokens.prev, state.tokens.curr); nospace(); var e = expression(0), s; if (e && e.type === "(string)") { - if (!option.evil && (e.value === "eval" || e.value === "execScript")) { - warning("eval is evil.", that); + if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { + warning("W061", that); } + countMember(e.value); - if (!option.sub && ix.test(e.value)) { - s = syntax[e.value]; - if (!s || !s.reserved) { - warning("['{a}'] is better written in dot notation.", - prevtoken, 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); - nospace(prevtoken, token); + + 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); prefix("[", function () { - var b = token.line !== nexttoken.line; + var b = state.tokens.curr.line !== state.tokens.next.line; this.first = []; if (b) { - indent += option.indent; - if (nexttoken.from === indent + option.indent) { - indent += option.indent; + indent += state.option.indent; + if (state.tokens.next.from === indent + state.option.indent) { + indent += state.option.indent; } } - while (nexttoken.id !== "(end)") { - while (nexttoken.id === ",") { - if (!option.es5) - warning("Extra comma."); + while (state.tokens.next.id !== "(end)") { + while (state.tokens.next.id === ",") { + if (!state.option.es5) + warning("W070"); advance(","); } - if (nexttoken.id === "]") { + if (state.tokens.next.id === "]") { break; } - if (b && token.line !== nexttoken.line) { + if (b && state.tokens.curr.line !== state.tokens.next.line) { indentation(); } this.first.push(expression(10)); - if (nexttoken.id === ",") { - comma(); - if (nexttoken.id === "]" && !option.es5) { - warning("Extra comma.", token); + if (state.tokens.next.id === ",") { + comma({ allowTrailing: true }); + if (state.tokens.next.id === "]" && !state.option.es5) { + warning("W070", state.tokens.curr); break; } } else { @@ -3469,7 +6416,7 @@ klass: } } if (b) { - indent -= option.indent; + indent -= state.option.indent; indentation(); } advance("]", this); @@ -3478,29 +6425,35 @@ klass: function property_name() { - var id = optionalidentifier(true); + var id = optionalidentifier(false, true); + if (!id) { - if (nexttoken.id === "(string)") { - id = nexttoken.value; + if (state.tokens.next.id === "(string)") { + id = state.tokens.next.value; advance(); - } else if (nexttoken.id === "(number)") { - id = nexttoken.value.toString(); + } else if (state.tokens.next.id === "(number)") { + id = state.tokens.next.value.toString(); advance(); } } + + if (id === "hasOwnProperty") { + warning("W001"); + } + return id; } function functionparams() { - var next = nexttoken; + var next = state.tokens.next; var params = []; var ident; advance("("); nospace(); - if (nexttoken.id === ")") { + if (state.tokens.next.id === ")") { advance(")"); return; } @@ -3508,12 +6461,12 @@ klass: for (;;) { ident = identifier(true); params.push(ident); - addlabel(ident, "unused", token); - if (nexttoken.id === ",") { + addlabel(ident, "unused", state.tokens.curr); + if (state.tokens.next.id === ",") { comma(); } else { advance(")", next); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); return params; } } @@ -3522,27 +6475,27 @@ klass: function doFunction(name, statement) { var f; - var oldOption = option; + var oldOption = state.option; var oldScope = scope; - option = Object.create(option); + state.option = Object.create(state.option); scope = Object.create(scope); funct = { - "(name)" : name || "\"" + anonname + "\"", - "(line)" : nexttoken.line, - "(character)": nexttoken.character, + "(name)" : name || "\"" + anonname + "\"", + "(line)" : state.tokens.next.line, + "(character)": state.tokens.next.character, "(context)" : funct, "(breakage)" : 0, "(loopage)" : 0, - "(metrics)" : createMetrics(nexttoken), - "(scope)" : scope, + "(metrics)" : createMetrics(state.tokens.next), + "(scope)" : scope, "(statement)": statement, - "(tokens)" : {} + "(tokens)" : {} }; f = funct; - token.funct = funct; + state.tokens.curr.funct = funct; functions.push(funct); @@ -3558,10 +6511,14 @@ klass: funct["(metrics)"].verifyMaxStatementsPerFunction(); funct["(metrics)"].verifyMaxComplexityPerFunction(); + if (state.option.unused === false) { + funct["(ignoreUnused)"] = true; + } + scope = oldScope; - option = oldOption; - funct["(last)"] = token.line; - funct["(lastcharacter)"] = token.character; + state.option = oldOption; + funct["(last)"] = state.tokens.curr.line; + funct["(lastcharacter)"] = state.tokens.curr.character; funct = funct["(context)"]; return f; @@ -3573,37 +6530,33 @@ klass: nestedBlockDepth: -1, ComplexityCount: 1, verifyMaxStatementsPerFunction: function () { - if (option.maxstatements && - this.statementCount > option.maxstatements) { - var message = "Too many statements per function (" + this.statementCount + ")."; - warning(message, functionStartToken); + if (state.option.maxstatements && + this.statementCount > state.option.maxstatements) { + warning("W071", functionStartToken, this.statementCount); } }, verifyMaxParametersPerFunction: function (params) { params = params || []; - if (option.maxparams && params.length > option.maxparams) { - var message = "Too many parameters per function (" + params.length + ")."; - warning(message, functionStartToken); + if (state.option.maxparams && params.length > state.option.maxparams) { + warning("W072", functionStartToken, params.length); } }, verifyMaxNestedBlockDepthPerFunction: function () { - if (option.maxdepth && + if (state.option.maxdepth && this.nestedBlockDepth > 0 && - this.nestedBlockDepth === option.maxdepth + 1) { - var message = "Blocks are nested too deeply (" + this.nestedBlockDepth + ")."; - warning(message); + this.nestedBlockDepth === state.option.maxdepth + 1) { + warning("W073", null, this.nestedBlockDepth); } }, verifyMaxComplexityPerFunction: function () { - var max = option.maxcomplexity; + var max = state.option.maxcomplexity; var cc = this.ComplexityCount; if (max && cc > max) { - var message = "Cyclomatic complexity is too high per function (" + cc + ")."; - warning(message, functionStartToken); + warning("W074", functionStartToken, cc); } } }; @@ -3613,141 +6566,178 @@ klass: funct["(metrics)"].ComplexityCount += 1; } + // Parse assignments that were found instead of conditionals. + // For example: if (a = 1) { ... } + + function parseCondAssignment() { + switch (state.tokens.next.id) { + case "=": + case "+=": + case "-=": + case "*=": + case "%=": + case "&=": + case "|=": + case "^=": + case "/=": + if (!state.option.boss) { + warning("W084"); + } + + advance(state.tokens.next.id); + expression(20); + } + } + (function (x) { x.nud = function () { var b, f, i, p, t; var props = {}; // All properties, including accessors - function saveProperty(name, token) { - if (props[name] && is_own(props, name)) - warning("Duplicate member '{a}'.", nexttoken, i); + 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].basicToken = token; + props[name].basictkn = tkn; } - function saveSetter(name, token) { - if (props[name] && is_own(props, name)) { + function saveSetter(name, tkn) { + if (props[name] && _.has(props, name)) { if (props[name].basic || props[name].setter) - warning("Duplicate member '{a}'.", nexttoken, i); + warning("W075", state.tokens.next, i); } else { props[name] = {}; } props[name].setter = true; - props[name].setterToken = token; + props[name].setterToken = tkn; } function saveGetter(name) { - if (props[name] && is_own(props, name)) { + if (props[name] && _.has(props, name)) { if (props[name].basic || props[name].getter) - warning("Duplicate member '{a}'.", nexttoken, i); + warning("W075", state.tokens.next, i); } else { props[name] = {}; } props[name].getter = true; - props[name].getterToken = token; + props[name].getterToken = state.tokens.curr; } - b = token.line !== nexttoken.line; + b = state.tokens.curr.line !== state.tokens.next.line; if (b) { - indent += option.indent; - if (nexttoken.from === indent + option.indent) { - indent += option.indent; + indent += state.option.indent; + if (state.tokens.next.from === indent + state.option.indent) { + indent += state.option.indent; } } + for (;;) { - if (nexttoken.id === "}") { + if (state.tokens.next.id === "}") { break; } + if (b) { indentation(); } - if (nexttoken.value === "get" && peek().id !== ":") { + + if (state.tokens.next.value === "get" && peek().id !== ":") { advance("get"); - if (!option.es5) { - error("get/set are ES5 features."); + + if (!state.option.es5) { + error("E034"); } + i = property_name(); if (!i) { - error("Missing property name."); + error("E035"); } + saveGetter(i); - t = nexttoken; - adjacent(token, nexttoken); + t = state.tokens.next; + adjacent(state.tokens.curr, state.tokens.next); f = doFunction(); p = f["(params)"]; + if (p) { - warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); + warning("W076", t, p[0], i); } - adjacent(token, nexttoken); - } else if (nexttoken.value === "set" && peek().id !== ":") { + + adjacent(state.tokens.curr, state.tokens.next); + } else if (state.tokens.next.value === "set" && peek().id !== ":") { advance("set"); - if (!option.es5) { - error("get/set are ES5 features."); + + if (!state.option.es5) { + error("E034"); } + i = property_name(); if (!i) { - error("Missing property name."); + error("E035"); } - saveSetter(i, nexttoken); - t = nexttoken; - adjacent(token, nexttoken); + + saveSetter(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("Expected a single parameter in set {a} function.", t, i); + warning("W077", t, i); } } else { i = property_name(); - saveProperty(i, nexttoken); + saveProperty(i, state.tokens.next); + if (typeof i !== "string") { break; } + advance(":"); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); expression(10); } countMember(i); - if (nexttoken.id === ",") { - comma(); - if (nexttoken.id === ",") { - warning("Extra comma.", token); - } else if (nexttoken.id === "}" && !option.es5) { - warning("Extra comma.", token); + if (state.tokens.next.id === ",") { + comma({ allowTrailing: true }); + if (state.tokens.next.id === ",") { + warning("W070", state.tokens.curr); + } else if (state.tokens.next.id === "}" && !state.option.es5) { + warning("W070", state.tokens.curr); } } else { break; } } if (b) { - indent -= option.indent; + indent -= state.option.indent; indentation(); } advance("}", this); // Check for lonely setters if in the ES5 mode. - if (option.es5) { + if (state.option.es5) { for (var name in props) { - if (is_own(props, name) && props[name].setter && !props[name].getter) { - warning("Setter is defined without getter.", props[name].setterToken); + if (_.has(props, name) && props[name].setter && !props[name].getter) { + warning("W078", props[name].setterToken); } } } return this; }; x.fud = function () { - error("Expected to see a statement and instead saw a block.", token); + error("E036", state.tokens.curr); }; }(delim("{"))); -// This Function is called when esnext option is set to true -// it adds the `const` statement to JSHINT + // This Function is called when esnext option is set to true + // it adds the `const` statement to JSHINT useESNextSyntax = function () { var conststatement = stmt("const", function (prefix) { @@ -3755,43 +6745,40 @@ klass: this.first = []; for (;;) { - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); id = identifier(); if (funct[id] === "const") { - warning("const '" + id + "' has already been declared"); + warning("E011", null, id); } if (funct["(global)"] && predefined[id] === false) { - warning("Redefinition of '{a}'.", token, id); + warning("W079", state.tokens.curr, id); } addlabel(id, "const"); if (prefix) { break; } - name = token; - this.first.push(token); + name = state.tokens.curr; + this.first.push(state.tokens.curr); - if (nexttoken.id !== "=") { - warning("const " + - "'{a}' is initialized to 'undefined'.", token, id); + if (state.tokens.next.id !== "=") { + warning("E012", state.tokens.curr, id); } - if (nexttoken.id === "=") { - nonadjacent(token, nexttoken); + if (state.tokens.next.id === "=") { + nonadjacent(state.tokens.curr, state.tokens.next); advance("="); - nonadjacent(token, nexttoken); - if (nexttoken.id === "undefined") { - warning("It is not necessary to initialize " + - "'{a}' to 'undefined'.", token, id); + nonadjacent(state.tokens.curr, state.tokens.next); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.curr, id); } - if (peek(0).id === "=" && nexttoken.identifier) { - error("Constant {a} was not declared correctly.", - nexttoken, nexttoken.value); + if (peek(0).id === "=" && state.tokens.next.identifier) { + error("E037", state.tokens.next, state.tokens.next.value); } value = expression(0); name.first = value; } - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); @@ -3806,8 +6793,8 @@ klass: // declaring a variable in a block can have unexpected consequences. var id, name, value; - if (funct["(onevar)"] && option.onevar) { - warning("Too many var statements."); + if (funct["(onevar)"] && state.option.onevar) { + warning("W081"); } else if (!funct["(global)"]) { funct["(onevar)"] = true; } @@ -3815,41 +6802,40 @@ klass: this.first = []; for (;;) { - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); id = identifier(); - if (option.esnext && funct[id] === "const") { - warning("const '" + id + "' has already been declared"); + if (state.option.esnext && funct[id] === "const") { + warning("E011", null, id); } if (funct["(global)"] && predefined[id] === false) { - warning("Redefinition of '{a}'.", token, id); + warning("W079", state.tokens.curr, id); } - addlabel(id, "unused", token); + addlabel(id, "unused", state.tokens.curr); if (prefix) { break; } - name = token; - this.first.push(token); + name = state.tokens.curr; + this.first.push(state.tokens.curr); - if (nexttoken.id === "=") { - nonadjacent(token, nexttoken); + if (state.tokens.next.id === "=") { + nonadjacent(state.tokens.curr, state.tokens.next); advance("="); - nonadjacent(token, nexttoken); - if (nexttoken.id === "undefined") { - warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); + nonadjacent(state.tokens.curr, state.tokens.next); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.curr, id); } - if (peek(0).id === "=" && nexttoken.identifier) { - error("Variable {a} was not declared correctly.", - nexttoken, nexttoken.value); + if (peek(0).id === "=" && state.tokens.next.identifier) { + error("E038", state.tokens.next, state.tokens.next.value); } value = expression(0); name.first = value; } - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); @@ -3860,22 +6846,19 @@ klass: blockstmt("function", function () { if (inblock) { - warning("Function declarations should not be placed in blocks. " + - "Use a function expression or move the statement to the top of " + - "the outer function.", token); + warning("W082", state.tokens.curr); } var i = identifier(); - if (option.esnext && funct[i] === "const") { - warning("const '" + i + "' has already been declared"); + if (state.option.esnext && funct[i] === "const") { + warning("E011", null, i); } - adjacent(token, nexttoken); - addlabel(i, "unction", token); + adjacent(state.tokens.curr, state.tokens.next); + addlabel(i, "unction", state.tokens.curr); doFunction(i, { statement: true }); - if (nexttoken.id === "(" && nexttoken.line === token.line) { - error( -"Function declarations are not invocable. Wrap the whole function invocation in parens."); + if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { + error("E039"); } return this; }); @@ -3883,37 +6866,32 @@ klass: prefix("function", function () { var i = optionalidentifier(); if (i) { - adjacent(token, nexttoken); + adjacent(state.tokens.curr, state.tokens.next); } else { - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); } doFunction(i); - if (!option.loopfunc && funct["(loopage)"]) { - warning("Don't make functions within a loop."); + if (!state.option.loopfunc && funct["(loopage)"]) { + warning("W083"); } return this; }); blockstmt("if", function () { - var t = nexttoken; + var t = state.tokens.next; increaseComplexityCount(); advance("("); nonadjacent(this, t); nospace(); expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + parseCondAssignment(); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); - if (nexttoken.id === "else") { - nonadjacent(token, nexttoken); + if (state.tokens.next.id === "else") { + nonadjacent(state.tokens.curr, state.tokens.next); advance("else"); - if (nexttoken.id === "if" || nexttoken.id === "switch") { + if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") { statement(true); } else { block(true, true); @@ -3930,72 +6908,71 @@ klass: var e; advance("catch"); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); advance("("); scope = Object.create(oldScope); - e = nexttoken.value; - if (nexttoken.type !== "(identifier)") { + e = state.tokens.next.value; + if (state.tokens.next.type !== "(identifier)") { e = null; - warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); + warning("E030", state.tokens.next, e); } advance(); advance(")"); funct = { - "(name)" : "(catch)", - "(line)" : nexttoken.line, - "(character)": nexttoken.character, + "(name)" : "(catch)", + "(line)" : state.tokens.next.line, + "(character)": state.tokens.next.character, "(context)" : funct, "(breakage)" : funct["(breakage)"], "(loopage)" : funct["(loopage)"], - "(scope)" : scope, + "(scope)" : scope, "(statement)": false, - "(metrics)" : createMetrics(nexttoken), - "(catch)" : true, - "(tokens)" : {} + "(metrics)" : createMetrics(state.tokens.next), + "(catch)" : true, + "(tokens)" : {} }; if (e) { addlabel(e, "exception"); } - token.funct = funct; + state.tokens.curr.funct = funct; functions.push(funct); block(false); scope = oldScope; - funct["(last)"] = token.line; - funct["(lastcharacter)"] = token.character; + funct["(last)"] = state.tokens.curr.line; + funct["(lastcharacter)"] = state.tokens.curr.character; funct = funct["(context)"]; } block(false); - if (nexttoken.id === "catch") { + if (state.tokens.next.id === "catch") { increaseComplexityCount(); doCatch(); b = true; } - if (nexttoken.id === "finally") { + if (state.tokens.next.id === "finally") { advance("finally"); block(false); return; } else if (!b) { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, "catch", nexttoken.value); + error("E021", state.tokens.next, "catch", state.tokens.next.value); } return this; }); blockstmt("while", function () { - var t = nexttoken; + var t = state.tokens.next; funct["(breakage)"] += 1; funct["(loopage)"] += 1; increaseComplexityCount(); @@ -4003,14 +6980,9 @@ klass: nonadjacent(this, t); nospace(); expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + parseCondAssignment(); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; @@ -4018,11 +6990,11 @@ klass: }).labelled = true; blockstmt("with", function () { - var t = nexttoken; - if (directive["use strict"]) { - error("'with' is not allowed in strict mode.", token); - } else if (!option.withstmt) { - warning("Don't use 'with'.", token); + 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("("); @@ -4030,14 +7002,14 @@ klass: nospace(); expression(0); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); return this; }); blockstmt("switch", function () { - var t = nexttoken, + var t = state.tokens.next, g = false; funct["(breakage)"] += 1; advance("("); @@ -4045,15 +7017,16 @@ klass: nospace(); this.condition = expression(20); advance(")", t); - nospace(prevtoken, token); - nonadjacent(token, nexttoken); - t = nexttoken; + nospace(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); + t = state.tokens.next; advance("{"); - nonadjacent(token, nexttoken); - indent += option.indent; + nonadjacent(state.tokens.curr, state.tokens.next); + indent += state.option.indent; this.cases = []; + for (;;) { - switch (nexttoken.id) { + switch (state.tokens.next.id) { case "case": switch (funct["(verb)"]) { case "break": @@ -4067,13 +7040,11 @@ klass: // 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 (!ft.test(lines[nexttoken.line - 2])) { - warning( - "Expected a 'break' statement before 'case'.", - token); + if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + warning("W086", state.tokens.curr, "case"); } } - indentation(-option.indent); + indentation(-state.option.indent); advance("case"); this.cases.push(expression(20)); increaseComplexityCount(); @@ -4089,54 +7060,50 @@ klass: case "throw": break; default: - if (!ft.test(lines[nexttoken.line - 2])) { - warning( - "Expected a 'break' statement before 'default'.", - token); + // 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(-option.indent); + indentation(-state.option.indent); advance("default"); g = true; advance(":"); break; case "}": - indent -= option.indent; + indent -= state.option.indent; indentation(); advance("}", t); - if (this.cases.length === 1 || this.condition.id === "true" || - this.condition.id === "false") { - if (!option.onecase) - warning("This 'switch' should be an 'if'.", this); - } funct["(breakage)"] -= 1; funct["(verb)"] = undefined; return; case "(end)": - error("Missing '{a}'.", nexttoken, "}"); + error("E023", state.tokens.next, "}"); return; default: if (g) { - switch (token.id) { + switch (state.tokens.curr.id) { case ",": - error("Each value should have its own case label."); + error("E040"); return; case ":": g = false; statements(); break; default: - error("Missing ':' on a case clause.", token); + error("E025", state.tokens.curr); return; } } else { - if (token.id === ":") { + if (state.tokens.curr.id === ":") { advance(":"); - error("Unexpected '{a}'.", token, ":"); + error("E024", state.tokens.curr, ":"); statements(); } else { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, "case", nexttoken.value); + error("E021", state.tokens.next, "case", state.tokens.next.value); return; } } @@ -4145,8 +7112,8 @@ klass: }).labelled = true; stmt("debugger", function () { - if (!option.debug) { - warning("All 'debugger' statements should be removed."); + if (!state.option.debug) { + warning("W087"); } return this; }).exps = true; @@ -4159,19 +7126,14 @@ klass: this.first = block(true); advance("while"); - var t = nexttoken; - nonadjacent(token, t); + var t = state.tokens.next; + nonadjacent(state.tokens.curr, t); advance("("); nospace(); expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + parseCondAssignment(); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; return this; @@ -4181,27 +7143,26 @@ klass: }()); blockstmt("for", function () { - var s, t = nexttoken; + var s, t = state.tokens.next; funct["(breakage)"] += 1; funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); nonadjacent(this, t); nospace(); - if (peek(nexttoken.id === "var" ? 1 : 0).id === "in") { - if (nexttoken.id === "var") { + if (peek(state.tokens.next.id === "var" ? 1 : 0).id === "in") { + if (state.tokens.next.id === "var") { advance("var"); varstatement.fud.call(varstatement, true); } else { - switch (funct[nexttoken.value]) { + switch (funct[state.tokens.next.value]) { case "unused": - funct[nexttoken.value] = "var"; + funct[state.tokens.next.value] = "var"; break; case "var": break; default: - warning("Bad for in variable '{a}'.", - nexttoken, nexttoken.value); + warning("W088", state.tokens.next, state.tokens.next.value); } advance(); } @@ -4209,57 +7170,50 @@ klass: expression(20); advance(")", t); s = block(true, true); - if (option.forin && s && (s.length > 1 || typeof s[0] !== "object" || + if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" || s[0].value !== "if")) { - warning("The body of a for in should be wrapped in an if statement to filter " + - "unwanted properties from the prototype.", this); + warning("W089", this); } funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; return this; } else { - if (nexttoken.id !== ";") { - if (nexttoken.id === "var") { + if (state.tokens.next.id !== ";") { + if (state.tokens.next.id === "var") { advance("var"); varstatement.fud.call(varstatement); } else { for (;;) { expression(0, "for"); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); } } } - nolinebreak(token); + nolinebreak(state.tokens.curr); advance(";"); - if (nexttoken.id !== ";") { + if (state.tokens.next.id !== ";") { expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + parseCondAssignment(); } - nolinebreak(token); + nolinebreak(state.tokens.curr); advance(";"); - if (nexttoken.id === ";") { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, ")", ";"); + if (state.tokens.next.id === ";") { + error("E021", state.tokens.next, ")", ";"); } - if (nexttoken.id !== ")") { + if (state.tokens.next.id !== ")") { for (;;) { expression(0, "for"); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); } } advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; @@ -4269,22 +7223,22 @@ klass: stmt("break", function () { - var v = nexttoken.value; + var v = state.tokens.next.value; if (funct["(breakage)"] === 0) - warning("Unexpected '{a}'.", nexttoken, this.value); + warning("W052", state.tokens.next, this.value); - if (!option.asi) + if (!state.option.asi) nolinebreak(this); - if (nexttoken.id !== ";") { - if (token.line === nexttoken.line) { + if (state.tokens.next.id !== ";") { + if (state.tokens.curr.line === state.tokens.next.line) { if (funct[v] !== "label") { - warning("'{a}' is not a statement label.", nexttoken, v); + warning("W090", state.tokens.next, v); } else if (scope[v] !== funct) { - warning("'{a}' is out of scope.", nexttoken, v); + warning("W091", state.tokens.next, v); } - this.first = nexttoken; + this.first = state.tokens.next; advance(); } } @@ -4294,26 +7248,26 @@ klass: stmt("continue", function () { - var v = nexttoken.value; + var v = state.tokens.next.value; if (funct["(breakage)"] === 0) - warning("Unexpected '{a}'.", nexttoken, this.value); + warning("W052", state.tokens.next, this.value); - if (!option.asi) + if (!state.option.asi) nolinebreak(this); - if (nexttoken.id !== ";") { - if (token.line === nexttoken.line) { + if (state.tokens.next.id !== ";") { + if (state.tokens.curr.line === state.tokens.next.line) { if (funct[v] !== "label") { - warning("'{a}' is not a statement label.", nexttoken, v); + warning("W090", state.tokens.next, v); } else if (scope[v] !== funct) { - warning("'{a}' is out of scope.", nexttoken, v); + warning("W091", state.tokens.next, v); } - this.first = nexttoken; + this.first = state.tokens.next; advance(); } } else if (!funct["(loopage)"]) { - warning("Unexpected '{a}'.", nexttoken, this.value); + warning("W052", state.tokens.next, this.value); } reachable("continue"); return this; @@ -4321,19 +7275,19 @@ klass: stmt("return", function () { - if (this.line === nexttoken.line) { - if (nexttoken.id === "(regexp)") - warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); + if (this.line === state.tokens.next.line) { + if (state.tokens.next.id === "(regexp)") + warning("W092"); - if (nexttoken.id !== ";" && !nexttoken.reach) { - nonadjacent(token, nexttoken); - if (peek().value === "=" && !option.boss) { - warningAt("Did you mean to return a conditional instead of an assignment?", - token.line, token.character + 1); - } + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + nonadjacent(state.tokens.curr, state.tokens.next); this.first = expression(0); + + if (this.first.value === "=" && !state.option.boss) { + warningAt("W093", this.first.line, this.first.character); + } } - } else if (!option.asi) { + } else if (!state.option.asi) { nolinebreak(this); // always warn (Line breaking error) } reachable("return"); @@ -4343,69 +7297,78 @@ klass: stmt("throw", function () { nolinebreak(this); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); this.first = expression(20); reachable("throw"); return this; }).exps = true; -// Superfluous reserved words + // Future Reserved Words - reserve("class"); - reserve("const"); - reserve("enum"); - reserve("export"); - reserve("extends"); - reserve("import"); - reserve("super"); + FutureReservedWord("abstract"); + FutureReservedWord("boolean"); + FutureReservedWord("byte"); + FutureReservedWord("char"); + FutureReservedWord("class", { es5: true }); + 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"); + FutureReservedWord("let", { 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"); + FutureReservedWord("yield", { es5: true, strictOnly: true }); - reserve("let"); - reserve("yield"); - reserve("implements"); - reserve("interface"); - reserve("package"); - reserve("private"); - reserve("protected"); - reserve("public"); - reserve("static"); - - -// Parse JSON + // Parse JSON function jsonValue() { function jsonObject() { - var o = {}, t = nexttoken; + var o = {}, t = state.tokens.next; advance("{"); - if (nexttoken.id !== "}") { + if (state.tokens.next.id !== "}") { for (;;) { - if (nexttoken.id === "(end)") { - error("Missing '}' to match '{' from line {a}.", - nexttoken, t.line); - } else if (nexttoken.id === "}") { - warning("Unexpected comma.", token); + 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 (nexttoken.id === ",") { - error("Unexpected comma.", nexttoken); - } else if (nexttoken.id !== "(string)") { - warning("Expected a string and instead saw {a}.", - nexttoken, nexttoken.value); + } 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[nexttoken.value] === true) { - warning("Duplicate key '{a}'.", - nexttoken, nexttoken.value); - } else if ((nexttoken.value === "__proto__" && - !option.proto) || (nexttoken.value === "__iterator__" && - !option.iterator)) { - warning("The '{a}' key may produce unexpected results.", - nexttoken, nexttoken.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[nexttoken.value] = true; + o[state.tokens.next.value] = true; } advance(); advance(":"); jsonValue(); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } advance(","); @@ -4415,21 +7378,20 @@ klass: } function jsonArray() { - var t = nexttoken; + var t = state.tokens.next; advance("["); - if (nexttoken.id !== "]") { + if (state.tokens.next.id !== "]") { for (;;) { - if (nexttoken.id === "(end)") { - error("Missing ']' to match '[' from line {a}.", - nexttoken, t.line); - } else if (nexttoken.id === "]") { - warning("Unexpected comma.", token); + 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 (nexttoken.id === ",") { - error("Unexpected comma.", nexttoken); + } else if (state.tokens.next.id === ",") { + error("E028", state.tokens.next); } jsonValue(); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } advance(","); @@ -4438,7 +7400,7 @@ klass: advance("]"); } - switch (nexttoken.id) { + switch (state.tokens.next.id) { case "{": jsonObject(); break; @@ -4454,14 +7416,14 @@ klass: break; case "-": advance("-"); - if (token.character !== nexttoken.from) { - warning("Unexpected space after '-'.", token); + if (state.tokens.curr.character !== state.tokens.next.from) { + warning("W011", state.tokens.curr); } - adjacent(token, nexttoken); + adjacent(state.tokens.curr, state.tokens.next); advance("(number)"); break; default: - error("Expected a JSON value.", nexttoken); + error("E003", state.tokens.next); } } @@ -4472,6 +7434,8 @@ klass: var optionKeys; var newOptionObj = {}; + state.reset(); + if (o && o.scope) { JSHINT.scope = o.scope; } else { @@ -4482,48 +7446,57 @@ klass: JSHINT.scope = "(main)"; } - predefined = Object.create(standard); - declared = Object.create(null); + predefined = Object.create(null); + combine(predefined, vars.ecmaIdentifiers); + combine(predefined, vars.reservedVars); + combine(predefined, g || {}); + declared = Object.create(null); + exported = Object.create(null); + ignored = 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; + var slice, prop; + if (item[0] === "-") { slice = item.slice(1); JSHINT.blacklist[slice] = slice; } else { - predefined[item] = true; + prop = Object.getOwnPropertyDescriptor(o.predef, item); + predefined[item] = prop ? prop.value : false; } }); } optionKeys = Object.keys(o); for (x = 0; x < optionKeys.length; x++) { - newOptionObj[optionKeys[x]] = o[optionKeys[x]]; + if (/^-W\d{3}$/g.test(optionKeys[x])) { + ignored[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] === "newcap" && o[optionKeys[x]] === false) + newOptionObj["(explicitNewcap)"] = true; - if (optionKeys[x] === "indent") - newOptionObj.white = true; + if (optionKeys[x] === "indent") + newOptionObj["(explicitIndent)"] = true; + } } } - option = newOptionObj; + state.option = newOptionObj; - option.indent = option.indent || 4; - option.maxerr = option.maxerr || 50; + state.option.indent = state.option.indent || 4; + state.option.maxerr = state.option.maxerr || 50; - tab = ""; - for (i = 0; i < option.indent; i += 1) { - tab += " "; - } indent = 1; global = Object.create(predefined); scope = global; @@ -4534,7 +7507,7 @@ klass: "(breakage)": 0, "(loopage)": 0, "(tokens)": {}, - "(metrics)": createMetrics(nexttoken) + "(metrics)": createMetrics(state.tokens.next) }; functions = [funct]; urls = []; @@ -4544,37 +7517,91 @@ klass: implied = {}; inblock = false; lookahead = []; - jsonmode = false; warnings = 0; - lines = []; unuseds = []; if (!isString(s) && !Array.isArray(s)) { - errorAt("Input is neither a string nor an array of strings.", 0); + errorAt("E004", 0); return false; } if (isString(s) && /^\s*$/g.test(s)) { - errorAt("Input is an empty string.", 0); + errorAt("E005", 0); return false; } if (s.length === 0) { - errorAt("Input is an empty array.", 0); + errorAt("E005", 0); return false; } - lex.init(s); + var api = { + get isJSON() { + return state.jsonMode; + }, - prereg = true; - directive = {}; + getOption: function (name) { + return state.option[name] || null; + }, - prevtoken = token = nexttoken = syntax["(begin)"]; + 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 (is_own(o, name)) { - checkOption(name, token); + if (_.has(o, name)) { + checkOption(name, state.tokens.curr); } } @@ -4585,26 +7612,28 @@ klass: //reset values comma.first = true; - quotmark = undefined; try { advance(); - switch (nexttoken.id) { + switch (state.tokens.next.id) { case "{": case "[": - option.laxbreak = true; - jsonmode = true; + state.option.laxbreak = true; + state.jsonMode = true; jsonValue(); break; default: directives(); - if (directive["use strict"] && !option.globalstrict) { - warning("Use the function form of \"use strict\".", prevtoken); + + if (state.directive["use strict"]) { + if (!state.option.globalstrict && !state.option.node) { + warning("W097", state.tokens.prev); + } } statements(); } - advance((nexttoken && nexttoken.value !== ".") ? "(end)" : undefined); + advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined); var markDefined = function (name, context) { do { @@ -4645,12 +7674,12 @@ klass: implied[name] = newImplied; }; - var warnUnused = function (name, token) { - var line = token.line; - var chr = token.character; + var warnUnused = function (name, tkn) { + var line = tkn.line; + var chr = tkn.character; - if (option.unused) - warningAt("'{a}' is defined but never used.", line, chr, name); + if (state.option.unused) + warningAt("W098", line, chr, name); unuseds.push({ name: name, @@ -4661,7 +7690,7 @@ klass: var checkUnused = function (func, key) { var type = func[key]; - var token = func["(tokens)"][key]; + var tkn = func["(tokens)"][key]; if (key.charAt(0) === "(") return; @@ -4673,7 +7702,12 @@ klass: if (func["(params)"] && func["(params)"].indexOf(key) !== -1) return; - warnUnused(key, token); + // Variable is in global scope and defined as exported. + if (func["(global)"] && _.has(exported, key)) { + return; + } + + warnUnused(key, tkn); }; // Check queued 'x is not defined' instances to see if they're still undefined. @@ -4682,14 +7716,18 @@ klass: if (markDefined(k[2].value, k[0])) { clearImplied(k[2].value, k[2].line); - } else { + } else if (state.option.undef) { warning.apply(warning, k.slice(1)); } } functions.forEach(function (func) { + if (func["(ignoreUnused)"]) { + return; + } + for (var key in func) { - if (is_own(func, key)) { + if (_.has(func, key)) { checkUnused(func, key); } } @@ -4719,19 +7757,22 @@ klass: }); for (var key in declared) { - if (is_own(declared, key) && !is_own(global, key)) { + if (_.has(declared, key) && !_.has(global, key)) { warnUnused(key, declared[key]); } } - } catch (e) { - if (e) { - var nt = nexttoken || {}; + } catch (err) { + if (err && err.name === "JSHintError") { + var nt = state.tokens.next || {}; JSHINT.errors.push({ - raw : e.raw, - reason : e.message, - line : e.line || nt.line, - character : e.character || nt.from + scope : "(main)", + raw : err.raw, + reason : err.message, + line : err.line || nt.line, + character : err.character || nt.from }, null); + } else { + throw err; } } @@ -4750,11 +7791,18 @@ klass: 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: option + options: state.option }; var implieds = []; var members = []; @@ -4764,12 +7812,12 @@ klass: data.errors = itself.errors; } - if (jsonmode) { + if (state.jsonMode) { data.json = true; } for (n in implied) { - if (is_own(implied, n)) { + if (_.has(implied, n)) { implieds.push({ name: n, line: implied[n] @@ -4838,4 +7886,10 @@ if (typeof exports === "object" && exports) { exports.JSHINT = JSHINT; } -}); \ No newline at end of file +//@ sourceURL=/src/stable/jshint.js +}); +require("/src/stable/jshint.js"); + +var jsHint = require("/src/stable/jshint.js"); +return function(require, exports, module) {module.exports = jsHint;} +}()) \ No newline at end of file diff --git a/lib/ace/mode/lua/luaparse.js b/lib/ace/mode/lua/luaparse.js index f961abc9..be4d5af4 100644 --- a/lib/ace/mode/lua/luaparse.js +++ b/lib/ace/mode/lua/luaparse.js @@ -1,20 +1,42 @@ define(function(require, exports, module) { -/*global exports:true require:true define:true console:true */ +/*global exports:true module:true require:true define:true global:true */ (function (root, name, factory) { 'use strict'; - if (typeof exports !== 'undefined') { - factory(exports); - } else if (typeof define === 'function' && define.amd) { + var freeExports = typeof exports === 'object' && exports + // While CommonJS defines `module` as an object, component define it as a + // function + , freeModule = (typeof module === 'object' || typeof module === 'function') && + module && module.exports === freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, and use + // it as `root` + var freeGlobal = typeof global === 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) + root = freeGlobal; + + // Some AMD build optimizers, like r.js, check for specific condition + // patterns like the following: + if (typeof define === 'function' && define.amd) { define(['exports'], factory); - } else { + } + // check for `exports` after `define` in case a build optimizer adds an + // `exports` object + else if (freeExports && !freeExports.nodeType) { + // in Node.js or RingoJS v0.8.0+ + if (freeModule) factory(freeModule.exports); + // in Narwhal or RingoJS v0.7.0- + else factory(freeExports); + } + // in a browser or Rhino + else { factory((root[name] = {})); } }(this, 'luaparse', function (exports) { 'use strict'; - exports.version = '0.0.11'; + exports.version = '0.0.14'; var input, options, length; @@ -306,10 +328,9 @@ define(function(require, exports, module) { var slice = Array.prototype.slice , toString = Object.prototype.toString - // Simple indexOf implementation which only provides what's required. - , indexOf = Array.prototype.indexOf || function indexOf(element) { - for (var i = 0, length = this.length; i < length; i++) { - if (this[i] === element) return i; + , indexOf = function indexOf(array, element) { + for (var i = 0, length = array.length; i < length; i++) { + if (array[i] === element) return i; } return -1; }; @@ -446,7 +467,7 @@ define(function(require, exports, module) { // containing its value and as well as its position in the input string (this // is always enabled to provide proper debug messages). // - // `readToken()` starts lexing and returns the following token in the stream. + // `lex()` starts lexing and returns the following token in the stream. var index , token @@ -456,7 +477,9 @@ define(function(require, exports, module) { , line , lineStart; - function readToken() { + exports.lex = lex; + + function lex() { skipWhiteSpace(); // Skip comments beginning with -- @@ -887,7 +910,6 @@ define(function(require, exports, module) { // Once the delimiter is found, iterate through the depth count and see // if it matches. - if (']' === character) { terminator = true; for (var i = 0; i < level; i++) { @@ -914,7 +936,7 @@ define(function(require, exports, module) { function next() { token = lookahead; - lookahead = readToken(); + lookahead = lex(); } // Consume a token if its value matches. Once consumed or not, return the @@ -1045,7 +1067,7 @@ define(function(require, exports, module) { // Add identifier name to the current scope if it doesnt already exist. function scopeIdentifierName(name) { - if (-1 !== indexOf.call(scopes[scopeDepth], name)) return; + if (-1 !== indexOf(scopes[scopeDepth], name)) return; scopes[scopeDepth].push(name); } @@ -1058,7 +1080,7 @@ define(function(require, exports, module) { // Attach scope information to node. If the node is global, store it in the // globals array so we can return the information to the user. function attachScope(node, isLocal) { - if (!isLocal && -1 === indexOf.call(globalNames, node.name)) { + if (!isLocal && -1 === indexOf(globalNames, node.name)) { globalNames.push(node.name); globals.push(node); } @@ -1068,7 +1090,7 @@ define(function(require, exports, module) { // Is the identifier name available in this scope. function scopeHasName(name) { - return (-1 !== indexOf.call(scopes[scopeDepth], name)); + return (-1 !== indexOf(scopes[scopeDepth], name)); } @@ -1285,9 +1307,9 @@ define(function(require, exports, module) { expect('end'); return ast.forNumericStatement(variable, start, end, step, body); - + } // If not, it's a Generic For Statement - } else { + else { // The namelist can contain one or more identifiers. var variables = [variable]; while (consume(',')) { @@ -1446,8 +1468,9 @@ define(function(require, exports, module) { if (consume(',')) continue; else if (consume(')')) break; + } // No arguments are allowed after a vararg. - } else if (VarargLiteral === token.type) { + else if (VarargLiteral === token.type) { parameters.push(parsePrimaryExpression()); expect(')'); break; @@ -1729,7 +1752,6 @@ define(function(require, exports, module) { var table = parseTableConstructor(); return ast.tableCallExpression(base, table); } - } else if (StringLiteral === token.type) { return ast.stringCallExpression(base, parsePrimaryExpression()); } @@ -1815,7 +1837,7 @@ define(function(require, exports, module) { length = input.length; // Initialize with a lookahead token. - lookahead = readToken(); + lookahead = lex(); var chunk = parseChunk(); if (options.comments) chunk.comments = comments; @@ -1823,9 +1845,6 @@ define(function(require, exports, module) { return chunk; } - // Expose the lex function - exports.lex = readToken; - })); /* vim: set sw=2 ts=2 et tw=79 : */ diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index 5a682fe5..7ef4c502 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -220,6 +220,6 @@ var UIWorkerClient = function(topLevelNamespaces, mod, classname) { UIWorkerClient.prototype = WorkerClient.prototype; exports.UIWorkerClient = UIWorkerClient; -exports.WorkerClient = WorkerClient; +exports.WorkerClient = UIWorkerClient; }); diff --git a/tool/update_deps.js b/tool/update_deps.js index e6bea5d0..b036d5b4 100644 --- a/tool/update_deps.js +++ b/tool/update_deps.js @@ -1,21 +1,11 @@ var https = require("https") , http = require("http") , url = require("url") - , fs = require("fs") + , fs = require("fs"); -var rootDir = __dirname + "/../lib/ace/" +var rootDir = __dirname + "/../lib/ace/"; var deps = [{ - path: "mode/javascript/jshint.js", - url: "https://raw.github.com/jshint/jshint/master/src/stable/jshint.js", - needsFixup: true, - postProcess: function(t) { - return t.replace( - /"Expected a conditional expression and instead saw an assignment."/g, - '"Assignment in conditional expression"' - ); - } -}, { path: "mode/css/csslint.js", url: "https://raw.github.com/stubbornella/csslint/master/release/csslint-node.js", needsFixup: true @@ -25,52 +15,52 @@ var deps = [{ needsFixup: false }, { path: "mode/lua/luaparse.js", - url: "https://raw.github.com/oxyc/luaparse/master/lib/luaparse.js", + url: "https://raw.github.com/oxyc/luaparse/master/luaparse.js", needsFixup: true -}] +}]; var download = function(href, callback) { var options = url.parse(href); - protocol = options.protocol === "https:" ? https : http; - console.log("connecting to " + options.host + " " + options.path) - var request = protocol.get(options, function(res) { - var data = "" - res.setEncoding("utf-8") + var protocol = options.protocol === "https:" ? https : http; + console.log("connecting to " + options.host + " " + options.path); + protocol.get(options, function(res) { + var data = ""; + res.setEncoding("utf-8"); res.on("data", function(chunk){ - data += chunk - }) + data += chunk; + }); res.on("end", function(){ - callback(data) - }) - }) -} + callback(data); + }); + }); +}; var getDep = function(dep) { download(dep.url, function(data) { if (dep.needsFixup) data = "define(function(require, exports, module) {\n" + data - + "\n});" + + "\n});"; if (dep.postProcess) - data = dep.postProcess(data) + data = dep.postProcess(data); fs.writeFile(rootDir + dep.path, data, "utf-8", function(err){ - if (err) throw err - console.log("File " + dep.path + " saved.") - }) - }) -} + if (err) throw err; + console.log("File " + dep.path + " saved."); + }); + }); +}; -deps.forEach(getDep) +deps.forEach(getDep); // coffee-script void function(){ - var rootHref = "https://raw.github.com/jashkenas/coffee-script/master/" - var path = "mode/coffee/" + var rootHref = "https://raw.github.com/jashkenas/coffee-script/master/"; + var path = "mode/coffee/"; - var subDir = "lib/coffee-script/" + var subDir = "lib/coffee-script/"; var deps = [ "helpers.js", "lexer.js", @@ -83,50 +73,110 @@ void function(){ name: x, href: rootHref + subDir + x, path: rootDir + path + x - } + }; }); - deps.push({name:"LICENSE", href: rootHref + "LICENSE"}) + deps.push({name:"LICENSE", href: rootHref + "LICENSE"}); - var downloads = {}, counter = 0 + var downloads = {}, counter = 0; deps.forEach(function(x) { download(x.href, function(data) { - counter++ - downloads[x.name] = data + counter++; + downloads[x.name] = data; if (counter == deps.length) - allDone() - }) - }) + allDone(); + }); + }); function allDone() { - deps.pop() - var license = downloads["LICENSE"].split('\n') - license = "/**\n * " + license.join("\n * ") + "\n */" + deps.pop(); + var license = downloads.LICENSE.split('\n'); + license = "/**\n * " + license.join("\n * ") + "\n */"; deps.forEach(function(x) { - var data = downloads[x.name] - console.log(x.name) - console.log(!data) + var data = downloads[x.name]; + console.log(x.name); + console.log(!data); if (!data) - return + return; if (x.name == "parser.js") { data = data.replace("var parser = (function(){", "") - .replace(/\nreturn (new Parser)[\s\S]*$/, "\n\nmodule.exports = $1;\n\n") + .replace(/\nreturn (new Parser)[\s\S]*$/, "\n\nmodule.exports = $1;\n\n"); } else { data = data.replace("(function() {", "") - .replace(/}\).call\(this\);\s*$/, "") + .replace(/\}\).call\(this\);\s*$/, ""); } data = license + "\n\n" + "define(function(require, exports, module) {\n" + data - + "\n});" + + "\n});"; fs.writeFile(x.path, data, "utf-8", function(err){ - if (err) throw err - console.log("File " + x.name + " saved.") - console.warn("mode/coffee/coffee-script file needs to updated manually") - console.warn("mode/coffee/parser.js: parseError function needs to be modified") - }) - }) + if (err) throw err; + console.log("File " + x.name + " saved."); + console.warn("mode/coffee/coffee-script file needs to updated manually"); + console.warn("mode/coffee/parser.js: parseError function needs to be modified"); + }); + }); } -}() +}(); + + + + +var spawn = require("child_process").spawn; + +var run = function(cmd, cb) { + var proc = spawn("cmd", ["/c " + cmd]); + + proc.stderr.setEncoding("utf8"); + proc.stderr.on('data', function (data) { + // console.error(data); + }); + + proc.stdout.setEncoding("utf8"); + proc.stdout.on('data', function (data) { + //console.log(data); + }); + + proc.on('exit', done); + proc.on('close', done); + function done(code) { + if (code !== 0) { + console.error(cmd + '::: process exited with code :::' + code); + } + cb() + } +} + +function unquote(str) { + return str.replace(/\\(.)/g, function(x, a) { + return a == "n" ? "\n" + : a == "t" ? "\t" + : a == "r" ? "\r" + : a + }); +} + +run("npm install jshint", function() { + var jshintDist = fs.readFileSync("node_modules/jshint/dist/jshint.js", "utf8"); + + jshintDist = jshintDist.replace( + /(require.define.*)Function\(\[([^\]]*?)\],\s*"(.*)"\s*\)/g, + function(a, def, args, content) { + return def + "function("+args.replace(/["']/g, "") + ") {\n" + + unquote(content) + + "\n}"; + } + ); + jshintDist = jshintDist.replace( + /"Expected a conditional expression and instead saw an assignment."/g, + '"Assignment in conditional expression"' + ); + jshintDist = 'define(function() {\n' + + jshintDist + '\n' + + 'var jsHint = require("/src/stable/jshint.js");\n' + + 'return function(require, exports, module) {module.exports = jsHint;}\n' + +'}())'; + fs.writeFileSync(rootDir + "mode/javascript/jshint.js", jshintDist); +}); \ No newline at end of file