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 HTML text, or a JSON text, or a CSS 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 Function Report, which shows all of the functions
67 // and the parameters and vars that they use. This can be used to find
68 // implied global variables and other problems. The report is in HTML and
69 // can be inserted in an HTML <body>.
71 // var myReport = JSLINT.report(errors_only);
73 // If errors_only is true, then the report will be limited to only errors.
75 // You can request a data structure that contains JSLint's results.
77 // var myData = JSLINT.data();
79 // It returns a structure with this form:
138 // Empty arrays will not be included.
140 // You can obtain the parse tree that JSLint constructed while parsing. The
141 // latest tree is kept in JSLINT.tree. A nice stringication can be produced
144 // JSON.stringify(JSLINT.tree, [
145 // 'string', 'arity', 'name', 'first',
146 // 'second', 'third', 'block', 'else'
149 // JSLint provides three directives. They look like slashstar comments, and
150 // allow for setting options, declaring global variables, and establishing a
151 // set of allowed property names.
153 // These directives respect function scope.
155 // The jslint directive is a special comment that can set one or more options.
156 // The current option set is
158 // anon true, if the space may be omitted in anonymous function declarations
159 // bitwise true, if bitwise operators should be allowed
160 // browser true, if the standard browser globals should be predefined
161 // cap true, if upper case HTML should be allowed
162 // 'continue' true, if the continuation statement should be tolerated
163 // css true, if CSS workarounds should be tolerated
164 // debug true, if debugger statements should be allowed
165 // devel true, if logging should be allowed (console, alert, etc.)
166 // eqeq true, if == should be allowed
167 // es5 true, if ES5 syntax should be allowed
168 // evil true, if eval should be allowed
169 // forin true, if for in statements need not filter
170 // fragment true, if HTML fragments should be allowed
171 // indent the indentation factor
172 // maxerr the maximum number of errors to allow
173 // maxlen the maximum length of a source line
174 // newcap true, if constructor names capitalization is ignored
175 // node true, if Node.js globals should be predefined
176 // nomen true, if names may have dangling _
177 // on true, if HTML event handlers should be allowed
178 // passfail true, if the scan should stop on first error
179 // plusplus true, if increment/decrement should be allowed
180 // properties true, if all property names must be declared with /*properties*/
181 // regexp true, if the . should be allowed in regexp literals
182 // rhino true, if the Rhino environment globals should be predefined
183 // undef true, if variables can be declared out of order
184 // unparam true, if unused parameters should be tolerated
185 // sloppy true, if the 'use strict'; pragma is optional
186 // sub true, if all forms of subscript notation are tolerated
187 // vars true, if multiple var statements per function should be allowed
188 // white true, if sloppy whitespace is tolerated
189 // widget true if the Yahoo Widgets globals should be predefined
190 // windows true, if MS Windows-specific globals should be predefined
195 evil: true, nomen: true, regexp: true
198 // The properties directive declares an exclusive list of property names.
199 // Any properties named in the program that are not in the list will
200 // produce a warning.
205 '\b', '\t', '\n', '\f', '\r', '!=', '!==', '"', '%', '\'', '(arguments)',
206 '(begin)', '(breakage)', '(context)', '(error)', '(identifier)', '(line)',
207 '(loopage)', '(name)', '(params)', '(scope)', '(token)', '(vars)', '(verb)',
208 '*', '+', '-', '/', '<', '<=', '==', '===', '>', '>=', ADSAFE,
209 Array, Date, Function, Object, '\\', a, a_label, a_not_allowed,
210 a_not_defined, a_scope, abbr, acronym, address, adsafe, adsafe_a,
211 adsafe_autocomplete, adsafe_bad_id, adsafe_div, adsafe_fragment, adsafe_go,
212 adsafe_html, adsafe_id, adsafe_id_go, adsafe_lib, adsafe_lib_second,
213 adsafe_missing_id, adsafe_name_a, adsafe_placement, adsafe_prefix_a,
214 adsafe_script, adsafe_source, adsafe_subscript_a, adsafe_tag, all,
215 already_defined, and, anon, applet, apply, approved, area, arity, article,
216 aside, assign, assign_exception, assignment_function_expression, at,
217 attribute_case_a, audio, autocomplete, avoid_a, b, background,
218 'background-attachment', 'background-color', 'background-image',
219 'background-position', 'background-repeat', bad_assignment, bad_color_a,
220 bad_constructor, bad_entity, bad_html, bad_id_a, bad_in_a, bad_invocation,
221 bad_name_a, bad_new, bad_number, bad_operand, bad_style, bad_type, bad_url_a,
222 bad_wrap, base, bdo, big, bitwise, block, blockquote, body, border,
223 'border-bottom', 'border-bottom-color', 'border-bottom-left-radius',
224 'border-bottom-right-radius', 'border-bottom-style', 'border-bottom-width',
225 'border-collapse', 'border-color', 'border-left', 'border-left-color',
226 'border-left-style', 'border-left-width', 'border-radius', 'border-right',
227 'border-right-color', 'border-right-style', 'border-right-width',
228 'border-spacing', 'border-style', 'border-top', 'border-top-color',
229 'border-top-left-radius', 'border-top-right-radius', 'border-top-style',
230 'border-top-width', 'border-width', bottom, br, braille, browser, button, c,
231 call, canvas, cap, caption, 'caption-side', center, charAt, charCodeAt,
232 character, cite, clear, clip, closure, cm, code, col, colgroup, color,
233 combine_var, command, conditional_assignment, confusing_a, confusing_regexp,
234 constructor_name_a, content, continue, control_a, 'counter-increment',
235 'counter-reset', create, css, cursor, d, dangerous_comment, dangling_a, data,
236 datalist, dd, debug, del, deleted, details, devel, dfn, dialog, dir,
237 direction, display, disrupt, div, dl, dt, duplicate_a, edge, edition, else,
238 em, embed, embossed, empty, 'empty-cells', empty_block, empty_case,
239 empty_class, entityify, eqeq, errors, es5, eval, evidence, evil, ex,
240 exception, exec, expected_a, expected_a_at_b_c, expected_a_b,
241 expected_a_b_from_c_d, expected_at_a, expected_attribute_a,
242 expected_attribute_value_a, expected_class_a, expected_fraction_a,
243 expected_id_a, expected_identifier_a, expected_identifier_a_reserved,
244 expected_lang_a, expected_linear_a, expected_media_a, expected_name_a,
245 expected_nonstandard_style_attribute, expected_number_a, expected_operator_a,
246 expected_percent_a, expected_positive_a, expected_pseudo_a,
247 expected_selector_a, expected_small_a, expected_space_a_b, expected_string_a,
248 expected_style_attribute, expected_style_pattern, expected_tagname_a,
249 expected_type_a, f, fieldset, figure, filter, first, flag, float, floor,
250 font, 'font-family', 'font-size', 'font-size-adjust', 'font-stretch',
251 'font-style', 'font-variant', 'font-weight', footer, forEach, for_if, forin,
252 form, fragment, frame, frameset, from, fromCharCode, fud, funct, function,
253 function_block, function_eval, function_loop, function_statement,
254 function_strict, functions, global, globals, h1, h2, h3, h4, h5, h6,
255 handheld, hasOwnProperty, head, header, height, hgroup, hr,
256 'hta:application', html, html_confusion_a, html_handlers, i, id, identifier,
257 identifier_function, iframe, img, immed, implied_evil, in, indent, indexOf,
258 infix_in, init, input, ins, insecure_a, isAlpha, isArray, isDigit, isNaN,
259 join, jslint, json, kbd, keygen, keys, label, label_a_b, labeled, lang, lbp,
260 leading_decimal_a, led, left, legend, length, 'letter-spacing', li, lib,
261 line, 'line-height', link, 'list-style', 'list-style-image',
262 'list-style-position', 'list-style-type', map, margin, 'margin-bottom',
263 'margin-left', 'margin-right', 'margin-top', mark, 'marker-offset', match,
264 'max-height', 'max-width', maxerr, maxlen, member, menu, message, meta,
265 meter, 'min-height', 'min-width', missing_a, missing_a_after_b,
266 missing_option, missing_property, missing_space_a_b, missing_url,
267 missing_use_strict, mixed, mm, mode, move_invocation, move_var, n, name,
268 name_function, nav, nested_comment, newcap, node, noframes, nomen, noscript,
269 not, not_a_constructor, not_a_defined, not_a_function, not_a_label,
270 not_a_scope, not_greater, nud, number, object, octal_a, ol, on, opacity,
271 open, optgroup, option, outer, outline, 'outline-color', 'outline-style',
272 'outline-width', output, overflow, 'overflow-x', 'overflow-y', p, padding,
273 'padding-bottom', 'padding-left', 'padding-right', 'padding-top',
274 'page-break-after', 'page-break-before', param, parameter_a_get_b,
275 parameter_arguments_a, parameter_set_a, params, paren, parent, passfail, pc,
276 plusplus, pop, position, postscript, pre, predef, print, progress,
277 projection, properties, prototype, pt, push, px, q, quote, quotes, r, radix,
278 range, raw, read_only, reason, redefinition_a, regexp, replace, report,
279 reserved, reserved_a, rhino, right, rp, rt, ruby, safe, samp, scanned_a_b,
280 screen, script, search, second, section, select, shift, slash_equal, slice,
281 sloppy, small, sort, source, span, speech, split, src, statement_block,
282 stopping, strange_loop, strict, string, strong, style, styleproperty, sub,
283 subscript, substr, sup, supplant, t, table, 'table-layout', tag_a_in_b,
284 tbody, td, test, 'text-align', 'text-decoration', 'text-indent',
285 'text-shadow', 'text-transform', textarea, tfoot, th, thead, third, thru,
286 time, title, toLowerCase, toString, toUpperCase, token, too_long, too_many,
287 top, tr, trailing_decimal_a, tree, tt, tty, tv, type, u, ul, unclosed,
288 unclosed_comment, unclosed_regexp, undef, undefined, unescaped_a,
289 unexpected_a, unexpected_char_a_b, unexpected_comment, unexpected_else,
290 unexpected_property_a, unexpected_space_a_b, 'unicode-bidi',
291 unnecessary_initialize, unnecessary_use, unparam, unreachable_a_b,
292 unrecognized_style_attribute_a, unrecognized_tag_a, unsafe, unused, url,
293 urls, use_array, use_braces, use_charAt, use_object, use_or, use_param,
294 used_before_a, var, var_a_not, vars, 'vertical-align', video, visibility,
295 was, weird_assignment, weird_condition, weird_new, weird_program,
296 weird_relation, weird_ternary, white, 'white-space', widget, width, windows,
297 'word-spacing', 'word-wrap', wrap, wrap_immediate, wrap_regexp,
298 write_is_wrong, writeable, 'z-index'
301 // The global directive is used to declare global variables that can
302 // be accessed by the program. If a declaration is true, then the variable
303 // is writeable. Otherwise, it is read-only.
305 // We build the application inside a function so that we produce only a single
306 // global variable. That function will be invoked immediately, and its return
307 // value is the JSLINT function itself. That function is also an object that
308 // can contain data and other functions.
310 var JSLINT = (function () {
313 function array_to_object(array, value) {
315 // Make an object from an array of keys and a common value.
317 var i, length = array.length, object = {};
318 for (i = 0; i < length; i += 1) {
319 object[array[i]] = value;
325 var adsafe_id, // The widget's ADsafe id.
326 adsafe_may, // The widget may load approved scripts.
327 adsafe_top, // At the top of the widget script.
328 adsafe_went, // ADSAFE.go has been called.
365 anonname, // The guessed name for anonymous functions.
366 approved, // ADsafe approved urls.
368 // These are operators that should not be used with the ! operator.
386 // These are property names that should not be permitted in the safe subset.
388 banned = array_to_object([
389 'arguments', 'callee', 'caller', 'constructor', 'eval', 'prototype',
390 'stack', 'unwatch', 'valueOf', 'watch'
392 begin, // The root token
394 // browser contains a set of global names that are commonly provided by a
395 // web browser environment.
397 browser = array_to_object([
398 'clearInterval', 'clearTimeout', 'document', 'event', 'frames',
399 'history', 'Image', 'localStorage', 'location', 'name', 'navigator',
400 'Option', 'parent', 'screen', 'sessionStorage', 'setInterval',
401 'setTimeout', 'Storage', 'window', 'XMLHttpRequest'
404 // bundle contains the text messages.
407 a_label: "'{a}' is a statement label.",
408 a_not_allowed: "'{a}' is not allowed.",
409 a_not_defined: "'{a}' is not defined.",
410 a_scope: "'{a}' used out of scope.",
411 adsafe_a: "ADsafe violation: '{a}'.",
412 adsafe_autocomplete: "ADsafe autocomplete violation.",
413 adsafe_bad_id: "ADSAFE violation: bad id.",
414 adsafe_div: "ADsafe violation: Wrap the widget in a div.",
415 adsafe_fragment: "ADSAFE: Use the fragment option.",
416 adsafe_go: "ADsafe violation: Misformed ADSAFE.go.",
417 adsafe_html: "Currently, ADsafe does not operate on whole HTML " +
418 "documents. It operates on <div> fragments and .js files.",
419 adsafe_id: "ADsafe violation: id does not match.",
420 adsafe_id_go: "ADsafe violation: Missing ADSAFE.id or ADSAFE.go.",
421 adsafe_lib: "ADsafe lib violation.",
422 adsafe_lib_second: "ADsafe: The second argument to lib must be a function.",
423 adsafe_missing_id: "ADSAFE violation: missing ID_.",
424 adsafe_name_a: "ADsafe name violation: '{a}'.",
425 adsafe_placement: "ADsafe script placement violation.",
426 adsafe_prefix_a: "ADsafe violation: An id must have a '{a}' prefix",
427 adsafe_script: "ADsafe script violation.",
428 adsafe_source: "ADsafe unapproved script source.",
429 adsafe_subscript_a: "ADsafe subscript '{a}'.",
430 adsafe_tag: "ADsafe violation: Disallowed tag '{a}'.",
431 already_defined: "'{a}' is already defined.",
432 and: "The '&&' subexpression should be wrapped in parens.",
433 assign_exception: "Do not assign to the exception parameter.",
434 assignment_function_expression: "Expected an assignment or " +
435 "function call and instead saw an expression.",
436 attribute_case_a: "Attribute '{a}' not all lower case.",
437 avoid_a: "Avoid '{a}'.",
438 bad_assignment: "Bad assignment.",
439 bad_color_a: "Bad hex color '{a}'.",
440 bad_constructor: "Bad constructor.",
441 bad_entity: "Bad entity.",
442 bad_html: "Bad HTML string",
443 bad_id_a: "Bad id: '{a}'.",
444 bad_in_a: "Bad for in variable '{a}'.",
445 bad_invocation: "Bad invocation.",
446 bad_name_a: "Bad name: '{a}'.",
447 bad_new: "Do not use 'new' for side effects.",
448 bad_number: "Bad number '{a}'.",
449 bad_operand: "Bad operand.",
450 bad_style: "Bad style.",
451 bad_type: "Bad type.",
452 bad_url_a: "Bad url '{a}'.",
453 bad_wrap: "Do not wrap function literals in parens unless they " +
454 "are to be immediately invoked.",
455 combine_var: "Combine this with the previous 'var' statement.",
456 conditional_assignment: "Expected a conditional expression and " +
457 "instead saw an assignment.",
458 confusing_a: "Confusing use of '{a}'.",
459 confusing_regexp: "Confusing regular expression.",
460 constructor_name_a: "A constructor name '{a}' should start with " +
461 "an uppercase letter.",
462 control_a: "Unexpected control character '{a}'.",
463 css: "A css file should begin with @charset 'UTF-8';",
464 dangling_a: "Unexpected dangling '_' in '{a}'.",
465 dangerous_comment: "Dangerous comment.",
466 deleted: "Only properties should be deleted.",
467 duplicate_a: "Duplicate '{a}'.",
468 empty_block: "Empty block.",
469 empty_case: "Empty case.",
470 empty_class: "Empty class.",
471 es5: "This is an ES5 feature.",
472 evil: "eval is evil.",
473 expected_a: "Expected '{a}'.",
474 expected_a_b: "Expected '{a}' and instead saw '{b}'.",
475 expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " +
476 "{c} and instead saw '{d}'.",
477 expected_at_a: "Expected an at-rule, and instead saw @{a}.",
478 expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
479 expected_attribute_a: "Expected an attribute, and instead saw [{a}].",
480 expected_attribute_value_a: "Expected an attribute value and " +
481 "instead saw '{a}'.",
482 expected_class_a: "Expected a class, and instead saw .{a}.",
483 expected_fraction_a: "Expected a number between 0 and 1 and " +
485 expected_id_a: "Expected an id, and instead saw #{a}.",
486 expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
487 expected_identifier_a_reserved: "Expected an identifier and " +
488 "instead saw '{a}' (a reserved word).",
489 expected_linear_a: "Expected a linear unit and instead saw '{a}'.",
490 expected_lang_a: "Expected a lang code, and instead saw :{a}.",
491 expected_media_a: "Expected a CSS media type, and instead saw '{a}'.",
492 expected_name_a: "Expected a name and instead saw '{a}'.",
493 expected_nonstandard_style_attribute: "Expected a non-standard " +
494 "style attribute and instead saw '{a}'.",
495 expected_number_a: "Expected a number and instead saw '{a}'.",
496 expected_operator_a: "Expected an operator and instead saw '{a}'.",
497 expected_percent_a: "Expected a percentage and instead saw '{a}'",
498 expected_positive_a: "Expected a positive number and instead saw '{a}'",
499 expected_pseudo_a: "Expected a pseudo, and instead saw :{a}.",
500 expected_selector_a: "Expected a CSS selector, and instead saw {a}.",
501 expected_small_a: "Expected a small positive integer and instead saw '{a}'",
502 expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.",
503 expected_string_a: "Expected a string and instead saw {a}.",
504 expected_style_attribute: "Excepted a style attribute, and instead saw '{a}'.",
505 expected_style_pattern: "Expected a style pattern, and instead saw '{a}'.",
506 expected_tagname_a: "Expected a tagName, and instead saw {a}.",
507 expected_type_a: "Expected a type, and instead saw {a}.",
508 for_if: "The body of a for in should be wrapped in an if " +
509 "statement to filter unwanted properties from the prototype.",
510 function_block: "Function statements should not be placed in blocks. " +
511 "Use a function expression or move the statement to the top of " +
512 "the outer function.",
513 function_eval: "The Function constructor is eval.",
514 function_loop: "Don't make functions within a loop.",
515 function_statement: "Function statements are not invocable. " +
516 "Wrap the whole function invocation in parens.",
517 function_strict: "Use the function form of 'use strict'.",
518 html_confusion_a: "HTML confusion in regular expression '<{a}'.",
519 html_handlers: "Avoid HTML event handlers.",
520 identifier_function: "Expected an identifier in an assignment " +
521 "and instead saw a function invocation.",
522 implied_evil: "Implied eval is evil. Pass a function instead of a string.",
523 infix_in: "Unexpected 'in'. Compare with undefined, or use the " +
524 "hasOwnProperty method instead.",
525 insecure_a: "Insecure '{a}'.",
526 isNaN: "Use the isNaN function to compare with NaN.",
527 label_a_b: "Label '{a}' on '{b}' statement.",
528 lang: "lang is deprecated.",
529 leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.",
530 missing_a: "Missing '{a}'.",
531 missing_a_after_b: "Missing '{a}' after '{b}'.",
532 missing_option: "Missing option value.",
533 missing_property: "Missing property name.",
534 missing_space_a_b: "Missing space between '{a}' and '{b}'.",
535 missing_url: "Missing url.",
536 missing_use_strict: "Missing 'use strict' statement.",
537 mixed: "Mixed spaces and tabs.",
538 move_invocation: "Move the invocation into the parens that " +
539 "contain the function.",
540 move_var: "Move 'var' declarations to the top of the function.",
541 name_function: "Missing name in function statement.",
542 nested_comment: "Nested comment.",
544 not_a_constructor: "Do not use {a} as a constructor.",
545 not_a_defined: "'{a}' has not been fully defined yet.",
546 not_a_function: "'{a}' is not a function.",
547 not_a_label: "'{a}' is not a label.",
548 not_a_scope: "'{a}' is out of scope.",
549 not_greater: "'{a}' should not be greater than '{b}'.",
550 octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.",
551 parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.",
552 parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.",
553 parameter_set_a: "Expected parameter (value) in set {a} function.",
554 radix: "Missing radix parameter.",
555 read_only: "Read only.",
556 redefinition_a: "Redefinition of '{a}'.",
557 reserved_a: "Reserved name '{a}'.",
558 scanned_a_b: "{a} ({b}% scanned).",
559 slash_equal: "A regular expression literal can be confused with '/='.",
560 statement_block: "Expected to see a statement and instead saw a block.",
561 stopping: "Stopping. ",
562 strange_loop: "Strange loop.",
563 strict: "Strict violation.",
564 subscript: "['{a}'] is better written in dot notation.",
565 tag_a_in_b: "A '<{a}>' must be within '<{b}>'.",
566 too_long: "Line too long.",
567 too_many: "Too many errors.",
568 trailing_decimal_a: "A trailing decimal point can be confused " +
569 "with a dot: '.{a}'.",
570 type: "type is unnecessary.",
571 unclosed: "Unclosed string.",
572 unclosed_comment: "Unclosed comment.",
573 unclosed_regexp: "Unclosed regular expression.",
574 unescaped_a: "Unescaped '{a}'.",
575 unexpected_a: "Unexpected '{a}'.",
576 unexpected_char_a_b: "Unexpected character '{a}' in {b}.",
577 unexpected_comment: "Unexpected comment.",
578 unexpected_else: "Unexpected 'else' after 'return'.",
579 unexpected_property_a: "Unexpected /*property*/ '{a}'.",
580 unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
581 unnecessary_initialize: "It is not necessary to initialize '{a}' " +
583 unnecessary_use: "Unnecessary 'use strict'.",
584 unreachable_a_b: "Unreachable '{a}' after '{b}'.",
585 unrecognized_style_attribute_a: "Unrecognized style attribute '{a}'.",
586 unrecognized_tag_a: "Unrecognized tag '<{a}>'.",
587 unsafe: "Unsafe character.",
588 url: "JavaScript URL.",
589 use_array: "Use the array literal notation [].",
590 use_braces: "Spaces are hard to count. Use {{a}}.",
591 use_charAt: "Use the charAt method.",
592 use_object: "Use the object literal notation {}.",
593 use_or: "Use the || operator.",
594 use_param: "Use a named parameter.",
595 used_before_a: "'{a}' was used before it was defined.",
596 var_a_not: "Variable {a} was not declared correctly.",
597 weird_assignment: "Weird assignment.",
598 weird_condition: "Weird condition.",
599 weird_new: "Weird construction. Delete 'new'.",
600 weird_program: "Weird program.",
601 weird_relation: "Weird relation.",
602 weird_ternary: "Weird ternary.",
603 wrap_immediate: "Wrap an immediate function invocation in parentheses " +
604 "to assist the reader in understanding that the expression " +
605 "is the result of a function, and not the function itself.",
606 wrap_regexp: "Wrap the /regexp/ literal in parens to " +
607 "disambiguate the slash operator.",
608 write_is_wrong: "document.write can be a form of eval."
614 css_colorData = array_to_object([
615 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
616 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
617 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral",
618 "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue",
619 "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki",
620 "darkmagenta", "darkolivegreen", "darkorange", "darkorchid",
621 "darkred", "darksalmon", "darkseagreen", "darkslateblue",
622 "darkslategray", "darkturquoise", "darkviolet", "deeppink",
623 "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite",
624 "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold",
625 "goldenrod", "gray", "green", "greenyellow", "honeydew", "hotpink",
626 "indianred", "indigo", "ivory", "khaki", "lavender",
627 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
628 "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgreen",
629 "lightpink", "lightsalmon", "lightseagreen", "lightskyblue",
630 "lightslategray", "lightsteelblue", "lightyellow", "lime",
631 "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
632 "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
633 "mediumslateblue", "mediumspringgreen", "mediumturquoise",
634 "mediumvioletred", "midnightblue", "mintcream", "mistyrose",
635 "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
636 "orange", "orangered", "orchid", "palegoldenrod", "palegreen",
637 "paleturquoise", "palevioletred", "papayawhip", "peachpuff",
638 "peru", "pink", "plum", "powderblue", "purple", "red", "rosybrown",
639 "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen",
640 "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray",
641 "snow", "springgreen", "steelblue", "tan", "teal", "thistle",
642 "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke",
643 "yellow", "yellowgreen",
645 "activeborder", "activecaption", "appworkspace", "background",
646 "buttonface", "buttonhighlight", "buttonshadow", "buttontext",
647 "captiontext", "graytext", "highlight", "highlighttext",
648 "inactiveborder", "inactivecaption", "inactivecaptiontext",
649 "infobackground", "infotext", "menu", "menutext", "scrollbar",
650 "threeddarkshadow", "threedface", "threedhighlight",
651 "threedlightshadow", "threedshadow", "window", "windowframe",
684 devel = array_to_object([
685 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH'
700 funct, // The current function, including the labels used in
701 // the function, as well as (breakage),
702 // (context), (loopage), (name), (params), (token),
706 'closure', 'exception', 'global', 'label', 'outer', 'undef',
710 functions, // All of the functions
711 global_funct, // The global body
712 global_scope, // The global scope
719 area: {empty: true, parent: ' map '},
724 base: {empty: true, parent: ' head '},
728 body: {parent: ' html noframes '},
731 canvas: {parent: ' body p div th td '},
732 caption: {parent: ' table '},
736 col: {empty: true, parent: ' table colgroup '},
737 colgroup: {parent: ' table '},
738 command: {parent: ' menu '},
740 dd: {parent: ' dl '},
748 dt: {parent: ' dl '},
756 frame: {empty: true, parent: ' frameset '},
757 frameset: {parent: ' html frameset '},
764 head: {parent: ' html '},
769 {empty: true, parent: ' head '},
774 input: {empty: true},
779 legend: {parent: ' details fieldset figure '},
780 li: {parent: ' dir menu ol ul '},
781 link: {empty: true, parent: ' head '},
785 meta: {empty: true, parent: ' head noframes noscript '},
788 noframes: {parent: ' html body '},
789 noscript: {parent: ' body head noframes '},
792 optgroup: {parent: ' select '},
793 option: {parent: ' optgroup select '},
796 param: {empty: true, parent: ' applet object '},
804 script: {empty: true, parent: ' body div frame head iframe p pre span '},
811 style: {parent: ' head ', empty: true},
815 tbody: {parent: ' table '},
816 td: {parent: ' tr '},
818 tfoot: {parent: ' table '},
819 th: {parent: ' tr '},
820 thead: {parent: ' table '},
822 title: {parent: ' head '},
823 tr: {parent: ' table tbody thead tfoot '},
834 itself, // JSLint itself
836 lex, // the tokenizer
839 node = array_to_object([
840 'Buffer', 'clearInterval', 'clearTimeout', 'console', 'exports',
841 'global', 'module', 'process', 'querystring', 'require',
842 'setInterval', 'setTimeout', '__dirname', '__filename'
845 numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true),
848 predefined, // Global variables defined by option
852 regexp_flag = array_to_object(['g', 'i', 'm'], true),
853 return_this = function return_this() {
856 rhino = array_to_object([
857 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass',
858 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal',
859 'serialize', 'spawn', 'sync', 'toint32', 'version'
862 scope, // An object containing an object for each variable in scope
863 semicolon_coda = array_to_object([';', '"', '\'', ')'], true),
867 // standard contains the global names that are provided by the
868 // ECMAScript standard.
870 standard = array_to_object([
871 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
872 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError',
873 'Function', 'isFinite', 'isNaN', 'JSON', 'Math', 'Number',
874 'Object', 'parseInt', 'parseFloat', 'RangeError', 'ReferenceError',
875 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError'
886 // widget contains the global names which are provided to a Yahoo
887 // (fna Konfabulator) widget.
889 widget = array_to_object([
890 'alert', 'animator', 'appleScript', 'beep', 'bytesToUIString',
891 'Canvas', 'chooseColor', 'chooseFile', 'chooseFolder',
892 'closeWidget', 'COM', 'convertPathToHFS', 'convertPathToPlatform',
893 'CustomAnimation', 'escape', 'FadeAnimation', 'filesystem', 'Flash',
894 'focusWidget', 'form', 'FormField', 'Frame', 'HotKey', 'Image',
895 'include', 'isApplicationRunning', 'iTunes', 'konfabulatorVersion',
896 'log', 'md5', 'MenuItem', 'MoveAnimation', 'openURL', 'play',
897 'Point', 'popupMenu', 'preferenceGroups', 'preferences', 'print',
898 'prompt', 'random', 'Rectangle', 'reloadWidget', 'ResizeAnimation',
899 'resolvePath', 'resumeUpdates', 'RotateAnimation', 'runCommand',
900 'runCommandInBg', 'saveAs', 'savePreferences', 'screen',
901 'ScrollBar', 'showWidgetPreferences', 'sleep', 'speak', 'Style',
902 'suppressUpdates', 'system', 'tellWidget', 'Text', 'TextArea',
903 'Timer', 'unescape', 'updateNow', 'URL', 'Web', 'widget', 'Window',
904 'XMLDOM', 'XMLHttpRequest', 'yahooCheckLogin', 'yahooLogin',
908 windows = array_to_object([
909 'ActiveXObject', 'CScript', 'Debug', 'Enumerator', 'System',
910 'VBArray', 'WScript', 'WSH'
913 // xmode is used to adapt to the exceptions in html parsing.
914 // It can have these states:
915 // '' .js script file
926 // Regular expressions. Some of these are stupidly long.
928 // unsafe comment or string
929 ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i,
930 // carriage return, carriage return linefeed, or linefeed
932 // unsafe characters that are silently deleted by one or more browsers
933 cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
934 // query characters for ids
935 dx = /[\[\]\/\\"'*<>.&:(){}+=#]/,
937 hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/,
939 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
941 jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i,
944 // characters in strings that need escapement
945 nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
947 ox = /[>&]|<[\/!]?|--/,
948 // attributes characters
949 qx = /[^a-zA-Z0-9+\-_\/. ]/,
951 sx = /^\s*([{}:#%.=,>+\[\]@()"';]|[*$\^~]=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/,
952 ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/,
954 tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!={0,2}|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/,
956 ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto|script/i,
966 function F() {} // Used by Object.create
968 // Provide critical ES5 functions to ES3.
970 if (typeof Array.prototype.filter !== 'function') {
971 Array.prototype.filter = function (f) {
972 var i, length = this.length, result = [], value;
973 for (i = 0; i < length; i += 1) {
986 if (typeof Array.prototype.forEach !== 'function') {
987 Array.prototype.forEach = function (f) {
988 var i, length = this.length;
989 for (i = 0; i < length; i += 1) {
998 if (typeof Array.isArray !== 'function') {
999 Array.isArray = function (o) {
1000 return Object.prototype.toString.apply(o) === '[object Array]';
1004 if (!Object.prototype.hasOwnProperty.call(Object, 'create')) {
1005 Object.create = function (o) {
1011 if (typeof Object.keys !== 'function') {
1012 Object.keys = function (o) {
1013 var array = [], key;
1015 if (Object.prototype.hasOwnProperty.call(o, key)) {
1023 if (typeof String.prototype.entityify !== 'function') {
1024 String.prototype.entityify = function () {
1026 .replace(/&/g, '&')
1027 .replace(/</g, '<')
1028 .replace(/>/g, '>');
1032 if (typeof String.prototype.isAlpha !== 'function') {
1033 String.prototype.isAlpha = function () {
1034 return (this >= 'a' && this <= 'z\uffff') ||
1035 (this >= 'A' && this <= 'Z\uffff');
1039 if (typeof String.prototype.isDigit !== 'function') {
1040 String.prototype.isDigit = function () {
1041 return (this >= '0' && this <= '9');
1045 if (typeof String.prototype.supplant !== 'function') {
1046 String.prototype.supplant = function (o) {
1047 return this.replace(/\{([^{}]*)\}/g, function (a, b) {
1048 var replacement = o[b];
1049 return typeof replacement === 'string' ||
1050 typeof replacement === 'number' ? replacement : a;
1056 function sanitize(a) {
1058 // Escapify a troublesome character.
1060 return escapes[a] ||
1061 '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
1065 function add_to_predefined(group) {
1066 Object.keys(group).forEach(function (name) {
1067 predefined[name] = group[name];
1075 add_to_predefined(rhino);
1076 option.rhino = false;
1079 add_to_predefined(devel);
1080 option.devel = false;
1082 if (option.browser) {
1083 add_to_predefined(browser);
1084 option.browser = false;
1086 if (option.windows) {
1087 add_to_predefined(windows);
1088 option.windows = false;
1091 add_to_predefined(node);
1092 option.node = false;
1095 if (option.widget) {
1096 add_to_predefined(widget);
1097 option.widget = false;
1103 // Produce an error warning.
1105 function artifact(tok) {
1109 return tok.number || tok.string;
1112 function quit(message, line, character) {
1114 name: 'JSLintError',
1116 character: character,
1117 message: bundle.scanned_a_b.supplant({
1119 b: Math.floor((line / lines.length) * 100)
1124 function warn(message, offender, a, b, c, d) {
1125 var character, line, warning;
1126 offender = offender || next_token; // ~~
1127 line = offender.line || 0;
1128 character = offender.from || 0;
1131 raw: bundle[message] || message,
1132 evidence: lines[line - 1] || '',
1134 character: character,
1135 a: a || (offender.id === '(number)'
1136 ? String(offender.number)
1142 warning.reason = warning.raw.supplant(warning);
1143 JSLINT.errors.push(warning);
1144 if (option.passfail) {
1145 quit(bundle.stopping, line, character);
1148 if (warnings >= option.maxerr) {
1149 quit(bundle.too_many, line, character);
1154 function warn_at(message, line, character, a, b, c, d) {
1155 return warn(message, {
1161 function stop(message, offender, a, b, c, d) {
1162 var warning = warn(message, offender, a, b, c, d);
1163 quit(bundle.stopping, warning.line, warning.character);
1166 function stop_at(message, line, character, a, b, c, d) {
1167 return stop(message, {
1173 function expected_at(at) {
1174 if (!option.white && next_token.from !== at) {
1175 warn('expected_a_at_b_c', next_token, '', at,
1180 function aint(it, name, expected) {
1181 if (it[name] !== expected) {
1182 warn('expected_a_b', it, expected, it[name]);
1189 // lexical analysis and token construction
1191 lex = (function lex() {
1192 var character, c, from, length, line, pos, source_row;
1194 // Private lex methods
1196 function next_line() {
1198 if (line >= lines.length) {
1202 source_row = lines[line];
1204 at = source_row.search(/ \t/);
1206 warn_at('mixed', line, at + 1);
1208 source_row = source_row.replace(/\t/g, tab);
1209 at = source_row.search(cx);
1211 warn_at('unsafe', line, at);
1213 if (option.maxlen && option.maxlen < source_row.length) {
1214 warn_at('too_long', line, source_row.length);
1219 // Produce a token object. The token inherits from a syntax symbol.
1221 function it(type, value) {
1223 if (type === '(string)' || type === '(range)') {
1224 if (jx.test(value)) {
1225 warn_at('url', line, from);
1228 the_token = Object.create(syntax[(
1229 type === '(punctuator)' || (type === '(identifier)' &&
1230 Object.prototype.hasOwnProperty.call(syntax, value))
1233 )] || syntax['(error)']);
1234 if (type === '(identifier)') {
1235 the_token.identifier = true;
1236 if (value === '__iterator__' || value === '__proto__') {
1237 stop_at('reserved_a', line, from, value);
1238 } else if (!option.nomen &&
1239 (value.charAt(0) === '_' ||
1240 value.charAt(value.length - 1) === '_')) {
1241 warn_at('dangling_a', line, from, value);
1244 if (type === '(number)') {
1245 the_token.number = +value;
1246 } else if (value !== undefined) {
1247 the_token.string = String(value);
1249 the_token.line = line;
1250 the_token.from = from;
1251 the_token.thru = character;
1254 ('(,=:[!&|?{};'.indexOf(id.charAt(id.length - 1)) >= 0) ||
1255 id === 'return' || id === 'case'
1261 var exec = x.exec(source_row), first;
1263 length = exec[0].length;
1265 c = first.charAt(0);
1266 source_row = source_row.slice(length);
1267 from = character + length - first.length;
1268 character += length;
1273 function string(x) {
1274 var c, pos = 0, r = '', result;
1277 var i = parseInt(source_row.substr(pos + 1, n), 16);
1279 if (i >= 32 && i <= 126 &&
1280 i !== 34 && i !== 92 && i !== 39) {
1281 warn_at('unexpected_a', line, character, '\\');
1284 c = String.fromCharCode(i);
1287 if (json_mode && x !== '"') {
1288 warn_at('expected_a', line, character, '"');
1291 if (xquote === x || (xmode === 'scriptstring' && !xquote)) {
1292 return it('(punctuator)', x);
1296 while (pos >= source_row.length) {
1298 if (xmode !== 'html' || !next_line()) {
1299 stop_at('unclosed', line, from);
1302 c = source_row.charAt(pos);
1305 source_row = source_row.slice(pos + 1);
1306 result = it('(string)', r);
1311 if (c === '\n' || c === '\r') {
1314 warn_at('control_a', line, character + pos,
1315 source_row.slice(0, pos));
1316 } else if (c === xquote) {
1317 warn_at('bad_html', line, character + pos);
1318 } else if (c === '<') {
1319 if (option.safe && xmode === 'html') {
1320 warn_at('adsafe_a', line, character + pos, c);
1321 } else if (source_row.charAt(pos + 1) === '/' && (xmode || option.safe)) {
1322 warn_at('expected_a_b', line, character,
1324 } else if (source_row.charAt(pos + 1) === '!' && (xmode || option.safe)) {
1325 warn_at('unexpected_a', line, character, '<!');
1327 } else if (c === '\\') {
1328 if (xmode === 'html') {
1330 warn_at('adsafe_a', line, character + pos, c);
1332 } else if (xmode === 'styleproperty') {
1335 c = source_row.charAt(pos);
1337 warn_at('unexpected_a', line, character, '\\');
1342 c = source_row.charAt(pos);
1346 warn_at('es5', line, character);
1352 warn_at('bad_html', line, character + pos);
1356 warn_at('unexpected_a', line, character, '\\\'');
1364 warn_at('unexpected_a', line, character, '\\v');
1370 warn_at('unexpected_a', line, character, '\\x');
1375 if (typeof descapes[c] !== 'string') {
1376 warn_at(c >= '0' && c <= '7' ? 'octal_a' : 'unexpected_a',
1377 line, character, '\\' + c);
1390 function number(snippet) {
1392 if (xmode !== 'style' && xmode !== 'styleproperty' &&
1393 source_row.charAt(0).isAlpha()) {
1394 warn_at('expected_space_a_b',
1395 line, character, c, source_row.charAt(0));
1398 digit = snippet.charAt(1);
1399 if (digit.isDigit()) {
1400 if (token.id !== '.' && xmode !== 'styleproperty') {
1401 warn_at('unexpected_a', line, character, snippet);
1403 } else if (json_mode && (digit === 'x' || digit === 'X')) {
1404 warn_at('unexpected_a', line, character, '0x');
1407 if (snippet.slice(snippet.length - 1) === '.') {
1408 warn_at('trailing_decimal_a', line, character, snippet);
1410 if (xmode !== 'style') {
1412 if (!isFinite(digit)) {
1413 warn_at('bad_number', line, character, snippet);
1417 return it('(number)', snippet);
1420 function comment(snippet) {
1421 if (comments_off || src || (xmode && xmode !== 'script' &&
1422 xmode !== 'style' && xmode !== 'styleproperty')) {
1423 warn_at('unexpected_comment', line, character);
1424 } else if (xmode === 'script' && /<\//i.test(source_row)) {
1425 warn_at('unexpected_a', line, character, '<\/');
1426 } else if (option.safe && ax.test(snippet)) {
1427 warn_at('dangerous_comment', line, character);
1446 c = source_row.charAt(length);
1450 stop_at('unclosed_regexp', line, from);
1454 warn_at('unescaped_a', line, from + length, '/');
1456 c = source_row.slice(0, length - 1);
1457 potential = Object.create(regexp_flag);
1459 letter = source_row.charAt(length);
1460 if (potential[letter] !== true) {
1463 potential[letter] = false;
1467 if (source_row.charAt(length).isAlpha()) {
1468 stop_at('unexpected_a', line, from, source_row.charAt(length));
1470 character += length;
1471 source_row = source_row.slice(length);
1472 quote = source_row.charAt(0);
1473 if (quote === '/' || quote === '*') {
1474 stop_at('confusing_regexp', line, from);
1476 result = it('(regexp)', c);
1480 c = source_row.charAt(length);
1482 warn_at('control_a', line, from + length, String(c));
1483 } else if (c === '<') {
1484 warn_at(bundle.unexpected_a, line, from + length, '\\');
1491 if (source_row.charAt(length) === '?') {
1493 switch (source_row.charAt(length)) {
1500 warn_at(bundle.expected_a_b, line, from + length,
1501 ':', source_row.charAt(length));
1512 warn_at('unescaped_a', line, from + length, ')');
1519 while (source_row.charAt(length) === ' ') {
1524 warn_at('use_braces', line, from + length, pos);
1528 c = source_row.charAt(length);
1531 if (!option.regexp) {
1532 warn_at('insecure_a', line, from + length, c);
1533 } else if (source_row.charAt(length) === ']') {
1534 stop_at('unescaped_a', line, from + length, '^');
1539 warn_at('empty_class', line, from + length - 1);
1543 c = source_row.charAt(length);
1548 warn_at('unescaped_a', line, from + length, c);
1555 warn_at('unescaped_a', line, from + length, '-');
1561 warn_at('unescaped_a', line, from + length - 1, '-');
1565 c = source_row.charAt(length);
1567 warn_at(bundle.control_a, line, from + length, String(c));
1568 } else if (c === '<') {
1569 warn_at(bundle.unexpected_a, line, from + length, '\\');
1575 warn_at('unescaped_a', line, from + length - 1, '/');
1579 if (xmode === 'script') {
1580 c = source_row.charAt(length);
1581 if (c === '!' || c === '/') {
1582 warn_at(bundle.html_confusion_a, line,
1594 if (!option.regexp) {
1595 warn_at('insecure_a', line, from + length, c);
1604 warn_at('unescaped_a', line, from + length, c);
1607 if (xmode === 'script') {
1608 c = source_row.charAt(length);
1609 if (c === '!' || c === '/') {
1610 warn_at(bundle.html_confusion_a, line, from + length, c);
1616 switch (source_row.charAt(length)) {
1621 if (source_row.charAt(length) === '?') {
1627 c = source_row.charAt(length);
1628 if (c < '0' || c > '9') {
1629 warn_at(bundle.expected_number_a, line,
1635 c = source_row.charAt(length);
1636 if (c < '0' || c > '9') {
1640 low = +c + (low * 10);
1646 c = source_row.charAt(length);
1647 if (c >= '0' && c <= '9') {
1651 c = source_row.charAt(length);
1652 if (c < '0' || c > '9') {
1656 high = +c + (high * 10);
1660 if (source_row.charAt(length) !== '}') {
1661 warn_at(bundle.expected_a_b, line, from + length,
1666 if (source_row.charAt(length) === '?') {
1670 warn_at(bundle.not_greater, line, from + length,
1677 c = source_row.slice(0, length - 1);
1678 character += length;
1679 source_row = source_row.slice(length);
1680 return it('(regexp)', c);
1683 // Public lex methods
1686 init: function (source) {
1687 if (typeof source === 'string') {
1688 lines = source.split(crlfx);
1697 range: function (begin, end) {
1700 if (source_row.charAt(0) !== begin) {
1701 stop_at('expected_a_b', line, character, begin,
1702 source_row.charAt(0));
1705 source_row = source_row.slice(1);
1707 c = source_row.charAt(0);
1710 stop_at('missing_a', line, character, c);
1713 source_row = source_row.slice(1);
1715 return it('(range)', value);
1718 warn_at('unexpected_a', line, character, c);
1725 // token -- this is called by advance to get the next token.
1727 token: function () {
1731 while (!source_row) {
1736 while (xmode === 'outer') {
1737 i = source_row.search(ox);
1742 source_row = source_row.slice(i);
1746 return it('(end)', '');
1750 snippet = match(rx[xmode] || tx);
1753 if (source_row.charAt(0) === ' ') {
1754 if (!option.white) {
1755 warn_at('unexpected_a', line, character,
1761 stop_at('unexpected_a', line, character,
1762 source_row.charAt(0));
1769 c = snippet.charAt(0);
1770 if (c.isAlpha() || c === '_' || c === '$') {
1771 return it('(identifier)', snippet);
1777 return number(snippet);
1785 return string(snippet);
1790 comment(source_row);
1798 i = source_row.search(lx);
1802 comment(source_row);
1804 stop_at('unclosed_comment', line, character);
1807 comment(source_row.slice(0, i));
1809 if (source_row.charAt(i) === '/') {
1810 stop_at('nested_comment', line, character);
1812 source_row = source_row.slice(i + 2);
1819 if (token.id === '/=') {
1828 : it('(punctuator)', snippet);
1836 i = source_row.indexOf('--');
1840 i = source_row.indexOf('<!');
1842 stop_at('nested_comment',
1843 line, character + i);
1846 stop_at('unclosed_comment', length, c);
1849 length = source_row.indexOf('<!');
1850 if (length >= 0 && length < i) {
1851 stop_at('nested_comment',
1852 line, character + length);
1855 if (source_row.charAt(i + 2) !== '>') {
1856 stop_at('expected_a', line, character, '-->');
1859 source_row = source_row.slice(i + 3);
1862 if (xmode === 'html' || xmode === 'styleproperty') {
1864 c = source_row.charAt(0);
1865 if ((c < '0' || c > '9') &&
1866 (c < 'a' || c > 'f') &&
1867 (c < 'A' || c > 'F')) {
1871 source_row = source_row.slice(1);
1874 if (snippet.length !== 4 && snippet.length !== 7) {
1875 warn_at('bad_color_a', line,
1876 from + length, snippet);
1878 return it('(color)', snippet);
1880 return it('(punctuator)', snippet);
1883 if (xmode === 'outer' && c === '&') {
1885 source_row = source_row.slice(1);
1887 c = source_row.charAt(0);
1889 source_row = source_row.slice(1);
1893 if (!((c >= '0' && c <= '9') ||
1894 (c >= 'a' && c <= 'z') ||
1896 stop_at('bad_entity', line, from + length,
1902 return it('(punctuator)', snippet);
1911 function add_label(token, kind, name) {
1913 // Define the symbol in the current function in the current scope.
1915 name = name || token.string;
1917 // Global variables cannot be created in the safe subset. If a global variable
1918 // already exists, do nothing. If it is predefined, define it.
1920 if (funct === global_funct) {
1922 warn('adsafe_a', token, name);
1924 if (typeof global_funct[name] !== 'string') {
1925 token.writeable = typeof predefined[name] === 'boolean'
1928 token.funct = funct;
1929 global_scope[name] = token;
1931 if (kind === 'becoming') {
1935 // Ordinary variables.
1939 // Warn if the variable already exists.
1941 if (typeof funct[name] === 'string') {
1942 if (funct[name] === 'undef') {
1943 if (!option.undef) {
1944 warn('used_before_a', token, name);
1948 warn('already_defined', token, name);
1952 // Add the symbol to the current function.
1954 token.funct = funct;
1955 token.writeable = true;
1956 scope[name] = token;
1963 function peek(distance) {
1965 // Peek ahead to a future token. The distance is how far ahead to look. The
1966 // default is the next token.
1968 var found, slot = 0;
1970 distance = distance || 0;
1971 while (slot <= distance) {
1972 found = lookahead[slot];
1974 found = lookahead[slot] = lex.token();
1982 function advance(id, match) {
1984 // Produce the next token, also looking for programming errors.
1988 // If indentation checking was requested, then inspect all of the line breakings.
1989 // The var statement is tricky because the names might be aligned or not. We
1990 // look at the first line break after the var to determine the programmer's
1993 if (var_mode && next_token.line !== token.line) {
1994 if ((var_mode !== indent || !next_token.edge) &&
1995 next_token.from === indent.at -
1996 (next_token.edge ? option.indent : 0)) {
1999 dent.at -= option.indent;
2000 if (dent === var_mode) {
2009 if (next_token.id === '?' && indent.mode === ':' &&
2010 token.line !== next_token.line) {
2011 indent.at -= option.indent;
2015 // If the token is an edge.
2017 if (next_token.edge) {
2018 if (next_token.edge === 'label') {
2020 } else if (next_token.edge === 'case' || indent.mode === 'statement') {
2021 expected_at(indent.at - option.indent);
2022 } else if (indent.mode !== 'array' || next_token.line !== token.line) {
2023 expected_at(indent.at);
2026 // If the token is not an edge, but is the first token on the line.
2028 } else if (next_token.line !== token.line) {
2029 if (next_token.from < indent.at + (indent.mode ===
2030 'expression' ? 0 : option.indent)) {
2031 expected_at(indent.at + option.indent);
2035 } else if (next_token.line !== token.line) {
2036 if (next_token.edge) {
2037 expected_at(indent.at);
2040 if (indent.mode === 'statement' || indent.mode === 'var') {
2041 expected_at(indent.at + option.indent);
2042 } else if (next_token.from < indent.at + (indent.mode ===
2043 'expression' ? 0 : option.indent)) {
2044 expected_at(indent.at + option.indent);
2052 if (next_token.id === '.') {
2053 warn('trailing_decimal_a');
2057 if (next_token.id === '-' || next_token.id === '--') {
2058 warn('confusing_a');
2062 if (next_token.id === '+' || next_token.id === '++') {
2063 warn('confusing_a');
2067 if (token.id === '(string)' || token.identifier) {
2068 anonname = token.string;
2071 if (id && next_token.id !== id) {
2073 warn('expected_a_b_from_c_d', next_token, id,
2074 match.id, match.line, artifact());
2075 } else if (!next_token.identifier || next_token.string !== id) {
2076 warn('expected_a_b', next_token, id, artifact());
2081 next_token = lookahead.shift() || lex.token();
2085 function advance_identifier(string) {
2086 if (next_token.identifier && next_token.string === string) {
2089 warn('expected_a_b', next_token, string, artifact());
2094 function do_safe() {
2095 if (option.adsafe) {
2100 option['continue'] =
2114 option.windows = false;
2117 delete predefined.Array;
2118 delete predefined.Date;
2119 delete predefined.Function;
2120 delete predefined.Object;
2121 delete predefined['eval'];
2131 function do_globals() {
2132 var name, writeable;
2134 if (next_token.id !== '(string)' && !next_token.identifier) {
2137 name = next_token.string;
2140 if (next_token.id === ':') {
2142 switch (next_token.id) {
2144 writeable = predefined[name] !== false;
2151 stop('unexpected_a');
2154 predefined[name] = writeable;
2155 if (next_token.id !== ',') {
2163 function do_jslint() {
2165 while (next_token.id === '(string)' || next_token.identifier) {
2166 name = next_token.string;
2167 if (!allowed_option[name]) {
2168 stop('unexpected_a');
2171 if (next_token.id !== ':') {
2172 stop('expected_a_b', next_token, ':', artifact());
2175 if (typeof allowed_option[name] === 'number') {
2176 value = next_token.number;
2177 if (value > allowed_option[name] || value <= 0 ||
2178 Math.floor(value) !== value) {
2179 stop('expected_small_a');
2181 option[name] = value;
2183 if (next_token.id === 'true') {
2184 option[name] = true;
2185 } else if (next_token.id === 'false') {
2186 option[name] = false;
2188 stop('unexpected_a');
2192 if (next_token.id === ',') {
2200 function do_properties() {
2202 option.properties = true;
2204 if (next_token.id !== '(string)' && !next_token.identifier) {
2207 name = next_token.string;
2209 if (next_token.id === ':') {
2212 if (next_token.id !== '(string)' && !next_token.identifier) {
2218 if (next_token.id !== ',') {
2226 directive = function directive() {
2227 var command = this.id,
2228 old_comments_off = comments_off,
2229 old_indent = indent;
2230 comments_off = true;
2232 if (next_token.line === token.line && next_token.from === token.thru) {
2233 warn('missing_space_a_b', next_token, artifact(token), artifact());
2235 if (lookahead.length > 0) {
2236 warn('unexpected_a', this);
2239 case '/*properties':
2247 warn('adsafe_a', this);
2254 warn('adsafe_a', this);
2259 stop('unexpected_a', this);
2261 comments_off = old_comments_off;
2263 indent = old_indent;
2267 // Indentation intention
2269 function edge(mode) {
2270 next_token.edge = indent ? indent.open && (mode || 'edge') : '';
2274 function step_in(mode) {
2276 if (typeof mode === 'number') {
2282 } else if (!indent) {
2288 } else if (mode === 'statement') {
2295 open = mode === 'var' || next_token.line !== token.line;
2297 at: (open || mode === 'control'
2298 ? indent.at + option.indent
2299 : indent.at) + (indent.wrap ? option.indent : 0),
2304 if (mode === 'var' && open) {
2310 function step_out(id, symbol) {
2312 if (indent && indent.open) {
2313 indent.at -= option.indent;
2316 advance(id, symbol);
2319 indent = indent.was;
2323 // Functions for conformance of whitespace.
2325 function one_space(left, right) {
2326 left = left || token;
2327 right = right || next_token;
2328 if (right.id !== '(end)' && !option.white &&
2329 (token.line !== right.line ||
2330 token.thru + 1 !== right.from)) {
2331 warn('expected_space_a_b', right, artifact(token), artifact(right));
2335 function one_space_only(left, right) {
2336 left = left || token;
2337 right = right || next_token;
2338 if (right.id !== '(end)' && (left.line !== right.line ||
2339 (!option.white && left.thru + 1 !== right.from))) {
2340 warn('expected_space_a_b', right, artifact(left), artifact(right));
2344 function no_space(left, right) {
2345 if (option.jqmspace)
2348 left = left || token;
2349 right = right || next_token;
2350 if ((!option.white || xmode === 'styleproperty' || xmode === 'style') &&
2351 left.thru !== right.from && left.line === right.line) {
2352 warn('unexpected_space_a_b', right, artifact(left), artifact(right));
2356 function no_space_only(left, right) {
2357 if (option.jqmspace)
2360 left = left || token;
2361 right = right || next_token;
2362 if (right.id !== '(end)' && (left.line !== right.line ||
2363 (!option.white && left.thru !== right.from))) {
2364 warn('unexpected_space_a_b', right, artifact(left), artifact(right));
2368 function spaces(left, right) {
2369 if (!option.white) {
2370 left = left || token;
2371 right = right || next_token;
2372 if (left.thru === right.from && left.line === right.line) {
2373 warn('missing_space_a_b', right, artifact(left), artifact(right));
2379 if (next_token.id !== ',') {
2380 warn_at('expected_a_b', token.line, token.thru, ',', artifact());
2382 if (!option.white) {
2391 function semicolon() {
2392 if (next_token.id !== ';') {
2393 warn_at('expected_a_b', token.line, token.thru, ';', artifact());
2395 if (!option.white) {
2399 if (semicolon_coda[next_token.id] !== true) {
2405 function use_strict() {
2406 if (next_token.string === 'use strict') {
2408 warn('unnecessary_use');
2414 option.newcap = false;
2415 option.undef = false;
2422 function are_similar(a, b) {
2426 if (Array.isArray(a)) {
2427 if (Array.isArray(b) && a.length === b.length) {
2429 for (i = 0; i < a.length; i += 1) {
2430 if (!are_similar(a[i], b[i])) {
2438 if (Array.isArray(b)) {
2441 if (a.id === '(number)' && b.id === '(number)') {
2442 return a.number === b.number;
2444 if (a.arity === b.arity && a.string === b.string) {
2449 return a.id === b.id && are_similar(a.first, b.first);
2451 return are_similar(a.first, b.first) &&
2452 are_similar(a.second, b.second);
2454 return are_similar(a.first, b.first) &&
2455 are_similar(a.second, b.second) &&
2456 are_similar(a.third, b.third);
2464 if (a.id === '.' && b.id === '[' && b.arity === 'infix') {
2465 return a.second.string === b.second.string && b.second.id === '(string)';
2467 if (a.id === '[' && a.arity === 'infix' && b.id === '.') {
2468 return a.second.string === b.second.string && a.second.id === '(string)';
2475 // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
2476 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
2477 // like .nud except that it is only used on the first token of a statement.
2478 // Having .fud makes it much easier to define statement-oriented languages like
2479 // JavaScript. I retained Pratt's nomenclature.
2481 // .nud Null denotation
2482 // .fud First null denotation
2483 // .led Left denotation
2484 // lbp Left binding power
2485 // rbp Right binding power
2487 // They are elements of the parsing method called Top Down Operator Precedence.
2489 function expression(rbp, initial) {
2491 // rbp is the right binding power.
2492 // initial indicates that this is the first expression of a statement.
2495 if (next_token.id === '(end)') {
2496 stop('unexpected_a', token, next_token.id);
2499 if (option.safe && scope[token.string] &&
2500 scope[token.string] === global_scope[token.string] &&
2501 (next_token.id !== '(' && next_token.id !== '.')) {
2502 warn('adsafe_a', token);
2505 anonname = 'anonymous';
2506 funct['(verb)'] = token.string;
2508 if (initial === true && token.fud) {
2514 if (next_token.id === '(number)' && token.id === '.') {
2515 warn('leading_decimal_a', token, artifact());
2519 stop('expected_identifier_a', token, token.id);
2521 while (rbp < next_token.lbp) {
2524 left = token.led(left);
2526 stop('expected_operator_a', token, token.id);
2534 // Functional constructors for making the symbols that will be inherited by
2537 function symbol(s, p) {
2539 if (!x || typeof x !== 'object') {
2549 function postscript(x) {
2550 x.postscript = true;
2554 function ultimate(s) {
2555 var x = symbol(s, 0);
2561 return postscript(x);
2565 function stmt(s, f) {
2567 x.identifier = x.reserved = true;
2572 function labeled_stmt(s, f) {
2577 function disrupt_stmt(s, f) {
2583 function reserve_name(x) {
2584 var c = x.id.charAt(0);
2585 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2586 x.identifier = x.reserved = true;
2592 function prefix(s, f) {
2593 var x = symbol(s, 150);
2595 x.nud = typeof f === 'function'
2598 if (s === 'typeof') {
2603 this.first = expression(150);
2604 this.arity = 'prefix';
2605 if (this.id === '++' || this.id === '--') {
2606 if (!option.plusplus) {
2607 warn('unexpected_a', this);
2608 } else if ((!this.first.identifier || this.first.reserved) &&
2609 this.first.id !== '.' && this.first.id !== '[') {
2610 warn('bad_operand', this);
2619 function type(s, t, nud) {
2629 function reserve(s, f) {
2631 x.identifier = x.reserved = true;
2632 if (typeof f === 'function') {
2639 function constant(name) {
2640 var x = reserve(name);
2642 x.nud = return_this;
2647 function reservevar(s, v) {
2648 return reserve(s, function () {
2649 if (typeof v === 'function') {
2657 function infix(s, p, f, w) {
2658 var x = symbol(s, p);
2660 x.led = function (left) {
2661 this.arity = 'infix';
2663 spaces(prev_token, token);
2666 if (!option.bitwise && this.bitwise) {
2667 warn('unexpected_a', this);
2669 if (typeof f === 'function') {
2670 return f(left, this);
2673 this.second = expression(p);
2679 function expected_relation(node, message) {
2681 warn(message || bundle.conditional_assignment, node);
2686 function expected_condition(node, message) {
2690 if (node.arity !== 'infix') {
2691 warn(message || bundle.weird_condition, node);
2706 warn(message || bundle.weird_condition, node);
2709 if (node.first.id === '.' && numbery[node.first.second.string] === true) {
2710 warn(message || bundle.weird_condition, node);
2717 function check_relation(node) {
2718 switch (node.arity) {
2723 warn('unexpected_a', node);
2726 warn('confusing_a', node);
2732 warn('unexpected_a', node);
2735 if (node.id === 'NaN') {
2736 warn('isNaN', node);
2743 function relation(s, eqeq) {
2744 return infix(s, 100, function (left, that) {
2745 check_relation(left);
2746 if (eqeq && !option.eqeq) {
2747 warn('expected_a_b', that, eqeq, that.id);
2749 var right = expression(100);
2750 if (are_similar(left, right) ||
2751 ((left.id === '(string)' || left.id === '(number)') &&
2752 (right.id === '(string)' || right.id === '(number)'))) {
2753 warn('weird_relation', that);
2756 that.second = check_relation(right);
2762 function assignop(s, op) {
2763 var x = infix(s, 20, function (left, that) {
2766 if (left.identifier) {
2767 if (scope[left.string]) {
2768 if (scope[left.string].writeable === false) {
2769 warn('read_only', left);
2774 if (funct['(params)']) {
2775 funct['(params)'].forEach(function (value) {
2776 if (value.string === left.string) {
2777 value.assign = true;
2781 } else if (option.safe) {
2784 if (typeof predefined[l.string] === 'boolean') {
2785 warn('adsafe_a', l);
2790 if (left === syntax['function']) {
2791 warn('identifier_function', token);
2793 if (left.id === '.' || left.id === '[') {
2794 if (!left.first || left.first.string === 'arguments') {
2795 warn('bad_assignment', that);
2797 } else if (left.identifier) {
2798 if (!left.reserved && funct[left.string] === 'exception') {
2799 warn('assign_exception', left);
2802 warn('bad_assignment', that);
2804 that.second = expression(19);
2805 if (that.id === '=' && are_similar(that.first, that.second)) {
2806 warn('weird_assignment', that);
2812 if (syntax[op].bitwise) {
2820 function bitwise(s, p) {
2821 var x = infix(s, p, 'number');
2827 function suffix(s) {
2828 var x = symbol(s, 150);
2829 x.led = function (left) {
2830 no_space_only(prev_token, token);
2831 if (!option.plusplus) {
2832 warn('unexpected_a', this);
2833 } else if ((!left.identifier || left.reserved) &&
2834 left.id !== '.' && left.id !== '[') {
2835 warn('bad_operand', this);
2838 this.arity = 'suffix';
2845 function optional_identifier() {
2846 if (next_token.identifier) {
2848 if (option.safe && banned[token.string]) {
2849 warn('adsafe_a', token);
2850 } else if (token.reserved && !option.es5) {
2851 warn('expected_identifier_a_reserved', token);
2853 return token.string;
2858 function identifier() {
2859 var i = optional_identifier();
2861 stop(token.id === 'function' && next_token.id === '('
2863 : 'expected_identifier_a');
2869 function statement() {
2871 var label, old_scope = scope, the_statement;
2873 // We don't like the empty statement.
2875 if (next_token.id === ';') {
2876 warn('unexpected_a');
2881 // Is this a labeled statement?
2883 if (next_token.identifier && !next_token.reserved && peek().id === ':') {
2888 scope = Object.create(old_scope);
2889 add_label(label, 'label');
2890 if (next_token.labeled !== true) {
2891 warn('label_a_b', next_token, label.string, artifact());
2892 } else if (jx.test(label.string + ':')) {
2894 } else if (funct === global_funct) {
2895 stop('unexpected_a', token);
2897 next_token.label = label;
2900 // Parse the statement.
2902 if (token.id !== 'else') {
2905 step_in('statement');
2906 the_statement = expression(0, true);
2907 if (the_statement) {
2909 // Look for the final semicolon.
2911 if (the_statement.arity === 'statement') {
2912 if (the_statement.id === 'switch' ||
2913 (the_statement.block && the_statement.id !== 'do')) {
2920 // If this is an expression statement, determine if it is acceptable.
2923 // statments. If it is to be used at all, new should only be used to make
2924 // objects, not side effects. The expression statements we do like do
2925 // assignment or invocation or delete.
2927 if (the_statement.id === '(') {
2928 if (the_statement.first.id === 'new') {
2931 } else if (!the_statement.assign &&
2932 the_statement.id !== 'delete' &&
2933 the_statement.id !== '++' &&
2934 the_statement.id !== '--') {
2935 warn('assignment_function_expression', token);
2942 return the_statement;
2946 function statements() {
2947 var array = [], disruptor, the_statement;
2949 // A disrupt statement may not be followed by any other statement.
2950 // If the last statement is disrupt, then the sequence is disrupt.
2952 while (next_token.postscript !== true) {
2953 if (next_token.id === ';') {
2954 warn('unexpected_a', next_token);
2957 if (next_token.string === 'use strict') {
2958 if ((!node_js && xmode !== 'script') || funct !== global_funct || array.length > 0) {
2959 warn('function_strict');
2964 warn('unreachable_a_b', next_token, next_token.string,
2968 the_statement = statement();
2969 if (the_statement) {
2970 array.push(the_statement);
2971 if (the_statement.disrupt) {
2972 disruptor = the_statement;
2973 array.disrupt = true;
2982 function block(ordinary) {
2984 // array block is array sequence of statements wrapped in braces.
2985 // ordinary is false for function bodies and try blocks.
2986 // ordinary is true for if statements, while, etc.
2990 old_in_block = in_block,
2992 old_strict_mode = strict_mode;
2994 in_block = ordinary;
2995 scope = Object.create(scope);
2997 if (next_token.id === '{') {
3000 if (!ordinary && !use_strict() && !old_strict_mode &&
3001 !option.sloppy && funct['(context)'] === global_funct) {
3002 warn('missing_use_strict');
3004 array = statements();
3005 strict_mode = old_strict_mode;
3006 step_out('}', curly);
3007 } else if (!ordinary) {
3008 stop('expected_a_b', next_token, '{', artifact());
3010 warn('expected_a_b', next_token, '{', artifact());
3011 array = [statement()];
3012 array.disrupt = array[0].disrupt;
3014 funct['(verb)'] = null;
3016 in_block = old_in_block;
3017 if (ordinary && array.length === 0) {
3018 warn('empty_block');
3024 function tally_property(name) {
3025 if (option.properties && typeof property[name] !== 'number') {
3026 warn('unexpected_property_a', token, name);
3028 if (typeof property[name] === 'number') {
3029 property[name] += 1;
3036 // ECMAScript parser
3038 syntax['(identifier)'] = {
3043 var name = this.string,
3044 variable = scope[name],
3048 // If the variable is not in scope, then we may have an undeclared variable.
3049 // Check the predefined list. If it was predefined, create the global
3052 if (typeof variable !== 'object') {
3053 writeable = predefined[name];
3054 if (typeof writeable === 'boolean') {
3055 global_scope[name] = variable = {
3057 writeable: writeable,
3060 global_funct[name] = 'var';
3062 // But if the variable is not in scope, and is not predefined, and if we are not
3063 // in the global scope, then we have an undefined variable error.
3066 if (!option.undef) {
3067 warn('used_before_a', token);
3069 scope[name] = variable = {
3074 funct[name] = 'undef';
3078 site = variable.funct;
3080 // The name is in scope and defined in the current function.
3082 if (funct === site) {
3084 // Change 'unused' to 'var', and reject labels.
3086 switch (funct[name]) {
3088 warn('unexpected_a', token);
3089 funct[name] = 'var';
3092 funct[name] = 'var';
3095 funct[name] = 'parameter';
3098 funct[name] = 'function';
3101 warn('a_label', token, name);
3105 // If the name is already defined in the current
3106 // function, but not as outer, then there is a scope error.
3109 switch (funct[name]) {
3114 warn('a_scope', token, name);
3117 warn('a_label', token, name);
3124 // If the name is defined in an outer function, make an outer entry, and if
3125 // it was unused, make it var.
3127 switch (site[name]) {
3135 site[name] = 'closure';
3136 funct[name] = site === global_funct
3141 site[name] = 'parameter';
3142 funct[name] = 'outer';
3145 funct[name] = 'undef';
3148 warn('a_label', token, name);
3156 stop('expected_operator_a');
3160 // Build the syntax table by declaring the syntactic elements.
3162 type('(array)', 'array');
3163 type('(color)', 'color');
3164 type('(function)', 'function');
3165 type('(number)', 'number', return_this);
3166 type('(object)', 'object');
3167 type('(string)', 'string', return_this);
3168 type('(boolean)', 'boolean', return_this);
3169 type('(range)', 'range');
3170 type('(regexp)', 'regexp', return_this);
3172 ultimate('(begin)');
3174 ultimate('(error)');
3175 postscript(symbol('</'));
3179 postscript(symbol('}'));
3182 postscript(symbol('"'));
3183 postscript(symbol('\''));
3190 postscript(reserve('case'));
3192 postscript(reserve('default'));
3196 reservevar('arguments', function (x) {
3197 if (strict_mode && funct === global_funct) {
3199 } else if (option.safe) {
3200 warn('adsafe_a', x);
3202 funct['(arguments)'] = true;
3204 reservevar('eval', function (x) {
3206 warn('adsafe_a', x);
3209 constant('false', 'boolean');
3210 constant('Infinity', 'number');
3211 constant('NaN', 'number');
3212 constant('null', '');
3213 reservevar('this', function (x) {
3215 warn('adsafe_a', x);
3216 } else if (strict_mode && funct['(token)'].arity === 'statement' &&
3217 funct['(name)'].charAt(0) > 'Z') {
3221 constant('true', 'boolean');
3222 constant('undefined', '');
3224 infix('?', 30, function (left, that) {
3226 that.first = expected_condition(expected_relation(left));
3227 that.second = expression(0);
3230 var colon = next_token;
3234 that.third = expression(10);
3235 that.arity = 'ternary';
3236 if (are_similar(that.second, that.third)) {
3237 warn('weird_ternary', colon);
3238 } else if (are_similar(that.first, that.second)) {
3239 warn('use_or', that);
3245 infix('||', 40, function (left, that) {
3246 function paren_check(that) {
3247 if (that.id === '&&' && !that.paren) {
3253 that.first = paren_check(expected_condition(expected_relation(left)));
3254 that.second = paren_check(expected_relation(expression(40)));
3255 if (are_similar(that.first, that.second)) {
3256 warn('weird_condition', that);
3261 infix('&&', 50, function (left, that) {
3262 that.first = expected_condition(expected_relation(left));
3263 that.second = expected_relation(expression(50));
3264 if (are_similar(that.first, that.second)) {
3265 warn('weird_condition', that);
3270 prefix('void', function () {
3271 this.first = expression(0);
3272 this.arity = 'prefix';
3274 warn('expected_a_b', this, 'undefined', 'void');
3275 } else if (this.first.number !== 0) {
3276 warn('expected_a_b', this.first, '0', artifact(this.first));
3285 relation('==', '===');
3287 relation('!=', '!==');
3296 bitwise('>>>', 120);
3298 infix('in', 120, function (left, that) {
3299 warn('infix_in', that);
3301 that.right = expression(130);
3304 infix('instanceof', 120);
3305 infix('+', 130, function (left, that) {
3306 if (left.id === '(number)') {
3307 if (left.number === 0) {
3308 warn('unexpected_a', left, '0');
3310 } else if (left.id === '(string)') {
3311 if (left.string === '') {
3312 warn('expected_a_b', left, 'String', '\'\'');
3315 var right = expression(130);
3316 if (right.id === '(number)') {
3317 if (right.number === 0) {
3318 warn('unexpected_a', right, '0');
3320 } else if (right.id === '(string)') {
3321 if (right.string === '') {
3322 warn('expected_a_b', right, 'String', '\'\'');
3325 if (left.id === right.id) {
3326 if (left.id === '(string)' || left.id === '(number)') {
3327 if (left.id === '(string)') {
3328 left.string += right.string;
3329 if (jx.test(left.string)) {
3333 left.number += right.number;
3335 left.thru = right.thru;
3340 that.second = right;
3344 prefix('+++', function () {
3345 warn('confusing_a', token);
3346 this.first = expression(150);
3347 this.arity = 'prefix';
3350 infix('+++', 130, function (left) {
3351 warn('confusing_a', token);
3353 this.second = expression(130);
3356 infix('-', 130, function (left, that) {
3357 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') {
3358 warn('unexpected_a', left);
3360 var right = expression(130);
3361 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') {
3362 warn('unexpected_a', right);
3364 if (left.id === right.id && left.id === '(number)') {
3365 left.number -= right.number;
3366 left.thru = right.thru;
3370 that.second = right;
3374 prefix('---', function () {
3375 warn('confusing_a', token);
3376 this.first = expression(150);
3377 this.arity = 'prefix';
3380 infix('---', 130, function (left) {
3381 warn('confusing_a', token);
3383 this.second = expression(130);
3386 infix('*', 140, function (left, that) {
3387 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') {
3388 warn('unexpected_a', left);
3390 var right = expression(140);
3391 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') {
3392 warn('unexpected_a', right);
3394 if (left.id === right.id && left.id === '(number)') {
3395 left.number *= right.number;
3396 left.thru = right.thru;
3400 that.second = right;
3403 infix('/', 140, function (left, that) {
3404 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') {
3405 warn('unexpected_a', left);
3407 var right = expression(140);
3408 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') {
3409 warn('unexpected_a', right);
3411 if (left.id === right.id && left.id === '(number)') {
3412 left.number /= right.number;
3413 left.thru = right.thru;
3417 that.second = right;
3420 infix('%', 140, function (left, that) {
3421 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') {
3422 warn('unexpected_a', left);
3424 var right = expression(140);
3425 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') {
3426 warn('unexpected_a', right);
3428 if (left.id === right.id && left.id === '(number)') {
3429 left.number %= right.number;
3430 left.thru = right.thru;
3434 that.second = right;
3443 prefix('delete', function () {
3445 var p = expression(0);
3446 if (!p || (p.id !== '.' && p.id !== '[')) {
3454 prefix('~', function () {
3456 if (!option.bitwise) {
3457 warn('unexpected_a', this);
3462 prefix('!', function () {
3464 this.first = expected_condition(expression(150));
3465 this.arity = 'prefix';
3466 if (bang[this.first.id] === true || this.first.assign) {
3467 warn('confusing_a', this);
3471 prefix('typeof', null);
3472 prefix('new', function () {
3474 var c = expression(160), n, p, v;
3476 if (c.id !== 'function') {
3480 warn('use_object', token);
3483 if (next_token.id === '(') {
3487 if (next_token.id !== ')') {
3490 if (n.id !== '(number)' || next_token.id === ',') {
3491 warn('use_array', p);
3493 while (next_token.id === ',') {
3495 p.second.push(expression(0));
3498 warn('use_array', token);
3503 warn('use_array', token);
3510 warn('not_a_constructor', c);
3514 warn('function_eval');
3522 if (c.id !== 'function') {
3523 v = c.string.charAt(0);
3524 if (!option.newcap && (v < 'A' || v > 'Z')) {
3525 warn('constructor_name_a', token);
3530 if (c.id !== '.' && c.id !== '[' && c.id !== '(') {
3531 warn('bad_constructor', token);
3535 warn('weird_new', this);
3537 if (next_token.id !== '(') {
3538 warn('missing_a', next_token, '()');
3543 infix('(', 160, function (left, that) {
3545 if (indent && indent.mode === 'expression') {
3546 no_space(prev_token, token);
3548 no_space_only(prev_token, token);
3550 if (!left.immed && left.id === 'function') {
3551 warn('wrap_immediate');
3554 if (left.identifier) {
3555 if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
3556 if (left.string !== 'Number' && left.string !== 'String' &&
3557 left.string !== 'Boolean' && left.string !== 'Date') {
3558 if (left.string === 'Math' || left.string === 'JSON') {
3559 warn('not_a_function', left);
3560 } else if (left.string === 'Object') {
3561 warn('use_object', token);
3562 } else if (left.string === 'Array' || !option.newcap) {
3563 warn('missing_a', left, 'new');
3567 } else if (left.id === '.') {
3568 if (option.safe && left.first.string === 'Math' &&
3569 left.second === 'random') {
3570 warn('adsafe_a', left);
3571 } else if (left.second.string === 'split' &&
3572 left.first.id === '(string)') {
3573 warn('use_array', left.second);
3577 if (next_token.id !== ')') {
3581 p.push(expression(10));
3582 if (next_token.id !== ',') {
3589 step_out(')', that);
3590 if (typeof left === 'object') {
3591 if (left.string === 'parseInt' && p.length === 1) {
3592 warn('radix', left);
3595 if (left.string === 'eval' || left.string === 'Function' ||
3596 left.string === 'execScript') {
3598 } else if (p[0] && p[0].id === '(string)' &&
3599 (left.string === 'setTimeout' ||
3600 left.string === 'setInterval')) {
3601 warn('implied_evil', left);
3604 if (!left.identifier && left.id !== '.' && left.id !== '[' &&
3605 left.id !== '(' && left.id !== '&&' && left.id !== '||' &&
3607 warn('bad_invocation', left);
3615 prefix('(', function () {
3616 step_in('expression');
3619 if (next_token.id === 'function') {
3620 next_token.immed = true;
3622 var value = expression(0);
3625 step_out(')', this);
3626 if (value.id === 'function') {
3627 switch (next_token.id) {
3629 warn('move_invocation');
3633 warn('unexpected_a');
3636 warn('bad_wrap', this);
3642 infix('.', 170, function (left, that) {
3643 no_space(prev_token, token);
3645 var name = identifier();
3646 if (typeof name === 'string') {
3647 tally_property(name);
3650 that.second = token;
3651 if (left && left.string === 'arguments' &&
3652 (name === 'callee' || name === 'caller')) {
3653 warn('avoid_a', left, 'arguments.' + name);
3654 } else if (!option.evil && left && left.string === 'document' &&
3655 (name === 'write' || name === 'writeln')) {
3656 warn('write_is_wrong', left);
3657 } else if (option.adsafe) {
3658 if (!adsafe_top && left.string === 'ADSAFE') {
3659 if (name === 'id' || name === 'lib') {
3660 warn('adsafe_a', that);
3661 } else if (name === 'go') {
3662 if (xmode !== 'script') {
3663 warn('adsafe_a', that);
3664 } else if (adsafe_went || next_token.id !== '(' ||
3665 peek(0).id !== '(string)' ||
3666 peek(0).string !== adsafe_id ||
3667 peek(1).id !== ',') {
3668 stop('adsafe_a', that, 'go');
3676 if (!option.evil && (name === 'eval' || name === 'execScript')) {
3678 } else if (option.safe) {
3680 if (banned[name] === true) {
3681 warn('adsafe_a', token, name);
3683 if (typeof predefined[left.string] !== 'boolean' || //// check for writeable
3684 next_token.id === '(') {
3687 if (next_token.id !== '.') {
3688 warn('adsafe_a', that);
3693 token.second = name;
3695 name = identifier();
3696 if (typeof name === 'string') {
3697 tally_property(name);
3704 infix('[', 170, function (left, that) {
3706 no_space_only(prev_token, token);
3713 if (e.id === '(number)' && left.id === 'arguments') {
3714 warn('use_param', left);
3718 if (option.safe && (banned[e.string] ||
3719 e.string.charAt(0) === '_' || e.string.slice(-1) === '_')) {
3720 warn('adsafe_subscript_a', e);
3721 } else if (!option.evil &&
3722 (e.string === 'eval' || e.string === 'execScript')) {
3724 } else if (!option.sub && ix.test(e.string)) {
3725 s = syntax[e.string];
3726 if (!s || !s.reserved) {
3727 warn('subscript', e);
3730 tally_property(e.string);
3734 warn('adsafe_subscript_a', e);
3737 step_out(']', that);
3738 no_space(prev_token, token);
3744 prefix('[', function () {
3745 this.arity = 'prefix';
3748 while (next_token.id !== '(end)') {
3749 while (next_token.id === ',') {
3750 warn('unexpected_a', next_token);
3753 if (next_token.id === ']') {
3756 indent.wrap = false;
3758 this.first.push(expression(10));
3759 if (next_token.id === ',') {
3761 if (next_token.id === ']' && !option.es5) {
3762 warn('unexpected_a', token);
3769 step_out(']', this);
3774 function property_name() {
3775 var id = optional_identifier(true);
3777 if (next_token.id === '(string)') {
3778 id = next_token.string;
3782 } else if (id.charAt(0) === '_' ||
3783 id.charAt(id.length - 1) === '_') {
3788 } else if (next_token.id === '(number)') {
3789 id = next_token.number.toString();
3797 function function_params() {
3798 var id, paren = next_token, params = [];
3802 if (next_token.id === ')') {
3804 step_out(')', paren);
3811 add_label(token, option.unparam ? 'parameter' : 'unparam');
3812 if (next_token.id === ',') {
3816 step_out(')', paren);
3824 function do_function(func, name) {
3825 var old_funct = funct,
3826 old_option = option,
3829 '(name)' : name || '\'' + (anonname || '').replace(nx, sanitize) + '\'',
3830 '(line)' : next_token.line,
3831 '(context)' : old_funct,
3837 option = Object.create(old_option);
3838 scope = Object.create(old_scope);
3839 functions.push(funct);
3842 add_label(func, 'function', name);
3844 func.writeable = false;
3845 func.first = funct['(params)'] = function_params();
3847 func.block = block(false);
3848 if (funct['(arguments)']) {
3849 func.first.forEach(function (value) {
3851 warn('parameter_arguments_a', value, value.string);
3856 option = old_option;
3862 assignop('+=', '+');
3863 assignop('-=', '-');
3864 assignop('*=', '*');
3865 assignop('/=', '/').nud = function () {
3866 stop('slash_equal');
3868 assignop('%=', '%');
3869 assignop('&=', '&');
3870 assignop('|=', '|');
3871 assignop('^=', '^');
3872 assignop('<<=', '<<');
3873 assignop('>>=', '>>');
3874 assignop('>>>=', '>>>');
3877 prefix('{', function () {
3878 var get, i, j, name, p, set, seen = {};
3879 this.arity = 'prefix';
3882 while (next_token.id !== '}') {
3883 indent.wrap = false;
3885 // JSLint recognizes the ES5 extension for get/set in object literals,
3886 // but requires that they be used in pairs.
3889 if (next_token.string === 'get' && peek().id !== ':') {
3897 i = property_name();
3899 stop('missing_property');
3903 if (funct['(loopage)']) {
3904 warn('function_loop', get);
3908 warn('parameter_a_get_b', p[0], p[0].string, i);
3917 j = property_name();
3919 stop('expected_a_b', token, i, j || next_token.string);
3922 if (set.block.length === 0) {
3923 warn('missing_a', token, 'throw');
3926 if (!p || p.length !== 1) {
3927 stop('parameter_set_a', set, 'value');
3928 } else if (p[0].string !== 'value') {
3929 stop('expected_a_b', p[0], 'value', p[0].string);
3931 name.first = [get, set];
3934 i = property_name();
3935 if (typeof i !== 'string') {
3936 stop('missing_property');
3940 name.first = expression(10);
3942 this.first.push(name);
3943 if (seen[i] === true) {
3944 warn('duplicate_a', next_token, i);
3948 if (next_token.id !== ',') {
3953 if (next_token.id !== ',') {
3956 warn('unexpected_a', next_token);
3958 if (next_token.id === '}' && !option.es5) {
3959 warn('unexpected_a', token);
3962 step_out('}', this);
3966 stmt('{', function () {
3967 warn('statement_block');
3968 this.arity = 'statement';
3969 this.block = statements();
3970 this.disrupt = this.block.disrupt;
3975 stmt('/*global', directive);
3976 stmt('/*globals', directive);
3977 stmt('/*jslint', directive);
3978 stmt('/*member', directive);
3979 stmt('/*members', directive);
3980 stmt('/*property', directive);
3981 stmt('/*properties', directive);
3983 stmt('var', function () {
3985 // JavaScript does not have block scope. It only has function scope. So,
3986 // declaring a variable in a block can have unexpected consequences.
3988 // var.first will contain an array, the array containing name tokens
3989 // and assignment tokens.
3991 var assign, id, name;
3993 if (funct['(vars)'] && !option.vars) {
3994 warn('combine_var');
3995 } else if (funct !== global_funct) {
3996 funct['(vars)'] = true;
3998 this.arity = 'statement';
4004 add_label(name, 'becoming');
4006 if (next_token.id === '=') {
4007 assign = next_token;
4008 assign.first = name;
4012 if (next_token.id === 'undefined') {
4013 warn('unnecessary_initialize', token, id);
4015 if (peek(0).id === '=' && next_token.identifier) {
4018 assign.second = expression(0);
4019 assign.arity = 'infix';
4020 this.first.push(assign);
4022 this.first.push(name);
4024 if (funct[id] === 'becoming') {
4025 funct[id] = 'unused';
4027 if (next_token.id !== ',') {
4031 indent.wrap = false;
4032 if (var_mode && next_token.line === token.line &&
4033 this.first.length === 1) {
4035 indent.open = false;
4036 indent.at -= option.indent;
4046 stmt('function', function () {
4049 warn('function_block', token);
4051 var name = next_token, id = identifier();
4052 add_label(name, 'unction');
4054 this.arity = 'statement';
4055 do_function(this, id);
4056 if (next_token.id === '(' && next_token.line === token.line) {
4057 stop('function_statement');
4062 prefix('function', function () {
4066 var id = optional_identifier();
4072 do_function(this, id);
4073 if (funct['(loopage)']) {
4074 warn('function_loop');
4076 switch (next_token.id) {
4086 if (peek().string !== 'bind' || peek(1).id !== '(') {
4087 warn('unexpected_a');
4091 stop('unexpected_a');
4093 this.arity = 'function';
4097 stmt('if', function () {
4098 var paren = next_token;
4104 this.arity = 'statement';
4105 this.first = expected_condition(expected_relation(expression(0)));
4107 step_out(')', paren);
4109 this.block = block(true);
4110 if (next_token.id === 'else') {
4114 this['else'] = next_token.id === 'if' || next_token.id === 'switch'
4117 if (this['else'].disrupt && this.block.disrupt) {
4118 this.disrupt = true;
4124 stmt('try', function () {
4126 // try.first The catch variable
4127 // try.second The catch clause
4128 // try.third The finally clause
4129 // try.block The try block
4131 var exception_variable, old_scope, paren;
4132 if (option.adsafe) {
4133 warn('adsafe_a', this);
4136 this.arity = 'statement';
4137 this.block = block(false);
4138 if (next_token.id === 'catch') {
4148 scope = Object.create(old_scope);
4149 exception_variable = next_token.string;
4150 this.first = exception_variable;
4151 if (!next_token.identifier) {
4152 warn('expected_identifier_a', next_token);
4154 add_label(next_token, 'exception');
4158 step_out(')', paren);
4160 this.second = block(false);
4163 if (next_token.id === 'finally') {
4167 this.third = block(false);
4168 } else if (!this.second) {
4169 stop('expected_a_b', next_token, 'catch', artifact());
4174 labeled_stmt('while', function () {
4176 var paren = next_token;
4177 funct['(breakage)'] += 1;
4178 funct['(loopage)'] += 1;
4183 this.arity = 'statement';
4184 this.first = expected_relation(expression(0));
4185 if (this.first.id !== 'true') {
4186 expected_condition(this.first, bundle.unexpected_a);
4189 step_out(')', paren);
4191 this.block = block(true);
4192 if (this.block.disrupt) {
4193 warn('strange_loop', prev_token);
4195 funct['(breakage)'] -= 1;
4196 funct['(loopage)'] -= 1;
4202 labeled_stmt('switch', function () {
4204 // switch.first the switch expression
4205 // switch.second the array of cases. A case is 'case' or 'default' token:
4206 // case.first the array of case expressions
4207 // case.second the array of statements
4208 // If all of the arrays of statements are disrupt, then the switch is disrupt.
4211 old_in_block = in_block,
4213 the_case = next_token,
4216 function find_duplicate_case(value) {
4217 if (are_similar(particular, value)) {
4218 warn('duplicate_a', value);
4222 funct['(breakage)'] += 1;
4227 this.arity = 'statement';
4228 this.first = expected_condition(expected_relation(expression(0)));
4230 step_out(')', the_case);
4236 while (next_token.id === 'case') {
4237 the_case = next_token;
4238 cases.forEach(find_duplicate_case);
4239 the_case.first = [];
4240 the_case.arity = 'case';
4246 particular = expression(0);
4247 cases.forEach(find_duplicate_case);
4248 cases.push(particular);
4249 the_case.first.push(particular);
4250 if (particular.id === 'NaN') {
4251 warn('unexpected_a', particular);
4255 if (next_token.id !== 'case') {
4263 the_case.second = statements();
4264 if (the_case.second && the_case.second.length > 0) {
4265 particular = the_case.second[the_case.second.length - 1];
4266 if (particular.disrupt) {
4267 if (particular.id === 'break') {
4271 warn('missing_a_after_b', next_token, 'break', 'case');
4276 this.second.push(the_case);
4278 if (this.second.length === 0) {
4279 warn('missing_a', next_token, 'case');
4281 if (next_token.id === 'default') {
4283 the_case = next_token;
4284 the_case.arity = 'case';
4290 the_case.second = statements();
4291 if (the_case.second && the_case.second.length > 0) {
4292 particular = the_case.second[the_case.second.length - 1];
4293 if (unbroken && particular.disrupt && particular.id !== 'break') {
4294 this.disrupt = true;
4297 this.second.push(the_case);
4299 funct['(breakage)'] -= 1;
4301 step_out('}', this);
4302 in_block = old_in_block;
4306 stmt('debugger', function () {
4307 if (!option.debug) {
4308 warn('unexpected_a', this);
4310 this.arity = 'statement';
4314 labeled_stmt('do', function () {
4315 funct['(breakage)'] += 1;
4316 funct['(loopage)'] += 1;
4318 this.arity = 'statement';
4319 this.block = block(true);
4320 if (this.block.disrupt) {
4321 warn('strange_loop', prev_token);
4325 var paren = next_token;
4331 this.first = expected_condition(expected_relation(expression(0)), bundle.unexpected_a);
4333 step_out(')', paren);
4334 funct['(breakage)'] -= 1;
4335 funct['(loopage)'] -= 1;
4339 labeled_stmt('for', function () {
4341 var blok, filter, ok = false, paren = next_token, value;
4342 this.arity = 'statement';
4343 funct['(breakage)'] += 1;
4344 funct['(loopage)'] += 1;
4346 if (next_token.id === ';') {
4356 spaces(this, paren);
4358 if (next_token.id === 'var') {
4362 if (peek(0).id === 'in') {
4365 switch (funct[value.string]) {
4367 funct[value.string] = 'var';
4373 warn('bad_in_a', value);
4378 this.second = expression(20);
4379 step_out(')', paren);
4381 if (!option.forin) {
4382 if (blok.length === 1 && typeof blok[0] === 'object' &&
4383 blok[0].string === 'if' && !blok[0]['else']) {
4384 filter = blok[0].first;
4385 while (filter.id === '&&') {
4386 filter = filter.first;
4388 switch (filter.id) {
4391 ok = filter.first.id === '['
4392 ? filter.first.first.string === this.second.string &&
4393 filter.first.second.string === this.first.string
4394 : filter.first.id === 'typeof' &&
4395 filter.first.first.id === '[' &&
4396 filter.first.first.first.string === this.second.string &&
4397 filter.first.first.second.string === this.first.string;
4400 ok = filter.first.id === '.' && ((
4401 filter.first.first.string === this.second.string &&
4402 filter.first.second.string === 'hasOwnProperty' &&
4403 filter.second[0].string === this.first.string
4405 filter.first.first.string === 'ADSAFE' &&
4406 filter.first.second.string === 'has' &&
4407 filter.second[0].string === this.second.string &&
4408 filter.second[1].string === this.first.string
4410 filter.first.first.id === '.' &&
4411 filter.first.first.first.id === '.' &&
4412 filter.first.first.first.first.string === 'Object' &&
4413 filter.first.first.first.second.string === 'prototype' &&
4414 filter.first.first.second.string === 'hasOwnProperty' &&
4415 filter.first.second.string === 'call' &&
4416 filter.second[0].string === this.second.string &&
4417 filter.second[1].string === this.first.string
4423 warn('for_if', this);
4430 this.first.push(expression(0, 'for'));
4431 if (next_token.id !== ',') {
4438 this.second = expected_relation(expression(0));
4439 if (this.second.id !== 'true') {
4440 expected_condition(this.second, bundle.unexpected_a);
4443 if (next_token.id === ';') {
4444 stop('expected_a_b', next_token, ')', ';');
4449 this.third.push(expression(0, 'for'));
4450 if (next_token.id !== ',') {
4456 step_out(')', paren);
4462 warn('strange_loop', prev_token);
4465 funct['(breakage)'] -= 1;
4466 funct['(loopage)'] -= 1;
4470 disrupt_stmt('break', function () {
4471 var label = next_token.string;
4472 this.arity = 'statement';
4473 if (funct['(breakage)'] === 0) {
4474 warn('unexpected_a', this);
4476 if (next_token.identifier && token.line === next_token.line) {
4478 if (funct[label] !== 'label') {
4479 warn('not_a_label', next_token);
4480 } else if (scope[label].funct !== funct) {
4481 warn('not_a_scope', next_token);
4483 this.first = next_token;
4489 disrupt_stmt('continue', function () {
4490 if (!option['continue']) {
4491 warn('unexpected_a', this);
4493 var label = next_token.string;
4494 this.arity = 'statement';
4495 if (funct['(breakage)'] === 0) {
4496 warn('unexpected_a', this);
4498 if (next_token.identifier && token.line === next_token.line) {
4500 if (funct[label] !== 'label') {
4501 warn('not_a_label', next_token);
4502 } else if (scope[label].funct !== funct) {
4503 warn('not_a_scope', next_token);
4505 this.first = next_token;
4511 disrupt_stmt('return', function () {
4512 if (funct === global_funct && xmode !== 'scriptstring') {
4513 warn('unexpected_a', this);
4515 this.arity = 'statement';
4516 if (next_token.id !== ';' && next_token.line === token.line) {
4518 if (next_token.id === '/' || next_token.id === '(regexp)') {
4519 warn('wrap_regexp');
4521 this.first = expression(20);
4523 if (peek(0).id === '}' && peek(1).id === 'else') {
4524 warn('unexpected_else', this);
4529 disrupt_stmt('throw', function () {
4530 this.arity = 'statement';
4532 this.first = expression(20);
4537 // Superfluous reserved words
4547 // Harmony reserved words
4549 reserve('implements');
4550 reserve('interface');
4554 reserve('protected');
4562 function json_value() {
4564 function json_object() {
4565 var brace = next_token, object = {};
4567 if (next_token.id !== '}') {
4568 while (next_token.id !== '(end)') {
4569 while (next_token.id === ',') {
4570 warn('unexpected_a', next_token);
4573 if (next_token.id !== '(string)') {
4574 warn('expected_string_a');
4576 if (object[next_token.string] === true) {
4577 warn('duplicate_a');
4578 } else if (next_token.string === '__proto__') {
4581 object[next_token.string] = true;
4586 if (next_token.id !== ',') {
4590 if (next_token.id === '}') {
4591 warn('unexpected_a', token);
4596 advance('}', brace);
4599 function json_array() {
4600 var bracket = next_token;
4602 if (next_token.id !== ']') {
4603 while (next_token.id !== '(end)') {
4604 while (next_token.id === ',') {
4605 warn('unexpected_a', next_token);
4609 if (next_token.id !== ',') {
4613 if (next_token.id === ']') {
4614 warn('unexpected_a', token);
4619 advance(']', bracket);
4622 switch (next_token.id) {
4639 advance('(number)');
4642 stop('unexpected_a');
4649 function css_name() {
4650 if (next_token.identifier) {
4657 function css_number() {
4658 if (next_token.id === '-') {
4662 if (next_token.id === '(number)') {
4663 advance('(number)');
4669 function css_string() {
4670 if (next_token.id === '(string)') {
4676 function css_color() {
4677 var i, number, paren, value;
4678 if (next_token.identifier) {
4679 value = next_token.string;
4680 if (value === 'rgb' || value === 'rgba') {
4684 for (i = 0; i < 3; i += 1) {
4688 number = next_token.number;
4689 if (next_token.id !== '(number)' || number < 0) {
4690 warn('expected_positive_a', next_token);
4694 if (next_token.id === '%') {
4697 warn('expected_percent_a', token, number);
4701 warn('expected_small_a', token, number);
4706 if (value === 'rgba') {
4708 number = next_token.number;
4709 if (next_token.id !== '(number)' || number < 0 || number > 1) {
4710 warn('expected_fraction_a', next_token);
4713 if (next_token.id === '%') {
4714 warn('unexpected_a');
4718 advance(')', paren);
4721 if (css_colorData[next_token.string] === true) {
4725 } else if (next_token.id === '(color)') {
4733 function css_length() {
4734 if (next_token.id === '-') {
4738 if (next_token.id === '(number)') {
4740 if (next_token.id !== '(string)' &&
4741 css_lengthData[next_token.string] === true) {
4744 } else if (+token.number !== 0) {
4745 warn('expected_linear_a');
4753 function css_line_height() {
4754 if (next_token.id === '-') {
4758 if (next_token.id === '(number)') {
4760 if (next_token.id !== '(string)' &&
4761 css_lengthData[next_token.string] === true) {
4771 function css_width() {
4772 if (next_token.identifier) {
4773 switch (next_token.string) {
4781 return css_length();
4786 function css_margin() {
4787 if (next_token.identifier) {
4788 if (next_token.string === 'auto') {
4793 return css_length();
4797 function css_attr() {
4798 if (next_token.identifier && next_token.string === 'attr') {
4801 if (!next_token.identifier) {
4802 warn('expected_name_a');
4812 function css_comma_list() {
4813 while (next_token.id !== ';') {
4814 if (!css_name() && !css_string()) {
4815 warn('expected_name_a');
4817 if (next_token.id !== ',') {
4825 function css_counter() {
4826 if (next_token.identifier && next_token.string === 'counter') {
4830 if (next_token.id === ',') {
4832 if (next_token.id !== '(string)') {
4833 warn('expected_string_a');
4840 if (next_token.identifier && next_token.string === 'counters') {
4843 if (!next_token.identifier) {
4844 warn('expected_name_a');
4847 if (next_token.id === ',') {
4849 if (next_token.id !== '(string)') {
4850 warn('expected_string_a');
4854 if (next_token.id === ',') {
4856 if (next_token.id !== '(string)') {
4857 warn('expected_string_a');
4868 function css_radius() {
4869 return css_length() && (next_token.id !== '(number)' || css_length());
4873 function css_shape() {
4875 if (next_token.identifier && next_token.string === 'rect') {
4878 for (i = 0; i < 4; i += 1) {
4879 if (!css_length()) {
4880 warn('expected_number_a');
4891 function css_url() {
4893 if (next_token.identifier && next_token.string === 'url') {
4894 next_token = lex.range('(', ')');
4895 url = next_token.string;
4897 if (c === '"' || c === '\'') {
4898 if (url.slice(-1) !== c) {
4901 url = url.slice(1, -1);
4902 if (url.indexOf(c) >= 0) {
4908 warn('missing_url');
4921 css_any = [css_url, function () {
4923 if (next_token.identifier) {
4924 switch (next_token.string.toLowerCase()) {
4929 warn('unexpected_a');
4936 if (next_token.id === ';' || next_token.id === '!' ||
4937 next_token.id === '(end)' || next_token.id === '}') {
4946 function font_face() {
4947 advance_identifier('font-family');
4949 if (!css_name() && !css_string()) {
4950 stop('expected_name_a');
4953 advance_identifier('src');
4956 if (next_token.string === 'local') {
4957 advance_identifier('local');
4959 if (ux.test(next_token.string)) {
4963 if (!css_name() && !css_string()) {
4964 stop('expected_name_a');
4967 } else if (!css_url()) {
4968 stop('expected_a_b', next_token, 'url', artifact());
4970 if (next_token.id !== ',') {
4979 css_border_style = [
4980 'none', 'dashed', 'dotted', 'double', 'groove',
4981 'hidden', 'inset', 'outset', 'ridge', 'solid'
4985 'auto', 'always', 'avoid', 'left', 'right'
5002 'auto', 'hidden', 'scroll', 'visible'
5005 css_attribute_data = {
5007 true, 'background-attachment', 'background-color',
5008 'background-image', 'background-position', 'background-repeat'
5010 'background-attachment': ['scroll', 'fixed'],
5011 'background-color': ['transparent', css_color],
5012 'background-image': ['none', css_url],
5013 'background-position': [
5014 2, [css_length, 'top', 'bottom', 'left', 'right', 'center']
5016 'background-repeat': [
5017 'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
5019 'border': [true, 'border-color', 'border-style', 'border-width'],
5021 true, 'border-bottom-color', 'border-bottom-style',
5022 'border-bottom-width'
5024 'border-bottom-color': css_color,
5025 'border-bottom-left-radius': css_radius,
5026 'border-bottom-right-radius': css_radius,
5027 'border-bottom-style': css_border_style,
5028 'border-bottom-width': css_width,
5029 'border-collapse': ['collapse', 'separate'],
5030 'border-color': ['transparent', 4, css_color],
5032 true, 'border-left-color', 'border-left-style', 'border-left-width'
5034 'border-left-color': css_color,
5035 'border-left-style': css_border_style,
5036 'border-left-width': css_width,
5037 'border-radius': function () {
5038 function count(separator) {
5043 if (!css_length()) {
5046 while (next_token.id === '(number)') {
5047 if (!css_length()) {
5058 return count() && (next_token.id !== '/' || count('/'));
5061 true, 'border-right-color', 'border-right-style',
5062 'border-right-width'
5064 'border-right-color': css_color,
5065 'border-right-style': css_border_style,
5066 'border-right-width': css_width,
5067 'border-spacing': [2, css_length],
5068 'border-style': [4, css_border_style],
5070 true, 'border-top-color', 'border-top-style', 'border-top-width'
5072 'border-top-color': css_color,
5073 'border-top-left-radius': css_radius,
5074 'border-top-right-radius': css_radius,
5075 'border-top-style': css_border_style,
5076 'border-top-width': css_width,
5077 'border-width': [4, css_width],
5078 bottom: [css_length, 'auto'],
5079 'caption-side' : ['bottom', 'left', 'right', 'top'],
5080 clear: ['both', 'left', 'none', 'right'],
5081 clip: [css_shape, 'auto'],
5084 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote',
5085 css_string, css_url, css_counter, css_attr
5087 'counter-increment': [
5094 css_url, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move',
5095 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize',
5096 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait'
5098 direction: ['ltr', 'rtl'],
5100 'block', 'compact', 'inline', 'inline-block', 'inline-table',
5101 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption',
5102 'table-cell', 'table-column', 'table-column-group',
5103 'table-footer-group', 'table-header-group', 'table-row',
5106 'empty-cells': ['show', 'hide'],
5107 'float': ['left', 'none', 'right'],
5109 'caption', 'icon', 'menu', 'message-box', 'small-caption',
5110 'status-bar', true, 'font-size', 'font-style', 'font-weight',
5113 'font-family': css_comma_list,
5115 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large',
5116 'xx-large', 'larger', 'smaller', css_length
5118 'font-size-adjust': ['none', css_number],
5120 'normal', 'wider', 'narrower', 'ultra-condensed',
5121 'extra-condensed', 'condensed', 'semi-condensed',
5122 'semi-expanded', 'expanded', 'extra-expanded'
5125 'normal', 'italic', 'oblique'
5128 'normal', 'small-caps'
5131 'normal', 'bold', 'bolder', 'lighter', css_number
5133 height: [css_length, 'auto'],
5134 left: [css_length, 'auto'],
5135 'letter-spacing': ['normal', css_length],
5136 'line-height': ['normal', css_line_height],
5138 true, 'list-style-image', 'list-style-position', 'list-style-type'
5140 'list-style-image': ['none', css_url],
5141 'list-style-position': ['inside', 'outside'],
5142 'list-style-type': [
5143 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero',
5144 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha',
5145 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana',
5146 'hiragana-iroha', 'katakana-oroha', 'none'
5148 margin: [4, css_margin],
5149 'margin-bottom': css_margin,
5150 'margin-left': css_margin,
5151 'margin-right': css_margin,
5152 'margin-top': css_margin,
5153 'marker-offset': [css_length, 'auto'],
5154 'max-height': [css_length, 'none'],
5155 'max-width': [css_length, 'none'],
5156 'min-height': css_length,
5157 'min-width': css_length,
5158 opacity: css_number,
5159 outline: [true, 'outline-color', 'outline-style', 'outline-width'],
5160 'outline-color': ['invert', css_color],
5162 'dashed', 'dotted', 'double', 'groove', 'inset', 'none',
5163 'outset', 'ridge', 'solid'
5165 'outline-width': css_width,
5166 overflow: css_overflow,
5167 'overflow-x': css_overflow,
5168 'overflow-y': css_overflow,
5169 padding: [4, css_length],
5170 'padding-bottom': css_length,
5171 'padding-left': css_length,
5172 'padding-right': css_length,
5173 'padding-top': css_length,
5174 'page-break-after': css_break,
5175 'page-break-before': css_break,
5176 position: ['absolute', 'fixed', 'relative', 'static'],
5177 quotes: [8, css_string],
5178 right: [css_length, 'auto'],
5179 'table-layout': ['auto', 'fixed'],
5180 'text-align': ['center', 'justify', 'left', 'right'],
5181 'text-decoration': [
5182 'none', 'underline', 'overline', 'line-through', 'blink'
5184 'text-indent': css_length,
5185 'text-shadow': ['none', 4, [css_color, css_length]],
5186 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'],
5187 top: [css_length, 'auto'],
5188 'unicode-bidi': ['normal', 'embed', 'bidi-override'],
5190 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle',
5191 'text-bottom', css_length
5193 visibility: ['visible', 'hidden', 'collapse'],
5195 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit'
5197 width: [css_length, 'auto'],
5198 'word-spacing': ['normal', css_length],
5199 'word-wrap': ['break-word', 'normal'],
5200 'z-index': ['auto', css_number]
5203 function style_attribute() {
5205 while (next_token.id === '*' || next_token.id === '#' ||
5206 next_token.string === '_') {
5208 warn('unexpected_a');
5212 if (next_token.id === '-') {
5214 warn('unexpected_a');
5217 if (!next_token.identifier) {
5218 warn('expected_nonstandard_style_attribute');
5223 if (!next_token.identifier) {
5224 warn('expected_style_attribute');
5226 if (Object.prototype.hasOwnProperty.call(css_attribute_data,
5227 next_token.string)) {
5228 v = css_attribute_data[next_token.string];
5232 warn('unrecognized_style_attribute_a');
5241 function style_value(v) {
5253 if (next_token.identifier && next_token.string === v) {
5260 if (i >= v.length) {
5265 if (typeof vi === 'boolean') {
5267 } else if (typeof vi === 'number') {
5276 if (style_value(vi)) {
5291 for (i = start; i < v.length; i += 1) {
5293 if (style_value(css_attribute_data[v[i]])) {
5307 function style_child() {
5308 if (next_token.id === '(number)') {
5310 if (next_token.string === 'n' && next_token.identifier) {
5313 if (next_token.id === '+') {
5317 advance('(number)');
5322 if (next_token.identifier &&
5323 (next_token.string === 'odd' || next_token.string === 'even')) {
5327 warn('unexpected_a');
5330 function substyle() {
5333 if (next_token.id === '}' || next_token.id === '(end)' ||
5334 (xquote && next_token.id === xquote)) {
5337 v = style_attribute();
5339 if (next_token.identifier && next_token.string === 'inherit') {
5342 if (!style_value(v)) {
5343 warn('unexpected_a');
5347 if (next_token.id === '!') {
5350 if (next_token.identifier && next_token.string === 'important') {
5353 warn('expected_a_b',
5354 next_token, 'important', artifact());
5357 if (next_token.id === '}' || next_token.id === xquote) {
5358 warn('expected_a_b', next_token, ';', artifact());
5365 function style_selector() {
5366 if (next_token.identifier) {
5367 if (!Object.prototype.hasOwnProperty.call(html_tag, option.cap
5368 ? next_token.string.toLowerCase()
5369 : next_token.string)) {
5370 warn('expected_tagname_a');
5374 switch (next_token.id) {
5382 switch (next_token.string) {
5391 case 'first-letter':
5393 case 'first-of-type':
5397 case 'last-of-type':
5399 case 'only-of-type':
5403 advance_identifier(next_token.string);
5406 advance_identifier('lang');
5408 if (!next_token.identifier) {
5409 warn('expected_lang_a');
5414 case 'nth-last-child':
5415 case 'nth-last-of-type':
5417 advance_identifier(next_token.string);
5423 advance_identifier('not');
5425 if (next_token.id === ':' && peek(0).string === 'not') {
5432 warn('expected_pseudo_a');
5437 if (!next_token.identifier) {
5438 warn('expected_id_a');
5447 if (!next_token.identifier) {
5448 warn('expected_class_a');
5454 if (!next_token.identifier) {
5455 warn('expected_attribute_a');
5458 if (next_token.id === '=' || next_token.string === '~=' ||
5459 next_token.string === '$=' ||
5460 next_token.string === '|=' ||
5461 next_token.id === '*=' ||
5462 next_token.id === '^=') {
5464 if (next_token.id !== '(string)') {
5465 warn('expected_string_a');
5472 stop('expected_selector_a');
5477 function style_pattern() {
5478 if (next_token.id === '{') {
5479 warn('expected_style_pattern');
5483 if (next_token.id === '</' || next_token.id === '{' ||
5484 next_token.id === '}' || next_token.id === '(end)') {
5487 if (next_token.id === ',') {
5493 function style_list() {
5494 while (next_token.id !== '}' && next_token.id !== '</' &&
5495 next_token.id !== '(end)') {
5497 xmode = 'styleproperty';
5498 if (next_token.id === ';') {
5511 while (next_token.id === '@') {
5514 switch (next_token.string) {
5516 advance_identifier('import');
5518 warn('expected_a_b',
5519 next_token, 'url', artifact());
5525 advance_identifier('media');
5527 if (!next_token.identifier || css_media[next_token.string] !== true) {
5528 stop('expected_media_a');
5531 if (next_token.id !== ',') {
5541 advance_identifier('font-face');
5547 stop('expected_at_a');
5556 function do_begin(n) {
5557 if (n !== 'html' && !option.fragment) {
5558 if (n === 'div' && option.adsafe) {
5559 stop('adsafe_fragment');
5561 stop('expected_a_b', token, 'html', n);
5564 if (option.adsafe) {
5566 stop('adsafe_html', token);
5568 if (option.fragment) {
5570 stop('adsafe_div', token);
5573 stop('adsafe_fragment', token);
5576 option.browser = true;
5579 function do_attribute(a, v) {
5582 u = typeof v === 'string' ? v.toUpperCase() : '';
5583 if (ids[u] === true) {
5584 warn('duplicate_a', next_token, v);
5586 if (!/^[A-Za-z][A-Za-z0-9._:\-]*$/.test(v)) {
5587 warn('bad_id_a', next_token, v);
5588 } else if (option.adsafe) {
5590 if (v.slice(0, adsafe_id.length) !== adsafe_id) {
5591 warn('adsafe_prefix_a', next_token, adsafe_id);
5592 } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {
5593 warn('adsafe_bad_id');
5597 if (!/^[A-Z]+_$/.test(v)) {
5598 warn('adsafe_bad_id');
5604 warn('unexpected_char_a_b', token, v.charAt(x), a);
5607 } else if (a === 'class' || a === 'type' || a === 'name') {
5610 warn('unexpected_char_a_b', token, v.charAt(x), a);
5613 } else if (a === 'href' || a === 'background' ||
5614 a === 'content' || a === 'data' ||
5615 a.indexOf('src') >= 0 || a.indexOf('url') >= 0) {
5616 if (option.safe && ux.test(v)) {
5617 stop('bad_url_a', next_token, v);
5620 } else if (a === 'for') {
5621 if (option.adsafe) {
5623 if (v.slice(0, adsafe_id.length) !== adsafe_id) {
5624 warn('adsafe_prefix_a', next_token, adsafe_id);
5625 } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {
5626 warn('adsafe_bad_id');
5629 warn('adsafe_bad_id');
5632 } else if (a === 'name') {
5633 if (option.adsafe && v.indexOf('_') >= 0) {
5634 warn('adsafe_name_a', next_token, v);
5639 function do_tag(name, attribute) {
5640 var i, tag = html_tag[name], script, x;
5644 bundle.unrecognized_tag_a,
5646 name === name.toLowerCase()
5648 : name + ' (capitalization error)'
5651 if (stack.length > 0) {
5652 if (name === 'html') {
5653 stop('unexpected_a', token, name);
5657 if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) {
5658 stop('tag_a_in_b', token, name, x);
5660 } else if (!option.adsafe && !option.fragment) {
5664 stop('tag_a_in_b', token, name, 'body');
5667 } while (stack[i].name !== 'body');
5672 if (option.adsafe && stack.length === 1 && !adsafe_id) {
5673 warn('adsafe_missing_id');
5679 if (attribute.lang) {
5680 warn('lang', token);
5682 if (option.adsafe && stack.length !== 1) {
5683 warn('adsafe_placement', token);
5685 if (attribute.src) {
5686 if (option.adsafe && (!adsafe_may || !approved[attribute.src])) {
5687 warn('adsafe_source', token);
5690 step_in(next_token.from);
5694 script = statements();
5696 // JSLint is also the static analyzer for ADsafe. See www.ADsafe.org.
5698 if (option.adsafe) {
5700 stop('adsafe_script', token);
5702 if (script.length !== 1 ||
5703 aint(script[0], 'id', '(') ||
5704 aint(script[0].first, 'id', '.') ||
5705 aint(script[0].first.first, 'string', 'ADSAFE') ||
5706 aint(script[0].second[0], 'string', adsafe_id)) {
5707 stop('adsafe_id_go');
5709 switch (script[0].first.second.string) {
5711 if (adsafe_may || adsafe_went ||
5712 script[0].second.length !== 1) {
5713 stop('adsafe_id', next_token);
5721 if (script[0].second.length !== 2 ||
5722 aint(script[0].second[1], 'id', 'function') ||
5723 !script[0].second[1].first ||
5724 aint(script[0].second[1].first[0], 'string', 'dom') ||
5725 script[0].second[1].first.length > 2 ||
5726 (script[0].second[1].first.length === 2 &&
5727 aint(script[0].second[1].first[1], 'string', 'lib'))) {
5728 stop('adsafe_go', next_token);
5733 stop('adsafe_id_go');
5740 advance_identifier('script');
5749 advance_identifier('style');
5752 switch (attribute.type) {
5764 if (option.adsafe && attribute.autocomplete !== 'off') {
5765 warn('adsafe_autocomplete');
5783 if (option.adsafe) {
5784 warn('adsafe_tag', next_token, name);
5791 function closetag(name) {
5792 return '</' + name + '>';
5796 var attribute, attributes, is_empty, name, old_white = option.white,
5797 quote, tag_name, tag, wmode;
5802 switch (next_token.string) {
5807 tag_name = next_token;
5808 name = tag_name.string;
5809 advance_identifier(name);
5811 name = name.toLowerCase();
5813 tag_name.name = name;
5818 tag = html_tag[name];
5819 if (typeof tag !== 'object') {
5820 stop('unrecognized_tag_a', tag_name, name);
5822 is_empty = tag.empty;
5823 tag_name.type = name;
5825 if (next_token.id === '/') {
5827 if (next_token.id !== '>') {
5828 warn('expected_a_b', next_token, '>', artifact());
5832 if (next_token.id && next_token.id.charAt(0) === '>') {
5835 if (!next_token.identifier) {
5836 if (next_token.id === '(end)' || next_token.id === '(error)') {
5837 warn('expected_a_b', next_token, '>', artifact());
5841 option.white = false;
5843 attribute = next_token.string;
5844 option.white = old_white;
5846 if (!option.cap && attribute !== attribute.toLowerCase()) {
5847 warn('attribute_case_a', token);
5849 attribute = attribute.toLowerCase();
5851 if (Object.prototype.hasOwnProperty.call(attributes, attribute)) {
5852 warn('duplicate_a', token, attribute);
5854 if (attribute.slice(0, 2) === 'on') {
5856 warn('html_handlers');
5858 xmode = 'scriptstring';
5860 quote = next_token.id;
5861 if (quote !== '"' && quote !== '\'') {
5862 stop('expected_a_b', next_token, '"', artifact());
5865 wmode = option.white;
5866 option.white = true;
5870 option.white = wmode;
5871 if (next_token.id !== quote) {
5872 stop('expected_a_b', next_token, quote, artifact());
5878 } else if (attribute === 'style') {
5879 xmode = 'scriptstring';
5881 quote = next_token.id;
5882 if (quote !== '"' && quote !== '\'') {
5883 stop('expected_a_b', next_token, '"', artifact());
5885 xmode = 'styleproperty';
5894 if (next_token.id === '=') {
5896 tag = next_token.string;
5897 if (!next_token.identifier &&
5898 next_token.id !== '"' &&
5899 next_token.id !== '\'' &&
5900 next_token.id !== '(string)' &&
5901 next_token.id !== '(string)' &&
5902 next_token.id !== '(color)') {
5903 warn('expected_attribute_value_a', token, attribute);
5910 attributes[attribute] = tag;
5911 do_attribute(attribute, tag);
5913 do_tag(name, attributes);
5915 stack.push(tag_name);
5923 if (!next_token.identifier) {
5926 name = next_token.string;
5928 name = name.toLowerCase();
5932 stop('unexpected_a', next_token, closetag(name));
5934 tag_name = stack.pop();
5936 stop('unexpected_a', next_token, closetag(name));
5938 if (tag_name.name !== name) {
5939 stop('expected_a_b',
5940 next_token, closetag(tag_name.name), closetag(name));
5942 if (next_token.id !== '>') {
5943 stop('expected_a_b', next_token, '>', artifact());
5955 if (next_token.id === '>' || next_token.id === '(end)') {
5958 if (next_token.string.indexOf('--') >= 0) {
5959 stop('unexpected_a', next_token, '--');
5961 if (next_token.string.indexOf('<') >= 0) {
5962 stop('unexpected_a', next_token, '<');
5964 if (next_token.string.indexOf('>') >= 0) {
5965 stop('unexpected_a', next_token, '>');
5972 if (stack.length !== 0) {
5973 warn('missing_a', next_token, '</' + stack.pop().string + '>');
5977 if (next_token.id === '(end)') {
5978 stop('missing_a', next_token,
5979 '</' + stack[stack.length - 1].string + '>');
5984 if (stack && stack.length === 0 && (option.adsafe ||
5985 !option.fragment || next_token.id === '(end)')) {
5989 if (next_token.id !== '(end)') {
5990 stop('unexpected_a');
5995 // The actual JSLINT function itself.
5997 itself = function JSLint(the_source, the_option) {
5999 var i, predef, tree;
6002 begin = prev_token = token = next_token =
6003 Object.create(syntax['(begin)']);
6005 add_to_predefined(standard);
6008 option = Object.create(the_option);
6009 predef = option.predef;
6011 if (Array.isArray(predef)) {
6012 for (i = 0; i < predef.length; i += 1) {
6013 predefined[predef[i]] = true;
6015 } else if (typeof predef === 'object') {
6016 add_to_predefined(predef);
6023 option.indent = +option.indent || 4;
6024 option.maxerr = +option.maxerr || 50;
6026 adsafe_may = adsafe_top = adsafe_went = false;
6028 if (option.approved) {
6029 for (i = 0; i < option.approved.length; i += 1) {
6030 approved[option.approved[i]] = option.approved[i];
6033 approved.test = 'test';
6036 for (i = 0; i < option.indent; i += 1) {
6039 global_scope = scope = {};
6040 global_funct = funct = {
6045 functions = [funct];
6047 comments_off = false;
6057 strict_mode = false;
6062 lex.init(the_source);
6068 if (next_token.id === '(number)') {
6069 stop('unexpected_a');
6070 } else if (next_token.string.charAt(0) === '<') {
6072 if (option.adsafe && !adsafe_went) {
6073 warn('adsafe_go', this);
6076 switch (next_token.id) {
6089 if (token.id !== '@' || !next_token.identifier ||
6090 next_token.string !== 'charset' || token.line !== 1 ||
6095 if (next_token.id !== '(string)' &&
6096 next_token.string !== 'UTF-8') {
6105 if (option.adsafe && option.fragment) {
6106 stop('expected_a_b',
6107 next_token, '<div>', artifact());
6110 // If the first token is a semicolon, ignore it. This is sometimes used when
6111 // files are intended to be appended to files that may be sloppy. A sloppy
6112 // file may be depending on semicolon insertion on its last line.
6115 if (next_token.id === ';' && !node_js) {
6119 tree = statements();
6121 JSLINT.tree = begin;
6122 // infer_types(tree);
6123 if (option.adsafe && (tree.length !== 1 ||
6124 aint(tree[0], 'id', '(') ||
6125 aint(tree[0].first, 'id', '.') ||
6126 aint(tree[0].first.first, 'string', 'ADSAFE') ||
6127 aint(tree[0].first.second, 'string', 'lib') ||
6128 tree[0].second.length !== 2 ||
6129 tree[0].second[0].id !== '(string)' ||
6130 aint(tree[0].second[1], 'id', 'function'))) {
6134 warn('weird_program', prev_token);
6142 JSLINT.errors.push({
6144 line : e.line || next_token.line,
6145 character : e.character || next_token.from
6149 return JSLINT.errors.length === 0;
6155 itself.data = function () {
6156 var data = {functions: []},
6167 if (itself.errors.length) {
6168 data.errors = itself.errors;
6175 if (urls.length > 0) {
6179 globals = Object.keys(global_scope).filter(function (value) {
6180 return value.charAt(0) !== '(' && typeof standard[value] !== 'boolean';
6182 if (globals.length > 0) {
6183 data.globals = globals;
6186 for (i = 1; i < functions.length; i += 1) {
6187 the_function = functions[i];
6189 for (j = 0; j < functionicity.length; j += 1) {
6190 function_data[functionicity[j]] = [];
6192 for (name in the_function) {
6193 if (Object.prototype.hasOwnProperty.call(the_function, name)) {
6194 if (name.charAt(0) !== '(') {
6195 kind = the_function[name];
6196 if (kind === 'unction' || kind === 'unparam') {
6199 if (Array.isArray(function_data[kind])) {
6200 function_data[kind].push(name);
6201 if (kind === 'unused') {
6204 line: the_function['(line)'],
6205 'function': the_function['(name)']
6207 } else if (kind === 'undef') {
6210 line: the_function['(line)'],
6211 'function': the_function['(name)']
6218 for (j = 0; j < functionicity.length; j += 1) {
6219 if (function_data[functionicity[j]].length === 0) {
6220 delete function_data[functionicity[j]];
6223 function_data.name = the_function['(name)'];
6224 function_data.params = the_function['(params)'];
6225 function_data.line = the_function['(line)'];
6226 data.functions.push(function_data);
6229 if (unused.length > 0) {
6230 data.unused = unused;
6232 if (undef.length > 0) {
6233 data['undefined'] = undef;
6237 for (name in property) {
6238 if (typeof property[name] === 'number') {
6239 data.member = property;
6248 itself.report = function (errors_only) {
6249 var data = itself.data(), err, evidence, i, italics, j, key, keys,
6250 length, mem = '', name, names, not_first, output = [], snippets,
6251 the_function, warning;
6253 function detail(h, value) {
6254 var comma_needed, singularity;
6255 if (Array.isArray(value)) {
6256 output.push('<div><i>' + h + '</i> ');
6257 value.sort().forEach(function (item) {
6258 if (item !== singularity) {
6260 output.push((comma_needed ? ', ' : '') + singularity);
6261 comma_needed = true;
6264 output.push('</div>');
6266 output.push('<div><i>' + h + '</i> ' + value + '</div>');
6270 if (data.errors || data.unused || data['undefined']) {
6272 output.push('<div id=errors><i>Error:</i>');
6274 for (i = 0; i < data.errors.length; i += 1) {
6275 warning = data.errors[i];
6277 evidence = warning.evidence || '';
6278 output.push('<p>Problem' + (isFinite(warning.line)
6279 ? ' at line ' + String(warning.line) +
6280 ' character ' + String(warning.character)
6282 ': ' + warning.reason.entityify() +
6283 '</p><p class=evidence>' +
6284 (evidence && (evidence.length > 80
6285 ? evidence.slice(0, 77) + '...'
6286 : evidence).entityify()) + '</p>');
6291 if (data['undefined']) {
6293 for (i = 0; i < data['undefined'].length; i += 1) {
6294 snippets[i] = '<code><u>' + data['undefined'][i].name + '</u></code> <i>' +
6295 String(data['undefined'][i].line) + ' </i> <small>' +
6296 data['undefined'][i]['function'] + '</small>';
6298 output.push('<p><i>Undefined variable:</i> ' + snippets.join(', ') + '</p>');
6302 for (i = 0; i < data.unused.length; i += 1) {
6303 snippets[i] = '<code><u>' + data.unused[i].name + '</u></code> <i>' +
6304 String(data.unused[i].line) + ' </i> <small>' +
6305 data.unused[i]['function'] + '</small>';
6307 output.push('<p><i>Unused variable:</i> ' + snippets.join(', ') + '</p>');
6310 output.push('<p>JSON: bad.</p>');
6312 output.push('</div>');
6317 output.push('<br><div id=functions>');
6320 detail("URLs<br>", data.urls, '<br>');
6323 if (xmode === 'style') {
6324 output.push('<p>CSS.</p>');
6325 } else if (data.json && !err) {
6326 output.push('<p>JSON: good.</p>');
6327 } else if (data.globals) {
6328 output.push('<div><i>Global</i> ' +
6329 data.globals.sort().join(', ') + '</div>');
6331 output.push('<div><i>No new global variables introduced.</i></div>');
6334 for (i = 0; i < data.functions.length; i += 1) {
6335 the_function = data.functions[i];
6337 if (the_function.params) {
6338 for (j = 0; j < the_function.params.length; j += 1) {
6339 names[j] = the_function.params[j].string;
6342 output.push('<br><div class=function><i>' +
6343 String(the_function.line) + '</i> ' +
6344 the_function.name.entityify() +
6345 '(' + names.join(', ') + ')</div>');
6346 detail('<big><b>Undefined</b></big>', the_function['undefined']);
6347 detail('<big><b>Unused</b></big>', the_function.unused);
6348 detail('Closure', the_function.closure);
6349 detail('Variable', the_function['var']);
6350 detail('Exception', the_function.exception);
6351 detail('Outer', the_function.outer);
6352 detail('Global', the_function.global);
6353 detail('Label', the_function.label);
6357 keys = Object.keys(data.member);
6360 output.push('<br><pre id=properties>/*properties<br>');
6365 for (i = 0; i < keys.length; i += 1) {
6367 if (data.member[key] > 0) {
6373 : '\'' + key.entityify().replace(nx, sanitize) + '\'';
6374 length += name.length + 2;
6375 if (data.member[key] === 1) {
6376 name = '<i>' + name + '</i>';
6380 if (mem.length + name.length - (italics * 7) > 80) {
6381 output.push(mem + '<br>');
6390 output.push(mem + '<br>*/</pre>');
6392 output.push('</div>');
6395 return output.join('');
6397 itself.jslint = itself;
6399 itself.edition = '2012-03-02';