4 // Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // The Software shall be used for Good, not Evil.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 // WARNING: JSLint will hurt your feelings.
28 // JSLINT is a global function. It takes two parameters.
30 // var myResult = JSLINT(source, option);
32 // The first parameter is either a string or an array of strings. If it is a
33 // string, it will be split on '\n' or '\r'. If it is an array of strings, it
34 // is assumed that each string represents one line. The source can be a
35 // JavaScript text or a JSON text.
37 // The second parameter is an optional object of options that control the
38 // operation of JSLINT. Most of the options are booleans: They are all
39 // optional and have a default value of false. One of the options, predef,
40 // can be an array of names, which will be used to declare global variables,
41 // or an object whose keys are used as global names, with a boolean value
42 // that determines if they are assignable.
44 // If it checks out, JSLINT returns true. Otherwise, it returns false.
46 // If false, you can inspect JSLINT.errors to find out the problems.
47 // JSLINT.errors is an array of objects containing these properties:
50 // line : The line (relative to 0) at which the lint was found
51 // character : The character (relative to 0) at which the lint was found
52 // reason : The problem
53 // evidence : The text line in which the problem occurred
54 // raw : The raw message before the details were inserted
55 // a : The first detail
56 // b : The second detail
57 // c : The third detail
58 // d : The fourth detail
61 // If a stopping error was found, a null will be the last element of the
62 // JSLINT.errors array. A stopping error means that JSLint was not confident
63 // enough to continue. It does not necessarily mean that the error was
64 // especially heinous.
66 // You can request a data structure that contains JSLint's results.
68 // var myData = JSLINT.data();
70 // It returns a structure with this form:
118 // You can request a Function Report, which shows all of the functions
119 // and the parameters and vars that they use. This can be used to find
120 // implied global variables and other problems. The report is in HTML and
121 // can be inserted into an HTML <body>. It should be given the result of the
122 // JSLINT.data function.
124 // var myReport = JSLINT.report(data);
126 // You can request an HTML error report.
128 // var myErrorReport = JSLINT.error_report(data);
130 // You can obtain an object containing all of the properties found in the
131 // file. JSLINT.property contains an object containing a key for each
132 // property used in the program, the value being the number of times that
133 // property name was used in the file.
135 // You can request a properties report, which produces a list of the program's
136 // properties in the form of a /*properties*/ declaration.
138 // var myPropertyReport = JSLINT.properties_report(JSLINT.property);
140 // You can obtain the parse tree that JSLint constructed while parsing. The
141 // latest tree is kept in JSLINT.tree. A nice stringification can be produced
144 // JSON.stringify(JSLINT.tree, [
145 // 'string', 'arity', 'name', 'first',
146 // 'second', 'third', 'block', 'else'
149 // You can request a context coloring table. It contains information that can be
150 // applied to the file that was analyzed. Context coloring colors functions
151 // based on their nesting level, and variables on the color of the functions
152 // in which they are defined.
154 // var myColorization = JSLINT.color(data);
156 // It returns an array containing objects of this form:
162 // level: 0 or higher
165 // JSLint provides three inline directives. They look like slashstar comments,
166 // and allow for setting options, declaring global variables, and establishing a
167 // set of allowed property names.
169 // These directives respect function scope.
171 // The jslint directive is a special comment that can set one or more options.
175 evil: true, nomen: true, regexp: true, todo: true
178 // The current option set is
180 // ass true, if assignment expressions should be allowed
181 // bitwise true, if bitwise operators should be allowed
182 // browser true, if the standard browser globals should be predefined
183 // closure true, if Google Closure idioms should be tolerated
184 // continue true, if the continuation statement should be tolerated
185 // debug true, if debugger statements should be allowed
186 // devel true, if logging should be allowed (console, alert, etc.)
187 // eqeq true, if == should be allowed
188 // evil true, if eval should be allowed
189 // forin true, if for in statements need not filter
190 // indent the indentation factor
191 // maxerr the maximum number of errors to allow
192 // maxlen the maximum length of a source line
193 // newcap true, if constructor names capitalization is ignored
194 // node true, if Node.js globals should be predefined
195 // nomen true, if names may have dangling _
196 // passfail true, if the scan should stop on first error
197 // plusplus true, if increment/decrement should be allowed
198 // properties true, if all property names must be declared with /*properties*/
199 // regexp true, if the . should be allowed in regexp literals
200 // rhino true, if the Rhino environment globals should be predefined
201 // unparam true, if unused parameters should be tolerated
202 // sloppy true, if the 'use strict'; pragma is optional
203 // stupid true, if really stupid practices are tolerated
204 // sub true, if all forms of subscript notation are tolerated
205 // todo true, if TODO comments are tolerated
206 // vars true, if multiple var statements per function should be allowed
207 // white true, if sloppy whitespace is tolerated
209 // The properties directive declares an exclusive list of property names.
210 // Any properties named in the program that are not in the list will
211 // produce a warning.
216 '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', '(begin)',
217 '(error)', '*', '+', '-', '/', '<', '<=', '==', '===', '>', '>=', '\\', a,
218 a_label, a_scope, already_defined, and, apply, arguments, arity, ass,
219 assign, assignment_expression, assignment_function_expression, at, avoid_a,
220 b, bad_assignment, bad_constructor, bad_in_a, bad_invocation, bad_new,
221 bad_number, bad_operand, bad_wrap, bitwise, block, break, breakage, browser,
222 c, call, charAt, charCodeAt, character, closure, code, color, combine_var,
223 comments, conditional_assignment, confusing_a, confusing_regexp,
224 constructor_name_a, continue, control_a, couch, create, d, dangling_a, data,
225 dead, debug, deleted, devel, disrupt, duplicate_a, edge, edition, elif,
226 else, empty_block, empty_case, empty_class, entityify, eqeq, error_report,
227 errors, evidence, evil, exception, exec, expected_a_at_b_c, expected_a_b,
228 expected_a_b_from_c_d, expected_id_a, expected_identifier_a,
229 expected_identifier_a_reserved, expected_number_a, expected_operator_a,
230 expected_positive_a, expected_small_a, expected_space_a_b,
231 expected_string_a, exports, f, first, flag, floor, forEach, for_if, forin,
232 from, fromCharCode, fud, function, function_block, function_eval,
233 function_loop, function_statement, function_strict, functions, global,
234 hasOwnProperty, id, identifier, identifier_function, immed, implied_evil,
235 indent, indexOf, infix_in, init, insecure_a, isAlpha, isArray, isDigit,
236 isNaN, join, jslint, json, keys, kind, label, labeled, lbp,
237 leading_decimal_a, led, left, length, level, line, loopage, master, match,
238 maxerr, maxlen, message, missing_a, missing_a_after_b, missing_property,
239 missing_space_a_b, missing_use_strict, mode, move_invocation, move_var, n,
240 name, name_function, nested_comment, newcap, node, nomen, not,
241 not_a_constructor, not_a_defined, not_a_function, not_a_label, not_a_scope,
242 not_greater, nud, number, octal_a, open, outer, parameter,
243 parameter_a_get_b, parameter_arguments_a, parameter_set_a, params, paren,
244 passfail, plusplus, pop, postscript, predef, properties, properties_report,
245 property, prototype, push, quote, r, radix, raw, read_only, reason,
246 redefinition_a_b, regexp, relation, replace, report, reserved, reserved_a,
247 rhino, right, scanned_a_b, scope, search, second, shift, slash_equal, slice,
248 sloppy, sort, split, statement, statement_block, stop, stopping,
249 strange_loop, strict, string, stupid, sub, subscript, substr, supplant,
250 sync_a, t, tag_a_in_b, test, third, thru, toString, todo, todo_comment,
251 token, tokens, too_long, too_many, trailing_decimal_a, tree, unclosed,
252 unclosed_comment, unclosed_regexp, unescaped_a, unexpected_a,
253 unexpected_char_a, unexpected_comment, unexpected_label_a,
254 unexpected_property_a, unexpected_space_a_b, unexpected_typeof_a,
255 uninitialized_a, unnecessary_else, unnecessary_initialize, unnecessary_use,
256 unparam, unreachable_a_b, unsafe, unused_a, url, use_array, use_braces,
257 use_nested_if, use_object, use_or, use_param, use_spaces, used,
258 used_before_a, var, var_a_not, var_loop, vars, varstatement, warn, warning,
259 was, weird_assignment, weird_condition, weird_new, weird_program,
260 weird_relation, weird_ternary, white, wrap, wrap_immediate, wrap_regexp,
261 write_is_wrong, writeable
264 // The global directive is used to declare global variables that can
265 // be accessed by the program. If a declaration is true, then the variable
266 // is writeable. Otherwise, it is read-only.
268 // We build the application inside a function so that we produce only a single
269 // global variable. That function will be invoked immediately, and its return
270 // value is the JSLINT function itself. That function is also an object that
271 // can contain data and other functions.
273 module
.exports
= (function () {
276 function array_to_object(array
, value
) {
278 // Make an object from an array of keys and a common value.
280 var i
, length
= array
.length
, object
= Object
.create(null);
281 for (i
= 0; i
< length
; i
+= 1) {
282 object
[array
[i
]] = value
;
288 var allowed_option
= {
319 anonname
, // The guessed name for anonymous functions.
321 // These are operators that should not be used with the ! operator.
338 begin
, // The root token
339 block_var
, // vars defined in the current block
341 // browser contains a set of global names that are commonly provided by a
342 // web browser environment.
344 browser
= array_to_object([
345 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData',
346 'frames', 'history', 'Image', 'localStorage', 'location', 'name',
347 'navigator', 'Option', 'parent', 'screen', 'sessionStorage',
348 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest'
351 // bundle contains the text messages.
354 a_label
: "'{a}' is a statement label.",
355 a_scope
: "'{a}' used out of scope.",
356 already_defined
: "'{a}' is already defined.",
357 and
: "The '&&' subexpression should be wrapped in parens.",
358 assignment_expression
: "Unexpected assignment expression.",
359 assignment_function_expression
: "Expected an assignment or " +
360 "function call and instead saw an expression.",
361 avoid_a
: "Avoid '{a}'.",
362 bad_assignment
: "Bad assignment.",
363 bad_constructor
: "Bad constructor.",
364 bad_in_a
: "Bad for in variable '{a}'.",
365 bad_invocation
: "Bad invocation.",
366 bad_new
: "Do not use 'new' for side effects.",
367 bad_number
: "Bad number '{a}'.",
368 bad_operand
: "Bad operand.",
369 bad_wrap
: "Do not wrap function literals in parens unless they " +
370 "are to be immediately invoked.",
371 combine_var
: "Combine this with the previous 'var' statement.",
372 conditional_assignment
: "Expected a conditional expression and " +
373 "instead saw an assignment.",
374 confusing_a
: "Confusing use of '{a}'.",
375 confusing_regexp
: "Confusing regular expression.",
376 constructor_name_a
: "A constructor name '{a}' should start with " +
377 "an uppercase letter.",
378 control_a
: "Unexpected control character '{a}'.",
379 dangling_a
: "Unexpected dangling '_' in '{a}'.",
380 deleted
: "Only properties should be deleted.",
381 duplicate_a
: "Duplicate '{a}'.",
382 empty_block
: "Empty block.",
383 empty_case
: "Empty case.",
384 empty_class
: "Empty class.",
385 evil
: "eval is evil.",
386 expected_a_b
: "Expected '{a}' and instead saw '{b}'.",
387 expected_a_b_from_c_d
: "Expected '{a}' to match '{b}' from line " +
388 "{c} and instead saw '{d}'.",
389 expected_a_at_b_c
: "Expected '{a}' at column {b}, not column {c}.",
390 expected_id_a
: "Expected an id, and instead saw #{a}.",
391 expected_identifier_a
: "Expected an identifier and instead saw '{a}'.",
392 expected_identifier_a_reserved
: "Expected an identifier and " +
393 "instead saw '{a}' (a reserved word).",
394 expected_number_a
: "Expected a number and instead saw '{a}'.",
395 expected_operator_a
: "Expected an operator and instead saw '{a}'.",
396 expected_positive_a
: "Expected a positive number and instead saw '{a}'",
397 expected_small_a
: "Expected a small positive integer and instead saw '{a}'",
398 expected_space_a_b
: "Expected exactly one space between '{a}' and '{b}'.",
399 expected_string_a
: "Expected a string and instead saw '{a}'.",
400 for_if
: "The body of a for in should be wrapped in an if " +
401 "statement to filter unwanted properties from the prototype.",
402 function_block
: "Function statements should not be placed in blocks." +
403 "Use a function expression or move the statement to the top of " +
404 "the outer function.",
405 function_eval
: "The Function constructor is eval.",
406 function_loop
: "Don't make functions within a loop.",
407 function_statement
: "Function statements are not invocable. " +
408 "Wrap the whole function invocation in parens.",
409 function_strict
: "Use the function form of 'use strict'.",
410 identifier_function
: "Expected an identifier in an assignment " +
411 "and instead saw a function invocation.",
412 implied_evil
: "Implied eval is evil. Pass a function instead of a string.",
413 infix_in
: "Unexpected 'in'. Compare with undefined, or use the " +
414 "hasOwnProperty method instead.",
415 insecure_a
: "Insecure '{a}'.",
416 isNaN
: "Use the isNaN function to compare with NaN.",
417 leading_decimal_a
: "A leading decimal point can be confused with a dot: '.{a}'.",
418 missing_a
: "Missing '{a}'.",
419 missing_a_after_b
: "Missing '{a}' after '{b}'.",
420 missing_property
: "Missing property name.",
421 missing_space_a_b
: "Missing space between '{a}' and '{b}'.",
422 missing_use_strict
: "Missing 'use strict' statement.",
423 move_invocation
: "Move the invocation into the parens that " +
424 "contain the function.",
425 move_var
: "Move 'var' declarations to the top of the function.",
426 name_function
: "Missing name in function statement.",
427 nested_comment
: "Nested comment.",
429 not_a_constructor
: "Do not use {a} as a constructor.",
430 not_a_defined
: "'{a}' has not been fully defined yet.",
431 not_a_function
: "'{a}' is not a function.",
432 not_a_label
: "'{a}' is not a label.",
433 not_a_scope
: "'{a}' is out of scope.",
434 not_greater
: "'{a}' should not be greater than '{b}'.",
435 octal_a
: "Don't use octal: '{a}'. Use '\\u....' instead.",
436 parameter_arguments_a
: "Do not mutate parameter '{a}' when using 'arguments'.",
437 parameter_a_get_b
: "Unexpected parameter '{a}' in get {b} function.",
438 parameter_set_a
: "Expected parameter (value) in set {a} function.",
439 radix
: "Missing radix parameter.",
440 read_only
: "Read only.",
441 redefinition_a_b
: "Redefinition of '{a}' from line {b}.",
442 reserved_a
: "Reserved name '{a}'.",
443 scanned_a_b
: "{a} ({b}% scanned).",
444 slash_equal
: "A regular expression literal can be confused with '/='.",
445 statement_block
: "Expected to see a statement and instead saw a block.",
446 stopping
: "Stopping.",
447 strange_loop
: "Strange loop.",
448 strict
: "Strict violation.",
449 subscript
: "['{a}'] is better written in dot notation.",
450 sync_a
: "Unexpected sync method: '{a}'.",
451 tag_a_in_b
: "A '<{a}>' must be within '<{b}>'.",
452 todo_comment
: "Unexpected TODO comment.",
453 too_long
: "Line too long.",
454 too_many
: "Too many errors.",
455 trailing_decimal_a
: "A trailing decimal point can be confused " +
456 "with a dot: '.{a}'.",
457 unclosed
: "Unclosed string.",
458 unclosed_comment
: "Unclosed comment.",
459 unclosed_regexp
: "Unclosed regular expression.",
460 unescaped_a
: "Unescaped '{a}'.",
461 unexpected_a
: "Unexpected '{a}'.",
462 unexpected_char_a
: "Unexpected character '{a}'.",
463 unexpected_comment
: "Unexpected comment.",
464 unexpected_label_a
: "Unexpected label '{a}'.",
465 unexpected_property_a
: "Unexpected /*property*/ '{a}'.",
466 unexpected_space_a_b
: "Unexpected space between '{a}' and '{b}'.",
467 unexpected_typeof_a
: "Unexpected 'typeof'. " +
468 "Use '===' to compare directly with {a}.",
469 uninitialized_a
: "Uninitialized '{a}'.",
470 unnecessary_else
: "Unnecessary 'else' after disruption.",
471 unnecessary_initialize
: "It is not necessary to initialize '{a}' " +
473 unnecessary_use
: "Unnecessary 'use strict'.",
474 unreachable_a_b
: "Unreachable '{a}' after '{b}'.",
475 unsafe
: "Unsafe character.",
476 unused_a
: "Unused '{a}'.",
477 url
: "JavaScript URL.",
478 use_array
: "Use the array literal notation [].",
479 use_braces
: "Spaces are hard to count. Use {{a}}.",
480 use_nested_if
: "Expected 'else { if' and instead saw 'else if'.",
481 use_object
: "Use the object literal notation {} or Object.create(null).",
482 use_or
: "Use the || operator.",
483 use_param
: "Use a named parameter.",
484 use_spaces
: "Use spaces, not tabs.",
485 used_before_a
: "'{a}' was used before it was defined.",
486 var_a_not
: "Variable {a} was not declared correctly.",
487 var_loop
: "Don't declare variables in a loop.",
488 weird_assignment
: "Weird assignment.",
489 weird_condition
: "Weird condition.",
490 weird_new
: "Weird construction. Delete 'new'.",
491 weird_program
: "Weird program.",
492 weird_relation
: "Weird relation.",
493 weird_ternary
: "Weird ternary.",
494 wrap_immediate
: "Wrap an immediate function invocation in " +
495 "parentheses to assist the reader in understanding that the " +
496 "expression is the result of a function, and not the " +
498 wrap_regexp
: "Wrap the /regexp/ literal in parens to " +
499 "disambiguate the slash operator.",
500 write_is_wrong
: "document.write can be a form of eval."
502 closure
= array_to_object([
507 couch
= array_to_object([
508 'emit', 'getRow', 'isArray', 'log', 'provides', 'registerType',
509 'require', 'send', 'start', 'sum', 'toJSON'
524 devel
= array_to_object([
525 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH'
540 funct
, // The current function
542 functions
, // All of the functions
543 global_funct
, // The global body
544 global_scope
, // The global scope
545 in_block
, // Where function statements are not allowed
547 itself
, // JSLINT itself
549 lex
, // the tokenizer
552 node
= array_to_object([
553 'Buffer', 'clearImmediate', 'clearInterval', 'clearTimeout',
554 'console', 'exports', 'global', 'module', 'process',
555 'require', 'setImmediate', 'setInterval', 'setTimeout',
556 '__dirname', '__filename'
559 numbery
= array_to_object(['indexOf', 'lastIndexOf', 'search'], true),
562 predefined
, // Global variables defined by option
567 regexp_flag
= array_to_object(['g', 'i', 'm'], true),
568 return_this
= function return_this() {
571 rhino
= array_to_object([
572 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass',
573 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal',
574 'serialize', 'spawn', 'sync', 'toint32', 'version'
577 scope
, // An object containing an object for each variable in scope
578 semicolon_coda
= array_to_object([';', '"', '\'', ')'], true),
580 // standard contains the global names that are provided by the
581 // ECMAScript standard.
583 standard
= array_to_object([
584 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
585 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError',
586 'Function', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'Number',
587 'Object', 'parseInt', 'parseFloat', 'Promise', 'Proxy',
588 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set',
589 'String', 'Symbol', 'SyntaxError', 'System', 'TypeError',
590 'URIError', 'WeakMap', 'WeakSet'
594 syntax
= Object
.create(null),
600 // Regular expressions. Some of these are stupidly long.
602 // carriage return, carriage return linefeed, or linefeed
604 // unsafe characters that are silently deleted by one or more browsers
605 cx
= /[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
607 ix
= /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
609 jx
= /^(?:javascript|jscript|ecmascript|vbscript)\s*:/i,
612 // characters in strings that need escapement
613 nx
= /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
617 tox
= /^\W*to\s*do(?:\W|$)/i,
619 tx
= /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!(\!|==?)?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/;
622 if (typeof String
.prototype.entityify
!== 'function') {
623 String
.prototype.entityify = function () {
625 .replace(/&/g
, '&')
626 .replace(/</g
, '<')
627 .replace(/>/g
, '>');
631 if (typeof String
.prototype.isAlpha
!== 'function') {
632 String
.prototype.isAlpha = function () {
633 return (this >= 'a' && this <= 'z\uffff') ||
634 (this >= 'A' && this <= 'Z\uffff');
638 if (typeof String
.prototype.isDigit
!== 'function') {
639 String
.prototype.isDigit = function () {
640 return (this >= '0' && this <= '9');
644 if (typeof String
.prototype.supplant
!== 'function') {
645 String
.prototype.supplant = function (o
) {
646 return this.replace(/\{([^{}]*)\}/g, function (a
, b
) {
647 var replacement
= o
[b
];
648 return typeof replacement
=== 'string' ||
649 typeof replacement
=== 'number' ? replacement
: a
;
655 function sanitize(a
) {
657 // Escapify a troublesome character.
660 '\\u' + ('0000' + a
.charCodeAt().toString(16)).slice(-4);
664 function add_to_predefined(group
) {
665 Object
.keys(group
).forEach(function (name
) {
666 predefined
[name
] = group
[name
];
672 if (option
.browser
) {
673 add_to_predefined(browser
);
674 option
.browser
= false;
676 if (option
.closure
) {
677 add_to_predefined(closure
);
680 add_to_predefined(couch
);
681 option
.couch
= false;
684 add_to_predefined(devel
);
685 option
.devel
= false;
688 add_to_predefined(node
);
693 add_to_predefined(rhino
);
694 option
.rhino
= false;
699 // Produce an error warning.
701 function artifact(tok
) {
705 return tok
.id
=== '(number)' ? tok
.number
: tok
.string
;
708 function quit(message
, line
, character
) {
712 character
: character
,
713 message
: bundle
.scanned_a_b
.supplant({
714 a
: bundle
[message
] || message
,
715 b
: Math
.floor((line
/ lines
.length
) * 100)
720 function warn(code
, line
, character
, a
, b
, c
, d
) {
721 var warning
= { // ~~
723 raw
: bundle
[code
] || code
,
725 evidence
: lines
[line
- 1] || '',
727 character
: character
,
728 a
: a
|| artifact(this),
733 warning
.reason
= warning
.raw
.supplant(warning
);
734 itself
.errors
.push(warning
);
735 if (option
.passfail
) {
736 quit('stopping', line
, character
);
739 if (warnings
>= option
.maxerr
) {
740 quit('too_many', line
, character
);
745 function stop(code
, line
, character
, a
, b
, c
, d
) {
746 var warning
= warn(code
, line
, character
, a
, b
, c
, d
);
747 quit('stopping', warning
.line
, warning
.character
);
750 function expected_at(at
) {
751 if (!option
.white
&& next_token
.from !== at
) {
752 next_token
.warn('expected_a_at_b_c', '', at
, next_token
.from);
756 // lexical analysis and token construction
758 lex
= (function lex() {
759 var character
, c
, from, length
, line
, pos
, source_row
;
761 // Private lex methods
763 function next_line() {
766 source_row
= lines
[line
];
768 if (source_row
=== undefined) {
771 at
= source_row
.search(/\t/);
774 source_row
= source_row
.replace(/\t/g, ' ');
776 warn('use_spaces', line
, at
+ 1);
779 at
= source_row
.search(cx
);
781 warn('unsafe', line
, at
);
783 if (option
.maxlen
&& option
.maxlen
< source_row
.length
) {
784 warn('too_long', line
, source_row
.length
);
789 // Produce a token object. The token inherits from a syntax symbol.
791 function it(type
, value
) {
793 if (type
=== '(string)') {
794 if (jx
.test(value
)) {
795 warn('url', line
, from);
798 the_token
= Object
.create(syntax
[(
799 type
=== '(punctuator)' || (type
=== '(identifier)' &&
800 Object
.prototype.hasOwnProperty
.call(syntax
, value
))
803 )] || syntax
['(error)']);
804 if (type
=== '(identifier)') {
805 the_token
.identifier
= true;
806 if (value
=== '__iterator__' || value
=== '__proto__') {
807 stop('reserved_a', line
, from, value
);
808 } else if (!option
.nomen
&&
809 (value
.charAt(0) === '_' ||
810 value
.charAt(value
.length
- 1) === '_')) {
811 warn('dangling_a', line
, from, value
);
814 if (type
=== '(number)') {
815 the_token
.number
= +value
;
816 } else if (value
!== undefined) {
817 the_token
.string
= String(value
);
819 the_token
.line
= line
;
820 the_token
.from = from;
821 the_token
.thru
= character
;
822 if (comments
.length
) {
823 the_token
.comments
= comments
;
828 ('(,=:[!&|?{};~+-*%^<>'.indexOf(id
.charAt(id
.length
- 1)) >= 0) ||
829 id
=== 'return' || id
=== 'case'
835 var exec
= x
.exec(source_row
), first
;
837 length
= exec
[0].length
;
840 source_row
= source_row
.slice(length
);
841 from = character
+ length
- first
.length
;
848 warn('unexpected_char_a', line
, character
- 1, '(space)');
852 c
= source_row
.charAt(0);
856 source_row
= source_row
.slice(1);
859 stop('unexpected_char_a', line
, character
, c
);
864 var ch
, at
= 0, r
= '', result
;
867 var i
= parseInt(source_row
.substr(at
+ 1, n
), 16);
869 if (i
>= 32 && i
<= 126 &&
870 i
!== 34 && i
!== 92 && i
!== 39) {
871 warn('unexpected_a', line
, character
, '\\');
874 ch
= String
.fromCharCode(i
);
877 if (json_mode
&& x
!== '"') {
878 warn('expected_a_b', line
, character
, '"', x
);
882 while (at
>= source_row
.length
) {
885 stop('unclosed', line
- 1, from);
888 ch
= source_row
.charAt(at
);
891 source_row
= source_row
.slice(at
+ 1);
892 result
= it('(string)', r
);
897 if (ch
=== '\n' || ch
=== '\r') {
900 warn('control_a', line
, character
+ at
,
901 source_row
.slice(0, at
));
902 } else if (ch
=== '\\') {
905 ch
= source_row
.charAt(at
);
908 warn('unexpected_a', line
, character
, '\\');
914 warn('unexpected_a', line
, character
, '\\\'');
922 warn('unexpected_a', line
, character
, '\\v');
928 warn('unexpected_a', line
, character
, '\\x');
933 if (typeof descapes
[ch
] !== 'string') {
934 warn(ch
>= '0' && ch
<= '7' ? 'octal_a' : 'unexpected_a',
935 line
, character
, '\\' + ch
);
947 function number(snippet
) {
949 if (source_row
.charAt(0).isAlpha()) {
950 warn('expected_space_a_b',
951 line
, character
, c
, source_row
.charAt(0));
954 digit
= snippet
.charAt(1);
955 if (digit
.isDigit()) {
956 if (token
.id
!== '.') {
957 warn('unexpected_a', line
, character
, snippet
);
959 } else if (json_mode
&& (digit
=== 'x' || digit
=== 'X')) {
960 warn('unexpected_a', line
, character
, '0x');
963 if (snippet
.slice(snippet
.length
- 1) === '.') {
964 warn('trailing_decimal_a', line
, character
, snippet
);
967 if (!isFinite(digit
)) {
968 warn('bad_number', line
, character
, snippet
);
971 return it('(number)', snippet
);
974 function comment(snippet
, type
) {
976 warn('unexpected_comment', line
, character
);
977 } else if (!option
.todo
&& tox
.test(snippet
)) {
978 warn('todo_comment', line
, character
);
1003 c
= source_row
.charAt(at
);
1007 stop('unclosed_regexp', line
, from);
1011 warn('unescaped_a', line
, from + at
, '/');
1013 c
= source_row
.slice(0, at
- 1);
1014 potential
= Object
.create(regexp_flag
);
1016 letter
= source_row
.charAt(at
);
1017 if (potential
[letter
] !== true) {
1020 potential
[letter
] = false;
1024 if (source_row
.charAt(at
).isAlpha()) {
1025 stop('unexpected_a', line
, from, source_row
.charAt(at
));
1028 source_row
= source_row
.slice(at
);
1029 quote
= source_row
.charAt(0);
1030 if (quote
=== '/' || quote
=== '*') {
1031 stop('confusing_regexp', line
, from);
1033 result
= it('(regexp)', c
);
1037 c
= source_row
.charAt(at
);
1039 warn('control_a', line
, from + at
, String(c
));
1040 } else if (c
=== '<') {
1041 warn('unexpected_a', line
, from + at
, '\\');
1048 if (source_row
.charAt(at
) === '?') {
1050 switch (source_row
.charAt(at
)) {
1057 warn('expected_a_b', line
, from + at
,
1058 ':', source_row
.charAt(at
));
1067 warn('unescaped_a', line
, from + at
, ')');
1074 while (source_row
.charAt(at
) === ' ') {
1079 warn('use_braces', line
, from + at
, pos
);
1083 c
= source_row
.charAt(at
);
1086 if (!option
.regexp
) {
1087 warn('insecure_a', line
, from + at
, c
);
1088 } else if (source_row
.charAt(at
) === ']') {
1089 stop('unescaped_a', line
, from + at
, '^');
1094 warn('empty_class', line
, from + at
- 1);
1098 c
= source_row
.charAt(at
);
1103 warn('unescaped_a', line
, from + at
, c
);
1110 warn('unescaped_a', line
, from + at
, '-');
1116 warn('unescaped_a', line
, from + at
- 1, '-');
1120 c
= source_row
.charAt(at
);
1122 warn('control_a', line
, from + at
, String(c
));
1123 } else if (c
=== '<') {
1124 warn('unexpected_a', line
, from + at
, '\\');
1130 warn('unescaped_a', line
, from + at
- 1, '/');
1139 if (!option
.regexp
) {
1140 warn('insecure_a', line
, from + at
, c
);
1149 warn('unescaped_a', line
, from + at
, c
);
1153 switch (source_row
.charAt(at
)) {
1158 if (source_row
.charAt(at
) === '?') {
1164 c
= source_row
.charAt(at
);
1165 if (c
< '0' || c
> '9') {
1166 warn('expected_number_a', line
,
1172 c
= source_row
.charAt(at
);
1173 if (c
< '0' || c
> '9') {
1177 low
= +c
+ (low
* 10);
1183 c
= source_row
.charAt(at
);
1184 if (c
>= '0' && c
<= '9') {
1188 c
= source_row
.charAt(at
);
1189 if (c
< '0' || c
> '9') {
1193 high
= +c
+ (high
* 10);
1197 if (source_row
.charAt(at
) !== '}') {
1198 warn('expected_a_b', line
, from + at
,
1203 if (source_row
.charAt(at
) === '?') {
1207 warn('not_greater', line
, from + at
,
1214 c
= source_row
.slice(0, at
- 1);
1216 source_row
= source_row
.slice(at
);
1217 return it('(regexp)', c
);
1220 // Public lex methods
1223 init: function (source
) {
1224 if (typeof source
=== 'string') {
1225 lines
= source
.split(crlfx
);
1234 // token -- this is called by advance to get the next token.
1236 token: function () {
1237 var first
, i
, snippet
;
1240 while (!source_row
) {
1245 snippet
= match(tx
);
1250 first
= snippet
.charAt(0);
1251 if (first
.isAlpha() || first
=== '_' || first
=== '$') {
1252 return it('(identifier)', snippet
);
1257 if (first
.isDigit()) {
1258 return number(snippet
);
1266 return string(snippet
);
1271 comment(source_row
, '//');
1279 i
= source_row
.search(lx
);
1283 character
= source_row
.length
;
1284 comment(source_row
);
1287 stop('unclosed_comment', line
, character
);
1290 comment(source_row
.slice(0, i
), '/*');
1292 if (source_row
.charAt(i
) === '/') {
1293 stop('nested_comment', line
, character
);
1295 source_row
= source_row
.slice(i
+ 2);
1302 if (token
.id
=== '/=') {
1303 stop('slash_equal', line
, from);
1307 : it('(punctuator)', snippet
);
1311 return it('(punctuator)', snippet
);
1319 function define(kind
, token
) {
1323 var name
= token
.string
,
1324 master
= scope
[name
]; // The current definition of the name
1326 // vars are created with a deadzone, so that the expression that initializes
1327 // the var cannot access the var. Functions are not writeable.
1332 token
.master
= master
;
1334 token
.writeable
= true;
1336 // Global variables are a little weird. They can be defined multiple times.
1337 // Some predefined global vars are (or should) not be writeable.
1339 if (kind
=== 'var' && funct
=== global_funct
) {
1341 if (predefined
[name
] === false) {
1342 token
.writeable
= false;
1344 global_scope
[name
] = token
;
1348 // It is an error if the name has already been defined in this scope, except
1349 // when reusing an exception variable name.
1352 if (master
.function === funct
) {
1353 if (master
.kind
!== 'exception' || kind
!== 'exception' ||
1355 token
.warn('already_defined', name
);
1357 } else if (master
.function !== global_funct
) {
1358 if (kind
=== 'var') {
1359 token
.warn('redefinition_a_b', name
, master
.line
);
1363 scope
[name
] = token
;
1364 if (kind
=== 'var') {
1365 block_var
.push(name
);
1370 function peek(distance
) {
1372 // Peek ahead to a future token. The distance is how far ahead to look. The
1373 // default is the next token.
1375 var found
, slot
= 0;
1377 distance
= distance
|| 0;
1378 while (slot
<= distance
) {
1379 found
= lookahead
[slot
];
1381 found
= lookahead
[slot
] = lex
.token();
1389 function advance(id
, match
) {
1391 // Produce the next token, also looking for programming errors.
1395 // If indentation checking was requested, then inspect all of the line breakings.
1396 // The var statement is tricky because the names might be aligned or not. We
1397 // look at the first line break after the var to determine the programmer's
1400 if (var_mode
&& next_token
.line
!== token
.line
) {
1401 if ((var_mode
!== indent
|| !next_token
.edge
) &&
1402 next_token
.from === indent
.at
-
1403 (next_token
.edge
? option
.indent
: 0)) {
1406 dent
.at
-= option
.indent
;
1407 if (dent
=== var_mode
) {
1416 if (next_token
.id
=== '?' && indent
.mode
=== ':' &&
1417 token
.line
!== next_token
.line
) {
1418 indent
.at
-= option
.indent
;
1422 // If the token is an edge.
1424 if (next_token
.edge
) {
1425 if (next_token
.edge
=== 'label') {
1427 } else if (next_token
.edge
=== 'case' || indent
.mode
=== 'statement') {
1428 expected_at(indent
.at
- option
.indent
);
1429 } else if (indent
.mode
!== 'array' || next_token
.line
!== token
.line
) {
1430 expected_at(indent
.at
);
1433 // If the token is not an edge, but is the first token on the line.
1435 } else if (next_token
.line
!== token
.line
) {
1436 if (next_token
.from < indent
.at
+ (indent
.mode
===
1437 'expression' ? 0 : option
.indent
)) {
1438 expected_at(indent
.at
+ option
.indent
);
1442 } else if (next_token
.line
!== token
.line
) {
1443 if (next_token
.edge
) {
1444 expected_at(indent
.at
);
1447 if (indent
.mode
=== 'statement' || indent
.mode
=== 'var') {
1448 expected_at(indent
.at
+ option
.indent
);
1449 } else if (next_token
.from < indent
.at
+ (indent
.mode
===
1450 'expression' ? 0 : option
.indent
)) {
1451 expected_at(indent
.at
+ option
.indent
);
1459 if (next_token
.id
=== '.') {
1460 next_token
.warn('trailing_decimal_a');
1464 if (next_token
.id
=== '-' || next_token
.id
=== '--') {
1465 next_token
.warn('confusing_a');
1469 if (next_token
.id
=== '+' || next_token
.id
=== '++') {
1470 next_token
.warn('confusing_a');
1474 if (token
.id
=== '(string)' || token
.identifier
) {
1475 anonname
= token
.string
;
1478 if (id
&& next_token
.id
!== id
) {
1480 next_token
.warn('expected_a_b_from_c_d', id
,
1481 match
.id
, match
.line
, artifact());
1482 } else if (!next_token
.identifier
|| next_token
.string
!== id
) {
1483 next_token
.warn('expected_a_b', id
, artifact());
1488 next_token
= lookahead
.shift() || lex
.token();
1489 next_token
.function = funct
;
1490 tokens
.push(next_token
);
1494 function do_globals() {
1495 var name
, writeable
;
1497 if (next_token
.id
!== '(string)' && !next_token
.identifier
) {
1500 name
= next_token
.string
;
1503 if (next_token
.id
=== ':') {
1505 switch (next_token
.id
) {
1507 writeable
= predefined
[name
] !== false;
1514 next_token
.stop('unexpected_a');
1517 predefined
[name
] = writeable
;
1518 if (next_token
.id
!== ',') {
1526 function do_jslint() {
1528 while (next_token
.id
=== '(string)' || next_token
.identifier
) {
1529 name
= next_token
.string
;
1530 if (!allowed_option
[name
]) {
1531 next_token
.stop('unexpected_a');
1534 if (next_token
.id
!== ':') {
1535 next_token
.stop('expected_a_b', ':', artifact());
1538 if (typeof allowed_option
[name
] === 'number') {
1539 value
= next_token
.number
;
1540 if (value
> allowed_option
[name
] || value
<= 0 ||
1541 Math
.floor(value
) !== value
) {
1542 next_token
.stop('expected_small_a');
1544 option
[name
] = value
;
1546 if (next_token
.id
=== 'true') {
1547 option
[name
] = true;
1548 } else if (next_token
.id
=== 'false') {
1549 option
[name
] = false;
1551 next_token
.stop('unexpected_a');
1555 if (next_token
.id
=== ',') {
1563 function do_properties() {
1565 option
.properties
= true;
1567 if (next_token
.id
!== '(string)' && !next_token
.identifier
) {
1570 name
= next_token
.string
;
1572 if (next_token
.id
=== ':') {
1575 if (next_token
.id
!== '(string)' && !next_token
.identifier
) {
1581 if (next_token
.id
!== ',') {
1589 directive
= function directive() {
1590 var command
= this.id
,
1591 old_comments_off
= comments_off
,
1592 old_indent
= indent
;
1593 comments_off
= true;
1595 if (next_token
.line
=== token
.line
&& next_token
.from === token
.thru
) {
1596 next_token
.warn('missing_space_a_b', artifact(token
), artifact());
1598 if (lookahead
.length
> 0) {
1599 this.warn('unexpected_a');
1602 case '/*properties':
1616 this.stop('unexpected_a');
1618 comments_off
= old_comments_off
;
1620 indent
= old_indent
;
1624 // Indentation intention
1626 function edge(mode
) {
1627 next_token
.edge
= indent
? indent
.open
&& (mode
|| 'edge') : '';
1631 function step_in(mode
) {
1633 if (typeof mode
=== 'number') {
1639 } else if (!indent
) {
1645 } else if (mode
=== 'statement') {
1652 open
= mode
=== 'var' || next_token
.line
!== token
.line
;
1654 at
: (open
|| mode
=== 'control'
1655 ? indent
.at
+ option
.indent
1656 : indent
.at
) + (indent
.wrap
? option
.indent
: 0),
1661 if (mode
=== 'var' && open
) {
1667 function step_out(id
, symbol
) {
1669 if (indent
&& indent
.open
) {
1670 indent
.at
-= option
.indent
;
1673 advance(id
, symbol
);
1676 indent
= indent
.was
;
1680 // Functions for conformance of whitespace.
1682 function one_space(left
, right
) {
1683 left
= left
|| token
;
1684 right
= right
|| next_token
;
1685 if (right
.id
!== '(end)' && !option
.white
&&
1686 (token
.line
!== right
.line
||
1687 token
.thru
+ 1 !== right
.from)) {
1688 right
.warn('expected_space_a_b', artifact(token
), artifact(right
));
1692 function one_space_only(left
, right
) {
1693 left
= left
|| token
;
1694 right
= right
|| next_token
;
1695 if (right
.id
!== '(end)' && (left
.line
!== right
.line
||
1696 (!option
.white
&& left
.thru
+ 1 !== right
.from))) {
1697 right
.warn('expected_space_a_b', artifact(left
), artifact(right
));
1701 function no_space(left
, right
) {
1702 left
= left
|| token
;
1703 right
= right
|| next_token
;
1704 if ((!option
.white
) &&
1705 left
.thru
!== right
.from && left
.line
=== right
.line
) {
1706 right
.warn('unexpected_space_a_b', artifact(left
), artifact(right
));
1710 function no_space_only(left
, right
) {
1711 left
= left
|| token
;
1712 right
= right
|| next_token
;
1713 if (right
.id
!== '(end)' && (left
.line
!== right
.line
||
1714 (!option
.white
&& left
.thru
!== right
.from))) {
1715 right
.warn('unexpected_space_a_b', artifact(left
), artifact(right
));
1719 function spaces(left
, right
) {
1720 if (!option
.white
) {
1721 left
= left
|| token
;
1722 right
= right
|| next_token
;
1723 if (left
.thru
=== right
.from && left
.line
=== right
.line
) {
1724 right
.warn('missing_space_a_b', artifact(left
), artifact(right
));
1730 if (next_token
.id
!== ',') {
1731 warn('expected_a_b', token
.line
, token
.thru
, ',', artifact());
1733 if (!option
.white
) {
1742 function semicolon() {
1743 if (next_token
.id
!== ';') {
1744 warn('expected_a_b', token
.line
, token
.thru
, ';', artifact());
1746 if (!option
.white
) {
1750 if (semicolon_coda
[next_token
.id
] !== true) {
1756 function use_strict() {
1757 if (next_token
.string
=== 'use strict') {
1759 next_token
.warn('unnecessary_use');
1771 function are_similar(a
, b
) {
1775 if (Array
.isArray(a
)) {
1776 if (Array
.isArray(b
) && a
.length
=== b
.length
) {
1778 for (i
= 0; i
< a
.length
; i
+= 1) {
1779 if (!are_similar(a
[i
], b
[i
])) {
1787 if (Array
.isArray(b
)) {
1790 if (a
.id
=== '(number)' && b
.id
=== '(number)') {
1791 return a
.number
=== b
.number
;
1793 if (a
.arity
=== b
.arity
&& a
.string
=== b
.string
) {
1796 return a
.string
=== b
.string
;
1799 return a
.id
=== b
.id
&& are_similar(a
.first
, b
.first
) &&
1800 a
.id
!== '{' && a
.id
!== '[';
1802 return are_similar(a
.first
, b
.first
) &&
1803 are_similar(a
.second
, b
.second
);
1805 return are_similar(a
.first
, b
.first
) &&
1806 are_similar(a
.second
, b
.second
) &&
1807 are_similar(a
.third
, b
.third
);
1815 if (a
.id
=== '.' && b
.id
=== '[' && b
.arity
=== 'infix') {
1816 return a
.second
.string
=== b
.second
.string
&& b
.second
.id
=== '(string)';
1818 if (a
.id
=== '[' && a
.arity
=== 'infix' && b
.id
=== '.') {
1819 return a
.second
.string
=== b
.second
.string
&& a
.second
.id
=== '(string)';
1825 // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
1826 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
1827 // like .nud except that it is only used on the first token of a statement.
1828 // Having .fud makes it much easier to define statement-oriented languages like
1829 // JavaScript. I retained Pratt's nomenclature.
1831 // .nud Null denotation
1832 // .fud First null denotation
1833 // .led Left denotation
1834 // lbp Left binding power
1835 // rbp Right binding power
1837 // They are elements of the parsing method called Top Down Operator Precedence.
1839 function expression(rbp
, initial
) {
1841 // rbp is the right binding power.
1842 // initial indicates that this is the first expression of a statement.
1845 if (next_token
.id
=== '(end)') {
1846 token
.stop('unexpected_a', next_token
.id
);
1850 anonname
= 'anonymous';
1852 if (initial
=== true && token
.fud
) {
1858 if (next_token
.id
=== '(number)' && token
.id
=== '.') {
1859 token
.warn('leading_decimal_a', artifact());
1863 token
.stop('expected_identifier_a', artifact(token
));
1865 while (rbp
< next_token
.lbp
) {
1867 left
= token
.led(left
);
1870 if (left
&& left
.assign
&& !initial
) {
1872 left
.warn('assignment_expression');
1874 if (left
.id
!== '=' && left
.first
.master
) {
1875 left
.first
.master
.used
= true;
1883 this.stop('unexpected_a');
1886 this.stop('expected_operator_a');
1888 warn: function (code
, a
, b
, c
, d
) {
1889 if (!this.warning
) {
1890 this.warning
= warn(code
, this.line
|| 0, this.from || 0,
1891 a
|| artifact(this), b
, c
, d
);
1894 stop: function (code
, a
, b
, c
, d
) {
1895 this.warning
= undefined;
1896 this.warn(code
, a
, b
, c
, d
);
1897 return quit('stopping', this.line
, this.character
);
1902 // Functional constructors for making the symbols that will be inherited by
1905 function symbol(s
, bp
) {
1908 x
= Object
.create(protosymbol
);
1909 x
.id
= x
.string
= s
;
1916 function postscript(x
) {
1917 x
.postscript
= true;
1921 function ultimate(s
) {
1922 var x
= symbol(s
, 0);
1928 return postscript(x
);
1931 function reserve_name(x
) {
1932 var c
= x
.id
.charAt(0);
1933 if ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z')) {
1934 x
.identifier
= x
.reserved
= true;
1939 function stmt(s
, f
) {
1942 return reserve_name(x
);
1945 function disrupt_stmt(s
, f
) {
1950 function labeled_stmt(s
, f
) {
1951 var x
= stmt(s
, function labeled() {
1953 if (funct
.breakage
) {
1954 funct
.breakage
.push(this);
1956 funct
.breakage
= [this];
1958 the_statement
= f
.apply(this);
1959 if (funct
.breakage
.length
> 1) {
1960 funct
.breakage
.pop();
1962 delete funct
.breakage
;
1964 return the_statement
;
1969 function prefix(s
, f
) {
1970 var x
= symbol(s
, 150);
1972 x
.nud = function () {
1974 that
.arity
= 'prefix';
1975 if (typeof f
=== 'function') {
1977 if (that
.arity
!== 'prefix') {
1981 if (s
=== 'typeof') {
1986 that
.first
= expression(150);
1991 if (!option
.plusplus
) {
1992 that
.warn('unexpected_a');
1993 } else if ((!that
.first
.identifier
|| that
.first
.reserved
) &&
1994 that
.first
.id
!== '.' && that
.first
.id
!== '[') {
1995 that
.warn('bad_operand');
1999 if (that
.first
.arity
=== 'prefix' ||
2000 that
.first
.arity
=== 'function') {
2001 that
.warn('unexpected_a');
2010 function type(s
, t
, nud
) {
2020 function reserve(s
, f
) {
2022 x
.identifier
= x
.reserved
= true;
2023 if (typeof f
=== 'function') {
2030 function constant(name
) {
2031 var x
= reserve(name
);
2033 x
.nud
= return_this
;
2038 function reservevar(s
, v
) {
2039 return reserve(s
, function () {
2040 if (typeof v
=== 'function') {
2048 function infix(s
, p
, f
, w
) {
2049 var x
= symbol(s
, p
);
2051 x
.led = function (left
) {
2052 this.arity
= 'infix';
2054 spaces(prev_token
, token
);
2057 if (!option
.bitwise
&& this.bitwise
) {
2058 this.warn('unexpected_a');
2060 if (typeof f
=== 'function') {
2061 return f(left
, this);
2064 this.second
= expression(p
);
2070 function expected_relation(node
, message
) {
2072 node
.warn(message
|| 'conditional_assignment');
2077 function expected_condition(node
, message
) {
2081 if (node
.arity
!== 'infix') {
2082 node
.warn(message
|| 'weird_condition');
2099 node
.warn(message
|| 'weird_condition');
2102 if (node
.first
.id
=== 'new' ||
2103 (node
.first
.string
=== 'Boolean') ||
2104 (node
.first
.id
=== '.' &&
2105 numbery
[node
.first
.second
.string
] === true)) {
2106 node
.warn(message
|| 'weird_condition');
2113 function check_relation(node
) {
2114 switch (node
.arity
) {
2119 node
.warn('unexpected_a');
2122 node
.warn('confusing_a');
2128 node
.warn('unexpected_a');
2131 if (node
.id
=== 'NaN') {
2133 } else if (node
.relation
) {
2134 node
.warn('weird_relation');
2141 function relation(s
, eqeq
) {
2142 var x
= infix(s
, 100, function (left
, that
) {
2143 check_relation(left
);
2144 if (eqeq
&& !option
.eqeq
) {
2145 that
.warn('expected_a_b', eqeq
, that
.id
);
2147 var right
= expression(100);
2148 if (are_similar(left
, right
) ||
2149 ((left
.id
=== '(string)' || left
.id
=== '(number)') &&
2150 (right
.id
=== '(string)' || right
.id
=== '(number)'))) {
2151 that
.warn('weird_relation');
2152 } else if (left
.id
=== 'typeof') {
2153 if (right
.id
!== '(string)') {
2154 right
.warn("expected_string_a", artifact(right
));
2155 } else if (right
.string
=== 'undefined' ||
2156 right
.string
=== 'null') {
2157 left
.warn("unexpected_typeof_a", right
.string
);
2159 } else if (right
.id
=== 'typeof') {
2160 if (left
.id
!== '(string)') {
2161 left
.warn("expected_string_a", artifact(left
));
2162 } else if (left
.string
=== 'undefined' ||
2163 left
.string
=== 'null') {
2164 right
.warn("unexpected_typeof_a", left
.string
);
2168 that
.second
= check_relation(right
);
2175 function lvalue(that
, s
) {
2177 if (that
.identifier
) {
2178 master
= scope
[that
.string
];
2180 if (scope
[that
.string
].writeable
!== true) {
2181 that
.warn('read_only');
2187 } else if (that
.reserved
) {
2188 that
.warn('expected_identifier_a_reserved');
2190 } else if (that
.id
=== '.' || that
.id
=== '[') {
2191 if (!that
.first
|| that
.first
.string
=== 'arguments') {
2192 that
.warn('bad_assignment');
2195 that
.warn('bad_assignment');
2200 function assignop(s
, op
) {
2201 var x
= infix(s
, 20, function (left
, that
) {
2205 that
.second
= expression(20);
2206 if (that
.id
=== '=' && are_similar(that
.first
, that
.second
)) {
2207 that
.warn('weird_assignment');
2210 while (next_token
.id
=== '=') {
2211 lvalue(next
.second
, '=');
2212 next_token
.first
= next
.second
;
2213 next
.second
= next_token
;
2216 next
.second
= expression(20);
2222 if (syntax
[op
].bitwise
) {
2230 function bitwise(s
, p
) {
2231 var x
= infix(s
, p
, 'number');
2237 function suffix(s
) {
2238 var x
= symbol(s
, 150);
2239 x
.led = function (left
) {
2240 no_space_only(prev_token
, token
);
2241 if (!option
.plusplus
) {
2242 this.warn('unexpected_a');
2243 } else if ((!left
.identifier
|| left
.reserved
) &&
2244 left
.id
!== '.' && left
.id
!== '[') {
2245 this.warn('bad_operand');
2248 this.arity
= 'suffix';
2255 function optional_identifier(variable
) {
2256 if (next_token
.identifier
) {
2258 if (token
.reserved
&& variable
) {
2259 token
.warn('expected_identifier_a_reserved');
2261 return token
.string
;
2266 function identifier(variable
) {
2267 var i
= optional_identifier(variable
);
2269 next_token
.stop(token
.id
=== 'function' && next_token
.id
=== '('
2271 : 'expected_identifier_a');
2277 function statement() {
2279 var label
, preamble
, the_statement
;
2281 // We don't like the empty statement.
2283 if (next_token
.id
=== ';') {
2284 next_token
.warn('unexpected_a');
2289 // Is this a labeled statement?
2291 if (next_token
.identifier
&& !next_token
.reserved
&& peek().id
=== ':') {
2296 define('label', label
);
2297 if (next_token
.labeled
!== true || funct
=== global_funct
) {
2298 label
.stop('unexpected_label_a');
2299 } else if (jx
.test(label
.string
+ ':')) {
2302 next_token
.label
= label
;
2304 label
.statement
= next_token
;
2307 // Parse the statement.
2309 preamble
= next_token
;
2310 if (token
.id
!== 'else') {
2313 step_in('statement');
2314 the_statement
= expression(0, true);
2315 if (the_statement
) {
2317 // Look for the final semicolon.
2319 if (the_statement
.arity
=== 'statement') {
2320 if (the_statement
.id
=== 'switch' ||
2321 (the_statement
.block
&& the_statement
.id
!== 'do')) {
2328 // If this is an expression statement, determine if it is acceptable.
2331 // statements. If it is to be used at all, new should only be used to make
2332 // objects, not side effects. The expression statements we do like do
2333 // assignment or invocation or delete.
2335 if (the_statement
.id
=== '(') {
2336 if (the_statement
.first
.id
=== 'new') {
2337 next_token
.warn('bad_new');
2339 } else if (the_statement
.id
=== '++' ||
2340 the_statement
.id
=== '--') {
2341 lvalue(the_statement
.first
);
2342 } else if (!the_statement
.assign
&&
2343 the_statement
.id
!== 'delete') {
2344 if (!option
.closure
|| !preamble
.comments
) {
2345 preamble
.warn('assignment_function_expression');
2355 return the_statement
;
2359 function statements() {
2360 var array
= [], disruptor
, the_statement
;
2362 // A disrupt statement may not be followed by any other statement.
2363 // If the last statement is disrupt, then the sequence is disrupt.
2365 while (next_token
.postscript
!== true) {
2366 if (next_token
.id
=== ';') {
2367 next_token
.warn('unexpected_a');
2370 if (next_token
.string
=== 'use strict') {
2371 if ((!node_js
) || funct
!== global_funct
|| array
.length
> 0) {
2372 next_token
.warn('function_strict');
2377 next_token
.warn('unreachable_a_b', next_token
.string
,
2381 the_statement
= statement();
2382 if (the_statement
) {
2383 array
.push(the_statement
);
2384 if (the_statement
.disrupt
) {
2385 disruptor
= the_statement
;
2386 array
.disrupt
= true;
2395 function block(kind
) {
2397 // A block is a sequence of statements wrapped in braces.
2401 old_block_var
= block_var
,
2402 old_in_block
= in_block
,
2403 old_strict_mode
= strict_mode
;
2405 in_block
= kind
!== 'function' && kind
!== 'try' && kind
!== 'catch';
2407 if (curly
.id
=== '{') {
2411 if (kind
=== 'function' && !use_strict() && !old_strict_mode
&&
2412 !option
.sloppy
&& funct
.level
=== 1) {
2413 next_token
.warn('missing_use_strict');
2415 array
= statements();
2416 strict_mode
= old_strict_mode
;
2417 step_out('}', curly
);
2418 } else if (in_block
) {
2419 curly
.stop('expected_a_b', '{', artifact());
2421 curly
.warn('expected_a_b', '{', artifact());
2422 array
= [statement()];
2423 array
.disrupt
= array
[0].disrupt
;
2425 if (kind
!== 'catch' && array
.length
=== 0 && !option
.debug
) {
2426 curly
.warn('empty_block');
2428 block_var
.forEach(function (name
) {
2429 scope
[name
].dead
= true;
2431 block_var
= old_block_var
;
2432 in_block
= old_in_block
;
2437 function tally_property(name
) {
2438 if (option
.properties
&& typeof property
[name
] !== 'number') {
2439 token
.warn('unexpected_property_a', name
);
2441 if (property
[name
]) {
2442 property
[name
] += 1;
2449 // ECMAScript parser
2452 var x
= symbol('(identifier)');
2453 x
.nud = function () {
2454 var name
= this.string
,
2455 master
= scope
[name
],
2458 // If the master is not in scope, then we may have an undeclared variable.
2459 // Check the predefined list. If it was predefined, create the global
2463 writeable
= predefined
[name
];
2464 if (typeof writeable
=== 'boolean') {
2465 global_scope
[name
] = master
= {
2467 function: global_funct
,
2470 writeable
: writeable
2473 // But if the variable is not in scope, and is not predefined, and if we are not
2474 // in the global scope, then we have an undefined variable error.
2477 token
.warn('used_before_a');
2480 this.master
= master
;
2483 // Annotate uses that cross scope boundaries.
2486 if (master
.kind
=== 'label') {
2487 this.warn('a_label');
2489 if (master
.dead
=== true || master
.dead
=== funct
) {
2490 this.warn('a_scope');
2493 if (master
.function !== funct
) {
2494 if (master
.function === global_funct
) {
2495 funct
.global
.push(name
);
2497 master
.function.closure
.push(name
);
2498 funct
.outer
.push(name
);
2505 x
.identifier
= true;
2509 // Build the syntax table by declaring the syntactic elements.
2511 type('(array)', 'array');
2512 type('(function)', 'function');
2513 type('(number)', 'number', return_this
);
2514 type('(object)', 'object');
2515 type('(string)', 'string', return_this
);
2516 type('(boolean)', 'boolean', return_this
);
2517 type('(regexp)', 'regexp', return_this
);
2519 ultimate('(begin)');
2521 ultimate('(error)');
2522 postscript(symbol('}'));
2525 postscript(symbol('"'));
2526 postscript(symbol('\''));
2533 postscript(reserve('case'));
2535 postscript(reserve('default'));
2539 reservevar('arguments', function (x
) {
2540 if (strict_mode
&& funct
=== global_funct
) {
2543 funct
.arguments
= true;
2546 constant('false', 'boolean');
2547 constant('Infinity', 'number');
2548 constant('NaN', 'number');
2549 constant('null', '');
2550 reservevar('this', function (x
) {
2551 if (strict_mode
&& funct
.statement
&& funct
.name
.charAt(0) > 'Z') {
2555 constant('true', 'boolean');
2556 constant('undefined', '');
2558 infix('?', 30, function (left
, that
) {
2560 that
.first
= expected_condition(expected_relation(left
));
2561 that
.second
= expression(0);
2564 var colon
= next_token
;
2568 that
.third
= expression(10);
2569 that
.arity
= 'ternary';
2570 if (are_similar(that
.second
, that
.third
)) {
2571 colon
.warn('weird_ternary');
2572 } else if (are_similar(that
.first
, that
.second
)) {
2573 that
.warn('use_or');
2579 infix('||', 40, function (left
, that
) {
2580 function paren_check(that
) {
2581 if (that
.id
=== '&&' && !that
.paren
) {
2587 that
.first
= paren_check(expected_condition(expected_relation(left
)));
2588 that
.second
= paren_check(expected_relation(expression(40)));
2589 if (are_similar(that
.first
, that
.second
)) {
2590 that
.warn('weird_condition');
2595 infix('&&', 50, function (left
, that
) {
2596 that
.first
= expected_condition(expected_relation(left
));
2597 that
.second
= expected_relation(expression(50));
2598 if (are_similar(that
.first
, that
.second
)) {
2599 that
.warn('weird_condition');
2604 prefix('void', function (that
) {
2605 that
.first
= expression(0);
2606 that
.warn('expected_a_b', 'undefined', 'void');
2614 relation('==', '===');
2616 relation('!=', '!==');
2625 bitwise('>>>', 120);
2627 infix('in', 120, function (left
, that
) {
2628 that
.warn('infix_in');
2630 that
.right
= expression(130);
2633 infix('instanceof', 120);
2634 infix('+', 130, function (left
, that
) {
2635 if (left
.id
=== '(number)') {
2636 if (left
.number
=== 0) {
2637 left
.warn('unexpected_a', '0');
2639 } else if (left
.id
=== '(string)') {
2640 if (left
.string
=== '') {
2641 left
.warn('expected_a_b', 'String', '\'\'');
2644 var right
= expression(130);
2645 if (right
.id
=== '(number)') {
2646 if (right
.number
=== 0) {
2647 right
.warn('unexpected_a', '0');
2649 } else if (right
.id
=== '(string)') {
2650 if (right
.string
=== '') {
2651 right
.warn('expected_a_b', 'String', '\'\'');
2654 if (left
.id
=== right
.id
) {
2655 if (left
.id
=== '(string)' || left
.id
=== '(number)') {
2656 if (left
.id
=== '(string)') {
2657 left
.string
+= right
.string
;
2658 if (jx
.test(left
.string
)) {
2662 left
.number
+= right
.number
;
2664 left
.thru
= right
.thru
;
2669 that
.second
= right
;
2673 prefix('+++', function () {
2674 token
.warn('confusing_a');
2675 this.first
= expression(150);
2676 this.arity
= 'prefix';
2679 infix('+++', 130, function (left
) {
2680 token
.warn('confusing_a');
2682 this.second
= expression(130);
2685 infix('-', 130, function (left
, that
) {
2686 if ((left
.id
=== '(number)' && left
.number
=== 0) || left
.id
=== '(string)') {
2687 left
.warn('unexpected_a');
2689 var right
= expression(130);
2690 if ((right
.id
=== '(number)' && right
.number
=== 0) || right
.id
=== '(string)') {
2691 right
.warn('unexpected_a');
2693 if (left
.id
=== right
.id
&& left
.id
=== '(number)') {
2694 left
.number
-= right
.number
;
2695 left
.thru
= right
.thru
;
2699 that
.second
= right
;
2703 prefix('---', function () {
2704 token
.warn('confusing_a');
2705 this.first
= expression(150);
2706 this.arity
= 'prefix';
2709 infix('---', 130, function (left
) {
2710 token
.warn('confusing_a');
2712 this.second
= expression(130);
2715 infix('*', 140, function (left
, that
) {
2716 if ((left
.id
=== '(number)' && (left
.number
=== 0 || left
.number
=== 1)) || left
.id
=== '(string)') {
2717 left
.warn('unexpected_a');
2719 var right
= expression(140);
2720 if ((right
.id
=== '(number)' && (right
.number
=== 0 || right
.number
=== 1)) || right
.id
=== '(string)') {
2721 right
.warn('unexpected_a');
2723 if (left
.id
=== right
.id
&& left
.id
=== '(number)') {
2724 left
.number
*= right
.number
;
2725 left
.thru
= right
.thru
;
2729 that
.second
= right
;
2732 infix('/', 140, function (left
, that
) {
2733 if ((left
.id
=== '(number)' && left
.number
=== 0) || left
.id
=== '(string)') {
2734 left
.warn('unexpected_a');
2736 var right
= expression(140);
2737 if ((right
.id
=== '(number)' && (right
.number
=== 0 || right
.number
=== 1)) || right
.id
=== '(string)') {
2738 right
.warn('unexpected_a');
2740 if (left
.id
=== right
.id
&& left
.id
=== '(number)') {
2741 left
.number
/= right
.number
;
2742 left
.thru
= right
.thru
;
2746 that
.second
= right
;
2749 infix('%', 140, function (left
, that
) {
2750 if ((left
.id
=== '(number)' && (left
.number
=== 0 || left
.number
=== 1)) || left
.id
=== '(string)') {
2751 left
.warn('unexpected_a');
2753 var right
= expression(140);
2754 if ((right
.id
=== '(number)' && right
.number
=== 0) || right
.id
=== '(string)') {
2755 right
.warn('unexpected_a');
2757 if (left
.id
=== right
.id
&& left
.id
=== '(number)') {
2758 left
.number
%= right
.number
;
2759 left
.thru
= right
.thru
;
2763 that
.second
= right
;
2772 prefix('delete', function (that
) {
2774 var p
= expression(0);
2775 if (!p
|| (p
.id
!== '.' && p
.id
!== '[')) {
2776 next_token
.warn('deleted');
2783 prefix('~', function (that
) {
2785 if (!option
.bitwise
) {
2786 that
.warn('unexpected_a');
2788 that
.first
= expression(150);
2791 function banger(that
) {
2793 that
.first
= expected_condition(expression(150));
2794 if (bang
[that
.first
.id
] === that
|| that
.first
.assign
) {
2795 that
.warn('confusing_a');
2799 prefix('!', banger
);
2800 prefix('!!', banger
);
2802 prefix('new', function (that
) {
2804 var c
= expression(160), n
, p
, v
;
2806 if (c
.id
!== 'function') {
2810 token
.warn('use_object');
2813 if (next_token
.id
=== '(') {
2817 if (next_token
.id
!== ')') {
2820 if (n
.id
=== '(string)' || next_token
.id
=== ',') {
2821 p
.warn('use_array');
2823 while (next_token
.id
=== ',') {
2825 p
.second
.push(expression(0));
2828 token
.warn('use_array');
2833 token
.warn('use_array');
2840 c
.warn('not_a_constructor');
2844 next_token
.warn('function_eval');
2852 if (c
.id
!== 'function') {
2853 v
= c
.string
.charAt(0);
2854 if (!option
.newcap
&& (v
< 'A' || v
> 'Z')) {
2855 token
.warn('constructor_name_a');
2860 if (c
.id
!== '.' && c
.id
!== '[' && c
.id
!== '(') {
2861 token
.warn('bad_constructor');
2865 that
.warn('weird_new');
2867 if (next_token
.id
!== '(') {
2868 next_token
.warn('missing_a', '()');
2873 infix('(', 160, function (left
, that
) {
2875 if (indent
&& indent
.mode
=== 'expression') {
2876 no_space(prev_token
, token
);
2878 no_space_only(prev_token
, token
);
2880 if (!left
.immed
&& left
.id
=== 'function') {
2881 next_token
.warn('wrap_immediate');
2884 if (left
.identifier
) {
2885 if (left
.string
.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
2886 if (left
.string
!== 'Number' && left
.string
!== 'String' &&
2887 left
.string
!== 'Boolean' && left
.string
!== 'Date') {
2888 if (left
.string
=== 'Math') {
2889 left
.warn('not_a_function');
2890 } else if (left
.string
=== 'Object') {
2891 token
.warn('use_object');
2892 } else if (left
.string
=== 'Array' || !option
.newcap
) {
2893 left
.warn('missing_a', 'new');
2896 } else if (left
.string
=== 'JSON') {
2897 left
.warn('not_a_function');
2899 } else if (left
.id
=== '.') {
2900 if (left
.second
.string
=== 'split' &&
2901 left
.first
.id
=== '(string)') {
2902 left
.second
.warn('use_array');
2906 if (next_token
.id
!== ')') {
2911 if (left
.string
=== 'Boolean' && (e
.id
=== '!' || e
.id
=== '~')) {
2912 e
.warn('weird_condition');
2915 if (next_token
.id
!== ',') {
2922 step_out(')', that
);
2923 if (typeof left
=== 'object') {
2924 if (left
.string
=== 'parseInt' && p
.length
=== 1) {
2926 } else if (left
.string
=== 'String' && p
.length
>= 1 && p
[0].id
=== '(string)') {
2927 left
.warn('unexpected_a');
2930 if (left
.string
=== 'eval' || left
.string
=== 'Function' ||
2931 left
.string
=== 'execScript') {
2933 } else if (p
[0] && p
[0].id
=== '(string)' &&
2934 (left
.string
=== 'setTimeout' ||
2935 left
.string
=== 'setInterval')) {
2936 left
.warn('implied_evil');
2939 if (!left
.identifier
&& left
.id
!== '.' && left
.id
!== '[' &&
2940 left
.id
!== '(' && left
.id
!== '&&' && left
.id
!== '||' &&
2942 left
.warn('bad_invocation');
2944 if (left
.id
=== '.') {
2946 left
.first
&& left
.first
.first
&&
2947 are_similar(p
[0], left
.first
.first
)) {
2948 if (left
.second
.string
=== 'call' ||
2949 (left
.second
.string
=== 'apply' && (p
.length
=== 1 ||
2950 (p
[1].arity
=== 'prefix' && p
[1].id
=== '[')))) {
2951 left
.second
.warn('unexpected_a');
2954 if (left
.second
.string
=== 'toString') {
2955 if (left
.first
.id
=== '(string)' || left
.first
.id
=== '(number)') {
2956 left
.second
.warn('unexpected_a');
2966 prefix('(', function (that
) {
2967 step_in('expression');
2970 if (next_token
.id
=== 'function') {
2971 next_token
.immed
= true;
2973 var value
= expression(0);
2976 step_out(')', that
);
2977 if (value
.id
=== 'function') {
2978 switch (next_token
.id
) {
2980 next_token
.warn('move_invocation');
2984 next_token
.warn('unexpected_a');
2987 that
.warn('bad_wrap');
2989 } else if (!value
.arity
) {
2990 if (!option
.closure
|| !that
.comments
) {
2991 that
.warn('unexpected_a');
2997 infix('.', 170, function (left
, that
) {
2998 no_space(prev_token
, token
);
3000 var name
= identifier();
3001 if (typeof name
=== 'string') {
3002 tally_property(name
);
3005 that
.second
= token
;
3006 if (left
&& left
.string
=== 'arguments' &&
3007 (name
=== 'callee' || name
=== 'caller')) {
3008 left
.warn('avoid_a', 'arguments.' + name
);
3009 } else if (!option
.evil
&& left
&& left
.string
=== 'document' &&
3010 (name
=== 'write' || name
=== 'writeln')) {
3011 left
.warn('write_is_wrong');
3012 } else if (!option
.stupid
&& syx
.test(name
)) {
3013 token
.warn('sync_a');
3014 } else if (left
&& left
.id
=== '{') {
3015 that
.warn('unexpected_a');
3017 if (!option
.evil
&& (name
=== 'eval' || name
=== 'execScript')) {
3018 next_token
.warn('evil');
3023 infix('[', 170, function (left
, that
) {
3025 no_space_only(prev_token
, token
);
3032 if (e
.id
=== '(number)' && left
.id
=== 'arguments') {
3033 left
.warn('use_param');
3038 (e
.string
=== 'eval' || e
.string
=== 'execScript')) {
3040 } else if (!option
.sub
&& ix
.test(e
.string
)) {
3041 s
= syntax
[e
.string
];
3042 if (!s
|| !s
.reserved
) {
3043 e
.warn('subscript');
3046 tally_property(e
.string
);
3049 if (left
&& (left
.id
=== '{' || (left
.id
=== '[' && left
.arity
=== 'prefix'))) {
3050 that
.warn('unexpected_a');
3052 step_out(']', that
);
3053 no_space(prev_token
, token
);
3059 prefix('[', function (that
) {
3062 while (next_token
.id
!== '(end)') {
3063 while (next_token
.id
=== ',') {
3064 next_token
.warn('unexpected_a');
3067 if (next_token
.id
=== ']') {
3070 indent
.wrap
= false;
3072 that
.first
.push(expression(10));
3073 if (next_token
.id
=== ',') {
3075 if (next_token
.id
=== ']') {
3076 token
.warn('unexpected_a');
3083 step_out(']', that
);
3088 function property_name() {
3089 var id
= optional_identifier();
3091 if (next_token
.id
=== '(string)') {
3092 id
= next_token
.string
;
3094 } else if (next_token
.id
=== '(number)') {
3095 id
= next_token
.number
.toString();
3105 assignop('+=', '+');
3106 assignop('-=', '-');
3107 assignop('*=', '*');
3108 assignop('/=', '/').nud = function () {
3109 next_token
.stop('slash_equal');
3111 assignop('%=', '%');
3112 assignop('&=', '&');
3113 assignop('|=', '|');
3114 assignop('^=', '^');
3115 assignop('<<=', '<<');
3116 assignop('>>=', '>>');
3117 assignop('>>>=', '>>>');
3119 function function_parameters() {
3120 var id
, parameters
= [], paren
= next_token
;
3122 token
.function = funct
;
3125 if (next_token
.id
!== ')') {
3129 if (token
.reserved
) {
3130 token
.warn('expected_identifier_a_reserved');
3132 define('parameter', token
);
3133 parameters
.push(id
);
3135 token
.writeable
= true;
3136 if (next_token
.id
!== ',') {
3143 step_out(')', paren
);
3147 function do_function(func
, name
) {
3148 var old_funct
= funct
,
3149 old_option
= option
,
3151 scope
= Object
.create(old_scope
);
3155 level
: old_funct
.level
+ 1,
3156 line
: next_token
.line
,
3158 name
: name
|| '\'' + (anonname
|| '').replace(nx
, sanitize
) + '\'',
3162 funct
.parameter
= function_parameters();
3163 func
.function = funct
;
3164 option
= Object
.create(old_option
);
3165 functions
.push(funct
);
3169 define('function', func
);
3173 func
.writeable
= false;
3175 func
.block
= block('function');
3176 Object
.keys(scope
).forEach(function (name
) {
3177 var master
= scope
[name
];
3178 if (!master
.used
&& master
.kind
!== 'exception' &&
3179 (master
.kind
!== 'parameter' || !option
.unparam
)) {
3180 master
.warn('unused_a');
3181 } else if (!master
.init
) {
3182 master
.warn('uninitialized_a');
3186 option
= old_option
;
3190 prefix('{', function (that
) {
3191 var get, i
, j
, name
, set, seen
= Object
.create(null);
3194 while (next_token
.id
!== '}') {
3195 indent
.wrap
= false;
3197 // JSLint recognizes the ES5 extension for get/set in object literals,
3198 // but requires that they be used in pairs.
3201 if (next_token
.string
=== 'get' && peek().id
!== ':') {
3206 i
= property_name();
3208 next_token
.stop('missing_property');
3212 if (funct
.loopage
) {
3213 get.warn('function_loop');
3215 if (get.function.parameter
.length
) {
3216 get.warn('parameter_a_get_b', get.function.parameter
[0], i
);
3225 j
= property_name();
3227 token
.stop('expected_a_b', i
, j
|| next_token
.string
);
3230 if (set.block
.length
=== 0) {
3231 token
.warn('missing_a', 'throw');
3233 if (set.function.parameter
.length
=== 0) {
3234 set.stop('parameter_set_a', 'value');
3235 } else if (set.function.parameter
[0] !== 'value') {
3236 set.stop('expected_a_b', 'value',
3237 set.function.parameter
[0]);
3239 name
.first
= [get, set];
3242 i
= property_name();
3243 if (typeof i
!== 'string') {
3244 next_token
.stop('missing_property');
3248 name
.first
= expression(10);
3250 that
.first
.push(name
);
3251 if (seen
[i
] === true) {
3252 next_token
.warn('duplicate_a', i
);
3256 if (next_token
.id
!== ',') {
3261 if (next_token
.id
!== ',') {
3264 next_token
.warn('unexpected_a');
3266 if (next_token
.id
=== '}') {
3267 token
.warn('unexpected_a');
3270 step_out('}', that
);
3274 stmt('{', function () {
3275 next_token
.warn('statement_block');
3276 this.arity
= 'statement';
3277 this.block
= statements();
3278 this.disrupt
= this.block
.disrupt
;
3283 stmt('/*global', directive
);
3284 stmt('/*globals', directive
);
3285 stmt('/*jslint', directive
);
3286 stmt('/*member', directive
);
3287 stmt('/*members', directive
);
3288 stmt('/*property', directive
);
3289 stmt('/*properties', directive
);
3291 stmt('var', function () {
3293 // JavaScript does not have block scope. It only has function scope. So,
3294 // declaring a variable in a block can have unexpected consequences.
3296 // var.first will contain an array, the array containing name tokens
3297 // and assignment tokens.
3299 var assign
, id
, name
;
3301 if (funct
.loopage
) {
3302 next_token
.warn('var_loop');
3303 } else if (funct
.varstatement
&& !option
.vars
) {
3304 next_token
.warn('combine_var');
3306 if (funct
!== global_funct
) {
3307 funct
.varstatement
= true;
3309 this.arity
= 'statement';
3314 id
= identifier(true);
3315 define('var', name
);
3317 if (next_token
.id
=== '=') {
3318 if (funct
=== global_funct
&& !name
.writeable
) {
3319 name
.warn('read_only');
3321 assign
= next_token
;
3322 assign
.first
= name
;
3326 if (next_token
.id
=== 'undefined') {
3327 token
.warn('unnecessary_initialize', id
);
3329 if (peek(0).id
=== '=' && next_token
.identifier
) {
3330 next_token
.stop('var_a_not');
3332 assign
.second
= expression(0);
3333 assign
.arity
= 'infix';
3335 this.first
.push(assign
);
3337 this.first
.push(name
);
3340 name
.writeable
= true;
3341 if (next_token
.id
!== ',') {
3345 indent
.wrap
= false;
3346 if (var_mode
&& next_token
.line
=== token
.line
&&
3347 this.first
.length
=== 1) {
3349 indent
.open
= false;
3350 indent
.at
-= option
.indent
;
3360 stmt('function', function () {
3363 token
.warn('function_block');
3365 var name
= next_token
,
3366 id
= identifier(true);
3367 define('var', name
);
3368 if (!name
.writeable
) {
3369 name
.warn('read_only');
3372 name
.statement
= true;
3374 this.arity
= 'statement';
3375 do_function(this, id
);
3376 if (next_token
.id
=== '(' && next_token
.line
=== token
.line
) {
3377 next_token
.stop('function_statement');
3382 prefix('function', function (that
) {
3383 var id
= optional_identifier(true), name
;
3391 do_function(that
, id
);
3393 name
.function = that
.function;
3395 if (funct
.loopage
) {
3396 that
.warn('function_loop');
3398 switch (next_token
.id
) {
3409 if (peek().string
!== 'bind' || peek(1).id
!== '(') {
3410 next_token
.warn('unexpected_a');
3414 next_token
.stop('unexpected_a');
3416 that
.arity
= 'function';
3420 stmt('if', function () {
3421 var paren
= next_token
;
3427 this.arity
= 'statement';
3428 this.first
= expected_condition(expected_relation(expression(0)));
3430 step_out(')', paren
);
3432 this.block
= block('if');
3433 if (next_token
.id
=== 'else') {
3434 if (this.block
.disrupt
) {
3435 next_token
.warn(this.elif
? 'use_nested_if' : 'unnecessary_else');
3440 if (next_token
.id
=== 'if') {
3441 next_token
.elif
= true;
3442 this.else = statement(true);
3444 this.else = block('else');
3446 if (this.else.disrupt
&& this.block
.disrupt
) {
3447 this.disrupt
= true;
3453 stmt('try', function () {
3455 // try.first The catch variable
3456 // try.second The catch clause
3457 // try.third The finally clause
3458 // try.block The try block
3460 var exception_variable
, paren
;
3462 this.arity
= 'statement';
3463 this.block
= block('try');
3464 if (next_token
.id
=== 'catch') {
3473 exception_variable
= next_token
;
3474 this.first
= identifier();
3475 define('exception', exception_variable
);
3476 exception_variable
.init
= true;
3478 step_out(')', paren
);
3480 this.second
= block('catch');
3481 if (this.second
.length
) {
3482 if (this.first
=== 'ignore') {
3483 exception_variable
.warn('unexpected_a');
3486 if (this.first
!== 'ignore') {
3487 exception_variable
.warn('expected_a_b', 'ignore',
3488 exception_variable
.string
);
3491 exception_variable
.dead
= true;
3493 if (next_token
.id
=== 'finally') {
3497 this.third
= block('finally');
3498 } else if (!this.second
) {
3499 next_token
.stop('expected_a_b', 'catch', artifact());
3504 labeled_stmt('while', function () {
3506 var paren
= next_token
;
3512 this.arity
= 'statement';
3513 this.first
= expected_relation(expression(0));
3514 if (this.first
.id
!== 'true') {
3515 expected_condition(this.first
, 'unexpected_a');
3518 step_out(')', paren
);
3520 this.block
= block('while');
3521 if (this.block
.disrupt
) {
3522 prev_token
.warn('strange_loop');
3530 labeled_stmt('switch', function () {
3532 // switch.first the switch expression
3533 // switch.second the array of cases. A case is 'case' or 'default' token:
3534 // case.first the array of case expressions
3535 // case.second the array of statements
3536 // If all of the arrays of statements are disrupt, then the switch is disrupt.
3539 old_in_block
= in_block
,
3542 the_case
= next_token
;
3544 function find_duplicate_case(value
) {
3545 if (are_similar(particular
, value
)) {
3546 value
.warn('duplicate_a');
3554 this.arity
= 'statement';
3555 this.first
= expected_condition(expected_relation(expression(0)));
3557 step_out(')', the_case
);
3563 if (that
.from !== next_token
.from && !option
.white
) {
3564 next_token
.warn('expected_a_at_b_c', next_token
.string
, that
.from, next_token
.from);
3566 while (next_token
.id
=== 'case') {
3567 the_case
= next_token
;
3568 the_case
.first
= [];
3569 the_case
.arity
= 'case';
3575 particular
= expression(0);
3576 cases
.forEach(find_duplicate_case
);
3577 cases
.push(particular
);
3578 the_case
.first
.push(particular
);
3579 if (particular
.id
=== 'NaN') {
3580 particular
.warn('unexpected_a');
3584 if (next_token
.id
!== 'case') {
3589 the_case
.second
= statements();
3590 if (the_case
.second
&& the_case
.second
.length
> 0) {
3591 if (!the_case
.second
[the_case
.second
.length
- 1].disrupt
) {
3592 next_token
.warn('missing_a_after_b', 'break', 'case');
3595 next_token
.warn('empty_case');
3597 this.second
.push(the_case
);
3599 if (this.second
.length
=== 0) {
3600 next_token
.warn('missing_a', 'case');
3602 if (next_token
.id
=== 'default') {
3604 the_case
= next_token
;
3605 the_case
.arity
= 'case';
3611 the_case
.second
= statements();
3612 if (the_case
.second
&& the_case
.second
.length
> 0) {
3613 this.disrupt
= the_case
.second
[the_case
.second
.length
- 1].disrupt
;
3615 the_case
.warn('empty_case');
3617 this.second
.push(the_case
);
3620 this.disrupt
= false;
3623 step_out('}', this);
3624 in_block
= old_in_block
;
3628 stmt('debugger', function () {
3629 if (!option
.debug
) {
3630 this.warn('unexpected_a');
3632 this.arity
= 'statement';
3636 labeled_stmt('do', function () {
3639 this.arity
= 'statement';
3640 this.block
= block('do');
3641 if (this.block
.disrupt
) {
3642 prev_token
.warn('strange_loop');
3646 var paren
= next_token
;
3652 this.first
= expected_condition(expected_relation(expression(0)), 'unexpected_a');
3654 step_out(')', paren
);
3659 labeled_stmt('for', function () {
3661 var blok
, filter
, master
, ok
= false, paren
= next_token
, value
;
3662 this.arity
= 'statement';
3665 if (next_token
.id
=== ';') {
3672 blok
= block('for');
3675 spaces(this, paren
);
3677 if (next_token
.id
=== 'var') {
3678 next_token
.stop('move_var');
3681 if (peek(0).id
=== 'in') {
3683 value
= expression(1000);
3684 master
= value
.master
;
3686 value
.stop('bad_in_a');
3688 if (master
.kind
!== 'var' || master
.function !== funct
||
3689 !master
.writeable
|| master
.dead
) {
3690 value
.warn('bad_in_a');
3696 this.second
= expression(20);
3697 step_out(')', paren
);
3698 blok
= block('for');
3699 if (!option
.forin
) {
3700 if (blok
.length
=== 1 && typeof blok
[0] === 'object') {
3701 if (blok
[0].id
=== 'if' && !blok
[0].else) {
3702 filter
= blok
[0].first
;
3703 while (filter
.id
=== '&&') {
3704 filter
= filter
.first
;
3706 switch (filter
.id
) {
3709 ok
= filter
.first
.id
=== '['
3710 ? are_similar(filter
.first
.first
, this.second
) &&
3711 are_similar(filter
.first
.second
, this.first
)
3712 : filter
.first
.id
=== 'typeof' &&
3713 filter
.first
.first
.id
=== '[' &&
3714 are_similar(filter
.first
.first
.first
, this.second
) &&
3715 are_similar(filter
.first
.first
.second
, this.first
);
3718 ok
= filter
.first
.id
=== '.' && ((
3719 are_similar(filter
.first
.first
, this.second
) &&
3720 filter
.first
.second
.string
=== 'hasOwnProperty' &&
3721 are_similar(filter
.second
[0], this.first
)
3723 filter
.first
.first
.id
=== '.' &&
3724 filter
.first
.first
.first
.first
&&
3725 filter
.first
.first
.first
.first
.string
=== 'Object' &&
3726 filter
.first
.first
.first
.id
=== '.' &&
3727 filter
.first
.first
.first
.second
.string
=== 'prototype' &&
3728 filter
.first
.first
.second
.string
=== 'hasOwnProperty' &&
3729 filter
.first
.second
.string
=== 'call' &&
3730 are_similar(filter
.second
[0], this.second
) &&
3731 are_similar(filter
.second
[1], this.first
)
3735 } else if (blok
[0].id
=== 'switch') {
3736 ok
= blok
[0].id
=== 'switch' &&
3737 blok
[0].first
.id
=== 'typeof' &&
3738 blok
[0].first
.first
.id
=== '[' &&
3739 are_similar(blok
[0].first
.first
.first
, this.second
) &&
3740 are_similar(blok
[0].first
.first
.second
, this.first
);
3744 this.warn('for_if');
3751 this.first
.push(expression(0, 'for'));
3752 if (next_token
.id
!== ',') {
3759 this.second
= expected_relation(expression(0));
3760 if (this.second
.id
!== 'true') {
3761 expected_condition(this.second
, 'unexpected_a');
3764 if (next_token
.id
=== ';') {
3765 next_token
.stop('expected_a_b', ')', ';');
3770 this.third
.push(expression(0, 'for'));
3771 if (next_token
.id
!== ',') {
3777 step_out(')', paren
);
3779 blok
= block('for');
3783 prev_token
.warn('strange_loop');
3790 function optional_label(that
) {
3791 var label
= next_token
.string
,
3793 that
.arity
= 'statement';
3794 if (!funct
.breakage
|| (!option
.continue && that
.id
=== 'continue')) {
3795 that
.warn('unexpected_a');
3796 } else if (next_token
.identifier
&& token
.line
=== next_token
.line
) {
3798 master
= scope
[label
];
3799 if (!master
|| master
.kind
!== 'label') {
3800 next_token
.warn('not_a_label');
3801 } else if (master
.dead
|| master
.function !== funct
) {
3802 next_token
.warn('not_a_scope');
3805 if (that
.id
=== 'break') {
3806 master
.statement
.break = true;
3808 if (funct
.breakage
[funct
.breakage
.length
- 1] === master
.statement
) {
3809 next_token
.warn('unexpected_a');
3812 that
.first
= next_token
;
3815 if (that
.id
=== 'break') {
3816 funct
.breakage
[funct
.breakage
.length
- 1].break = true;
3823 disrupt_stmt('break', function () {
3824 return optional_label(this);
3827 disrupt_stmt('continue', function () {
3828 return optional_label(this);
3831 disrupt_stmt('return', function () {
3832 if (funct
=== global_funct
) {
3833 this.warn('unexpected_a');
3835 this.arity
= 'statement';
3836 if (next_token
.id
!== ';' && next_token
.line
=== token
.line
) {
3837 if (option
.closure
) {
3842 if (next_token
.id
=== '/' || next_token
.id
=== '(regexp)') {
3843 next_token
.warn('wrap_regexp');
3845 this.first
= expression(0);
3846 if (this.first
.assign
) {
3847 this.first
.warn('unexpected_a');
3853 disrupt_stmt('throw', function () {
3854 this.arity
= 'statement';
3856 this.first
= expression(20);
3861 // Superfluous reserved words
3871 // Harmony reserved words
3873 reserve('implements');
3874 reserve('interface');
3878 reserve('protected');
3886 function json_value() {
3888 function json_object() {
3889 var brace
= next_token
, object
= Object
.create(null);
3891 if (next_token
.id
!== '}') {
3892 while (next_token
.id
!== '(end)') {
3893 while (next_token
.id
=== ',') {
3894 next_token
.warn('unexpected_a');
3897 if (next_token
.id
!== '(string)') {
3898 next_token
.warn('expected_string_a');
3900 if (object
[next_token
.string
] === true) {
3901 next_token
.warn('duplicate_a');
3902 } else if (next_token
.string
=== '__proto__') {
3903 next_token
.warn('dangling_a');
3905 object
[next_token
.string
] = true;
3910 if (next_token
.id
!== ',') {
3914 if (next_token
.id
=== '}') {
3915 token
.warn('unexpected_a');
3920 advance('}', brace
);
3923 function json_array() {
3924 var bracket
= next_token
;
3926 if (next_token
.id
!== ']') {
3927 while (next_token
.id
!== '(end)') {
3928 while (next_token
.id
=== ',') {
3929 next_token
.warn('unexpected_a');
3933 if (next_token
.id
!== ',') {
3937 if (next_token
.id
=== ']') {
3938 token
.warn('unexpected_a');
3943 advance(']', bracket
);
3946 switch (next_token
.id
) {
3963 advance('(number)');
3966 next_token
.stop('unexpected_a');
3971 // The actual JSLINT function itself.
3973 itself
= function JSLint(the_source
, the_option
) {
3975 var i
, predef
, tree
;
3978 itself
.properties
= '';
3979 begin
= prev_token
= token
= next_token
=
3980 Object
.create(syntax
['(begin)']);
3982 predefined
= Object
.create(null);
3983 add_to_predefined(standard
);
3984 property
= Object
.create(null);
3986 option
= Object
.create(the_option
);
3987 predef
= option
.predef
;
3989 if (Array
.isArray(predef
)) {
3990 for (i
= 0; i
< predef
.length
; i
+= 1) {
3991 predefined
[predef
[i
]] = true;
3993 } else if (typeof predef
=== 'object') {
3994 add_to_predefined(predef
);
3998 option
= Object
.create(null);
4000 option
.indent
= +option
.indent
|| 4;
4001 option
.maxerr
= +option
.maxerr
|| 50;
4002 global_scope
= scope
= Object
.create(null);
4003 global_funct
= funct
= {
4008 functions
= [funct
];
4012 comments_off
= false;
4019 strict_mode
= false;
4022 lex
.init(the_source
);
4028 if (next_token
.id
=== '(number)') {
4029 next_token
.stop('unexpected_a');
4031 switch (next_token
.id
) {
4034 comments_off
= true;
4040 // If the first token is a semicolon, ignore it. This is sometimes used when
4041 // files are intended to be appended to files that may be sloppy. A sloppy
4042 // file may be depending on semicolon insertion on its last line.
4045 if (next_token
.id
=== ';' && !node_js
) {
4046 next_token
.edge
= true;
4049 tree
= statements();
4051 itself
.tree
= begin
;
4053 prev_token
.warn('weird_program');
4059 itself
.property
= property
;
4062 itself
.errors
.push({
4064 line
: e
.line
|| next_token
.line
,
4065 character
: e
.character
|| next_token
.from
4069 return itself
.errors
.length
=== 0;
4072 function unique(array
) {
4073 array
= array
.sort();
4074 var i
, length
= 0, previous
, value
;
4075 for (i
= 0; i
< array
.length
; i
+= 1) {
4077 if (value
!== previous
) {
4078 array
[length
] = value
;
4083 array
.length
= length
;
4089 itself
.data = function () {
4090 var data
= {functions
: []},
4095 data
.errors
= itself
.errors
;
4096 data
.json
= json_mode
;
4097 data
.global
= unique(Object
.keys(global_scope
));
4099 function selects(name
) {
4100 var kind
= the_scope
[name
].kind
;
4105 function_data
[kind
].push(name
);
4110 for (i
= 1; i
< functions
.length
; i
+= 1) {
4111 the_function
= functions
[i
];
4113 name
: the_function
.name
,
4114 line
: the_function
.line
,
4115 level
: the_function
.level
,
4116 parameter
: the_function
.parameter
,
4119 closure
: unique(the_function
.closure
),
4120 outer
: unique(the_function
.outer
),
4121 global
: unique(the_function
.global
),
4124 the_scope
= the_function
.scope
;
4125 Object
.keys(the_scope
).forEach(selects
);
4126 function_data
.var.sort();
4127 function_data
.exception
.sort();
4128 function_data
.label
.sort();
4129 data
.functions
.push(function_data
);
4131 data
.tokens
= tokens
;
4135 itself
.error_report = function (data
) {
4136 var evidence
, i
, output
= [], warning
;
4137 if (data
.errors
.length
) {
4139 output
.push('<cite>JSON: bad.</cite><br>');
4141 for (i
= 0; i
< data
.errors
.length
; i
+= 1) {
4142 warning
= data
.errors
[i
];
4144 evidence
= warning
.evidence
|| '';
4145 output
.push('<cite>');
4146 if (isFinite(warning
.line
)) {
4147 output
.push('<address>line ' +
4148 String(warning
.line
) +
4149 ' character ' + String(warning
.character
) +
4152 output
.push(warning
.reason
.entityify() + '</cite>');
4154 output
.push('<pre>' + evidence
.entityify() + '</pre>');
4159 return output
.join('');
4163 itself
.report = function (data
) {
4164 var dl
, i
, j
, names
, output
= [], the_function
;
4166 function detail(h
, array
) {
4167 var comma_needed
= false;
4169 output
.push("<dt>" + h
+ "</dt><dd>");
4170 array
.forEach(function (item
) {
4171 output
.push((comma_needed
? ', ' : '') + item
);
4172 comma_needed
= true;
4174 output
.push("</dd>");
4178 output
.push('<dl class=level0>');
4179 if (data
.global
.length
) {
4180 detail('global', data
.global
);
4182 } else if (data
.json
) {
4183 if (!data
.errors
.length
) {
4184 output
.push("<dt>JSON: good.</dt>");
4187 output
.push("<dt><i>No new global variables introduced.</i></dt>");
4190 output
.push("</dl>");
4195 if (data
.functions
) {
4196 for (i
= 0; i
< data
.functions
.length
; i
+= 1) {
4197 the_function
= data
.functions
[i
];
4199 if (the_function
.params
) {
4200 for (j
= 0; j
< the_function
.params
.length
; j
+= 1) {
4201 names
[j
] = the_function
.params
[j
].string
;
4204 output
.push('<dl class=level' + the_function
.level
+
4205 '><address>line ' + String(the_function
.line
) +
4206 '</address>' + the_function
.name
.entityify());
4207 detail('parameter', the_function
.parameter
);
4208 detail('variable', the_function
.var);
4209 detail('exception', the_function
.exception
);
4210 detail('closure', the_function
.closure
);
4211 detail('outer', the_function
.outer
);
4212 detail('global', the_function
.global
);
4213 detail('label', the_function
.label
);
4214 output
.push('</dl>');
4217 return output
.join('');
4220 itself
.properties_report = function (property
) {
4226 keys
= Object
.keys(property
).sort(),
4230 output
= ['/*properties'];
4231 for (i
= 0; i
< keys
.length
; i
+= 1) {
4233 if (property
[key
] > 0) {
4239 : '\'' + key
.replace(nx
, sanitize
) + '\'';
4240 if (mem
.length
+ name
.length
>= 80) {
4250 output
.push(mem
, '*/\n');
4251 return output
.join('\n');
4254 itself
.color = function (data
) {
4261 data_token
= data
.tokens
[0];
4262 while (data_token
&& data_token
.id
!== '(end)') {
4263 from = data_token
.from;
4264 line
= data_token
.line
;
4265 thru
= data_token
.thru
;
4266 level
= data_token
.function.level
;
4268 thru
= data_token
.thru
;
4269 data_token
= data
.tokens
[i
];
4271 } while (data_token
&& data_token
.line
=== line
&&
4272 data_token
.from - thru
< 5 &&
4273 level
=== data_token
.function.level
);
4284 itself
.jslint
= itself
;
4286 itself
.edition
= '2014-07-08';