]> git.r.bdr.sh - rbdr/dotfiles/blame - vim/ftplugin/javascript/jslint/jslint-core.js
Update prezto
[rbdr/dotfiles] / vim / ftplugin / javascript / jslint / jslint-core.js
CommitLineData
32081fee
BB
1// jslint.js
2// 2012-02-23
3
4// Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
5
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:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// The Software shall be used for Good, not Evil.
17
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
24// SOFTWARE.
25
26// WARNING: JSLint will hurt your feelings.
27
28// JSLINT is a global function. It takes two parameters.
29
30// var myResult = JSLINT(source, option);
31
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.
36
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.
43
44// If it checks out, JSLINT returns true. Otherwise, it returns false.
45
46// If false, you can inspect JSLINT.errors to find out the problems.
47// JSLINT.errors is an array of objects containing these properties:
48
49// {
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
59// }
60
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.
65
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>.
70
71// var myReport = JSLINT.report(errors_only);
72
73// If errors_only is true, then the report will be limited to only errors.
74
75// You can request a data structure that contains JSLint's results.
76
77// var myData = JSLINT.data();
78
79// It returns a structure with this form:
80
81// {
82// errors: [
83// {
84// line: NUMBER,
85// character: NUMBER,
86// reason: STRING,
87// evidence: STRING
88// }
89// ],
90// functions: [
91// {
92// name: STRING,
93// line: NUMBER,
94// last: NUMBER,
95// params: [
96// {
97// string: STRING
98// }
99// ],
100// closure: [
101// STRING
102// ],
103// var: [
104// STRING
105// ],
106// exception: [
107// STRING
108// ],
109// outer: [
110// STRING
111// ],
112// unused: [
113// STRING
114// ],
115// undef: [
116// STRING
117// ],
118// global: [
119// STRING
120// ],
121// label: [
122// STRING
123// ]
124// }
125// ],
126// globals: [
127// STRING
128// ],
129// member: {
130// STRING: NUMBER
131// },
132// urls: [
133// STRING
134// ],
135// json: BOOLEAN
136// }
137
138// Empty arrays will not be included.
139
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
142// with
143
144// JSON.stringify(JSLINT.tree, [
145// 'string', 'arity', 'name', 'first',
146// 'second', 'third', 'block', 'else'
147// ], 4));
148
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.
152
153// These directives respect function scope.
154
155// The jslint directive is a special comment that can set one or more options.
156// The current option set is
157
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
191
192// For example:
193
194/*jslint
195 evil: true, nomen: true, regexp: true
196*/
197
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.
201
202// For example:
203
204/*properties
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'
299*/
300
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.
304
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.
309
310var JSLINT = (function () {
311 'use strict';
312
313 function array_to_object(array, value) {
314
315// Make an object from an array of keys and a common value.
316
317 var i, length = array.length, object = {};
318 for (i = 0; i < length; i += 1) {
319 object[array[i]] = value;
320 }
321 return object;
322 }
323
324
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.
329 allowed_option = {
330 anon : true,
331 bitwise : true,
332 browser : true,
333 cap : true,
334 'continue': true,
335 css : true,
336 debug : true,
337 devel : true,
338 eqeq : true,
339 es5 : true,
340 evil : true,
341 forin : true,
342 fragment : true,
343 indent : 10,
344 maxerr : 1000,
345 maxlen : 256,
346 newcap : true,
347 node : true,
348 nomen : true,
349 on : true,
350 passfail : true,
351 plusplus : true,
352 properties: true,
353 regexp : true,
354 rhino : true,
355 undef : true,
356 unparam : true,
357 sloppy : true,
358 sub : true,
359 vars : true,
360 white : true,
361 widget : true,
362 windows : true
363 },
364 anonname, // The guessed name for anonymous functions.
365 approved, // ADsafe approved urls.
366
367// These are operators that should not be used with the ! operator.
368
369 bang = {
370 '<' : true,
371 '<=' : true,
372 '==' : true,
373 '===': true,
374 '!==': true,
375 '!=' : true,
376 '>' : true,
377 '>=' : true,
378 '+' : true,
379 '-' : true,
380 '*' : true,
381 '/' : true,
382 '%' : true
383 },
384
385// These are property names that should not be permitted in the safe subset.
386
387 banned = array_to_object([
388 'arguments', 'callee', 'caller', 'constructor', 'eval', 'prototype',
389 'stack', 'unwatch', 'valueOf', 'watch'
390 ], true),
391 begin, // The root token
392
393// browser contains a set of global names that are commonly provided by a
394// web browser environment.
395
396 browser = array_to_object([
397 'clearInterval', 'clearTimeout', 'document', 'event', 'frames',
398 'history', 'Image', 'localStorage', 'location', 'name', 'navigator',
399 'Option', 'parent', 'screen', 'sessionStorage', 'setInterval',
400 'setTimeout', 'Storage', 'window', 'XMLHttpRequest'
401 ], false),
402
403// bundle contains the text messages.
404
405 bundle = {
406 a_label: "'{a}' is a statement label.",
407 a_not_allowed: "'{a}' is not allowed.",
408 a_not_defined: "'{a}' is not defined.",
409 a_scope: "'{a}' used out of scope.",
410 adsafe_a: "ADsafe violation: '{a}'.",
411 adsafe_autocomplete: "ADsafe autocomplete violation.",
412 adsafe_bad_id: "ADSAFE violation: bad id.",
413 adsafe_div: "ADsafe violation: Wrap the widget in a div.",
414 adsafe_fragment: "ADSAFE: Use the fragment option.",
415 adsafe_go: "ADsafe violation: Misformed ADSAFE.go.",
416 adsafe_html: "Currently, ADsafe does not operate on whole HTML " +
417 "documents. It operates on <div> fragments and .js files.",
418 adsafe_id: "ADsafe violation: id does not match.",
419 adsafe_id_go: "ADsafe violation: Missing ADSAFE.id or ADSAFE.go.",
420 adsafe_lib: "ADsafe lib violation.",
421 adsafe_lib_second: "ADsafe: The second argument to lib must be a function.",
422 adsafe_missing_id: "ADSAFE violation: missing ID_.",
423 adsafe_name_a: "ADsafe name violation: '{a}'.",
424 adsafe_placement: "ADsafe script placement violation.",
425 adsafe_prefix_a: "ADsafe violation: An id must have a '{a}' prefix",
426 adsafe_script: "ADsafe script violation.",
427 adsafe_source: "ADsafe unapproved script source.",
428 adsafe_subscript_a: "ADsafe subscript '{a}'.",
429 adsafe_tag: "ADsafe violation: Disallowed tag '{a}'.",
430 already_defined: "'{a}' is already defined.",
431 and: "The '&&' subexpression should be wrapped in parens.",
432 assign_exception: "Do not assign to the exception parameter.",
433 assignment_function_expression: "Expected an assignment or " +
434 "function call and instead saw an expression.",
435 attribute_case_a: "Attribute '{a}' not all lower case.",
436 avoid_a: "Avoid '{a}'.",
437 bad_assignment: "Bad assignment.",
438 bad_color_a: "Bad hex color '{a}'.",
439 bad_constructor: "Bad constructor.",
440 bad_entity: "Bad entity.",
441 bad_html: "Bad HTML string",
442 bad_id_a: "Bad id: '{a}'.",
443 bad_in_a: "Bad for in variable '{a}'.",
444 bad_invocation: "Bad invocation.",
445 bad_name_a: "Bad name: '{a}'.",
446 bad_new: "Do not use 'new' for side effects.",
447 bad_number: "Bad number '{a}'.",
448 bad_operand: "Bad operand.",
449 bad_style: "Bad style.",
450 bad_type: "Bad type.",
451 bad_url_a: "Bad url '{a}'.",
452 bad_wrap: "Do not wrap function literals in parens unless they " +
453 "are to be immediately invoked.",
454 combine_var: "Combine this with the previous 'var' statement.",
455 conditional_assignment: "Expected a conditional expression and " +
456 "instead saw an assignment.",
457 confusing_a: "Confusing use of '{a}'.",
458 confusing_regexp: "Confusing regular expression.",
459 constructor_name_a: "A constructor name '{a}' should start with " +
460 "an uppercase letter.",
461 control_a: "Unexpected control character '{a}'.",
462 css: "A css file should begin with @charset 'UTF-8';",
463 dangling_a: "Unexpected dangling '_' in '{a}'.",
464 dangerous_comment: "Dangerous comment.",
465 deleted: "Only properties should be deleted.",
466 duplicate_a: "Duplicate '{a}'.",
467 empty_block: "Empty block.",
468 empty_case: "Empty case.",
469 empty_class: "Empty class.",
470 es5: "This is an ES5 feature.",
471 evil: "eval is evil.",
472 expected_a: "Expected '{a}'.",
473 expected_a_b: "Expected '{a}' and instead saw '{b}'.",
474 expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " +
475 "{c} and instead saw '{d}'.",
476 expected_at_a: "Expected an at-rule, and instead saw @{a}.",
477 expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
478 expected_attribute_a: "Expected an attribute, and instead saw [{a}].",
479 expected_attribute_value_a: "Expected an attribute value and " +
480 "instead saw '{a}'.",
481 expected_class_a: "Expected a class, and instead saw .{a}.",
482 expected_fraction_a: "Expected a number between 0 and 1 and " +
483 "instead saw '{a}'",
484 expected_id_a: "Expected an id, and instead saw #{a}.",
485 expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
486 expected_identifier_a_reserved: "Expected an identifier and " +
487 "instead saw '{a}' (a reserved word).",
488 expected_linear_a: "Expected a linear unit and instead saw '{a}'.",
489 expected_lang_a: "Expected a lang code, and instead saw :{a}.",
490 expected_media_a: "Expected a CSS media type, and instead saw '{a}'.",
491 expected_name_a: "Expected a name and instead saw '{a}'.",
492 expected_nonstandard_style_attribute: "Expected a non-standard " +
493 "style attribute and instead saw '{a}'.",
494 expected_number_a: "Expected a number and instead saw '{a}'.",
495 expected_operator_a: "Expected an operator and instead saw '{a}'.",
496 expected_percent_a: "Expected a percentage and instead saw '{a}'",
497 expected_positive_a: "Expected a positive number and instead saw '{a}'",
498 expected_pseudo_a: "Expected a pseudo, and instead saw :{a}.",
499 expected_selector_a: "Expected a CSS selector, and instead saw {a}.",
500 expected_small_a: "Expected a small positive integer and instead saw '{a}'",
501 expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.",
502 expected_string_a: "Expected a string and instead saw {a}.",
503 expected_style_attribute: "Excepted a style attribute, and instead saw '{a}'.",
504 expected_style_pattern: "Expected a style pattern, and instead saw '{a}'.",
505 expected_tagname_a: "Expected a tagName, and instead saw {a}.",
506 expected_type_a: "Expected a type, and instead saw {a}.",
507 for_if: "The body of a for in should be wrapped in an if " +
508 "statement to filter unwanted properties from the prototype.",
509 function_block: "Function statements should not be placed in blocks. " +
510 "Use a function expression or move the statement to the top of " +
511 "the outer function.",
512 function_eval: "The Function constructor is eval.",
513 function_loop: "Don't make functions within a loop.",
514 function_statement: "Function statements are not invocable. " +
515 "Wrap the whole function invocation in parens.",
516 function_strict: "Use the function form of 'use strict'.",
517 html_confusion_a: "HTML confusion in regular expression '<{a}'.",
518 html_handlers: "Avoid HTML event handlers.",
519 identifier_function: "Expected an identifier in an assignment " +
520 "and instead saw a function invocation.",
521 implied_evil: "Implied eval is evil. Pass a function instead of a string.",
522 infix_in: "Unexpected 'in'. Compare with undefined, or use the " +
523 "hasOwnProperty method instead.",
524 insecure_a: "Insecure '{a}'.",
525 isNaN: "Use the isNaN function to compare with NaN.",
526 label_a_b: "Label '{a}' on '{b}' statement.",
527 lang: "lang is deprecated.",
528 leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.",
529 missing_a: "Missing '{a}'.",
530 missing_a_after_b: "Missing '{a}' after '{b}'.",
531 missing_option: "Missing option value.",
532 missing_property: "Missing property name.",
533 missing_space_a_b: "Missing space between '{a}' and '{b}'.",
534 missing_url: "Missing url.",
535 missing_use_strict: "Missing 'use strict' statement.",
536 mixed: "Mixed spaces and tabs.",
537 move_invocation: "Move the invocation into the parens that " +
538 "contain the function.",
539 move_var: "Move 'var' declarations to the top of the function.",
540 name_function: "Missing name in function statement.",
541 nested_comment: "Nested comment.",
542 not: "Nested not.",
543 not_a_constructor: "Do not use {a} as a constructor.",
544 not_a_defined: "'{a}' has not been fully defined yet.",
545 not_a_function: "'{a}' is not a function.",
546 not_a_label: "'{a}' is not a label.",
547 not_a_scope: "'{a}' is out of scope.",
548 not_greater: "'{a}' should not be greater than '{b}'.",
549 octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.",
550 parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.",
551 parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.",
552 parameter_set_a: "Expected parameter (value) in set {a} function.",
553 radix: "Missing radix parameter.",
554 read_only: "Read only.",
555 redefinition_a: "Redefinition of '{a}'.",
556 reserved_a: "Reserved name '{a}'.",
557 scanned_a_b: "{a} ({b}% scanned).",
558 slash_equal: "A regular expression literal can be confused with '/='.",
559 statement_block: "Expected to see a statement and instead saw a block.",
560 stopping: "Stopping. ",
561 strange_loop: "Strange loop.",
562 strict: "Strict violation.",
563 subscript: "['{a}'] is better written in dot notation.",
564 tag_a_in_b: "A '<{a}>' must be within '<{b}>'.",
565 too_long: "Line too long.",
566 too_many: "Too many errors.",
567 trailing_decimal_a: "A trailing decimal point can be confused " +
568 "with a dot: '.{a}'.",
569 type: "type is unnecessary.",
570 unclosed: "Unclosed string.",
571 unclosed_comment: "Unclosed comment.",
572 unclosed_regexp: "Unclosed regular expression.",
573 unescaped_a: "Unescaped '{a}'.",
574 unexpected_a: "Unexpected '{a}'.",
575 unexpected_char_a_b: "Unexpected character '{a}' in {b}.",
576 unexpected_comment: "Unexpected comment.",
577 unexpected_else: "Unexpected 'else' after 'return'.",
578 unexpected_property_a: "Unexpected /*property*/ '{a}'.",
579 unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
580 unnecessary_initialize: "It is not necessary to initialize '{a}' " +
581 "to 'undefined'.",
582 unnecessary_use: "Unnecessary 'use strict'.",
583 unreachable_a_b: "Unreachable '{a}' after '{b}'.",
584 unrecognized_style_attribute_a: "Unrecognized style attribute '{a}'.",
585 unrecognized_tag_a: "Unrecognized tag '<{a}>'.",
586 unsafe: "Unsafe character.",
587 url: "JavaScript URL.",
588 use_array: "Use the array literal notation [].",
589 use_braces: "Spaces are hard to count. Use {{a}}.",
590 use_charAt: "Use the charAt method.",
591 use_object: "Use the object literal notation {}.",
592 use_or: "Use the || operator.",
593 use_param: "Use a named parameter.",
594 used_before_a: "'{a}' was used before it was defined.",
595 var_a_not: "Variable {a} was not declared correctly.",
596 weird_assignment: "Weird assignment.",
597 weird_condition: "Weird condition.",
598 weird_new: "Weird construction. Delete 'new'.",
599 weird_program: "Weird program.",
600 weird_relation: "Weird relation.",
601 weird_ternary: "Weird ternary.",
602 wrap_immediate: "Wrap an immediate function invocation in parentheses " +
603 "to assist the reader in understanding that the expression " +
604 "is the result of a function, and not the function itself.",
605 wrap_regexp: "Wrap the /regexp/ literal in parens to " +
606 "disambiguate the slash operator.",
607 write_is_wrong: "document.write can be a form of eval."
608 },
609 comments_off,
610 css_attribute_data,
611 css_any,
612
613 css_colorData = array_to_object([
614 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
615 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
616 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral",
617 "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue",
618 "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki",
619 "darkmagenta", "darkolivegreen", "darkorange", "darkorchid",
620 "darkred", "darksalmon", "darkseagreen", "darkslateblue",
621 "darkslategray", "darkturquoise", "darkviolet", "deeppink",
622 "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite",
623 "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold",
624 "goldenrod", "gray", "green", "greenyellow", "honeydew", "hotpink",
625 "indianred", "indigo", "ivory", "khaki", "lavender",
626 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
627 "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgreen",
628 "lightpink", "lightsalmon", "lightseagreen", "lightskyblue",
629 "lightslategray", "lightsteelblue", "lightyellow", "lime",
630 "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
631 "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
632 "mediumslateblue", "mediumspringgreen", "mediumturquoise",
633 "mediumvioletred", "midnightblue", "mintcream", "mistyrose",
634 "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
635 "orange", "orangered", "orchid", "palegoldenrod", "palegreen",
636 "paleturquoise", "palevioletred", "papayawhip", "peachpuff",
637 "peru", "pink", "plum", "powderblue", "purple", "red", "rosybrown",
638 "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen",
639 "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray",
640 "snow", "springgreen", "steelblue", "tan", "teal", "thistle",
641 "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke",
642 "yellow", "yellowgreen",
643
644 "activeborder", "activecaption", "appworkspace", "background",
645 "buttonface", "buttonhighlight", "buttonshadow", "buttontext",
646 "captiontext", "graytext", "highlight", "highlighttext",
647 "inactiveborder", "inactivecaption", "inactivecaptiontext",
648 "infobackground", "infotext", "menu", "menutext", "scrollbar",
649 "threeddarkshadow", "threedface", "threedhighlight",
650 "threedlightshadow", "threedshadow", "window", "windowframe",
651 "windowtext"
652 ], true),
653
654 css_border_style,
655 css_break,
656
657 css_lengthData = {
658 '%': true,
659 'cm': true,
660 'em': true,
661 'ex': true,
662 'in': true,
663 'mm': true,
664 'pc': true,
665 'pt': true,
666 'px': true
667 },
668
669 css_media,
670 css_overflow,
671
672 descapes = {
673 'b': '\b',
674 't': '\t',
675 'n': '\n',
676 'f': '\f',
677 'r': '\r',
678 '"': '"',
679 '/': '/',
680 '\\': '\\'
681 },
682
683 devel = array_to_object([
684 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH'
685 ], false),
686 directive,
687 escapes = {
688 '\b': '\\b',
689 '\t': '\\t',
690 '\n': '\\n',
691 '\f': '\\f',
692 '\r': '\\r',
693 '\'': '\\\'',
694 '"' : '\\"',
695 '/' : '\\/',
696 '\\': '\\\\'
697 },
698
699 funct, // The current function, including the labels used in
700 // the function, as well as (breakage),
701 // (context), (loopage), (name), (params), (token),
702 // (vars), (verb)
703
704 functionicity = [
705 'closure', 'exception', 'global', 'label', 'outer', 'undef',
706 'unused', 'var'
707 ],
708
709 functions, // All of the functions
710 global_funct, // The global body
711 global_scope, // The global scope
712 html_tag = {
713 a: {},
714 abbr: {},
715 acronym: {},
716 address: {},
717 applet: {},
718 area: {empty: true, parent: ' map '},
719 article: {},
720 aside: {},
721 audio: {},
722 b: {},
723 base: {empty: true, parent: ' head '},
724 bdo: {},
725 big: {},
726 blockquote: {},
727 body: {parent: ' html noframes '},
728 br: {empty: true},
729 button: {},
730 canvas: {parent: ' body p div th td '},
731 caption: {parent: ' table '},
732 center: {},
733 cite: {},
734 code: {},
735 col: {empty: true, parent: ' table colgroup '},
736 colgroup: {parent: ' table '},
737 command: {parent: ' menu '},
738 datalist: {},
739 dd: {parent: ' dl '},
740 del: {},
741 details: {},
742 dialog: {},
743 dfn: {},
744 dir: {},
745 div: {},
746 dl: {},
747 dt: {parent: ' dl '},
748 em: {},
749 embed: {},
750 fieldset: {},
751 figure: {},
752 font: {},
753 footer: {},
754 form: {},
755 frame: {empty: true, parent: ' frameset '},
756 frameset: {parent: ' html frameset '},
757 h1: {},
758 h2: {},
759 h3: {},
760 h4: {},
761 h5: {},
762 h6: {},
763 head: {parent: ' html '},
764 header: {},
765 hgroup: {},
766 hr: {empty: true},
767 'hta:application':
768 {empty: true, parent: ' head '},
769 html: {parent: '*'},
770 i: {},
771 iframe: {},
772 img: {empty: true},
773 input: {empty: true},
774 ins: {},
775 kbd: {},
776 keygen: {},
777 label: {},
778 legend: {parent: ' details fieldset figure '},
779 li: {parent: ' dir menu ol ul '},
780 link: {empty: true, parent: ' head '},
781 map: {},
782 mark: {},
783 menu: {},
784 meta: {empty: true, parent: ' head noframes noscript '},
785 meter: {},
786 nav: {},
787 noframes: {parent: ' html body '},
788 noscript: {parent: ' body head noframes '},
789 object: {},
790 ol: {},
791 optgroup: {parent: ' select '},
792 option: {parent: ' optgroup select '},
793 output: {},
794 p: {},
795 param: {empty: true, parent: ' applet object '},
796 pre: {},
797 progress: {},
798 q: {},
799 rp: {},
800 rt: {},
801 ruby: {},
802 samp: {},
803 script: {empty: true, parent: ' body div frame head iframe p pre span '},
804 section: {},
805 select: {},
806 small: {},
807 span: {},
808 source: {},
809 strong: {},
810 style: {parent: ' head ', empty: true},
811 sub: {},
812 sup: {},
813 table: {},
814 tbody: {parent: ' table '},
815 td: {parent: ' tr '},
816 textarea: {},
817 tfoot: {parent: ' table '},
818 th: {parent: ' tr '},
819 thead: {parent: ' table '},
820 time: {},
821 title: {parent: ' head '},
822 tr: {parent: ' table tbody thead tfoot '},
823 tt: {},
824 u: {},
825 ul: {},
826 'var': {},
827 video: {}
828 },
829
830 ids, // HTML ids
831 in_block,
832 indent,
833 itself, // JSLint itself
834 json_mode,
835 lex, // the tokenizer
836 lines,
837 lookahead,
838 node = array_to_object([
839 'Buffer', 'clearInterval', 'clearTimeout', 'console', 'exports',
840 'global', 'module', 'process', 'querystring', 'require',
841 'setInterval', 'setTimeout', '__dirname', '__filename'
842 ], false),
843 node_js,
844 numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true),
845 next_token,
846 option,
847 predefined, // Global variables defined by option
848 prereg,
849 prev_token,
850 property,
851 regexp_flag = array_to_object(['g', 'i', 'm'], true),
852 return_this = function return_this() {
853 return this;
854 },
855 rhino = array_to_object([
856 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass',
857 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal',
858 'serialize', 'spawn', 'sync', 'toint32', 'version'
859 ], false),
860
861 scope, // An object containing an object for each variable in scope
862 semicolon_coda = array_to_object([';', '"', '\'', ')'], true),
863 src,
864 stack,
865
866// standard contains the global names that are provided by the
867// ECMAScript standard.
868
869 standard = array_to_object([
870 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
871 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError',
872 'Function', 'isFinite', 'isNaN', 'JSON', 'Math', 'Number',
873 'Object', 'parseInt', 'parseFloat', 'RangeError', 'ReferenceError',
874 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError'
875 ], false),
876
877 strict_mode,
878 syntax = {},
879 tab,
880 token,
881 urls,
882 var_mode,
883 warnings,
884
885// widget contains the global names which are provided to a Yahoo
886// (fna Konfabulator) widget.
887
888 widget = array_to_object([
889 'alert', 'animator', 'appleScript', 'beep', 'bytesToUIString',
890 'Canvas', 'chooseColor', 'chooseFile', 'chooseFolder',
891 'closeWidget', 'COM', 'convertPathToHFS', 'convertPathToPlatform',
892 'CustomAnimation', 'escape', 'FadeAnimation', 'filesystem', 'Flash',
893 'focusWidget', 'form', 'FormField', 'Frame', 'HotKey', 'Image',
894 'include', 'isApplicationRunning', 'iTunes', 'konfabulatorVersion',
895 'log', 'md5', 'MenuItem', 'MoveAnimation', 'openURL', 'play',
896 'Point', 'popupMenu', 'preferenceGroups', 'preferences', 'print',
897 'prompt', 'random', 'Rectangle', 'reloadWidget', 'ResizeAnimation',
898 'resolvePath', 'resumeUpdates', 'RotateAnimation', 'runCommand',
899 'runCommandInBg', 'saveAs', 'savePreferences', 'screen',
900 'ScrollBar', 'showWidgetPreferences', 'sleep', 'speak', 'Style',
901 'suppressUpdates', 'system', 'tellWidget', 'Text', 'TextArea',
902 'Timer', 'unescape', 'updateNow', 'URL', 'Web', 'widget', 'Window',
903 'XMLDOM', 'XMLHttpRequest', 'yahooCheckLogin', 'yahooLogin',
904 'yahooLogout'
905 ], true),
906
907 windows = array_to_object([
908 'ActiveXObject', 'CScript', 'Debug', 'Enumerator', 'System',
909 'VBArray', 'WScript', 'WSH'
910 ], false),
911
912// xmode is used to adapt to the exceptions in html parsing.
913// It can have these states:
914// '' .js script file
915// 'html'
916// 'outer'
917// 'script'
918// 'style'
919// 'scriptstring'
920// 'styleproperty'
921
922 xmode,
923 xquote,
924
925// Regular expressions. Some of these are stupidly long.
926
927// unsafe comment or string
928 ax = /@cc|<\/?|script|\]\s*\]|<\s*!|&lt/i,
929// carriage return, carriage return linefeed, or linefeed
930 crlfx = /\r\n?|\n/,
931// unsafe characters that are silently deleted by one or more browsers
932 cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
933// query characters for ids
934 dx = /[\[\]\/\\"'*<>.&:(){}+=#]/,
935// html token
936 hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/,
937// identifier
938 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
939// javascript url
940 jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i,
941// star slash
942 lx = /\*\/|\/\*/,
943// characters in strings that need escapement
944 nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
945// outer html token
946 ox = /[>&]|<[\/!]?|--/,
947// attributes characters
948 qx = /[^a-zA-Z0-9+\-_\/ ]/,
949// style
950 sx = /^\s*([{}:#%.=,>+\[\]@()"';]|[*$\^~]=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/,
951 ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/,
952// token
953 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]+)?)/,
954// url badness
955 ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto|script/i,
956
957 rx = {
958 outer: hx,
959 html: hx,
960 style: sx,
961 styleproperty: ssx
962 };
963
964
965 function F() {} // Used by Object.create
966
967// Provide critical ES5 functions to ES3.
968
969 if (typeof Array.prototype.filter !== 'function') {
970 Array.prototype.filter = function (f) {
971 var i, length = this.length, result = [], value;
972 for (i = 0; i < length; i += 1) {
973 try {
974 value = this[i];
975 if (f(value)) {
976 result.push(value);
977 }
978 } catch (ignore) {
979 }
980 }
981 return result;
982 };
983 }
984
985 if (typeof Array.prototype.forEach !== 'function') {
986 Array.prototype.forEach = function (f) {
987 var i, length = this.length;
988 for (i = 0; i < length; i += 1) {
989 try {
990 f(this[i]);
991 } catch (ignore) {
992 }
993 }
994 };
995 }
996
997 if (typeof Array.isArray !== 'function') {
998 Array.isArray = function (o) {
999 return Object.prototype.toString.apply(o) === '[object Array]';
1000 };
1001 }
1002
1003 if (!Object.prototype.hasOwnProperty.call(Object, 'create')) {
1004 Object.create = function (o) {
1005 F.prototype = o;
1006 return new F();
1007 };
1008 }
1009
1010 if (typeof Object.keys !== 'function') {
1011 Object.keys = function (o) {
1012 var array = [], key;
1013 for (key in o) {
1014 if (Object.prototype.hasOwnProperty.call(o, key)) {
1015 array.push(key);
1016 }
1017 }
1018 return array;
1019 };
1020 }
1021
1022 if (typeof String.prototype.entityify !== 'function') {
1023 String.prototype.entityify = function () {
1024 return this
1025 .replace(/&/g, '&amp;')
1026 .replace(/</g, '&lt;')
1027 .replace(/>/g, '&gt;');
1028 };
1029 }
1030
1031 if (typeof String.prototype.isAlpha !== 'function') {
1032 String.prototype.isAlpha = function () {
1033 return (this >= 'a' && this <= 'z\uffff') ||
1034 (this >= 'A' && this <= 'Z\uffff');
1035 };
1036 }
1037
1038 if (typeof String.prototype.isDigit !== 'function') {
1039 String.prototype.isDigit = function () {
1040 return (this >= '0' && this <= '9');
1041 };
1042 }
1043
1044 if (typeof String.prototype.supplant !== 'function') {
1045 String.prototype.supplant = function (o) {
1046 return this.replace(/\{([^{}]*)\}/g, function (a, b) {
1047 var replacement = o[b];
1048 return typeof replacement === 'string' ||
1049 typeof replacement === 'number' ? replacement : a;
1050 });
1051 };
1052 }
1053
1054
1055 function sanitize(a) {
1056
1057// Escapify a troublesome character.
1058
1059 return escapes[a] ||
1060 '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
1061 }
1062
1063
1064 function add_to_predefined(group) {
1065 Object.keys(group).forEach(function (name) {
1066 predefined[name] = group[name];
1067 });
1068 }
1069
1070
1071 function assume() {
1072 if (!option.safe) {
1073 if (option.rhino) {
1074 add_to_predefined(rhino);
1075 option.rhino = false;
1076 }
1077 if (option.devel) {
1078 add_to_predefined(devel);
1079 option.devel = false;
1080 }
1081 if (option.browser) {
1082 add_to_predefined(browser);
1083 option.browser = false;
1084 }
1085 if (option.windows) {
1086 add_to_predefined(windows);
1087 option.windows = false;
1088 }
1089 if (option.node) {
1090 add_to_predefined(node);
1091 option.node = false;
1092 node_js = true;
1093 }
1094 if (option.widget) {
1095 add_to_predefined(widget);
1096 option.widget = false;
1097 }
1098 }
1099 }
1100
1101
1102// Produce an error warning.
1103
1104 function artifact(tok) {
1105 if (!tok) {
1106 tok = next_token;
1107 }
1108 return tok.number || tok.string;
1109 }
1110
1111 function quit(message, line, character) {
1112 throw {
1113 name: 'JSLintError',
1114 line: line,
1115 character: character,
1116 message: bundle.scanned_a_b.supplant({
1117 a: message,
1118 b: Math.floor((line / lines.length) * 100)
1119 })
1120 };
1121 }
1122
1123 function warn(message, offender, a, b, c, d) {
1124 var character, line, warning;
1125 offender = offender || next_token; // ~~
1126 line = offender.line || 0;
1127 character = offender.from || 0;
1128 warning = {
1129 id: '(error)',
1130 raw: bundle[message] || message,
1131 evidence: lines[line - 1] || '',
1132 line: line,
1133 character: character,
1134 a: a || (offender.id === '(number)'
1135 ? String(offender.number)
1136 : offender.string),
1137 b: b,
1138 c: c,
1139 d: d
1140 };
1141 warning.reason = warning.raw.supplant(warning);
1142 JSLINT.errors.push(warning);
1143 if (option.passfail) {
1144 quit(bundle.stopping, line, character);
1145 }
1146 warnings += 1;
1147 if (warnings >= option.maxerr) {
1148 quit(bundle.too_many, line, character);
1149 }
1150 return warning;
1151 }
1152
1153 function warn_at(message, line, character, a, b, c, d) {
1154 return warn(message, {
1155 line: line,
1156 from: character
1157 }, a, b, c, d);
1158 }
1159
1160 function stop(message, offender, a, b, c, d) {
1161 var warning = warn(message, offender, a, b, c, d);
1162 quit(bundle.stopping, warning.line, warning.character);
1163 }
1164
1165 function stop_at(message, line, character, a, b, c, d) {
1166 return stop(message, {
1167 line: line,
1168 from: character
1169 }, a, b, c, d);
1170 }
1171
1172 function expected_at(at) {
1173 if (!option.white && next_token.from !== at) {
1174 warn('expected_a_at_b_c', next_token, '', at,
1175 next_token.from);
1176 }
1177 }
1178
1179 function aint(it, name, expected) {
1180 if (it[name] !== expected) {
1181 warn('expected_a_b', it, expected, it[name]);
1182 return true;
1183 }
1184 return false;
1185 }
1186
1187
1188// lexical analysis and token construction
1189
1190 lex = (function lex() {
1191 var character, c, from, length, line, pos, source_row;
1192
1193// Private lex methods
1194
1195 function next_line() {
1196 var at;
1197 if (line >= lines.length) {
1198 return false;
1199 }
1200 character = 1;
1201 source_row = lines[line];
1202 line += 1;
1203 at = source_row.search(/ \t/);
1204 if (at >= 0) {
1205 warn_at('mixed', line, at + 1);
1206 }
1207 source_row = source_row.replace(/\t/g, tab);
1208 at = source_row.search(cx);
1209 if (at >= 0) {
1210 warn_at('unsafe', line, at);
1211 }
1212 if (option.maxlen && option.maxlen < source_row.length) {
1213 warn_at('too_long', line, source_row.length);
1214 }
1215 return true;
1216 }
1217
1218// Produce a token object. The token inherits from a syntax symbol.
1219
1220 function it(type, value) {
1221 var id, the_token;
1222 if (type === '(string)' || type === '(range)') {
1223 if (jx.test(value)) {
1224 warn_at('url', line, from);
1225 }
1226 }
1227 the_token = Object.create(syntax[(
1228 type === '(punctuator)' || (type === '(identifier)' &&
1229 Object.prototype.hasOwnProperty.call(syntax, value))
1230 ? value
1231 : type
1232 )] || syntax['(error)']);
1233 if (type === '(identifier)') {
1234 the_token.identifier = true;
1235 if (value === '__iterator__' || value === '__proto__') {
1236 stop_at('reserved_a', line, from, value);
1237 } else if (!option.nomen &&
1238 (value.charAt(0) === '_' ||
1239 value.charAt(value.length - 1) === '_')) {
1240 warn_at('dangling_a', line, from, value);
1241 }
1242 }
1243 if (type === '(number)') {
1244 the_token.number = +value;
1245 } else if (value !== undefined) {
1246 the_token.string = String(value);
1247 }
1248 the_token.line = line;
1249 the_token.from = from;
1250 the_token.thru = character;
1251 id = the_token.id;
1252 prereg = id && (
1253 ('(,=:[!&|?{};'.indexOf(id.charAt(id.length - 1)) >= 0) ||
1254 id === 'return' || id === 'case'
1255 );
1256 return the_token;
1257 }
1258
1259 function match(x) {
1260 var exec = x.exec(source_row), first;
1261 if (exec) {
1262 length = exec[0].length;
1263 first = exec[1];
1264 c = first.charAt(0);
1265 source_row = source_row.slice(length);
1266 from = character + length - first.length;
1267 character += length;
1268 return first;
1269 }
1270 }
1271
1272 function string(x) {
1273 var c, pos = 0, r = '', result;
1274
1275 function hex(n) {
1276 var i = parseInt(source_row.substr(pos + 1, n), 16);
1277 pos += n;
1278 if (i >= 32 && i <= 126 &&
1279 i !== 34 && i !== 92 && i !== 39) {
1280 warn_at('unexpected_a', line, character, '\\');
1281 }
1282 character += n;
1283 c = String.fromCharCode(i);
1284 }
1285
1286 if (json_mode && x !== '"') {
1287 warn_at('expected_a', line, character, '"');
1288 }
1289
1290 if (xquote === x || (xmode === 'scriptstring' && !xquote)) {
1291 return it('(punctuator)', x);
1292 }
1293
1294 for (;;) {
1295 while (pos >= source_row.length) {
1296 pos = 0;
1297 if (xmode !== 'html' || !next_line()) {
1298 stop_at('unclosed', line, from);
1299 }
1300 }
1301 c = source_row.charAt(pos);
1302 if (c === x) {
1303 character += 1;
1304 source_row = source_row.slice(pos + 1);
1305 result = it('(string)', r);
1306 result.quote = x;
1307 return result;
1308 }
1309 if (c < ' ') {
1310 if (c === '\n' || c === '\r') {
1311 break;
1312 }
1313 warn_at('control_a', line, character + pos,
1314 source_row.slice(0, pos));
1315 } else if (c === xquote) {
1316 warn_at('bad_html', line, character + pos);
1317 } else if (c === '<') {
1318 if (option.safe && xmode === 'html') {
1319 warn_at('adsafe_a', line, character + pos, c);
1320 } else if (source_row.charAt(pos + 1) === '/' && (xmode || option.safe)) {
1321 warn_at('expected_a_b', line, character,
1322 '<\\/', '</');
1323 } else if (source_row.charAt(pos + 1) === '!' && (xmode || option.safe)) {
1324 warn_at('unexpected_a', line, character, '<!');
1325 }
1326 } else if (c === '\\') {
1327 if (xmode === 'html') {
1328 if (option.safe) {
1329 warn_at('adsafe_a', line, character + pos, c);
1330 }
1331 } else if (xmode === 'styleproperty') {
1332 pos += 1;
1333 character += 1;
1334 c = source_row.charAt(pos);
1335 if (c !== x) {
1336 warn_at('unexpected_a', line, character, '\\');
1337 }
1338 } else {
1339 pos += 1;
1340 character += 1;
1341 c = source_row.charAt(pos);
1342 switch (c) {
1343 case '':
1344 if (!option.es5) {
1345 warn_at('es5', line, character);
1346 }
1347 next_line();
1348 pos = -1;
1349 break;
1350 case xquote:
1351 warn_at('bad_html', line, character + pos);
1352 break;
1353 case '\'':
1354 if (json_mode) {
1355 warn_at('unexpected_a', line, character, '\\\'');
1356 }
1357 break;
1358 case 'u':
1359 hex(4);
1360 break;
1361 case 'v':
1362 if (json_mode) {
1363 warn_at('unexpected_a', line, character, '\\v');
1364 }
1365 c = '\v';
1366 break;
1367 case 'x':
1368 if (json_mode) {
1369 warn_at('unexpected_a', line, character, '\\x');
1370 }
1371 hex(2);
1372 break;
1373 default:
1374 if (typeof descapes[c] !== 'string') {
1375 warn_at(c >= '0' && c <= '7' ? 'octal_a' : 'unexpected_a',
1376 line, character, '\\' + c);
1377 } else {
1378 c = descapes[c];
1379 }
1380 }
1381 }
1382 }
1383 r += c;
1384 character += 1;
1385 pos += 1;
1386 }
1387 }
1388
1389 function number(snippet) {
1390 var digit;
1391 if (xmode !== 'style' && xmode !== 'styleproperty' &&
1392 source_row.charAt(0).isAlpha()) {
1393 warn_at('expected_space_a_b',
1394 line, character, c, source_row.charAt(0));
1395 }
1396 if (c === '0') {
1397 digit = snippet.charAt(1);
1398 if (digit.isDigit()) {
1399 if (token.id !== '.' && xmode !== 'styleproperty') {
1400 warn_at('unexpected_a', line, character, snippet);
1401 }
1402 } else if (json_mode && (digit === 'x' || digit === 'X')) {
1403 warn_at('unexpected_a', line, character, '0x');
1404 }
1405 }
1406 if (snippet.slice(snippet.length - 1) === '.') {
1407 warn_at('trailing_decimal_a', line, character, snippet);
1408 }
1409 if (xmode !== 'style') {
1410 digit = +snippet;
1411 if (!isFinite(digit)) {
1412 warn_at('bad_number', line, character, snippet);
1413 }
1414 snippet = digit;
1415 }
1416 return it('(number)', snippet);
1417 }
1418
1419 function comment(snippet) {
1420 if (comments_off || src || (xmode && xmode !== 'script' &&
1421 xmode !== 'style' && xmode !== 'styleproperty')) {
1422 warn_at('unexpected_comment', line, character);
1423 } else if (xmode === 'script' && /<\//i.test(source_row)) {
1424 warn_at('unexpected_a', line, character, '<\/');
1425 } else if (option.safe && ax.test(snippet)) {
1426 warn_at('dangerous_comment', line, character);
1427 }
1428 }
1429
1430 function regexp() {
1431 var b,
1432 bit,
1433 captures = 0,
1434 depth = 0,
1435 flag = '',
1436 high,
1437 letter,
1438 length = 0,
1439 low,
1440 potential,
1441 quote,
1442 result;
1443 for (;;) {
1444 b = true;
1445 c = source_row.charAt(length);
1446 length += 1;
1447 switch (c) {
1448 case '':
1449 stop_at('unclosed_regexp', line, from);
1450 return;
1451 case '/':
1452 if (depth > 0) {
1453 warn_at('unescaped_a', line, from + length, '/');
1454 }
1455 c = source_row.slice(0, length - 1);
1456 potential = Object.create(regexp_flag);
1457 for (;;) {
1458 letter = source_row.charAt(length);
1459 if (potential[letter] !== true) {
1460 break;
1461 }
1462 potential[letter] = false;
1463 length += 1;
1464 flag += letter;
1465 }
1466 if (source_row.charAt(length).isAlpha()) {
1467 stop_at('unexpected_a', line, from, source_row.charAt(length));
1468 }
1469 character += length;
1470 source_row = source_row.slice(length);
1471 quote = source_row.charAt(0);
1472 if (quote === '/' || quote === '*') {
1473 stop_at('confusing_regexp', line, from);
1474 }
1475 result = it('(regexp)', c);
1476 result.flag = flag;
1477 return result;
1478 case '\\':
1479 c = source_row.charAt(length);
1480 if (c < ' ') {
1481 warn_at('control_a', line, from + length, String(c));
1482 } else if (c === '<') {
1483 warn_at(bundle.unexpected_a, line, from + length, '\\');
1484 }
1485 length += 1;
1486 break;
1487 case '(':
1488 depth += 1;
1489 b = false;
1490 if (source_row.charAt(length) === '?') {
1491 length += 1;
1492 switch (source_row.charAt(length)) {
1493 case ':':
1494 case '=':
1495 case '!':
1496 length += 1;
1497 break;
1498 default:
1499 warn_at(bundle.expected_a_b, line, from + length,
1500 ':', source_row.charAt(length));
1501 }
1502 } else {
1503 captures += 1;
1504 }
1505 break;
1506 case '|':
1507 b = false;
1508 break;
1509 case ')':
1510 if (depth === 0) {
1511 warn_at('unescaped_a', line, from + length, ')');
1512 } else {
1513 depth -= 1;
1514 }
1515 break;
1516 case ' ':
1517 pos = 1;
1518 while (source_row.charAt(length) === ' ') {
1519 length += 1;
1520 pos += 1;
1521 }
1522 if (pos > 1) {
1523 warn_at('use_braces', line, from + length, pos);
1524 }
1525 break;
1526 case '[':
1527 c = source_row.charAt(length);
1528 if (c === '^') {
1529 length += 1;
1530 if (!option.regexp) {
1531 warn_at('insecure_a', line, from + length, c);
1532 } else if (source_row.charAt(length) === ']') {
1533 stop_at('unescaped_a', line, from + length, '^');
1534 }
1535 }
1536 bit = false;
1537 if (c === ']') {
1538 warn_at('empty_class', line, from + length - 1);
1539 bit = true;
1540 }
1541klass: do {
1542 c = source_row.charAt(length);
1543 length += 1;
1544 switch (c) {
1545 case '[':
1546 case '^':
1547 warn_at('unescaped_a', line, from + length, c);
1548 bit = true;
1549 break;
1550 case '-':
1551 if (bit) {
1552 bit = false;
1553 } else {
1554 warn_at('unescaped_a', line, from + length, '-');
1555 bit = true;
1556 }
1557 break;
1558 case ']':
1559 if (!bit) {
1560 warn_at('unescaped_a', line, from + length - 1, '-');
1561 }
1562 break klass;
1563 case '\\':
1564 c = source_row.charAt(length);
1565 if (c < ' ') {
1566 warn_at(bundle.control_a, line, from + length, String(c));
1567 } else if (c === '<') {
1568 warn_at(bundle.unexpected_a, line, from + length, '\\');
1569 }
1570 length += 1;
1571 bit = true;
1572 break;
1573 case '/':
1574 warn_at('unescaped_a', line, from + length - 1, '/');
1575 bit = true;
1576 break;
1577 case '<':
1578 if (xmode === 'script') {
1579 c = source_row.charAt(length);
1580 if (c === '!' || c === '/') {
1581 warn_at(bundle.html_confusion_a, line,
1582 from + length, c);
1583 }
1584 }
1585 bit = true;
1586 break;
1587 default:
1588 bit = true;
1589 }
1590 } while (c);
1591 break;
1592 case '.':
1593 if (!option.regexp) {
1594 warn_at('insecure_a', line, from + length, c);
1595 }
1596 break;
1597 case ']':
1598 case '?':
1599 case '{':
1600 case '}':
1601 case '+':
1602 case '*':
1603 warn_at('unescaped_a', line, from + length, c);
1604 break;
1605 case '<':
1606 if (xmode === 'script') {
1607 c = source_row.charAt(length);
1608 if (c === '!' || c === '/') {
1609 warn_at(bundle.html_confusion_a, line, from + length, c);
1610 }
1611 }
1612 break;
1613 }
1614 if (b) {
1615 switch (source_row.charAt(length)) {
1616 case '?':
1617 case '+':
1618 case '*':
1619 length += 1;
1620 if (source_row.charAt(length) === '?') {
1621 length += 1;
1622 }
1623 break;
1624 case '{':
1625 length += 1;
1626 c = source_row.charAt(length);
1627 if (c < '0' || c > '9') {
1628 warn_at(bundle.expected_number_a, line,
1629 from + length, c);
1630 }
1631 length += 1;
1632 low = +c;
1633 for (;;) {
1634 c = source_row.charAt(length);
1635 if (c < '0' || c > '9') {
1636 break;
1637 }
1638 length += 1;
1639 low = +c + (low * 10);
1640 }
1641 high = low;
1642 if (c === ',') {
1643 length += 1;
1644 high = Infinity;
1645 c = source_row.charAt(length);
1646 if (c >= '0' && c <= '9') {
1647 length += 1;
1648 high = +c;
1649 for (;;) {
1650 c = source_row.charAt(length);
1651 if (c < '0' || c > '9') {
1652 break;
1653 }
1654 length += 1;
1655 high = +c + (high * 10);
1656 }
1657 }
1658 }
1659 if (source_row.charAt(length) !== '}') {
1660 warn_at(bundle.expected_a_b, line, from + length,
1661 '}', c);
1662 } else {
1663 length += 1;
1664 }
1665 if (source_row.charAt(length) === '?') {
1666 length += 1;
1667 }
1668 if (low > high) {
1669 warn_at(bundle.not_greater, line, from + length,
1670 low, high);
1671 }
1672 break;
1673 }
1674 }
1675 }
1676 c = source_row.slice(0, length - 1);
1677 character += length;
1678 source_row = source_row.slice(length);
1679 return it('(regexp)', c);
1680 }
1681
1682// Public lex methods
1683
1684 return {
1685 init: function (source) {
1686 if (typeof source === 'string') {
1687 lines = source.split(crlfx);
1688 } else {
1689 lines = source;
1690 }
1691 line = 0;
1692 next_line();
1693 from = 1;
1694 },
1695
1696 range: function (begin, end) {
1697 var c, value = '';
1698 from = character;
1699 if (source_row.charAt(0) !== begin) {
1700 stop_at('expected_a_b', line, character, begin,
1701 source_row.charAt(0));
1702 }
1703 for (;;) {
1704 source_row = source_row.slice(1);
1705 character += 1;
1706 c = source_row.charAt(0);
1707 switch (c) {
1708 case '':
1709 stop_at('missing_a', line, character, c);
1710 break;
1711 case end:
1712 source_row = source_row.slice(1);
1713 character += 1;
1714 return it('(range)', value);
1715 case xquote:
1716 case '\\':
1717 warn_at('unexpected_a', line, character, c);
1718 break;
1719 }
1720 value += c;
1721 }
1722 },
1723
1724// token -- this is called by advance to get the next token.
1725
1726 token: function () {
1727 var c, i, snippet;
1728
1729 for (;;) {
1730 while (!source_row) {
1731 if (!next_line()) {
1732 return it('(end)');
1733 }
1734 }
1735 while (xmode === 'outer') {
1736 i = source_row.search(ox);
1737 if (i === 0) {
1738 break;
1739 } else if (i > 0) {
1740 character += 1;
1741 source_row = source_row.slice(i);
1742 break;
1743 } else {
1744 if (!next_line()) {
1745 return it('(end)', '');
1746 }
1747 }
1748 }
1749 snippet = match(rx[xmode] || tx);
1750 if (!snippet) {
1751 if (source_row) {
1752 if (source_row.charAt(0) === ' ') {
1753 if (!option.white) {
1754 warn_at('unexpected_a', line, character,
1755 '(space)');
1756 }
1757 character += 1;
1758 source_row = '';
1759 } else {
1760 stop_at('unexpected_a', line, character,
1761 source_row.charAt(0));
1762 }
1763 }
1764 } else {
1765
1766// identifier
1767
1768 c = snippet.charAt(0);
1769 if (c.isAlpha() || c === '_' || c === '$') {
1770 return it('(identifier)', snippet);
1771 }
1772
1773// number
1774
1775 if (c.isDigit()) {
1776 return number(snippet);
1777 }
1778 switch (snippet) {
1779
1780// string
1781
1782 case '"':
1783 case "'":
1784 return string(snippet);
1785
1786// // comment
1787
1788 case '//':
1789 comment(source_row);
1790 source_row = '';
1791 break;
1792
1793// /* comment
1794
1795 case '/*':
1796 for (;;) {
1797 i = source_row.search(lx);
1798 if (i >= 0) {
1799 break;
1800 }
1801 comment(source_row);
1802 if (!next_line()) {
1803 stop_at('unclosed_comment', line, character);
1804 }
1805 }
1806 comment(source_row.slice(0, i));
1807 character += i + 2;
1808 if (source_row.charAt(i) === '/') {
1809 stop_at('nested_comment', line, character);
1810 }
1811 source_row = source_row.slice(i + 2);
1812 break;
1813
1814 case '':
1815 break;
1816// /
1817 case '/':
1818 if (token.id === '/=') {
1819 stop_at(
1820 bundle.slash_equal,
1821 line,
1822 from
1823 );
1824 }
1825 return prereg
1826 ? regexp()
1827 : it('(punctuator)', snippet);
1828
1829// punctuator
1830
1831 case '<!--':
1832 length = line;
1833// c = character;
1834 for (;;) {
1835 i = source_row.indexOf('--');
1836 if (i >= 0) {
1837 break;
1838 }
1839 i = source_row.indexOf('<!');
1840 if (i >= 0) {
1841 stop_at('nested_comment',
1842 line, character + i);
1843 }
1844 if (!next_line()) {
1845 stop_at('unclosed_comment', length, c);
1846 }
1847 }
1848 length = source_row.indexOf('<!');
1849 if (length >= 0 && length < i) {
1850 stop_at('nested_comment',
1851 line, character + length);
1852 }
1853 character += i;
1854 if (source_row.charAt(i + 2) !== '>') {
1855 stop_at('expected_a', line, character, '-->');
1856 }
1857 character += 3;
1858 source_row = source_row.slice(i + 3);
1859 break;
1860 case '#':
1861 if (xmode === 'html' || xmode === 'styleproperty') {
1862 for (;;) {
1863 c = source_row.charAt(0);
1864 if ((c < '0' || c > '9') &&
1865 (c < 'a' || c > 'f') &&
1866 (c < 'A' || c > 'F')) {
1867 break;
1868 }
1869 character += 1;
1870 source_row = source_row.slice(1);
1871 snippet += c;
1872 }
1873 if (snippet.length !== 4 && snippet.length !== 7) {
1874 warn_at('bad_color_a', line,
1875 from + length, snippet);
1876 }
1877 return it('(color)', snippet);
1878 }
1879 return it('(punctuator)', snippet);
1880
1881 default:
1882 if (xmode === 'outer' && c === '&') {
1883 character += 1;
1884 source_row = source_row.slice(1);
1885 for (;;) {
1886 c = source_row.charAt(0);
1887 character += 1;
1888 source_row = source_row.slice(1);
1889 if (c === ';') {
1890 break;
1891 }
1892 if (!((c >= '0' && c <= '9') ||
1893 (c >= 'a' && c <= 'z') ||
1894 c === '#')) {
1895 stop_at('bad_entity', line, from + length,
1896 character);
1897 }
1898 }
1899 break;
1900 }
1901 return it('(punctuator)', snippet);
1902 }
1903 }
1904 }
1905 }
1906 };
1907 }());
1908
1909
1910 function add_label(token, kind, name) {
1911
1912// Define the symbol in the current function in the current scope.
1913
1914 name = name || token.string;
1915
1916// Global variables cannot be created in the safe subset. If a global variable
1917// already exists, do nothing. If it is predefined, define it.
1918
1919 if (funct === global_funct) {
1920 if (option.safe) {
1921 warn('adsafe_a', token, name);
1922 }
1923 if (typeof global_funct[name] !== 'string') {
1924 token.writeable = typeof predefined[name] === 'boolean'
1925 ? predefined[name]
1926 : true;
1927 token.funct = funct;
1928 global_scope[name] = token;
1929 }
1930 if (kind === 'becoming') {
1931 kind = 'var';
1932 }
1933
1934// Ordinary variables.
1935
1936 } else {
1937
1938// Warn if the variable already exists.
1939
1940 if (typeof funct[name] === 'string') {
1941 if (funct[name] === 'undef') {
1942 if (!option.undef) {
1943 warn('used_before_a', token, name);
1944 }
1945 kind = 'var';
1946 } else {
1947 warn('already_defined', token, name);
1948 }
1949 } else {
1950
1951// Add the symbol to the current function.
1952
1953 token.funct = funct;
1954 token.writeable = true;
1955 scope[name] = token;
1956 }
1957 }
1958 funct[name] = kind;
1959 }
1960
1961
1962 function peek(distance) {
1963
1964// Peek ahead to a future token. The distance is how far ahead to look. The
1965// default is the next token.
1966
1967 var found, slot = 0;
1968
1969 distance = distance || 0;
1970 while (slot <= distance) {
1971 found = lookahead[slot];
1972 if (!found) {
1973 found = lookahead[slot] = lex.token();
1974 }
1975 slot += 1;
1976 }
1977 return found;
1978 }
1979
1980
1981 function advance(id, match) {
1982
1983// Produce the next token, also looking for programming errors.
1984
1985 if (indent) {
1986
1987// If indentation checking was requested, then inspect all of the line breakings.
1988// The var statement is tricky because the names might be aligned or not. We
1989// look at the first line break after the var to determine the programmer's
1990// intention.
1991
1992 if (var_mode && next_token.line !== token.line) {
1993 if ((var_mode !== indent || !next_token.edge) &&
1994 next_token.from === indent.at -
1995 (next_token.edge ? option.indent : 0)) {
1996 var dent = indent;
1997 for (;;) {
1998 dent.at -= option.indent;
1999 if (dent === var_mode) {
2000 break;
2001 }
2002 dent = dent.was;
2003 }
2004 dent.open = false;
2005 }
2006 var_mode = null;
2007 }
2008 if (next_token.id === '?' && indent.mode === ':' &&
2009 token.line !== next_token.line) {
2010 indent.at -= option.indent;
2011 }
2012 if (indent.open) {
2013
2014// If the token is an edge.
2015
2016 if (next_token.edge) {
2017 if (next_token.edge === 'label') {
2018 expected_at(1);
2019 } else if (next_token.edge === 'case' || indent.mode === 'statement') {
2020 expected_at(indent.at - option.indent);
2021 } else if (indent.mode !== 'array' || next_token.line !== token.line) {
2022 expected_at(indent.at);
2023 }
2024
2025// If the token is not an edge, but is the first token on the line.
2026
2027 } else if (next_token.line !== token.line) {
2028 if (next_token.from < indent.at + (indent.mode ===
2029 'expression' ? 0 : option.indent)) {
2030 expected_at(indent.at + option.indent);
2031 }
2032 indent.wrap = true;
2033 }
2034 } else if (next_token.line !== token.line) {
2035 if (next_token.edge) {
2036 expected_at(indent.at);
2037 } else {
2038 indent.wrap = true;
2039 if (indent.mode === 'statement' || indent.mode === 'var') {
2040 expected_at(indent.at + option.indent);
2041 } else if (next_token.from < indent.at + (indent.mode ===
2042 'expression' ? 0 : option.indent)) {
2043 expected_at(indent.at + option.indent);
2044 }
2045 }
2046 }
2047 }
2048
2049 switch (token.id) {
2050 case '(number)':
2051 if (next_token.id === '.') {
2052 warn('trailing_decimal_a');
2053 }
2054 break;
2055 case '-':
2056 if (next_token.id === '-' || next_token.id === '--') {
2057 warn('confusing_a');
2058 }
2059 break;
2060 case '+':
2061 if (next_token.id === '+' || next_token.id === '++') {
2062 warn('confusing_a');
2063 }
2064 break;
2065 }
2066 if (token.id === '(string)' || token.identifier) {
2067 anonname = token.string;
2068 }
2069
2070 if (id && next_token.id !== id) {
2071 if (match) {
2072 warn('expected_a_b_from_c_d', next_token, id,
2073 match.id, match.line, artifact());
2074 } else if (!next_token.identifier || next_token.string !== id) {
2075 warn('expected_a_b', next_token, id, artifact());
2076 }
2077 }
2078 prev_token = token;
2079 token = next_token;
2080 next_token = lookahead.shift() || lex.token();
2081 }
2082
2083
2084 function advance_identifier(string) {
2085 if (next_token.identifier && next_token.string === string) {
2086 advance();
2087 } else {
2088 warn('expected_a_b', next_token, string, artifact());
2089 }
2090 }
2091
2092
2093 function do_safe() {
2094 if (option.adsafe) {
2095 option.safe = true;
2096 }
2097 if (option.safe) {
2098 option.browser =
2099 option['continue'] =
2100 option.css =
2101 option.debug =
2102 option.devel =
2103 option.evil =
2104 option.forin =
2105 option.newcap =
2106 option.nomen =
2107 option.on =
2108 option.rhino =
2109 option.sloppy =
2110 option.sub =
2111 option.undef =
2112 option.widget =
2113 option.windows = false;
2114
2115
2116 delete predefined.Array;
2117 delete predefined.Date;
2118 delete predefined.Function;
2119 delete predefined.Object;
2120 delete predefined['eval'];
2121
2122 add_to_predefined({
2123 ADSAFE: false,
2124 lib: false
2125 });
2126 }
2127 }
2128
2129
2130 function do_globals() {
2131 var name, writeable;
2132 for (;;) {
2133 if (next_token.id !== '(string)' && !next_token.identifier) {
2134 return;
2135 }
2136 name = next_token.string;
2137 advance();
2138 writeable = false;
2139 if (next_token.id === ':') {
2140 advance(':');
2141 switch (next_token.id) {
2142 case 'true':
2143 writeable = predefined[name] !== false;
2144 advance('true');
2145 break;
2146 case 'false':
2147 advance('false');
2148 break;
2149 default:
2150 stop('unexpected_a');
2151 }
2152 }
2153 predefined[name] = writeable;
2154 if (next_token.id !== ',') {
2155 return;
2156 }
2157 advance(',');
2158 }
2159 }
2160
2161
2162 function do_jslint() {
2163 var name, value;
2164 while (next_token.id === '(string)' || next_token.identifier) {
2165 name = next_token.string;
2166 if (!allowed_option[name]) {
2167 stop('unexpected_a');
2168 }
2169 advance();
2170 if (next_token.id !== ':') {
2171 stop('expected_a_b', next_token, ':', artifact());
2172 }
2173 advance(':');
2174 if (typeof allowed_option[name] === 'number') {
2175 value = next_token.number;
2176 if (value > allowed_option[name] || value <= 0 ||
2177 Math.floor(value) !== value) {
2178 stop('expected_small_a');
2179 }
2180 option[name] = value;
2181 } else {
2182 if (next_token.id === 'true') {
2183 option[name] = true;
2184 } else if (next_token.id === 'false') {
2185 option[name] = false;
2186 } else {
2187 stop('unexpected_a');
2188 }
2189 }
2190 advance();
2191 if (next_token.id === ',') {
2192 advance(',');
2193 }
2194 }
2195 assume();
2196 }
2197
2198
2199 function do_properties() {
2200 var name;
2201 option.properties = true;
2202 for (;;) {
2203 if (next_token.id !== '(string)' && !next_token.identifier) {
2204 return;
2205 }
2206 name = next_token.string;
2207 advance();
2208 if (next_token.id === ':') {
2209 for (;;) {
2210 advance();
2211 if (next_token.id !== '(string)' && !next_token.identifier) {
2212 break;
2213 }
2214 }
2215 }
2216 property[name] = 0;
2217 if (next_token.id !== ',') {
2218 return;
2219 }
2220 advance(',');
2221 }
2222 }
2223
2224
2225 directive = function directive() {
2226 var command = this.id,
2227 old_comments_off = comments_off,
2228 old_indent = indent;
2229 comments_off = true;
2230 indent = null;
2231 if (next_token.line === token.line && next_token.from === token.thru) {
2232 warn('missing_space_a_b', next_token, artifact(token), artifact());
2233 }
2234 if (lookahead.length > 0) {
2235 warn('unexpected_a', this);
2236 }
2237 switch (command) {
2238 case '/*properties':
2239 case '/*property':
2240 case '/*members':
2241 case '/*member':
2242 do_properties();
2243 break;
2244 case '/*jslint':
2245 if (option.safe) {
2246 warn('adsafe_a', this);
2247 }
2248 do_jslint();
2249 break;
2250 case '/*globals':
2251 case '/*global':
2252 if (option.safe) {
2253 warn('adsafe_a', this);
2254 }
2255 do_globals();
2256 break;
2257 default:
2258 stop('unexpected_a', this);
2259 }
2260 comments_off = old_comments_off;
2261 advance('*/');
2262 indent = old_indent;
2263 };
2264
2265
2266// Indentation intention
2267
2268 function edge(mode) {
2269 next_token.edge = indent ? indent.open && (mode || 'edge') : '';
2270 }
2271
2272
2273 function step_in(mode) {
2274 var open;
2275 if (typeof mode === 'number') {
2276 indent = {
2277 at: +mode,
2278 open: true,
2279 was: indent
2280 };
2281 } else if (!indent) {
2282 indent = {
2283 at: 1,
2284 mode: 'statement',
2285 open: true
2286 };
2287 } else if (mode === 'statement') {
2288 indent = {
2289 at: indent.at,
2290 open: true,
2291 was: indent
2292 };
2293 } else {
2294 open = mode === 'var' || next_token.line !== token.line;
2295 indent = {
2296 at: (open || mode === 'control'
2297 ? indent.at + option.indent
2298 : indent.at) + (indent.wrap ? option.indent : 0),
2299 mode: mode,
2300 open: open,
2301 was: indent
2302 };
2303 if (mode === 'var' && open) {
2304 var_mode = indent;
2305 }
2306 }
2307 }
2308
2309 function step_out(id, symbol) {
2310 if (id) {
2311 if (indent && indent.open) {
2312 indent.at -= option.indent;
2313 edge();
2314 }
2315 advance(id, symbol);
2316 }
2317 if (indent) {
2318 indent = indent.was;
2319 }
2320 }
2321
2322// Functions for conformance of whitespace.
2323
2324 function one_space(left, right) {
2325 left = left || token;
2326 right = right || next_token;
2327 if (right.id !== '(end)' && !option.white &&
2328 (token.line !== right.line ||
2329 token.thru + 1 !== right.from)) {
2330 warn('expected_space_a_b', right, artifact(token), artifact(right));
2331 }
2332 }
2333
2334 function one_space_only(left, right) {
2335 left = left || token;
2336 right = right || next_token;
2337 if (right.id !== '(end)' && (left.line !== right.line ||
2338 (!option.white && left.thru + 1 !== right.from))) {
2339 warn('expected_space_a_b', right, artifact(left), artifact(right));
2340 }
2341 }
2342
2343 function no_space(left, right) {
2344 left = left || token;
2345 right = right || next_token;
2346 if ((!option.white || xmode === 'styleproperty' || xmode === 'style') &&
2347 left.thru !== right.from && left.line === right.line) {
2348 warn('unexpected_space_a_b', right, artifact(left), artifact(right));
2349 }
2350 }
2351
2352 function no_space_only(left, right) {
2353 left = left || token;
2354 right = right || next_token;
2355 if (right.id !== '(end)' && (left.line !== right.line ||
2356 (!option.white && left.thru !== right.from))) {
2357 warn('unexpected_space_a_b', right, artifact(left), artifact(right));
2358 }
2359 }
2360
2361 function spaces(left, right) {
2362 if (!option.white) {
2363 left = left || token;
2364 right = right || next_token;
2365 if (left.thru === right.from && left.line === right.line) {
2366 warn('missing_space_a_b', right, artifact(left), artifact(right));
2367 }
2368 }
2369 }
2370
2371 function comma() {
2372 if (next_token.id !== ',') {
2373 warn_at('expected_a_b', token.line, token.thru, ',', artifact());
2374 } else {
2375 if (!option.white) {
2376 no_space_only();
2377 }
2378 advance(',');
2379 spaces();
2380 }
2381 }
2382
2383
2384 function semicolon() {
2385 if (next_token.id !== ';') {
2386 warn_at('expected_a_b', token.line, token.thru, ';', artifact());
2387 } else {
2388 if (!option.white) {
2389 no_space_only();
2390 }
2391 advance(';');
2392 if (semicolon_coda[next_token.id] !== true) {
2393 spaces();
2394 }
2395 }
2396 }
2397
2398 function use_strict() {
2399 if (next_token.string === 'use strict') {
2400 if (strict_mode) {
2401 warn('unnecessary_use');
2402 }
2403 edge();
2404 advance();
2405 semicolon();
2406 strict_mode = true;
2407 option.newcap = false;
2408 option.undef = false;
2409 return true;
2410 }
2411 return false;
2412 }
2413
2414
2415 function are_similar(a, b) {
2416 if (a === b) {
2417 return true;
2418 }
2419 if (Array.isArray(a)) {
2420 if (Array.isArray(b) && a.length === b.length) {
2421 var i;
2422 for (i = 0; i < a.length; i += 1) {
2423 if (!are_similar(a[i], b[i])) {
2424 return false;
2425 }
2426 }
2427 return true;
2428 }
2429 return false;
2430 }
2431 if (Array.isArray(b)) {
2432 return false;
2433 }
2434 if (a.id === '(number)' && b.id === '(number)') {
2435 return a.number === b.number;
2436 }
2437 if (a.arity === b.arity && a.string === b.string) {
2438 switch (a.arity) {
2439 case 'prefix':
2440 case 'suffix':
2441 case undefined:
2442 return a.id === b.id && are_similar(a.first, b.first);
2443 case 'infix':
2444 return are_similar(a.first, b.first) &&
2445 are_similar(a.second, b.second);
2446 case 'ternary':
2447 return are_similar(a.first, b.first) &&
2448 are_similar(a.second, b.second) &&
2449 are_similar(a.third, b.third);
2450 case 'function':
2451 case 'regexp':
2452 return false;
2453 default:
2454 return true;
2455 }
2456 } else {
2457 if (a.id === '.' && b.id === '[' && b.arity === 'infix') {
2458 return a.second.string === b.second.string && b.second.id === '(string)';
2459 }
2460 if (a.id === '[' && a.arity === 'infix' && b.id === '.') {
2461 return a.second.string === b.second.string && a.second.id === '(string)';
2462 }
2463 }
2464 return false;
2465 }
2466
2467
2468// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
2469// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
2470// like .nud except that it is only used on the first token of a statement.
2471// Having .fud makes it much easier to define statement-oriented languages like
2472// JavaScript. I retained Pratt's nomenclature.
2473
2474// .nud Null denotation
2475// .fud First null denotation
2476// .led Left denotation
2477// lbp Left binding power
2478// rbp Right binding power
2479
2480// They are elements of the parsing method called Top Down Operator Precedence.
2481
2482 function expression(rbp, initial) {
2483
2484// rbp is the right binding power.
2485// initial indicates that this is the first expression of a statement.
2486
2487 var left;
2488 if (next_token.id === '(end)') {
2489 stop('unexpected_a', token, next_token.id);
2490 }
2491 advance();
2492 if (option.safe && scope[token.string] &&
2493 scope[token.string] === global_scope[token.string] &&
2494 (next_token.id !== '(' && next_token.id !== '.')) {
2495 warn('adsafe_a', token);
2496 }
2497 if (initial) {
2498 anonname = 'anonymous';
2499 funct['(verb)'] = token.string;
2500 }
2501 if (initial === true && token.fud) {
2502 left = token.fud();
2503 } else {
2504 if (token.nud) {
2505 left = token.nud();
2506 } else {
2507 if (next_token.id === '(number)' && token.id === '.') {
2508 warn('leading_decimal_a', token, artifact());
2509 advance();
2510 return token;
2511 }
2512 stop('expected_identifier_a', token, token.id);
2513 }
2514 while (rbp < next_token.lbp) {
2515 advance();
2516 if (token.led) {
2517 left = token.led(left);
2518 } else {
2519 stop('expected_operator_a', token, token.id);
2520 }
2521 }
2522 }
2523 return left;
2524 }
2525
2526
2527// Functional constructors for making the symbols that will be inherited by
2528// tokens.
2529
2530 function symbol(s, p) {
2531 var x = syntax[s];
2532 if (!x || typeof x !== 'object') {
2533 syntax[s] = x = {
2534 id: s,
2535 lbp: p || 0,
2536 string: s
2537 };
2538 }
2539 return x;
2540 }
2541
2542 function postscript(x) {
2543 x.postscript = true;
2544 return x;
2545 }
2546
2547 function ultimate(s) {
2548 var x = symbol(s, 0);
2549 x.from = 1;
2550 x.thru = 1;
2551 x.line = 0;
2552 x.edge = 'edge';
2553 s.string = s;
2554 return postscript(x);
2555 }
2556
2557
2558 function stmt(s, f) {
2559 var x = symbol(s);
2560 x.identifier = x.reserved = true;
2561 x.fud = f;
2562 return x;
2563 }
2564
2565 function labeled_stmt(s, f) {
2566 var x = stmt(s, f);
2567 x.labeled = true;
2568 }
2569
2570 function disrupt_stmt(s, f) {
2571 var x = stmt(s, f);
2572 x.disrupt = true;
2573 }
2574
2575
2576 function reserve_name(x) {
2577 var c = x.id.charAt(0);
2578 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2579 x.identifier = x.reserved = true;
2580 }
2581 return x;
2582 }
2583
2584
2585 function prefix(s, f) {
2586 var x = symbol(s, 150);
2587 reserve_name(x);
2588 x.nud = typeof f === 'function'
2589 ? f
2590 : function () {
2591 if (s === 'typeof') {
2592 one_space();
2593 } else {
2594 no_space_only();
2595 }
2596 this.first = expression(150);
2597 this.arity = 'prefix';
2598 if (this.id === '++' || this.id === '--') {
2599 if (!option.plusplus) {
2600 warn('unexpected_a', this);
2601 } else if ((!this.first.identifier || this.first.reserved) &&
2602 this.first.id !== '.' && this.first.id !== '[') {
2603 warn('bad_operand', this);
2604 }
2605 }
2606 return this;
2607 };
2608 return x;
2609 }
2610
2611
2612 function type(s, t, nud) {
2613 var x = symbol(s);
2614 x.arity = t;
2615 if (nud) {
2616 x.nud = nud;
2617 }
2618 return x;
2619 }
2620
2621
2622 function reserve(s, f) {
2623 var x = symbol(s);
2624 x.identifier = x.reserved = true;
2625 if (typeof f === 'function') {
2626 x.nud = f;
2627 }
2628 return x;
2629 }
2630
2631
2632 function constant(name) {
2633 var x = reserve(name);
2634 x.string = name;
2635 x.nud = return_this;
2636 return x;
2637 }
2638
2639
2640 function reservevar(s, v) {
2641 return reserve(s, function () {
2642 if (typeof v === 'function') {
2643 v(this);
2644 }
2645 return this;
2646 });
2647 }
2648
2649
2650 function infix(s, p, f, w) {
2651 var x = symbol(s, p);
2652 reserve_name(x);
2653 x.led = function (left) {
2654 this.arity = 'infix';
2655 if (!w) {
2656 spaces(prev_token, token);
2657 spaces();
2658 }
2659 if (!option.bitwise && this.bitwise) {
2660 warn('unexpected_a', this);
2661 }
2662 if (typeof f === 'function') {
2663 return f(left, this);
2664 }
2665 this.first = left;
2666 this.second = expression(p);
2667 return this;
2668 };
2669 return x;
2670 }
2671
2672 function expected_relation(node, message) {
2673 if (node.assign) {
2674 warn(message || bundle.conditional_assignment, node);
2675 }
2676 return node;
2677 }
2678
2679 function expected_condition(node, message) {
2680 switch (node.id) {
2681 case '[':
2682 case '-':
2683 if (node.arity !== 'infix') {
2684 warn(message || bundle.weird_condition, node);
2685 }
2686 break;
2687 case 'false':
2688 case 'function':
2689 case 'Infinity':
2690 case 'NaN':
2691 case 'null':
2692 case 'true':
2693 case 'undefined':
2694 case 'void':
2695 case '(number)':
2696 case '(regexp)':
2697 case '(string)':
2698 case '{':
2699 warn(message || bundle.weird_condition, node);
2700 break;
2701 case '(':
2702 if (node.first.id === '.' && numbery[node.first.second.string] === true) {
2703 warn(message || bundle.weird_condition, node);
2704 }
2705 break;
2706 }
2707 return node;
2708 }
2709
2710 function check_relation(node) {
2711 switch (node.arity) {
2712 case 'prefix':
2713 switch (node.id) {
2714 case '{':
2715 case '[':
2716 warn('unexpected_a', node);
2717 break;
2718 case '!':
2719 warn('confusing_a', node);
2720 break;
2721 }
2722 break;
2723 case 'function':
2724 case 'regexp':
2725 warn('unexpected_a', node);
2726 break;
2727 default:
2728 if (node.id === 'NaN') {
2729 warn('isNaN', node);
2730 }
2731 }
2732 return node;
2733 }
2734
2735
2736 function relation(s, eqeq) {
2737 return infix(s, 100, function (left, that) {
2738 check_relation(left);
2739 if (eqeq && !option.eqeq) {
2740 warn('expected_a_b', that, eqeq, that.id);
2741 }
2742 var right = expression(100);
2743 if (are_similar(left, right) ||
2744 ((left.id === '(string)' || left.id === '(number)') &&
2745 (right.id === '(string)' || right.id === '(number)'))) {
2746 warn('weird_relation', that);
2747 }
2748 that.first = left;
2749 that.second = check_relation(right);
2750 return that;
2751 });
2752 }
2753
2754
2755 function assignop(s, op) {
2756 var x = infix(s, 20, function (left, that) {
2757 var l;
2758 that.first = left;
2759 if (left.identifier) {
2760 if (scope[left.string]) {
2761 if (scope[left.string].writeable === false) {
2762 warn('read_only', left);
2763 }
2764 } else {
2765 stop('read_only');
2766 }
2767 if (funct['(params)']) {
2768 funct['(params)'].forEach(function (value) {
2769 if (value.string === left.string) {
2770 value.assign = true;
2771 }
2772 });
2773 }
2774 } else if (option.safe) {
2775 l = left;
2776 do {
2777 if (typeof predefined[l.string] === 'boolean') {
2778 warn('adsafe_a', l);
2779 }
2780 l = l.first;
2781 } while (l);
2782 }
2783 if (left === syntax['function']) {
2784 warn('identifier_function', token);
2785 }
2786 if (left.id === '.' || left.id === '[') {
2787 if (!left.first || left.first.string === 'arguments') {
2788 warn('bad_assignment', that);
2789 }
2790 } else if (left.identifier) {
2791 if (!left.reserved && funct[left.string] === 'exception') {
2792 warn('assign_exception', left);
2793 }
2794 } else {
2795 warn('bad_assignment', that);
2796 }
2797 that.second = expression(19);
2798 if (that.id === '=' && are_similar(that.first, that.second)) {
2799 warn('weird_assignment', that);
2800 }
2801 return that;
2802 });
2803 x.assign = true;
2804 if (op) {
2805 if (syntax[op].bitwise) {
2806 x.bitwise = true;
2807 }
2808 }
2809 return x;
2810 }
2811
2812
2813 function bitwise(s, p) {
2814 var x = infix(s, p, 'number');
2815 x.bitwise = true;
2816 return x;
2817 }
2818
2819
2820 function suffix(s) {
2821 var x = symbol(s, 150);
2822 x.led = function (left) {
2823 no_space_only(prev_token, token);
2824 if (!option.plusplus) {
2825 warn('unexpected_a', this);
2826 } else if ((!left.identifier || left.reserved) &&
2827 left.id !== '.' && left.id !== '[') {
2828 warn('bad_operand', this);
2829 }
2830 this.first = left;
2831 this.arity = 'suffix';
2832 return this;
2833 };
2834 return x;
2835 }
2836
2837
2838 function optional_identifier() {
2839 if (next_token.identifier) {
2840 advance();
2841 if (option.safe && banned[token.string]) {
2842 warn('adsafe_a', token);
2843 } else if (token.reserved && !option.es5) {
2844 warn('expected_identifier_a_reserved', token);
2845 }
2846 return token.string;
2847 }
2848 }
2849
2850
2851 function identifier() {
2852 var i = optional_identifier();
2853 if (!i) {
2854 stop(token.id === 'function' && next_token.id === '('
2855 ? 'name_function'
2856 : 'expected_identifier_a');
2857 }
2858 return i;
2859 }
2860
2861
2862 function statement() {
2863
2864 var label, old_scope = scope, the_statement;
2865
2866// We don't like the empty statement.
2867
2868 if (next_token.id === ';') {
2869 warn('unexpected_a');
2870 semicolon();
2871 return;
2872 }
2873
2874// Is this a labeled statement?
2875
2876 if (next_token.identifier && !next_token.reserved && peek().id === ':') {
2877 edge('label');
2878 label = next_token;
2879 advance();
2880 advance(':');
2881 scope = Object.create(old_scope);
2882 add_label(label, 'label');
2883 if (next_token.labeled !== true) {
2884 warn('label_a_b', next_token, label.string, artifact());
2885 } else if (jx.test(label.string + ':')) {
2886 warn('url', label);
2887 } else if (funct === global_funct) {
2888 stop('unexpected_a', token);
2889 }
2890 next_token.label = label;
2891 }
2892
2893// Parse the statement.
2894
2895 if (token.id !== 'else') {
2896 edge();
2897 }
2898 step_in('statement');
2899 the_statement = expression(0, true);
2900 if (the_statement) {
2901
2902// Look for the final semicolon.
2903
2904 if (the_statement.arity === 'statement') {
2905 if (the_statement.id === 'switch' ||
2906 (the_statement.block && the_statement.id !== 'do')) {
2907 spaces();
2908 } else {
2909 semicolon();
2910 }
2911 } else {
2912
2913// If this is an expression statement, determine if it is acceptable.
2914// We do not like
2915// new Blah();
2916// statments. If it is to be used at all, new should only be used to make
2917// objects, not side effects. The expression statements we do like do
2918// assignment or invocation or delete.
2919
2920 if (the_statement.id === '(') {
2921 if (the_statement.first.id === 'new') {
2922 warn('bad_new');
2923 }
2924 } else if (!the_statement.assign &&
2925 the_statement.id !== 'delete' &&
2926 the_statement.id !== '++' &&
2927 the_statement.id !== '--') {
2928 warn('assignment_function_expression', token);
2929 }
2930 semicolon();
2931 }
2932 }
2933 step_out();
2934 scope = old_scope;
2935 return the_statement;
2936 }
2937
2938
2939 function statements() {
2940 var array = [], disruptor, the_statement;
2941
2942// A disrupt statement may not be followed by any other statement.
2943// If the last statement is disrupt, then the sequence is disrupt.
2944
2945 while (next_token.postscript !== true) {
2946 if (next_token.id === ';') {
2947 warn('unexpected_a', next_token);
2948 semicolon();
2949 } else {
2950 if (next_token.string === 'use strict') {
2951 if ((!node_js && xmode !== 'script') || funct !== global_funct || array.length > 0) {
2952 warn('function_strict');
2953 }
2954 use_strict();
2955 }
2956 if (disruptor) {
2957 warn('unreachable_a_b', next_token, next_token.string,
2958 disruptor.string);
2959 disruptor = null;
2960 }
2961 the_statement = statement();
2962 if (the_statement) {
2963 array.push(the_statement);
2964 if (the_statement.disrupt) {
2965 disruptor = the_statement;
2966 array.disrupt = true;
2967 }
2968 }
2969 }
2970 }
2971 return array;
2972 }
2973
2974
2975 function block(ordinary) {
2976
2977// array block is array sequence of statements wrapped in braces.
2978// ordinary is false for function bodies and try blocks.
2979// ordinary is true for if statements, while, etc.
2980
2981 var array,
2982 curly = next_token,
2983 old_in_block = in_block,
2984 old_scope = scope,
2985 old_strict_mode = strict_mode;
2986
2987 in_block = ordinary;
2988 scope = Object.create(scope);
2989 spaces();
2990 if (next_token.id === '{') {
2991 advance('{');
2992 step_in();
2993 if (!ordinary && !use_strict() && !old_strict_mode &&
2994 !option.sloppy && funct['(context)'] === global_funct) {
2995 warn('missing_use_strict');
2996 }
2997 array = statements();
2998 strict_mode = old_strict_mode;
2999 step_out('}', curly);
3000 } else if (!ordinary) {
3001 stop('expected_a_b', next_token, '{', artifact());
3002 } else {
3003 warn('expected_a_b', next_token, '{', artifact());
3004 array = [statement()];
3005 array.disrupt = array[0].disrupt;
3006 }
3007 funct['(verb)'] = null;
3008 scope = old_scope;
3009 in_block = old_in_block;
3010 if (ordinary && array.length === 0) {
3011 warn('empty_block');
3012 }
3013 return array;
3014 }
3015
3016
3017 function tally_property(name) {
3018 if (option.properties && typeof property[name] !== 'number') {
3019 warn('unexpected_property_a', token, name);
3020 }
3021 if (typeof property[name] === 'number') {
3022 property[name] += 1;
3023 } else {
3024 property[name] = 1;
3025 }
3026 }
3027
3028
3029// ECMAScript parser
3030
3031 syntax['(identifier)'] = {
3032 id: '(identifier)',
3033 lbp: 0,
3034 identifier: true,
3035 nud: function () {
3036 var name = this.string,
3037 variable = scope[name],
3038 site,
3039 writeable;
3040
3041// If the variable is not in scope, then we may have an undeclared variable.
3042// Check the predefined list. If it was predefined, create the global
3043// variable.
3044
3045 if (typeof variable !== 'object') {
3046 writeable = predefined[name];
3047 if (typeof writeable === 'boolean') {
3048 global_scope[name] = variable = {
3049 string: name,
3050 writeable: writeable,
3051 funct: global_funct
3052 };
3053 global_funct[name] = 'var';
3054
3055// But if the variable is not in scope, and is not predefined, and if we are not
3056// in the global scope, then we have an undefined variable error.
3057
3058 } else {
3059 if (!option.undef) {
3060 warn('used_before_a', token);
3061 }
3062 scope[name] = variable = {
3063 string: name,
3064 writeable: true,
3065 funct: funct
3066 };
3067 funct[name] = 'undef';
3068 }
3069
3070 }
3071 site = variable.funct;
3072
3073// The name is in scope and defined in the current function.
3074
3075 if (funct === site) {
3076
3077// Change 'unused' to 'var', and reject labels.
3078
3079 switch (funct[name]) {
3080 case 'becoming':
3081 warn('unexpected_a', token);
3082 funct[name] = 'var';
3083 break;
3084 case 'unused':
3085 funct[name] = 'var';
3086 break;
3087 case 'unparam':
3088 funct[name] = 'parameter';
3089 break;
3090 case 'unction':
3091 funct[name] = 'function';
3092 break;
3093 case 'label':
3094 warn('a_label', token, name);
3095 break;
3096 }
3097
3098// If the name is already defined in the current
3099// function, but not as outer, then there is a scope error.
3100
3101 } else {
3102 switch (funct[name]) {
3103 case 'closure':
3104 case 'function':
3105 case 'var':
3106 case 'unused':
3107 warn('a_scope', token, name);
3108 break;
3109 case 'label':
3110 warn('a_label', token, name);
3111 break;
3112 case 'outer':
3113 case 'global':
3114 break;
3115 default:
3116
3117// If the name is defined in an outer function, make an outer entry, and if
3118// it was unused, make it var.
3119
3120 switch (site[name]) {
3121 case 'becoming':
3122 case 'closure':
3123 case 'function':
3124 case 'parameter':
3125 case 'unction':
3126 case 'unused':
3127 case 'var':
3128 site[name] = 'closure';
3129 funct[name] = site === global_funct
3130 ? 'global'
3131 : 'outer';
3132 break;
3133 case 'unparam':
3134 site[name] = 'parameter';
3135 funct[name] = 'outer';
3136 break;
3137 case 'undef':
3138 funct[name] = 'undef';
3139 break;
3140 case 'label':
3141 warn('a_label', token, name);
3142 break;
3143 }
3144 }
3145 }
3146 return this;
3147 },
3148 led: function () {
3149 stop('expected_operator_a');
3150 }
3151 };
3152
3153// Build the syntax table by declaring the syntactic elements.
3154
3155 type('(array)', 'array');
3156 type('(color)', 'color');
3157 type('(function)', 'function');
3158 type('(number)', 'number', return_this);
3159 type('(object)', 'object');
3160 type('(string)', 'string', return_this);
3161 type('(boolean)', 'boolean', return_this);
3162 type('(range)', 'range');
3163 type('(regexp)', 'regexp', return_this);
3164
3165 ultimate('(begin)');
3166 ultimate('(end)');
3167 ultimate('(error)');
3168 postscript(symbol('</'));
3169 symbol('<!');
3170 symbol('<!--');
3171 symbol('-->');
3172 postscript(symbol('}'));
3173 symbol(')');
3174 symbol(']');
3175 postscript(symbol('"'));
3176 postscript(symbol('\''));
3177 symbol(';');
3178 symbol(':');
3179 symbol(',');
3180 symbol('#');
3181 symbol('@');
3182 symbol('*/');
3183 postscript(reserve('case'));
3184 reserve('catch');
3185 postscript(reserve('default'));
3186 reserve('else');
3187 reserve('finally');
3188
3189 reservevar('arguments', function (x) {
3190 if (strict_mode && funct === global_funct) {
3191 warn('strict', x);
3192 } else if (option.safe) {
3193 warn('adsafe_a', x);
3194 }
3195 funct['(arguments)'] = true;
3196 });
3197 reservevar('eval', function (x) {
3198 if (option.safe) {
3199 warn('adsafe_a', x);
3200 }
3201 });
3202 constant('false', 'boolean');
3203 constant('Infinity', 'number');
3204 constant('NaN', 'number');
3205 constant('null', '');
3206 reservevar('this', function (x) {
3207 if (option.safe) {
3208 warn('adsafe_a', x);
3209 } else if (strict_mode && funct['(token)'].arity === 'statement' &&
3210 funct['(name)'].charAt(0) > 'Z') {
3211 warn('strict', x);
3212 }
3213 });
3214 constant('true', 'boolean');
3215 constant('undefined', '');
3216
3217 infix('?', 30, function (left, that) {
3218 step_in('?');
3219 that.first = expected_condition(expected_relation(left));
3220 that.second = expression(0);
3221 spaces();
3222 step_out();
3223 var colon = next_token;
3224 advance(':');
3225 step_in(':');
3226 spaces();
3227 that.third = expression(10);
3228 that.arity = 'ternary';
3229 if (are_similar(that.second, that.third)) {
3230 warn('weird_ternary', colon);
3231 } else if (are_similar(that.first, that.second)) {
3232 warn('use_or', that);
3233 }
3234 step_out();
3235 return that;
3236 });
3237
3238 infix('||', 40, function (left, that) {
3239 function paren_check(that) {
3240 if (that.id === '&&' && !that.paren) {
3241 warn('and', that);
3242 }
3243 return that;
3244 }
3245
3246 that.first = paren_check(expected_condition(expected_relation(left)));
3247 that.second = paren_check(expected_relation(expression(40)));
3248 if (are_similar(that.first, that.second)) {
3249 warn('weird_condition', that);
3250 }
3251 return that;
3252 });
3253
3254 infix('&&', 50, function (left, that) {
3255 that.first = expected_condition(expected_relation(left));
3256 that.second = expected_relation(expression(50));
3257 if (are_similar(that.first, that.second)) {
3258 warn('weird_condition', that);
3259 }
3260 return that;
3261 });
3262
3263 prefix('void', function () {
3264 this.first = expression(0);
3265 this.arity = 'prefix';
3266 if (option.es5) {
3267 warn('expected_a_b', this, 'undefined', 'void');
3268 } else if (this.first.number !== 0) {
3269 warn('expected_a_b', this.first, '0', artifact(this.first));
3270 }
3271 return this;
3272 });
3273
3274 bitwise('|', 70);
3275 bitwise('^', 80);
3276 bitwise('&', 90);
3277
3278 relation('==', '===');
3279 relation('===');
3280 relation('!=', '!==');
3281 relation('!==');
3282 relation('<');
3283 relation('>');
3284 relation('<=');
3285 relation('>=');
3286
3287 bitwise('<<', 120);
3288 bitwise('>>', 120);
3289 bitwise('>>>', 120);
3290
3291 infix('in', 120, function (left, that) {
3292 warn('infix_in', that);
3293 that.left = left;
3294 that.right = expression(130);
3295 return that;
3296 });
3297 infix('instanceof', 120);
3298 infix('+', 130, function (left, that) {
3299 if (left.id === '(number)') {
3300 if (left.number === 0) {
3301 warn('unexpected_a', left, '0');
3302 }
3303 } else if (left.id === '(string)') {
3304 if (left.string === '') {
3305 warn('expected_a_b', left, 'String', '\'\'');
3306 }
3307 }
3308 var right = expression(130);
3309 if (right.id === '(number)') {
3310 if (right.number === 0) {
3311 warn('unexpected_a', right, '0');
3312 }
3313 } else if (right.id === '(string)') {
3314 if (right.string === '') {
3315 warn('expected_a_b', right, 'String', '\'\'');
3316 }
3317 }
3318 if (left.id === right.id) {
3319 if (left.id === '(string)' || left.id === '(number)') {
3320 if (left.id === '(string)') {
3321 left.string += right.string;
3322 if (jx.test(left.string)) {
3323 warn('url', left);
3324 }
3325 } else {
3326 left.number += right.number;
3327 }
3328 left.thru = right.thru;
3329 return left;
3330 }
3331 }
3332 that.first = left;
3333 that.second = right;
3334 return that;
3335 });
3336 prefix('+', 'num');
3337 prefix('+++', function () {
3338 warn('confusing_a', token);
3339 this.first = expression(150);
3340 this.arity = 'prefix';
3341 return this;
3342 });
3343 infix('+++', 130, function (left) {
3344 warn('confusing_a', token);
3345 this.first = left;
3346 this.second = expression(130);
3347 return this;
3348 });
3349 infix('-', 130, function (left, that) {
3350 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') {
3351 warn('unexpected_a', left);
3352 }
3353 var right = expression(130);
3354 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') {
3355 warn('unexpected_a', right);
3356 }
3357 if (left.id === right.id && left.id === '(number)') {
3358 left.number -= right.number;
3359 left.thru = right.thru;
3360 return left;
3361 }
3362 that.first = left;
3363 that.second = right;
3364 return that;
3365 });
3366 prefix('-');
3367 prefix('---', function () {
3368 warn('confusing_a', token);
3369 this.first = expression(150);
3370 this.arity = 'prefix';
3371 return this;
3372 });
3373 infix('---', 130, function (left) {
3374 warn('confusing_a', token);
3375 this.first = left;
3376 this.second = expression(130);
3377 return this;
3378 });
3379 infix('*', 140, function (left, that) {
3380 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') {
3381 warn('unexpected_a', left);
3382 }
3383 var right = expression(140);
3384 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') {
3385 warn('unexpected_a', right);
3386 }
3387 if (left.id === right.id && left.id === '(number)') {
3388 left.number *= right.number;
3389 left.thru = right.thru;
3390 return left;
3391 }
3392 that.first = left;
3393 that.second = right;
3394 return that;
3395 });
3396 infix('/', 140, function (left, that) {
3397 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') {
3398 warn('unexpected_a', left);
3399 }
3400 var right = expression(140);
3401 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') {
3402 warn('unexpected_a', right);
3403 }
3404 if (left.id === right.id && left.id === '(number)') {
3405 left.number /= right.number;
3406 left.thru = right.thru;
3407 return left;
3408 }
3409 that.first = left;
3410 that.second = right;
3411 return that;
3412 });
3413 infix('%', 140, function (left, that) {
3414 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') {
3415 warn('unexpected_a', left);
3416 }
3417 var right = expression(140);
3418 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') {
3419 warn('unexpected_a', right);
3420 }
3421 if (left.id === right.id && left.id === '(number)') {
3422 left.number %= right.number;
3423 left.thru = right.thru;
3424 return left;
3425 }
3426 that.first = left;
3427 that.second = right;
3428 return that;
3429 });
3430
3431 suffix('++');
3432 prefix('++');
3433
3434 suffix('--');
3435 prefix('--');
3436 prefix('delete', function () {
3437 one_space();
3438 var p = expression(0);
3439 if (!p || (p.id !== '.' && p.id !== '[')) {
3440 warn('deleted');
3441 }
3442 this.first = p;
3443 return this;
3444 });
3445
3446
3447 prefix('~', function () {
3448 no_space_only();
3449 if (!option.bitwise) {
3450 warn('unexpected_a', this);
3451 }
3452 expression(150);
3453 return this;
3454 });
3455 prefix('!', function () {
3456 no_space_only();
3457 this.first = expected_condition(expression(150));
3458 this.arity = 'prefix';
3459 if (bang[this.first.id] === true || this.first.assign) {
3460 warn('confusing_a', this);
3461 }
3462 return this;
3463 });
3464 prefix('typeof', null);
3465 prefix('new', function () {
3466 one_space();
3467 var c = expression(160), n, p, v;
3468 this.first = c;
3469 if (c.id !== 'function') {
3470 if (c.identifier) {
3471 switch (c.string) {
3472 case 'Object':
3473 warn('use_object', token);
3474 break;
3475 case 'Array':
3476 if (next_token.id === '(') {
3477 p = next_token;
3478 p.first = this;
3479 advance('(');
3480 if (next_token.id !== ')') {
3481 n = expression(0);
3482 p.second = [n];
3483 if (n.id !== '(number)' || next_token.id === ',') {
3484 warn('use_array', p);
3485 }
3486 while (next_token.id === ',') {
3487 advance(',');
3488 p.second.push(expression(0));
3489 }
3490 } else {
3491 warn('use_array', token);
3492 }
3493 advance(')', p);
3494 return p;
3495 }
3496 warn('use_array', token);
3497 break;
3498 case 'Number':
3499 case 'String':
3500 case 'Boolean':
3501 case 'Math':
3502 case 'JSON':
3503 warn('not_a_constructor', c);
3504 break;
3505 case 'Function':
3506 if (!option.evil) {
3507 warn('function_eval');
3508 }
3509 break;
3510 case 'Date':
3511 case 'RegExp':
3512 case 'this':
3513 break;
3514 default:
3515 if (c.id !== 'function') {
3516 v = c.string.charAt(0);
3517 if (!option.newcap && (v < 'A' || v > 'Z')) {
3518 warn('constructor_name_a', token);
3519 }
3520 }
3521 }
3522 } else {
3523 if (c.id !== '.' && c.id !== '[' && c.id !== '(') {
3524 warn('bad_constructor', token);
3525 }
3526 }
3527 } else {
3528 warn('weird_new', this);
3529 }
3530 if (next_token.id !== '(') {
3531 warn('missing_a', next_token, '()');
3532 }
3533 return this;
3534 });
3535
3536 infix('(', 160, function (left, that) {
3537 var p;
3538 if (indent && indent.mode === 'expression') {
3539 no_space(prev_token, token);
3540 } else {
3541 no_space_only(prev_token, token);
3542 }
3543 if (!left.immed && left.id === 'function') {
3544 warn('wrap_immediate');
3545 }
3546 p = [];
3547 if (left.identifier) {
3548 if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
3549 if (left.string !== 'Number' && left.string !== 'String' &&
3550 left.string !== 'Boolean' && left.string !== 'Date') {
3551 if (left.string === 'Math' || left.string === 'JSON') {
3552 warn('not_a_function', left);
3553 } else if (left.string === 'Object') {
3554 warn('use_object', token);
3555 } else if (left.string === 'Array' || !option.newcap) {
3556 warn('missing_a', left, 'new');
3557 }
3558 }
3559 }
3560 } else if (left.id === '.') {
3561 if (option.safe && left.first.string === 'Math' &&
3562 left.second === 'random') {
3563 warn('adsafe_a', left);
3564 } else if (left.second.string === 'split' &&
3565 left.first.id === '(string)') {
3566 warn('use_array', left.second);
3567 }
3568 }
3569 step_in();
3570 if (next_token.id !== ')') {
3571 no_space();
3572 for (;;) {
3573 edge();
3574 p.push(expression(10));
3575 if (next_token.id !== ',') {
3576 break;
3577 }
3578 comma();
3579 }
3580 }
3581 no_space();
3582 step_out(')', that);
3583 if (typeof left === 'object') {
3584 if (left.string === 'parseInt' && p.length === 1) {
3585 warn('radix', left);
3586 }
3587 if (!option.evil) {
3588 if (left.string === 'eval' || left.string === 'Function' ||
3589 left.string === 'execScript') {
3590 warn('evil', left);
3591 } else if (p[0] && p[0].id === '(string)' &&
3592 (left.string === 'setTimeout' ||
3593 left.string === 'setInterval')) {
3594 warn('implied_evil', left);
3595 }
3596 }
3597 if (!left.identifier && left.id !== '.' && left.id !== '[' &&
3598 left.id !== '(' && left.id !== '&&' && left.id !== '||' &&
3599 left.id !== '?') {
3600 warn('bad_invocation', left);
3601 }
3602 }
3603 that.first = left;
3604 that.second = p;
3605 return that;
3606 }, true);
3607
3608 prefix('(', function () {
3609 step_in('expression');
3610 no_space();
3611 edge();
3612 if (next_token.id === 'function') {
3613 next_token.immed = true;
3614 }
3615 var value = expression(0);
3616 value.paren = true;
3617 no_space();
3618 step_out(')', this);
3619 if (value.id === 'function') {
3620 switch (next_token.id) {
3621 case '(':
3622 warn('move_invocation');
3623 break;
3624 case '.':
3625 case '[':
3626 warn('unexpected_a');
3627 break;
3628 default:
3629 warn('bad_wrap', this);
3630 }
3631 }
3632 return value;
3633 });
3634
3635 infix('.', 170, function (left, that) {
3636 no_space(prev_token, token);
3637 no_space();
3638 var name = identifier();
3639 if (typeof name === 'string') {
3640 tally_property(name);
3641 }
3642 that.first = left;
3643 that.second = token;
3644 if (left && left.string === 'arguments' &&
3645 (name === 'callee' || name === 'caller')) {
3646 warn('avoid_a', left, 'arguments.' + name);
3647 } else if (!option.evil && left && left.string === 'document' &&
3648 (name === 'write' || name === 'writeln')) {
3649 warn('write_is_wrong', left);
3650 } else if (option.adsafe) {
3651 if (!adsafe_top && left.string === 'ADSAFE') {
3652 if (name === 'id' || name === 'lib') {
3653 warn('adsafe_a', that);
3654 } else if (name === 'go') {
3655 if (xmode !== 'script') {
3656 warn('adsafe_a', that);
3657 } else if (adsafe_went || next_token.id !== '(' ||
3658 peek(0).id !== '(string)' ||
3659 peek(0).string !== adsafe_id ||
3660 peek(1).id !== ',') {
3661 stop('adsafe_a', that, 'go');
3662 }
3663 adsafe_went = true;
3664 adsafe_may = false;
3665 }
3666 }
3667 adsafe_top = false;
3668 }
3669 if (!option.evil && (name === 'eval' || name === 'execScript')) {
3670 warn('evil');
3671 } else if (option.safe) {
3672 for (;;) {
3673 if (banned[name] === true) {
3674 warn('adsafe_a', token, name);
3675 }
3676 if (typeof predefined[left.string] !== 'boolean' || //// check for writeable
3677 next_token.id === '(') {
3678 break;
3679 }
3680 if (next_token.id !== '.') {
3681 warn('adsafe_a', that);
3682 break;
3683 }
3684 advance('.');
3685 token.first = that;
3686 token.second = name;
3687 that = token;
3688 name = identifier();
3689 if (typeof name === 'string') {
3690 tally_property(name);
3691 }
3692 }
3693 }
3694 return that;
3695 }, true);
3696
3697 infix('[', 170, function (left, that) {
3698 var e, s;
3699 no_space_only(prev_token, token);
3700 no_space();
3701 step_in();
3702 edge();
3703 e = expression(0);
3704 switch (e.id) {
3705 case '(number)':
3706 if (e.id === '(number)' && left.id === 'arguments') {
3707 warn('use_param', left);
3708 }
3709 break;
3710 case '(string)':
3711 if (option.safe && (banned[e.string] ||
3712 e.string.charAt(0) === '_' || e.string.slice(-1) === '_')) {
3713 warn('adsafe_subscript_a', e);
3714 } else if (!option.evil &&
3715 (e.string === 'eval' || e.string === 'execScript')) {
3716 warn('evil', e);
3717 } else if (!option.sub && ix.test(e.string)) {
3718 s = syntax[e.string];
3719 if (!s || !s.reserved) {
3720 warn('subscript', e);
3721 }
3722 }
3723 tally_property(e.string);
3724 break;
3725 default:
3726 if (option.safe) {
3727 warn('adsafe_subscript_a', e);
3728 }
3729 }
3730 step_out(']', that);
3731 no_space(prev_token, token);
3732 that.first = left;
3733 that.second = e;
3734 return that;
3735 }, true);
3736
3737 prefix('[', function () {
3738 this.arity = 'prefix';
3739 this.first = [];
3740 step_in('array');
3741 while (next_token.id !== '(end)') {
3742 while (next_token.id === ',') {
3743 warn('unexpected_a', next_token);
3744 advance(',');
3745 }
3746 if (next_token.id === ']') {
3747 break;
3748 }
3749 indent.wrap = false;
3750 edge();
3751 this.first.push(expression(10));
3752 if (next_token.id === ',') {
3753 comma();
3754 if (next_token.id === ']' && !option.es5) {
3755 warn('unexpected_a', token);
3756 break;
3757 }
3758 } else {
3759 break;
3760 }
3761 }
3762 step_out(']', this);
3763 return this;
3764 }, 170);
3765
3766
3767 function property_name() {
3768 var id = optional_identifier(true);
3769 if (!id) {
3770 if (next_token.id === '(string)') {
3771 id = next_token.string;
3772 if (option.safe) {
3773 if (banned[id]) {
3774 warn('adsafe_a');
3775 } else if (id.charAt(0) === '_' ||
3776 id.charAt(id.length - 1) === '_') {
3777 warn('dangling_a');
3778 }
3779 }
3780 advance();
3781 } else if (next_token.id === '(number)') {
3782 id = next_token.number.toString();
3783 advance();
3784 }
3785 }
3786 return id;
3787 }
3788
3789
3790 function function_params() {
3791 var id, paren = next_token, params = [];
3792 advance('(');
3793 step_in();
3794 no_space();
3795 if (next_token.id === ')') {
3796 no_space();
3797 step_out(')', paren);
3798 return params;
3799 }
3800 for (;;) {
3801 edge();
3802 id = identifier();
3803 params.push(token);
3804 add_label(token, option.unparam ? 'parameter' : 'unparam');
3805 if (next_token.id === ',') {
3806 comma();
3807 } else {
3808 no_space();
3809 step_out(')', paren);
3810 return params;
3811 }
3812 }
3813 }
3814
3815
3816
3817 function do_function(func, name) {
3818 var old_funct = funct,
3819 old_option = option,
3820 old_scope = scope;
3821 funct = {
3822 '(name)' : name || '\'' + (anonname || '').replace(nx, sanitize) + '\'',
3823 '(line)' : next_token.line,
3824 '(context)' : old_funct,
3825 '(breakage)' : 0,
3826 '(loopage)' : 0,
3827 '(scope)' : scope,
3828 '(token)' : func
3829 };
3830 option = Object.create(old_option);
3831 scope = Object.create(old_scope);
3832 functions.push(funct);
3833 func.name = name;
3834 if (name) {
3835 add_label(func, 'function', name);
3836 }
3837 func.writeable = false;
3838 func.first = funct['(params)'] = function_params();
3839 one_space();
3840 func.block = block(false);
3841 if (funct['(arguments)']) {
3842 func.first.forEach(function (value) {
3843 if (value.assign) {
3844 warn('parameter_arguments_a', value, value.string);
3845 }
3846 });
3847 }
3848 funct = old_funct;
3849 option = old_option;
3850 scope = old_scope;
3851 }
3852
3853
3854 assignop('=');
3855 assignop('+=', '+');
3856 assignop('-=', '-');
3857 assignop('*=', '*');
3858 assignop('/=', '/').nud = function () {
3859 stop('slash_equal');
3860 };
3861 assignop('%=', '%');
3862 assignop('&=', '&');
3863 assignop('|=', '|');
3864 assignop('^=', '^');
3865 assignop('<<=', '<<');
3866 assignop('>>=', '>>');
3867 assignop('>>>=', '>>>');
3868
3869
3870 prefix('{', function () {
3871 var get, i, j, name, p, set, seen = {};
3872 this.arity = 'prefix';
3873 this.first = [];
3874 step_in();
3875 while (next_token.id !== '}') {
3876 indent.wrap = false;
3877
3878// JSLint recognizes the ES5 extension for get/set in object literals,
3879// but requires that they be used in pairs.
3880
3881 edge();
3882 if (next_token.string === 'get' && peek().id !== ':') {
3883 if (!option.es5) {
3884 warn('es5');
3885 }
3886 get = next_token;
3887 advance('get');
3888 one_space_only();
3889 name = next_token;
3890 i = property_name();
3891 if (!i) {
3892 stop('missing_property');
3893 }
3894 get.string = '';
3895 do_function(get);
3896 if (funct['(loopage)']) {
3897 warn('function_loop', get);
3898 }
3899 p = get.first;
3900 if (p) {
3901 warn('parameter_a_get_b', p[0], p[0].string, i);
3902 }
3903 comma();
3904 set = next_token;
3905 spaces();
3906 edge();
3907 advance('set');
3908 set.string = '';
3909 one_space_only();
3910 j = property_name();
3911 if (i !== j) {
3912 stop('expected_a_b', token, i, j || next_token.string);
3913 }
3914 do_function(set);
3915 if (set.block.length === 0) {
3916 warn('missing_a', token, 'throw');
3917 }
3918 p = set.first;
3919 if (!p || p.length !== 1) {
3920 stop('parameter_set_a', set, 'value');
3921 } else if (p[0].string !== 'value') {
3922 stop('expected_a_b', p[0], 'value', p[0].string);
3923 }
3924 name.first = [get, set];
3925 } else {
3926 name = next_token;
3927 i = property_name();
3928 if (typeof i !== 'string') {
3929 stop('missing_property');
3930 }
3931 advance(':');
3932 spaces();
3933 name.first = expression(10);
3934 }
3935 this.first.push(name);
3936 if (seen[i] === true) {
3937 warn('duplicate_a', next_token, i);
3938 }
3939 seen[i] = true;
3940 tally_property(i);
3941 if (next_token.id !== ',') {
3942 break;
3943 }
3944 for (;;) {
3945 comma();
3946 if (next_token.id !== ',') {
3947 break;
3948 }
3949 warn('unexpected_a', next_token);
3950 }
3951 if (next_token.id === '}' && !option.es5) {
3952 warn('unexpected_a', token);
3953 }
3954 }
3955 step_out('}', this);
3956 return this;
3957 });
3958
3959 stmt('{', function () {
3960 warn('statement_block');
3961 this.arity = 'statement';
3962 this.block = statements();
3963 this.disrupt = this.block.disrupt;
3964 advance('}', this);
3965 return this;
3966 });
3967
3968 stmt('/*global', directive);
3969 stmt('/*globals', directive);
3970 stmt('/*jslint', directive);
3971 stmt('/*member', directive);
3972 stmt('/*members', directive);
3973 stmt('/*property', directive);
3974 stmt('/*properties', directive);
3975
3976 stmt('var', function () {
3977
3978// JavaScript does not have block scope. It only has function scope. So,
3979// declaring a variable in a block can have unexpected consequences.
3980
3981// var.first will contain an array, the array containing name tokens
3982// and assignment tokens.
3983
3984 var assign, id, name;
3985
3986 if (funct['(vars)'] && !option.vars) {
3987 warn('combine_var');
3988 } else if (funct !== global_funct) {
3989 funct['(vars)'] = true;
3990 }
3991 this.arity = 'statement';
3992 this.first = [];
3993 step_in('var');
3994 for (;;) {
3995 name = next_token;
3996 id = identifier();
3997 add_label(name, 'becoming');
3998
3999 if (next_token.id === '=') {
4000 assign = next_token;
4001 assign.first = name;
4002 spaces();
4003 advance('=');
4004 spaces();
4005 if (next_token.id === 'undefined') {
4006 warn('unnecessary_initialize', token, id);
4007 }
4008 if (peek(0).id === '=' && next_token.identifier) {
4009 stop('var_a_not');
4010 }
4011 assign.second = expression(0);
4012 assign.arity = 'infix';
4013 this.first.push(assign);
4014 } else {
4015 this.first.push(name);
4016 }
4017 if (funct[id] === 'becoming') {
4018 funct[id] = 'unused';
4019 }
4020 if (next_token.id !== ',') {
4021 break;
4022 }
4023 comma();
4024 indent.wrap = false;
4025 if (var_mode && next_token.line === token.line &&
4026 this.first.length === 1) {
4027 var_mode = null;
4028 indent.open = false;
4029 indent.at -= option.indent;
4030 }
4031 spaces();
4032 edge();
4033 }
4034 var_mode = null;
4035 step_out();
4036 return this;
4037 });
4038
4039 stmt('function', function () {
4040 one_space();
4041 if (in_block) {
4042 warn('function_block', token);
4043 }
4044 var name = next_token, id = identifier();
4045 add_label(name, 'unction');
4046 no_space();
4047 this.arity = 'statement';
4048 do_function(this, id);
4049 if (next_token.id === '(' && next_token.line === token.line) {
4050 stop('function_statement');
4051 }
4052 return this;
4053 });
4054
4055 prefix('function', function () {
4056 if (!option.anon) {
4057 one_space();
4058 }
4059 var id = optional_identifier();
4060 if (id) {
4061 no_space();
4062 } else {
4063 id = '';
4064 }
4065 do_function(this, id);
4066 if (funct['(loopage)']) {
4067 warn('function_loop');
4068 }
4069 switch (next_token.id) {
4070 case ';':
4071 case '(':
4072 case ')':
4073 case ',':
4074 case ']':
4075 case '}':
4076 case ':':
4077 break;
4078 case '.':
4079 if (peek().string !== 'bind' || peek(1).id !== '(') {
4080 warn('unexpected_a');
4081 }
4082 break;
4083 default:
4084 stop('unexpected_a');
4085 }
4086 this.arity = 'function';
4087 return this;
4088 });
4089
4090 stmt('if', function () {
4091 var paren = next_token;
4092 one_space();
4093 advance('(');
4094 step_in('control');
4095 no_space();
4096 edge();
4097 this.arity = 'statement';
4098 this.first = expected_condition(expected_relation(expression(0)));
4099 no_space();
4100 step_out(')', paren);
4101 one_space();
4102 this.block = block(true);
4103 if (next_token.id === 'else') {
4104 one_space();
4105 advance('else');
4106 one_space();
4107 this['else'] = next_token.id === 'if' || next_token.id === 'switch'
4108 ? statement(true)
4109 : block(true);
4110 if (this['else'].disrupt && this.block.disrupt) {
4111 this.disrupt = true;
4112 }
4113 }
4114 return this;
4115 });
4116
4117 stmt('try', function () {
4118
4119// try.first The catch variable
4120// try.second The catch clause
4121// try.third The finally clause
4122// try.block The try block
4123
4124 var exception_variable, old_scope, paren;
4125 if (option.adsafe) {
4126 warn('adsafe_a', this);
4127 }
4128 one_space();
4129 this.arity = 'statement';
4130 this.block = block(false);
4131 if (next_token.id === 'catch') {
4132 one_space();
4133 advance('catch');
4134 one_space();
4135 paren = next_token;
4136 advance('(');
4137 step_in('control');
4138 no_space();
4139 edge();
4140 old_scope = scope;
4141 scope = Object.create(old_scope);
4142 exception_variable = next_token.string;
4143 this.first = exception_variable;
4144 if (!next_token.identifier) {
4145 warn('expected_identifier_a', next_token);
4146 } else {
4147 add_label(next_token, 'exception');
4148 }
4149 advance();
4150 no_space();
4151 step_out(')', paren);
4152 one_space();
4153 this.second = block(false);
4154 scope = old_scope;
4155 }
4156 if (next_token.id === 'finally') {
4157 one_space();
4158 advance('finally');
4159 one_space();
4160 this.third = block(false);
4161 } else if (!this.second) {
4162 stop('expected_a_b', next_token, 'catch', artifact());
4163 }
4164 return this;
4165 });
4166
4167 labeled_stmt('while', function () {
4168 one_space();
4169 var paren = next_token;
4170 funct['(breakage)'] += 1;
4171 funct['(loopage)'] += 1;
4172 advance('(');
4173 step_in('control');
4174 no_space();
4175 edge();
4176 this.arity = 'statement';
4177 this.first = expected_relation(expression(0));
4178 if (this.first.id !== 'true') {
4179 expected_condition(this.first, bundle.unexpected_a);
4180 }
4181 no_space();
4182 step_out(')', paren);
4183 one_space();
4184 this.block = block(true);
4185 if (this.block.disrupt) {
4186 warn('strange_loop', prev_token);
4187 }
4188 funct['(breakage)'] -= 1;
4189 funct['(loopage)'] -= 1;
4190 return this;
4191 });
4192
4193 reserve('with');
4194
4195 labeled_stmt('switch', function () {
4196
4197// switch.first the switch expression
4198// switch.second the array of cases. A case is 'case' or 'default' token:
4199// case.first the array of case expressions
4200// case.second the array of statements
4201// If all of the arrays of statements are disrupt, then the switch is disrupt.
4202
4203 var cases = [],
4204 old_in_block = in_block,
4205 particular,
4206 the_case = next_token,
4207 unbroken = true;
4208
4209 function find_duplicate_case(value) {
4210 if (are_similar(particular, value)) {
4211 warn('duplicate_a', value);
4212 }
4213 }
4214
4215 funct['(breakage)'] += 1;
4216 one_space();
4217 advance('(');
4218 no_space();
4219 step_in();
4220 this.arity = 'statement';
4221 this.first = expected_condition(expected_relation(expression(0)));
4222 no_space();
4223 step_out(')', the_case);
4224 one_space();
4225 advance('{');
4226 step_in();
4227 in_block = true;
4228 this.second = [];
4229 while (next_token.id === 'case') {
4230 the_case = next_token;
4231 cases.forEach(find_duplicate_case);
4232 the_case.first = [];
4233 the_case.arity = 'case';
4234 spaces();
4235 edge('case');
4236 advance('case');
4237 for (;;) {
4238 one_space();
4239 particular = expression(0);
4240 cases.forEach(find_duplicate_case);
4241 cases.push(particular);
4242 the_case.first.push(particular);
4243 if (particular.id === 'NaN') {
4244 warn('unexpected_a', particular);
4245 }
4246 no_space_only();
4247 advance(':');
4248 if (next_token.id !== 'case') {
4249 break;
4250 }
4251 spaces();
4252 edge('case');
4253 advance('case');
4254 }
4255 spaces();
4256 the_case.second = statements();
4257 if (the_case.second && the_case.second.length > 0) {
4258 particular = the_case.second[the_case.second.length - 1];
4259 if (particular.disrupt) {
4260 if (particular.id === 'break') {
4261 unbroken = false;
4262 }
4263 } else {
4264 warn('missing_a_after_b', next_token, 'break', 'case');
4265 }
4266 } else {
4267 warn('empty_case');
4268 }
4269 this.second.push(the_case);
4270 }
4271 if (this.second.length === 0) {
4272 warn('missing_a', next_token, 'case');
4273 }
4274 if (next_token.id === 'default') {
4275 spaces();
4276 the_case = next_token;
4277 the_case.arity = 'case';
4278 edge('case');
4279 advance('default');
4280 no_space_only();
4281 advance(':');
4282 spaces();
4283 the_case.second = statements();
4284 if (the_case.second && the_case.second.length > 0) {
4285 particular = the_case.second[the_case.second.length - 1];
4286 if (unbroken && particular.disrupt && particular.id !== 'break') {
4287 this.disrupt = true;
4288 }
4289 }
4290 this.second.push(the_case);
4291 }
4292 funct['(breakage)'] -= 1;
4293 spaces();
4294 step_out('}', this);
4295 in_block = old_in_block;
4296 return this;
4297 });
4298
4299 stmt('debugger', function () {
4300 if (!option.debug) {
4301 warn('unexpected_a', this);
4302 }
4303 this.arity = 'statement';
4304 return this;
4305 });
4306
4307 labeled_stmt('do', function () {
4308 funct['(breakage)'] += 1;
4309 funct['(loopage)'] += 1;
4310 one_space();
4311 this.arity = 'statement';
4312 this.block = block(true);
4313 if (this.block.disrupt) {
4314 warn('strange_loop', prev_token);
4315 }
4316 one_space();
4317 advance('while');
4318 var paren = next_token;
4319 one_space();
4320 advance('(');
4321 step_in();
4322 no_space();
4323 edge();
4324 this.first = expected_condition(expected_relation(expression(0)), bundle.unexpected_a);
4325 no_space();
4326 step_out(')', paren);
4327 funct['(breakage)'] -= 1;
4328 funct['(loopage)'] -= 1;
4329 return this;
4330 });
4331
4332 labeled_stmt('for', function () {
4333
4334 var blok, filter, ok = false, paren = next_token, value;
4335 this.arity = 'statement';
4336 funct['(breakage)'] += 1;
4337 funct['(loopage)'] += 1;
4338 advance('(');
4339 if (next_token.id === ';') {
4340 no_space();
4341 advance(';');
4342 no_space();
4343 advance(';');
4344 no_space();
4345 advance(')');
4346 blok = block(true);
4347 } else {
4348 step_in('control');
4349 spaces(this, paren);
4350 no_space();
4351 if (next_token.id === 'var') {
4352 stop('move_var');
4353 }
4354 edge();
4355 if (peek(0).id === 'in') {
4356 this.forin = true;
4357 value = next_token;
4358 switch (funct[value.string]) {
4359 case 'unused':
4360 funct[value.string] = 'var';
4361 break;
4362 case 'closure':
4363 case 'var':
4364 break;
4365 default:
4366 warn('bad_in_a', value);
4367 }
4368 advance();
4369 advance('in');
4370 this.first = value;
4371 this.second = expression(20);
4372 step_out(')', paren);
4373 blok = block(true);
4374 if (!option.forin) {
4375 if (blok.length === 1 && typeof blok[0] === 'object' &&
4376 blok[0].string === 'if' && !blok[0]['else']) {
4377 filter = blok[0].first;
4378 while (filter.id === '&&') {
4379 filter = filter.first;
4380 }
4381 switch (filter.id) {
4382 case '===':
4383 case '!==':
4384 ok = filter.first.id === '['
4385 ? filter.first.first.string === this.second.string &&
4386 filter.first.second.string === this.first.string
4387 : filter.first.id === 'typeof' &&
4388 filter.first.first.id === '[' &&
4389 filter.first.first.first.string === this.second.string &&
4390 filter.first.first.second.string === this.first.string;
4391 break;
4392 case '(':
4393 ok = filter.first.id === '.' && ((
4394 filter.first.first.string === this.second.string &&
4395 filter.first.second.string === 'hasOwnProperty' &&
4396 filter.second[0].string === this.first.string
4397 ) || (
4398 filter.first.first.string === 'ADSAFE' &&
4399 filter.first.second.string === 'has' &&
4400 filter.second[0].string === this.second.string &&
4401 filter.second[1].string === this.first.string
4402 ) || (
4403 filter.first.first.id === '.' &&
4404 filter.first.first.first.id === '.' &&
4405 filter.first.first.first.first.string === 'Object' &&
4406 filter.first.first.first.second.string === 'prototype' &&
4407 filter.first.first.second.string === 'hasOwnProperty' &&
4408 filter.first.second.string === 'call' &&
4409 filter.second[0].string === this.second.string &&
4410 filter.second[1].string === this.first.string
4411 ));
4412 break;
4413 }
4414 }
4415 if (!ok) {
4416 warn('for_if', this);
4417 }
4418 }
4419 } else {
4420 edge();
4421 this.first = [];
4422 for (;;) {
4423 this.first.push(expression(0, 'for'));
4424 if (next_token.id !== ',') {
4425 break;
4426 }
4427 comma();
4428 }
4429 semicolon();
4430 edge();
4431 this.second = expected_relation(expression(0));
4432 if (this.second.id !== 'true') {
4433 expected_condition(this.second, bundle.unexpected_a);
4434 }
4435 semicolon(token);
4436 if (next_token.id === ';') {
4437 stop('expected_a_b', next_token, ')', ';');
4438 }
4439 this.third = [];
4440 edge();
4441 for (;;) {
4442 this.third.push(expression(0, 'for'));
4443 if (next_token.id !== ',') {
4444 break;
4445 }
4446 comma();
4447 }
4448 no_space();
4449 step_out(')', paren);
4450 one_space();
4451 blok = block(true);
4452 }
4453 }
4454 if (blok.disrupt) {
4455 warn('strange_loop', prev_token);
4456 }
4457 this.block = blok;
4458 funct['(breakage)'] -= 1;
4459 funct['(loopage)'] -= 1;
4460 return this;
4461 });
4462
4463 disrupt_stmt('break', function () {
4464 var label = next_token.string;
4465 this.arity = 'statement';
4466 if (funct['(breakage)'] === 0) {
4467 warn('unexpected_a', this);
4468 }
4469 if (next_token.identifier && token.line === next_token.line) {
4470 one_space_only();
4471 if (funct[label] !== 'label') {
4472 warn('not_a_label', next_token);
4473 } else if (scope[label].funct !== funct) {
4474 warn('not_a_scope', next_token);
4475 }
4476 this.first = next_token;
4477 advance();
4478 }
4479 return this;
4480 });
4481
4482 disrupt_stmt('continue', function () {
4483 if (!option['continue']) {
4484 warn('unexpected_a', this);
4485 }
4486 var label = next_token.string;
4487 this.arity = 'statement';
4488 if (funct['(breakage)'] === 0) {
4489 warn('unexpected_a', this);
4490 }
4491 if (next_token.identifier && token.line === next_token.line) {
4492 one_space_only();
4493 if (funct[label] !== 'label') {
4494 warn('not_a_label', next_token);
4495 } else if (scope[label].funct !== funct) {
4496 warn('not_a_scope', next_token);
4497 }
4498 this.first = next_token;
4499 advance();
4500 }
4501 return this;
4502 });
4503
4504 disrupt_stmt('return', function () {
4505 if (funct === global_funct && xmode !== 'scriptstring') {
4506 warn('unexpected_a', this);
4507 }
4508 this.arity = 'statement';
4509 if (next_token.id !== ';' && next_token.line === token.line) {
4510 one_space_only();
4511 if (next_token.id === '/' || next_token.id === '(regexp)') {
4512 warn('wrap_regexp');
4513 }
4514 this.first = expression(20);
4515 }
4516 if (peek(0).id === '}' && peek(1).id === 'else') {
4517 warn('unexpected_else', this);
4518 }
4519 return this;
4520 });
4521
4522 disrupt_stmt('throw', function () {
4523 this.arity = 'statement';
4524 one_space_only();
4525 this.first = expression(20);
4526 return this;
4527 });
4528
4529
4530// Superfluous reserved words
4531
4532 reserve('class');
4533 reserve('const');
4534 reserve('enum');
4535 reserve('export');
4536 reserve('extends');
4537 reserve('import');
4538 reserve('super');
4539
4540// Harmony reserved words
4541
4542 reserve('implements');
4543 reserve('interface');
4544 reserve('let');
4545 reserve('package');
4546 reserve('private');
4547 reserve('protected');
4548 reserve('public');
4549 reserve('static');
4550 reserve('yield');
4551
4552
4553// Parse JSON
4554
4555 function json_value() {
4556
4557 function json_object() {
4558 var brace = next_token, object = {};
4559 advance('{');
4560 if (next_token.id !== '}') {
4561 while (next_token.id !== '(end)') {
4562 while (next_token.id === ',') {
4563 warn('unexpected_a', next_token);
4564 advance(',');
4565 }
4566 if (next_token.id !== '(string)') {
4567 warn('expected_string_a');
4568 }
4569 if (object[next_token.string] === true) {
4570 warn('duplicate_a');
4571 } else if (next_token.string === '__proto__') {
4572 warn('dangling_a');
4573 } else {
4574 object[next_token.string] = true;
4575 }
4576 advance();
4577 advance(':');
4578 json_value();
4579 if (next_token.id !== ',') {
4580 break;
4581 }
4582 advance(',');
4583 if (next_token.id === '}') {
4584 warn('unexpected_a', token);
4585 break;
4586 }
4587 }
4588 }
4589 advance('}', brace);
4590 }
4591
4592 function json_array() {
4593 var bracket = next_token;
4594 advance('[');
4595 if (next_token.id !== ']') {
4596 while (next_token.id !== '(end)') {
4597 while (next_token.id === ',') {
4598 warn('unexpected_a', next_token);
4599 advance(',');
4600 }
4601 json_value();
4602 if (next_token.id !== ',') {
4603 break;
4604 }
4605 advance(',');
4606 if (next_token.id === ']') {
4607 warn('unexpected_a', token);
4608 break;
4609 }
4610 }
4611 }
4612 advance(']', bracket);
4613 }
4614
4615 switch (next_token.id) {
4616 case '{':
4617 json_object();
4618 break;
4619 case '[':
4620 json_array();
4621 break;
4622 case 'true':
4623 case 'false':
4624 case 'null':
4625 case '(number)':
4626 case '(string)':
4627 advance();
4628 break;
4629 case '-':
4630 advance('-');
4631 no_space_only();
4632 advance('(number)');
4633 break;
4634 default:
4635 stop('unexpected_a');
4636 }
4637 }
4638
4639
4640// CSS parsing.
4641
4642 function css_name() {
4643 if (next_token.identifier) {
4644 advance();
4645 return true;
4646 }
4647 }
4648
4649
4650 function css_number() {
4651 if (next_token.id === '-') {
4652 advance('-');
4653 no_space_only();
4654 }
4655 if (next_token.id === '(number)') {
4656 advance('(number)');
4657 return true;
4658 }
4659 }
4660
4661
4662 function css_string() {
4663 if (next_token.id === '(string)') {
4664 advance();
4665 return true;
4666 }
4667 }
4668
4669 function css_color() {
4670 var i, number, paren, value;
4671 if (next_token.identifier) {
4672 value = next_token.string;
4673 if (value === 'rgb' || value === 'rgba') {
4674 advance();
4675 paren = next_token;
4676 advance('(');
4677 for (i = 0; i < 3; i += 1) {
4678 if (i) {
4679 comma();
4680 }
4681 number = next_token.number;
4682 if (next_token.id !== '(number)' || number < 0) {
4683 warn('expected_positive_a', next_token);
4684 advance();
4685 } else {
4686 advance();
4687 if (next_token.id === '%') {
4688 advance('%');
4689 if (number > 100) {
4690 warn('expected_percent_a', token, number);
4691 }
4692 } else {
4693 if (number > 255) {
4694 warn('expected_small_a', token, number);
4695 }
4696 }
4697 }
4698 }
4699 if (value === 'rgba') {
4700 comma();
4701 number = next_token.number;
4702 if (next_token.id !== '(number)' || number < 0 || number > 1) {
4703 warn('expected_fraction_a', next_token);
4704 }
4705 advance();
4706 if (next_token.id === '%') {
4707 warn('unexpected_a');
4708 advance('%');
4709 }
4710 }
4711 advance(')', paren);
4712 return true;
4713 }
4714 if (css_colorData[next_token.string] === true) {
4715 advance();
4716 return true;
4717 }
4718 } else if (next_token.id === '(color)') {
4719 advance();
4720 return true;
4721 }
4722 return false;
4723 }
4724
4725
4726 function css_length() {
4727 if (next_token.id === '-') {
4728 advance('-');
4729 no_space_only();
4730 }
4731 if (next_token.id === '(number)') {
4732 advance();
4733 if (next_token.id !== '(string)' &&
4734 css_lengthData[next_token.string] === true) {
4735 no_space_only();
4736 advance();
4737 } else if (+token.number !== 0) {
4738 warn('expected_linear_a');
4739 }
4740 return true;
4741 }
4742 return false;
4743 }
4744
4745
4746 function css_line_height() {
4747 if (next_token.id === '-') {
4748 advance('-');
4749 no_space_only();
4750 }
4751 if (next_token.id === '(number)') {
4752 advance();
4753 if (next_token.id !== '(string)' &&
4754 css_lengthData[next_token.string] === true) {
4755 no_space_only();
4756 advance();
4757 }
4758 return true;
4759 }
4760 return false;
4761 }
4762
4763
4764 function css_width() {
4765 if (next_token.identifier) {
4766 switch (next_token.string) {
4767 case 'thin':
4768 case 'medium':
4769 case 'thick':
4770 advance();
4771 return true;
4772 }
4773 } else {
4774 return css_length();
4775 }
4776 }
4777
4778
4779 function css_margin() {
4780 if (next_token.identifier) {
4781 if (next_token.string === 'auto') {
4782 advance();
4783 return true;
4784 }
4785 } else {
4786 return css_length();
4787 }
4788 }
4789
4790 function css_attr() {
4791 if (next_token.identifier && next_token.string === 'attr') {
4792 advance();
4793 advance('(');
4794 if (!next_token.identifier) {
4795 warn('expected_name_a');
4796 }
4797 advance();
4798 advance(')');
4799 return true;
4800 }
4801 return false;
4802 }
4803
4804
4805 function css_comma_list() {
4806 while (next_token.id !== ';') {
4807 if (!css_name() && !css_string()) {
4808 warn('expected_name_a');
4809 }
4810 if (next_token.id !== ',') {
4811 return true;
4812 }
4813 comma();
4814 }
4815 }
4816
4817
4818 function css_counter() {
4819 if (next_token.identifier && next_token.string === 'counter') {
4820 advance();
4821 advance('(');
4822 advance();
4823 if (next_token.id === ',') {
4824 comma();
4825 if (next_token.id !== '(string)') {
4826 warn('expected_string_a');
4827 }
4828 advance();
4829 }
4830 advance(')');
4831 return true;
4832 }
4833 if (next_token.identifier && next_token.string === 'counters') {
4834 advance();
4835 advance('(');
4836 if (!next_token.identifier) {
4837 warn('expected_name_a');
4838 }
4839 advance();
4840 if (next_token.id === ',') {
4841 comma();
4842 if (next_token.id !== '(string)') {
4843 warn('expected_string_a');
4844 }
4845 advance();
4846 }
4847 if (next_token.id === ',') {
4848 comma();
4849 if (next_token.id !== '(string)') {
4850 warn('expected_string_a');
4851 }
4852 advance();
4853 }
4854 advance(')');
4855 return true;
4856 }
4857 return false;
4858 }
4859
4860
4861 function css_radius() {
4862 return css_length() && (next_token.id !== '(number)' || css_length());
4863 }
4864
4865
4866 function css_shape() {
4867 var i;
4868 if (next_token.identifier && next_token.string === 'rect') {
4869 advance();
4870 advance('(');
4871 for (i = 0; i < 4; i += 1) {
4872 if (!css_length()) {
4873 warn('expected_number_a');
4874 break;
4875 }
4876 }
4877 advance(')');
4878 return true;
4879 }
4880 return false;
4881 }
4882
4883
4884 function css_url() {
4885 var c, url;
4886 if (next_token.identifier && next_token.string === 'url') {
4887 next_token = lex.range('(', ')');
4888 url = next_token.string;
4889 c = url.charAt(0);
4890 if (c === '"' || c === '\'') {
4891 if (url.slice(-1) !== c) {
4892 warn('bad_url_a');
4893 } else {
4894 url = url.slice(1, -1);
4895 if (url.indexOf(c) >= 0) {
4896 warn('bad_url_a');
4897 }
4898 }
4899 }
4900 if (!url) {
4901 warn('missing_url');
4902 }
4903 if (ux.test(url)) {
4904 stop('bad_url_a');
4905 }
4906 urls.push(url);
4907 advance();
4908 return true;
4909 }
4910 return false;
4911 }
4912
4913
4914 css_any = [css_url, function () {
4915 for (;;) {
4916 if (next_token.identifier) {
4917 switch (next_token.string.toLowerCase()) {
4918 case 'url':
4919 css_url();
4920 break;
4921 case 'expression':
4922 warn('unexpected_a');
4923 advance();
4924 break;
4925 default:
4926 advance();
4927 }
4928 } else {
4929 if (next_token.id === ';' || next_token.id === '!' ||
4930 next_token.id === '(end)' || next_token.id === '}') {
4931 return true;
4932 }
4933 advance();
4934 }
4935 }
4936 }];
4937
4938
4939 function font_face() {
4940 advance_identifier('font-family');
4941 advance(':');
4942 if (!css_name() && !css_string()) {
4943 stop('expected_name_a');
4944 }
4945 semicolon();
4946 advance_identifier('src');
4947 advance(':');
4948 while (true) {
4949 if (next_token.string === 'local') {
4950 advance_identifier('local');
4951 advance('(');
4952 if (ux.test(next_token.string)) {
4953 stop('bad_url_a');
4954 }
4955
4956 if (!css_name() && !css_string()) {
4957 stop('expected_name_a');
4958 }
4959 advance(')');
4960 } else if (!css_url()) {
4961 stop('expected_a_b', next_token, 'url', artifact());
4962 }
4963 if (next_token.id !== ',') {
4964 break;
4965 }
4966 comma();
4967 }
4968 semicolon();
4969 }
4970
4971
4972 css_border_style = [
4973 'none', 'dashed', 'dotted', 'double', 'groove',
4974 'hidden', 'inset', 'outset', 'ridge', 'solid'
4975 ];
4976
4977 css_break = [
4978 'auto', 'always', 'avoid', 'left', 'right'
4979 ];
4980
4981 css_media = {
4982 'all': true,
4983 'braille': true,
4984 'embossed': true,
4985 'handheld': true,
4986 'print': true,
4987 'projection': true,
4988 'screen': true,
4989 'speech': true,
4990 'tty': true,
4991 'tv': true
4992 };
4993
4994 css_overflow = [
4995 'auto', 'hidden', 'scroll', 'visible'
4996 ];
4997
4998 css_attribute_data = {
4999 background: [
5000 true, 'background-attachment', 'background-color',
5001 'background-image', 'background-position', 'background-repeat'
5002 ],
5003 'background-attachment': ['scroll', 'fixed'],
5004 'background-color': ['transparent', css_color],
5005 'background-image': ['none', css_url],
5006 'background-position': [
5007 2, [css_length, 'top', 'bottom', 'left', 'right', 'center']
5008 ],
5009 'background-repeat': [
5010 'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
5011 ],
5012 'border': [true, 'border-color', 'border-style', 'border-width'],
5013 'border-bottom': [
5014 true, 'border-bottom-color', 'border-bottom-style',
5015 'border-bottom-width'
5016 ],
5017 'border-bottom-color': css_color,
5018 'border-bottom-left-radius': css_radius,
5019 'border-bottom-right-radius': css_radius,
5020 'border-bottom-style': css_border_style,
5021 'border-bottom-width': css_width,
5022 'border-collapse': ['collapse', 'separate'],
5023 'border-color': ['transparent', 4, css_color],
5024 'border-left': [
5025 true, 'border-left-color', 'border-left-style', 'border-left-width'
5026 ],
5027 'border-left-color': css_color,
5028 'border-left-style': css_border_style,
5029 'border-left-width': css_width,
5030 'border-radius': function () {
5031 function count(separator) {
5032 var n = 1;
5033 if (separator) {
5034 advance(separator);
5035 }
5036 if (!css_length()) {
5037 return false;
5038 }
5039 while (next_token.id === '(number)') {
5040 if (!css_length()) {
5041 return false;
5042 }
5043 n += 1;
5044 }
5045 if (n > 4) {
5046 warn('bad_style');
5047 }
5048 return true;
5049 }
5050
5051 return count() && (next_token.id !== '/' || count('/'));
5052 },
5053 'border-right': [
5054 true, 'border-right-color', 'border-right-style',
5055 'border-right-width'
5056 ],
5057 'border-right-color': css_color,
5058 'border-right-style': css_border_style,
5059 'border-right-width': css_width,
5060 'border-spacing': [2, css_length],
5061 'border-style': [4, css_border_style],
5062 'border-top': [
5063 true, 'border-top-color', 'border-top-style', 'border-top-width'
5064 ],
5065 'border-top-color': css_color,
5066 'border-top-left-radius': css_radius,
5067 'border-top-right-radius': css_radius,
5068 'border-top-style': css_border_style,
5069 'border-top-width': css_width,
5070 'border-width': [4, css_width],
5071 bottom: [css_length, 'auto'],
5072 'caption-side' : ['bottom', 'left', 'right', 'top'],
5073 clear: ['both', 'left', 'none', 'right'],
5074 clip: [css_shape, 'auto'],
5075 color: css_color,
5076 content: [
5077 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote',
5078 css_string, css_url, css_counter, css_attr
5079 ],
5080 'counter-increment': [
5081 css_name, 'none'
5082 ],
5083 'counter-reset': [
5084 css_name, 'none'
5085 ],
5086 cursor: [
5087 css_url, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move',
5088 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize',
5089 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait'
5090 ],
5091 direction: ['ltr', 'rtl'],
5092 display: [
5093 'block', 'compact', 'inline', 'inline-block', 'inline-table',
5094 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption',
5095 'table-cell', 'table-column', 'table-column-group',
5096 'table-footer-group', 'table-header-group', 'table-row',
5097 'table-row-group'
5098 ],
5099 'empty-cells': ['show', 'hide'],
5100 'float': ['left', 'none', 'right'],
5101 font: [
5102 'caption', 'icon', 'menu', 'message-box', 'small-caption',
5103 'status-bar', true, 'font-size', 'font-style', 'font-weight',
5104 'font-family'
5105 ],
5106 'font-family': css_comma_list,
5107 'font-size': [
5108 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large',
5109 'xx-large', 'larger', 'smaller', css_length
5110 ],
5111 'font-size-adjust': ['none', css_number],
5112 'font-stretch': [
5113 'normal', 'wider', 'narrower', 'ultra-condensed',
5114 'extra-condensed', 'condensed', 'semi-condensed',
5115 'semi-expanded', 'expanded', 'extra-expanded'
5116 ],
5117 'font-style': [
5118 'normal', 'italic', 'oblique'
5119 ],
5120 'font-variant': [
5121 'normal', 'small-caps'
5122 ],
5123 'font-weight': [
5124 'normal', 'bold', 'bolder', 'lighter', css_number
5125 ],
5126 height: [css_length, 'auto'],
5127 left: [css_length, 'auto'],
5128 'letter-spacing': ['normal', css_length],
5129 'line-height': ['normal', css_line_height],
5130 'list-style': [
5131 true, 'list-style-image', 'list-style-position', 'list-style-type'
5132 ],
5133 'list-style-image': ['none', css_url],
5134 'list-style-position': ['inside', 'outside'],
5135 'list-style-type': [
5136 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero',
5137 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha',
5138 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana',
5139 'hiragana-iroha', 'katakana-oroha', 'none'
5140 ],
5141 margin: [4, css_margin],
5142 'margin-bottom': css_margin,
5143 'margin-left': css_margin,
5144 'margin-right': css_margin,
5145 'margin-top': css_margin,
5146 'marker-offset': [css_length, 'auto'],
5147 'max-height': [css_length, 'none'],
5148 'max-width': [css_length, 'none'],
5149 'min-height': css_length,
5150 'min-width': css_length,
5151 opacity: css_number,
5152 outline: [true, 'outline-color', 'outline-style', 'outline-width'],
5153 'outline-color': ['invert', css_color],
5154 'outline-style': [
5155 'dashed', 'dotted', 'double', 'groove', 'inset', 'none',
5156 'outset', 'ridge', 'solid'
5157 ],
5158 'outline-width': css_width,
5159 overflow: css_overflow,
5160 'overflow-x': css_overflow,
5161 'overflow-y': css_overflow,
5162 padding: [4, css_length],
5163 'padding-bottom': css_length,
5164 'padding-left': css_length,
5165 'padding-right': css_length,
5166 'padding-top': css_length,
5167 'page-break-after': css_break,
5168 'page-break-before': css_break,
5169 position: ['absolute', 'fixed', 'relative', 'static'],
5170 quotes: [8, css_string],
5171 right: [css_length, 'auto'],
5172 'table-layout': ['auto', 'fixed'],
5173 'text-align': ['center', 'justify', 'left', 'right'],
5174 'text-decoration': [
5175 'none', 'underline', 'overline', 'line-through', 'blink'
5176 ],
5177 'text-indent': css_length,
5178 'text-shadow': ['none', 4, [css_color, css_length]],
5179 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'],
5180 top: [css_length, 'auto'],
5181 'unicode-bidi': ['normal', 'embed', 'bidi-override'],
5182 'vertical-align': [
5183 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle',
5184 'text-bottom', css_length
5185 ],
5186 visibility: ['visible', 'hidden', 'collapse'],
5187 'white-space': [
5188 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit'
5189 ],
5190 width: [css_length, 'auto'],
5191 'word-spacing': ['normal', css_length],
5192 'word-wrap': ['break-word', 'normal'],
5193 'z-index': ['auto', css_number]
5194 };
5195
5196 function style_attribute() {
5197 var v;
5198 while (next_token.id === '*' || next_token.id === '#' ||
5199 next_token.string === '_') {
5200 if (!option.css) {
5201 warn('unexpected_a');
5202 }
5203 advance();
5204 }
5205 if (next_token.id === '-') {
5206 if (!option.css) {
5207 warn('unexpected_a');
5208 }
5209 advance('-');
5210 if (!next_token.identifier) {
5211 warn('expected_nonstandard_style_attribute');
5212 }
5213 advance();
5214 return css_any;
5215 }
5216 if (!next_token.identifier) {
5217 warn('expected_style_attribute');
5218 } else {
5219 if (Object.prototype.hasOwnProperty.call(css_attribute_data,
5220 next_token.string)) {
5221 v = css_attribute_data[next_token.string];
5222 } else {
5223 v = css_any;
5224 if (!option.css) {
5225 warn('unrecognized_style_attribute_a');
5226 }
5227 }
5228 }
5229 advance();
5230 return v;
5231 }
5232
5233
5234 function style_value(v) {
5235 var i = 0,
5236 n,
5237 once,
5238 match,
5239 round,
5240 start = 0,
5241 vi;
5242 switch (typeof v) {
5243 case 'function':
5244 return v();
5245 case 'string':
5246 if (next_token.identifier && next_token.string === v) {
5247 advance();
5248 return true;
5249 }
5250 return false;
5251 }
5252 for (;;) {
5253 if (i >= v.length) {
5254 return false;
5255 }
5256 vi = v[i];
5257 i += 1;
5258 if (typeof vi === 'boolean') {
5259 break;
5260 } else if (typeof vi === 'number') {
5261 n = vi;
5262 vi = v[i];
5263 i += 1;
5264 } else {
5265 n = 1;
5266 }
5267 match = false;
5268 while (n > 0) {
5269 if (style_value(vi)) {
5270 match = true;
5271 n -= 1;
5272 } else {
5273 break;
5274 }
5275 }
5276 if (match) {
5277 return true;
5278 }
5279 }
5280 start = i;
5281 once = [];
5282 for (;;) {
5283 round = false;
5284 for (i = start; i < v.length; i += 1) {
5285 if (!once[i]) {
5286 if (style_value(css_attribute_data[v[i]])) {
5287 match = true;
5288 round = true;
5289 once[i] = true;
5290 break;
5291 }
5292 }
5293 }
5294 if (!round) {
5295 return match;
5296 }
5297 }
5298 }
5299
5300 function style_child() {
5301 if (next_token.id === '(number)') {
5302 advance();
5303 if (next_token.string === 'n' && next_token.identifier) {
5304 no_space_only();
5305 advance();
5306 if (next_token.id === '+') {
5307 no_space_only();
5308 advance('+');
5309 no_space_only();
5310 advance('(number)');
5311 }
5312 }
5313 return;
5314 }
5315 if (next_token.identifier &&
5316 (next_token.string === 'odd' || next_token.string === 'even')) {
5317 advance();
5318 return;
5319 }
5320 warn('unexpected_a');
5321 }
5322
5323 function substyle() {
5324 var v;
5325 for (;;) {
5326 if (next_token.id === '}' || next_token.id === '(end)' ||
5327 (xquote && next_token.id === xquote)) {
5328 return;
5329 }
5330 v = style_attribute();
5331 advance(':');
5332 if (next_token.identifier && next_token.string === 'inherit') {
5333 advance();
5334 } else {
5335 if (!style_value(v)) {
5336 warn('unexpected_a');
5337 advance();
5338 }
5339 }
5340 if (next_token.id === '!') {
5341 advance('!');
5342 no_space_only();
5343 if (next_token.identifier && next_token.string === 'important') {
5344 advance();
5345 } else {
5346 warn('expected_a_b',
5347 next_token, 'important', artifact());
5348 }
5349 }
5350 if (next_token.id === '}' || next_token.id === xquote) {
5351 warn('expected_a_b', next_token, ';', artifact());
5352 } else {
5353 semicolon();
5354 }
5355 }
5356 }
5357
5358 function style_selector() {
5359 if (next_token.identifier) {
5360 if (!Object.prototype.hasOwnProperty.call(html_tag, option.cap
5361 ? next_token.string.toLowerCase()
5362 : next_token.string)) {
5363 warn('expected_tagname_a');
5364 }
5365 advance();
5366 } else {
5367 switch (next_token.id) {
5368 case '>':
5369 case '+':
5370 advance();
5371 style_selector();
5372 break;
5373 case ':':
5374 advance(':');
5375 switch (next_token.string) {
5376 case 'active':
5377 case 'after':
5378 case 'before':
5379 case 'checked':
5380 case 'disabled':
5381 case 'empty':
5382 case 'enabled':
5383 case 'first-child':
5384 case 'first-letter':
5385 case 'first-line':
5386 case 'first-of-type':
5387 case 'focus':
5388 case 'hover':
5389 case 'last-child':
5390 case 'last-of-type':
5391 case 'link':
5392 case 'only-of-type':
5393 case 'root':
5394 case 'target':
5395 case 'visited':
5396 advance_identifier(next_token.string);
5397 break;
5398 case 'lang':
5399 advance_identifier('lang');
5400 advance('(');
5401 if (!next_token.identifier) {
5402 warn('expected_lang_a');
5403 }
5404 advance(')');
5405 break;
5406 case 'nth-child':
5407 case 'nth-last-child':
5408 case 'nth-last-of-type':
5409 case 'nth-of-type':
5410 advance_identifier(next_token.string);
5411 advance('(');
5412 style_child();
5413 advance(')');
5414 break;
5415 case 'not':
5416 advance_identifier('not');
5417 advance('(');
5418 if (next_token.id === ':' && peek(0).string === 'not') {
5419 warn('not');
5420 }
5421 style_selector();
5422 advance(')');
5423 break;
5424 default:
5425 warn('expected_pseudo_a');
5426 }
5427 break;
5428 case '#':
5429 advance('#');
5430 if (!next_token.identifier) {
5431 warn('expected_id_a');
5432 }
5433 advance();
5434 break;
5435 case '*':
5436 advance('*');
5437 break;
5438 case '.':
5439 advance('.');
5440 if (!next_token.identifier) {
5441 warn('expected_class_a');
5442 }
5443 advance();
5444 break;
5445 case '[':
5446 advance('[');
5447 if (!next_token.identifier) {
5448 warn('expected_attribute_a');
5449 }
5450 advance();
5451 if (next_token.id === '=' || next_token.string === '~=' ||
5452 next_token.string === '$=' ||
5453 next_token.string === '|=' ||
5454 next_token.id === '*=' ||
5455 next_token.id === '^=') {
5456 advance();
5457 if (next_token.id !== '(string)') {
5458 warn('expected_string_a');
5459 }
5460 advance();
5461 }
5462 advance(']');
5463 break;
5464 default:
5465 stop('expected_selector_a');
5466 }
5467 }
5468 }
5469
5470 function style_pattern() {
5471 if (next_token.id === '{') {
5472 warn('expected_style_pattern');
5473 }
5474 for (;;) {
5475 style_selector();
5476 if (next_token.id === '</' || next_token.id === '{' ||
5477 next_token.id === '}' || next_token.id === '(end)') {
5478 return '';
5479 }
5480 if (next_token.id === ',') {
5481 comma();
5482 }
5483 }
5484 }
5485
5486 function style_list() {
5487 while (next_token.id !== '}' && next_token.id !== '</' &&
5488 next_token.id !== '(end)') {
5489 style_pattern();
5490 xmode = 'styleproperty';
5491 if (next_token.id === ';') {
5492 semicolon();
5493 } else {
5494 advance('{');
5495 substyle();
5496 xmode = 'style';
5497 advance('}');
5498 }
5499 }
5500 }
5501
5502 function styles() {
5503 var i;
5504 while (next_token.id === '@') {
5505 i = peek();
5506 advance('@');
5507 switch (next_token.string) {
5508 case 'import':
5509 advance_identifier('import');
5510 if (!css_url()) {
5511 warn('expected_a_b',
5512 next_token, 'url', artifact());
5513 advance();
5514 }
5515 semicolon();
5516 break;
5517 case 'media':
5518 advance_identifier('media');
5519 for (;;) {
5520 if (!next_token.identifier || css_media[next_token.string] !== true) {
5521 stop('expected_media_a');
5522 }
5523 advance();
5524 if (next_token.id !== ',') {
5525 break;
5526 }
5527 comma();
5528 }
5529 advance('{');
5530 style_list();
5531 advance('}');
5532 break;
5533 case 'font-face':
5534 advance_identifier('font-face');
5535 advance('{');
5536 font_face();
5537 advance('}');
5538 break;
5539 default:
5540 stop('expected_at_a');
5541 }
5542 }
5543 style_list();
5544 }
5545
5546
5547// Parse HTML
5548
5549 function do_begin(n) {
5550 if (n !== 'html' && !option.fragment) {
5551 if (n === 'div' && option.adsafe) {
5552 stop('adsafe_fragment');
5553 } else {
5554 stop('expected_a_b', token, 'html', n);
5555 }
5556 }
5557 if (option.adsafe) {
5558 if (n === 'html') {
5559 stop('adsafe_html', token);
5560 }
5561 if (option.fragment) {
5562 if (n !== 'div') {
5563 stop('adsafe_div', token);
5564 }
5565 } else {
5566 stop('adsafe_fragment', token);
5567 }
5568 }
5569 option.browser = true;
5570 }
5571
5572 function do_attribute(a, v) {
5573 var u, x;
5574 if (a === 'id') {
5575 u = typeof v === 'string' ? v.toUpperCase() : '';
5576 if (ids[u] === true) {
5577 warn('duplicate_a', next_token, v);
5578 }
5579 if (!/^[A-Za-z][A-Za-z0-9._:\-]*$/.test(v)) {
5580 warn('bad_id_a', next_token, v);
5581 } else if (option.adsafe) {
5582 if (adsafe_id) {
5583 if (v.slice(0, adsafe_id.length) !== adsafe_id) {
5584 warn('adsafe_prefix_a', next_token, adsafe_id);
5585 } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {
5586 warn('adsafe_bad_id');
5587 }
5588 } else {
5589 adsafe_id = v;
5590 if (!/^[A-Z]+_$/.test(v)) {
5591 warn('adsafe_bad_id');
5592 }
5593 }
5594 }
5595 x = v.search(dx);
5596 if (x >= 0) {
5597 warn('unexpected_char_a_b', token, v.charAt(x), a);
5598 }
5599 ids[u] = true;
5600 } else if (a === 'class' || a === 'type' || a === 'name') {
5601 x = v.search(qx);
5602 if (x >= 0) {
5603 warn('unexpected_char_a_b', token, v.charAt(x), a);
5604 }
5605 ids[u] = true;
5606 } else if (a === 'href' || a === 'background' ||
5607 a === 'content' || a === 'data' ||
5608 a.indexOf('src') >= 0 || a.indexOf('url') >= 0) {
5609 if (option.safe && ux.test(v)) {
5610 stop('bad_url_a', next_token, v);
5611 }
5612 urls.push(v);
5613 } else if (a === 'for') {
5614 if (option.adsafe) {
5615 if (adsafe_id) {
5616 if (v.slice(0, adsafe_id.length) !== adsafe_id) {
5617 warn('adsafe_prefix_a', next_token, adsafe_id);
5618 } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {
5619 warn('adsafe_bad_id');
5620 }
5621 } else {
5622 warn('adsafe_bad_id');
5623 }
5624 }
5625 } else if (a === 'name') {
5626 if (option.adsafe && v.indexOf('_') >= 0) {
5627 warn('adsafe_name_a', next_token, v);
5628 }
5629 }
5630 }
5631
5632 function do_tag(name, attribute) {
5633 var i, tag = html_tag[name], script, x;
5634 src = false;
5635 if (!tag) {
5636 stop(
5637 bundle.unrecognized_tag_a,
5638 next_token,
5639 name === name.toLowerCase()
5640 ? name
5641 : name + ' (capitalization error)'
5642 );
5643 }
5644 if (stack.length > 0) {
5645 if (name === 'html') {
5646 stop('unexpected_a', token, name);
5647 }
5648 x = tag.parent;
5649 if (x) {
5650 if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) {
5651 stop('tag_a_in_b', token, name, x);
5652 }
5653 } else if (!option.adsafe && !option.fragment) {
5654 i = stack.length;
5655 do {
5656 if (i <= 0) {
5657 stop('tag_a_in_b', token, name, 'body');
5658 }
5659 i -= 1;
5660 } while (stack[i].name !== 'body');
5661 }
5662 }
5663 switch (name) {
5664 case 'div':
5665 if (option.adsafe && stack.length === 1 && !adsafe_id) {
5666 warn('adsafe_missing_id');
5667 }
5668 break;
5669 case 'script':
5670 xmode = 'script';
5671 advance('>');
5672 if (attribute.lang) {
5673 warn('lang', token);
5674 }
5675 if (option.adsafe && stack.length !== 1) {
5676 warn('adsafe_placement', token);
5677 }
5678 if (attribute.src) {
5679 if (option.adsafe && (!adsafe_may || !approved[attribute.src])) {
5680 warn('adsafe_source', token);
5681 }
5682 } else {
5683 step_in(next_token.from);
5684 edge();
5685 use_strict();
5686 adsafe_top = true;
5687 script = statements();
5688
5689// JSLint is also the static analyzer for ADsafe. See www.ADsafe.org.
5690
5691 if (option.adsafe) {
5692 if (adsafe_went) {
5693 stop('adsafe_script', token);
5694 }
5695 if (script.length !== 1 ||
5696 aint(script[0], 'id', '(') ||
5697 aint(script[0].first, 'id', '.') ||
5698 aint(script[0].first.first, 'string', 'ADSAFE') ||
5699 aint(script[0].second[0], 'string', adsafe_id)) {
5700 stop('adsafe_id_go');
5701 }
5702 switch (script[0].first.second.string) {
5703 case 'id':
5704 if (adsafe_may || adsafe_went ||
5705 script[0].second.length !== 1) {
5706 stop('adsafe_id', next_token);
5707 }
5708 adsafe_may = true;
5709 break;
5710 case 'go':
5711 if (adsafe_went) {
5712 stop('adsafe_go');
5713 }
5714 if (script[0].second.length !== 2 ||
5715 aint(script[0].second[1], 'id', 'function') ||
5716 !script[0].second[1].first ||
5717 aint(script[0].second[1].first[0], 'string', 'dom') ||
5718 script[0].second[1].first.length > 2 ||
5719 (script[0].second[1].first.length === 2 &&
5720 aint(script[0].second[1].first[1], 'string', 'lib'))) {
5721 stop('adsafe_go', next_token);
5722 }
5723 adsafe_went = true;
5724 break;
5725 default:
5726 stop('adsafe_id_go');
5727 }
5728 }
5729 indent = null;
5730 }
5731 xmode = 'html';
5732 advance('</');
5733 advance_identifier('script');
5734 xmode = 'outer';
5735 break;
5736 case 'style':
5737 xmode = 'style';
5738 advance('>');
5739 styles();
5740 xmode = 'html';
5741 advance('</');
5742 advance_identifier('style');
5743 break;
5744 case 'input':
5745 switch (attribute.type) {
5746 case 'button':
5747 case 'checkbox':
5748 case 'radio':
5749 case 'reset':
5750 case 'submit':
5751 break;
5752 case 'file':
5753 case 'hidden':
5754 case 'image':
5755 case 'password':
5756 case 'text':
5757 if (option.adsafe && attribute.autocomplete !== 'off') {
5758 warn('adsafe_autocomplete');
5759 }
5760 break;
5761 default:
5762 warn('bad_type');
5763 }
5764 break;
5765 case 'applet':
5766 case 'body':
5767 case 'embed':
5768 case 'frame':
5769 case 'frameset':
5770 case 'head':
5771 case 'iframe':
5772 case 'noembed':
5773 case 'noframes':
5774 case 'object':
5775 case 'param':
5776 if (option.adsafe) {
5777 warn('adsafe_tag', next_token, name);
5778 }
5779 break;
5780 }
5781 }
5782
5783
5784 function closetag(name) {
5785 return '</' + name + '>';
5786 }
5787
5788 function html() {
5789 var attribute, attributes, is_empty, name, old_white = option.white,
5790 quote, tag_name, tag, wmode;
5791 xmode = 'html';
5792 xquote = '';
5793 stack = null;
5794 for (;;) {
5795 switch (next_token.string) {
5796 case '<':
5797 xmode = 'html';
5798 advance('<');
5799 attributes = {};
5800 tag_name = next_token;
5801 name = tag_name.string;
5802 advance_identifier(name);
5803 if (option.cap) {
5804 name = name.toLowerCase();
5805 }
5806 tag_name.name = name;
5807 if (!stack) {
5808 stack = [];
5809 do_begin(name);
5810 }
5811 tag = html_tag[name];
5812 if (typeof tag !== 'object') {
5813 stop('unrecognized_tag_a', tag_name, name);
5814 }
5815 is_empty = tag.empty;
5816 tag_name.type = name;
5817 for (;;) {
5818 if (next_token.id === '/') {
5819 advance('/');
5820 if (next_token.id !== '>') {
5821 warn('expected_a_b', next_token, '>', artifact());
5822 }
5823 break;
5824 }
5825 if (next_token.id && next_token.id.charAt(0) === '>') {
5826 break;
5827 }
5828 if (!next_token.identifier) {
5829 if (next_token.id === '(end)' || next_token.id === '(error)') {
5830 warn('expected_a_b', next_token, '>', artifact());
5831 }
5832 warn('bad_name_a');
5833 }
5834 option.white = false;
5835 spaces();
5836 attribute = next_token.string;
5837 option.white = old_white;
5838 advance();
5839 if (!option.cap && attribute !== attribute.toLowerCase()) {
5840 warn('attribute_case_a', token);
5841 }
5842 attribute = attribute.toLowerCase();
5843 xquote = '';
5844 if (Object.prototype.hasOwnProperty.call(attributes, attribute)) {
5845 warn('duplicate_a', token, attribute);
5846 }
5847 if (attribute.slice(0, 2) === 'on') {
5848 if (!option.on) {
5849 warn('html_handlers');
5850 }
5851 xmode = 'scriptstring';
5852 advance('=');
5853 quote = next_token.id;
5854 if (quote !== '"' && quote !== '\'') {
5855 stop('expected_a_b', next_token, '"', artifact());
5856 }
5857 xquote = quote;
5858 wmode = option.white;
5859 option.white = true;
5860 advance(quote);
5861 use_strict();
5862 statements();
5863 option.white = wmode;
5864 if (next_token.id !== quote) {
5865 stop('expected_a_b', next_token, quote, artifact());
5866 }
5867 xmode = 'html';
5868 xquote = '';
5869 advance(quote);
5870 tag = false;
5871 } else if (attribute === 'style') {
5872 xmode = 'scriptstring';
5873 advance('=');
5874 quote = next_token.id;
5875 if (quote !== '"' && quote !== '\'') {
5876 stop('expected_a_b', next_token, '"', artifact());
5877 }
5878 xmode = 'styleproperty';
5879 xquote = quote;
5880 advance(quote);
5881 substyle();
5882 xmode = 'html';
5883 xquote = '';
5884 advance(quote);
5885 tag = false;
5886 } else {
5887 if (next_token.id === '=') {
5888 advance('=');
5889 tag = next_token.string;
5890 if (!next_token.identifier &&
5891 next_token.id !== '"' &&
5892 next_token.id !== '\'' &&
5893 next_token.id !== '(string)' &&
5894 next_token.id !== '(string)' &&
5895 next_token.id !== '(color)') {
5896 warn('expected_attribute_value_a', token, attribute);
5897 }
5898 advance();
5899 } else {
5900 tag = true;
5901 }
5902 }
5903 attributes[attribute] = tag;
5904 do_attribute(attribute, tag);
5905 }
5906 do_tag(name, attributes);
5907 if (!is_empty) {
5908 stack.push(tag_name);
5909 }
5910 xmode = 'outer';
5911 advance('>');
5912 break;
5913 case '</':
5914 xmode = 'html';
5915 advance('</');
5916 if (!next_token.identifier) {
5917 warn('bad_name_a');
5918 }
5919 name = next_token.string;
5920 if (option.cap) {
5921 name = name.toLowerCase();
5922 }
5923 advance();
5924 if (!stack) {
5925 stop('unexpected_a', next_token, closetag(name));
5926 }
5927 tag_name = stack.pop();
5928 if (!tag_name) {
5929 stop('unexpected_a', next_token, closetag(name));
5930 }
5931 if (tag_name.name !== name) {
5932 stop('expected_a_b',
5933 next_token, closetag(tag_name.name), closetag(name));
5934 }
5935 if (next_token.id !== '>') {
5936 stop('expected_a_b', next_token, '>', artifact());
5937 }
5938 xmode = 'outer';
5939 advance('>');
5940 break;
5941 case '<!':
5942 if (option.safe) {
5943 warn('adsafe_a');
5944 }
5945 xmode = 'html';
5946 for (;;) {
5947 advance();
5948 if (next_token.id === '>' || next_token.id === '(end)') {
5949 break;
5950 }
5951 if (next_token.string.indexOf('--') >= 0) {
5952 stop('unexpected_a', next_token, '--');
5953 }
5954 if (next_token.string.indexOf('<') >= 0) {
5955 stop('unexpected_a', next_token, '<');
5956 }
5957 if (next_token.string.indexOf('>') >= 0) {
5958 stop('unexpected_a', next_token, '>');
5959 }
5960 }
5961 xmode = 'outer';
5962 advance('>');
5963 break;
5964 case '(end)':
5965 return;
5966 default:
5967 if (next_token.id === '(end)') {
5968 stop('missing_a', next_token,
5969 '</' + stack[stack.length - 1].string + '>');
5970 } else {
5971 advance();
5972 }
5973 }
5974 if (stack && stack.length === 0 && (option.adsafe ||
5975 !option.fragment || next_token.id === '(end)')) {
5976 break;
5977 }
5978 }
5979 if (next_token.id !== '(end)') {
5980 stop('unexpected_a');
5981 }
5982 }
5983
5984
5985// The actual JSLINT function itself.
5986
5987 itself = function JSLint(the_source, the_option) {
5988
5989 var i, predef, tree;
5990 JSLINT.errors = [];
5991 JSLINT.tree = '';
5992 begin = prev_token = token = next_token =
5993 Object.create(syntax['(begin)']);
5994 predefined = {};
5995 add_to_predefined(standard);
5996 property = {};
5997 if (the_option) {
5998 option = Object.create(the_option);
5999 predef = option.predef;
6000 if (predef) {
6001 if (Array.isArray(predef)) {
6002 for (i = 0; i < predef.length; i += 1) {
6003 predefined[predef[i]] = true;
6004 }
6005 } else if (typeof predef === 'object') {
6006 add_to_predefined(predef);
6007 }
6008 }
6009 do_safe();
6010 } else {
6011 option = {};
6012 }
6013 option.indent = +option.indent || 4;
6014 option.maxerr = +option.maxerr || 50;
6015 adsafe_id = '';
6016 adsafe_may = adsafe_top = adsafe_went = false;
6017 approved = {};
6018 if (option.approved) {
6019 for (i = 0; i < option.approved.length; i += 1) {
6020 approved[option.approved[i]] = option.approved[i];
6021 }
6022 } else {
6023 approved.test = 'test';
6024 }
6025 tab = '';
6026 for (i = 0; i < option.indent; i += 1) {
6027 tab += ' ';
6028 }
6029 global_scope = scope = {};
6030 global_funct = funct = {
6031 '(scope)': scope,
6032 '(breakage)': 0,
6033 '(loopage)': 0
6034 };
6035 functions = [funct];
6036
6037 comments_off = false;
6038 ids = {};
6039 in_block = false;
6040 indent = null;
6041 json_mode = false;
6042 lookahead = [];
6043 node_js = false;
6044 prereg = true;
6045 src = false;
6046 stack = null;
6047 strict_mode = false;
6048 urls = [];
6049 var_mode = null;
6050 warnings = 0;
6051 xmode = '';
6052 lex.init(the_source);
6053
6054 assume();
6055
6056 try {
6057 advance();
6058 if (next_token.id === '(number)') {
6059 stop('unexpected_a');
6060 } else if (next_token.string.charAt(0) === '<') {
6061 html();
6062 if (option.adsafe && !adsafe_went) {
6063 warn('adsafe_go', this);
6064 }
6065 } else {
6066 switch (next_token.id) {
6067 case '{':
6068 case '[':
6069 json_mode = true;
6070 json_value();
6071 break;
6072 case '@':
6073 case '*':
6074 case '#':
6075 case '.':
6076 case ':':
6077 xmode = 'style';
6078 advance();
6079 if (token.id !== '@' || !next_token.identifier ||
6080 next_token.string !== 'charset' || token.line !== 1 ||
6081 token.from !== 1) {
6082 stop('css');
6083 }
6084 advance();
6085 if (next_token.id !== '(string)' &&
6086 next_token.string !== 'UTF-8') {
6087 stop('css');
6088 }
6089 advance();
6090 semicolon();
6091 styles();
6092 break;
6093
6094 default:
6095 if (option.adsafe && option.fragment) {
6096 stop('expected_a_b',
6097 next_token, '<div>', artifact());
6098 }
6099
6100// If the first token is a semicolon, ignore it. This is sometimes used when
6101// files are intended to be appended to files that may be sloppy. A sloppy
6102// file may be depending on semicolon insertion on its last line.
6103
6104 step_in(1);
6105 if (next_token.id === ';' && !node_js) {
6106 semicolon();
6107 }
6108 adsafe_top = true;
6109 tree = statements();
6110 begin.first = tree;
6111 JSLINT.tree = begin;
6112 // infer_types(tree);
6113 if (option.adsafe && (tree.length !== 1 ||
6114 aint(tree[0], 'id', '(') ||
6115 aint(tree[0].first, 'id', '.') ||
6116 aint(tree[0].first.first, 'string', 'ADSAFE') ||
6117 aint(tree[0].first.second, 'string', 'lib') ||
6118 tree[0].second.length !== 2 ||
6119 tree[0].second[0].id !== '(string)' ||
6120 aint(tree[0].second[1], 'id', 'function'))) {
6121 stop('adsafe_lib');
6122 }
6123 if (tree.disrupt) {
6124 warn('weird_program', prev_token);
6125 }
6126 }
6127 }
6128 indent = null;
6129 advance('(end)');
6130 } catch (e) {
6131 if (e) { // ~~
6132 JSLINT.errors.push({
6133 reason : e.message,
6134 line : e.line || next_token.line,
6135 character : e.character || next_token.from
6136 }, null);
6137 }
6138 }
6139 return JSLINT.errors.length === 0;
6140 };
6141
6142
6143// Data summary.
6144
6145 itself.data = function () {
6146 var data = {functions: []},
6147 function_data,
6148 globals,
6149 i,
6150 j,
6151 kind,
6152 members = [],
6153 name,
6154 the_function,
6155 undef = [],
6156 unused = [];
6157 if (itself.errors.length) {
6158 data.errors = itself.errors;
6159 }
6160
6161 if (json_mode) {
6162 data.json = true;
6163 }
6164
6165 if (urls.length > 0) {
6166 data.urls = urls;
6167 }
6168
6169 globals = Object.keys(global_scope).filter(function (value) {
6170 return value.charAt(0) !== '(' && typeof standard[value] !== 'boolean';
6171 });
6172 if (globals.length > 0) {
6173 data.globals = globals;
6174 }
6175
6176 for (i = 1; i < functions.length; i += 1) {
6177 the_function = functions[i];
6178 function_data = {};
6179 for (j = 0; j < functionicity.length; j += 1) {
6180 function_data[functionicity[j]] = [];
6181 }
6182 for (name in the_function) {
6183 if (Object.prototype.hasOwnProperty.call(the_function, name)) {
6184 if (name.charAt(0) !== '(') {
6185 kind = the_function[name];
6186 if (kind === 'unction' || kind === 'unparam') {
6187 kind = 'unused';
6188 }
6189 if (Array.isArray(function_data[kind])) {
6190 function_data[kind].push(name);
6191 if (kind === 'unused') {
6192 unused.push({
6193 name: name,
6194 line: the_function['(line)'],
6195 'function': the_function['(name)']
6196 });
6197 } else if (kind === 'undef') {
6198 undef.push({
6199 name: name,
6200 line: the_function['(line)'],
6201 'function': the_function['(name)']
6202 });
6203 }
6204 }
6205 }
6206 }
6207 }
6208 for (j = 0; j < functionicity.length; j += 1) {
6209 if (function_data[functionicity[j]].length === 0) {
6210 delete function_data[functionicity[j]];
6211 }
6212 }
6213 function_data.name = the_function['(name)'];
6214 function_data.params = the_function['(params)'];
6215 function_data.line = the_function['(line)'];
6216 data.functions.push(function_data);
6217 }
6218
6219 if (unused.length > 0) {
6220 data.unused = unused;
6221 }
6222 if (undef.length > 0) {
6223 data['undefined'] = undef;
6224 }
6225
6226 members = [];
6227 for (name in property) {
6228 if (typeof property[name] === 'number') {
6229 data.member = property;
6230 break;
6231 }
6232 }
6233
6234 return data;
6235 };
6236
6237
6238 itself.report = function (errors_only) {
6239 var data = itself.data(), err, evidence, i, italics, j, key, keys,
6240 length, mem = '', name, names, not_first, output = [], snippets,
6241 the_function, warning;
6242
6243 function detail(h, value) {
6244 var comma_needed, singularity;
6245 if (Array.isArray(value)) {
6246 output.push('<div><i>' + h + '</i> ');
6247 value.sort().forEach(function (item) {
6248 if (item !== singularity) {
6249 singularity = item;
6250 output.push((comma_needed ? ', ' : '') + singularity);
6251 comma_needed = true;
6252 }
6253 });
6254 output.push('</div>');
6255 } else if (value) {
6256 output.push('<div><i>' + h + '</i> ' + value + '</div>');
6257 }
6258 }
6259
6260 if (data.errors || data.unused || data['undefined']) {
6261 err = true;
6262 output.push('<div id=errors><i>Error:</i>');
6263 if (data.errors) {
6264 for (i = 0; i < data.errors.length; i += 1) {
6265 warning = data.errors[i];
6266 if (warning) {
6267 evidence = warning.evidence || '';
6268 output.push('<p>Problem' + (isFinite(warning.line)
6269 ? ' at line ' + String(warning.line) +
6270 ' character ' + String(warning.character)
6271 : '') +
6272 ': ' + warning.reason.entityify() +
6273 '</p><p class=evidence>' +
6274 (evidence && (evidence.length > 80
6275 ? evidence.slice(0, 77) + '...'
6276 : evidence).entityify()) + '</p>');
6277 }
6278 }
6279 }
6280
6281 if (data['undefined']) {
6282 snippets = [];
6283 for (i = 0; i < data['undefined'].length; i += 1) {
6284 snippets[i] = '<code><u>' + data['undefined'][i].name + '</u></code>&nbsp;<i>' +
6285 String(data['undefined'][i].line) + ' </i> <small>' +
6286 data['undefined'][i]['function'] + '</small>';
6287 }
6288 output.push('<p><i>Undefined variable:</i> ' + snippets.join(', ') + '</p>');
6289 }
6290 if (data.unused) {
6291 snippets = [];
6292 for (i = 0; i < data.unused.length; i += 1) {
6293 snippets[i] = '<code><u>' + data.unused[i].name + '</u></code>&nbsp;<i>' +
6294 String(data.unused[i].line) + ' </i> <small>' +
6295 data.unused[i]['function'] + '</small>';
6296 }
6297 output.push('<p><i>Unused variable:</i> ' + snippets.join(', ') + '</p>');
6298 }
6299 if (data.json) {
6300 output.push('<p>JSON: bad.</p>');
6301 }
6302 output.push('</div>');
6303 }
6304
6305 if (!errors_only) {
6306
6307 output.push('<br><div id=functions>');
6308
6309 if (data.urls) {
6310 detail("URLs<br>", data.urls, '<br>');
6311 }
6312
6313 if (xmode === 'style') {
6314 output.push('<p>CSS.</p>');
6315 } else if (data.json && !err) {
6316 output.push('<p>JSON: good.</p>');
6317 } else if (data.globals) {
6318 output.push('<div><i>Global</i> ' +
6319 data.globals.sort().join(', ') + '</div>');
6320 } else {
6321 output.push('<div><i>No new global variables introduced.</i></div>');
6322 }
6323
6324 for (i = 0; i < data.functions.length; i += 1) {
6325 the_function = data.functions[i];
6326 names = [];
6327 if (the_function.params) {
6328 for (j = 0; j < the_function.params.length; j += 1) {
6329 names[j] = the_function.params[j].string;
6330 }
6331 }
6332 output.push('<br><div class=function><i>' +
6333 String(the_function.line) + '</i> ' +
6334 the_function.name.entityify() +
6335 '(' + names.join(', ') + ')</div>');
6336 detail('<big><b>Undefined</b></big>', the_function['undefined']);
6337 detail('<big><b>Unused</b></big>', the_function.unused);
6338 detail('Closure', the_function.closure);
6339 detail('Variable', the_function['var']);
6340 detail('Exception', the_function.exception);
6341 detail('Outer', the_function.outer);
6342 detail('Global', the_function.global);
6343 detail('Label', the_function.label);
6344 }
6345
6346 if (data.member) {
6347 keys = Object.keys(data.member);
6348 if (keys.length) {
6349 keys = keys.sort();
6350 output.push('<br><pre id=properties>/*properties<br>');
6351 mem = ' ';
6352 italics = 0;
6353 j = 0;
6354 not_first = false;
6355 for (i = 0; i < keys.length; i += 1) {
6356 key = keys[i];
6357 if (data.member[key] > 0) {
6358 if (not_first) {
6359 mem += ', ';
6360 }
6361 name = ix.test(key)
6362 ? key
6363 : '\'' + key.entityify().replace(nx, sanitize) + '\'';
6364 length += name.length + 2;
6365 if (data.member[key] === 1) {
6366 name = '<i>' + name + '</i>';
6367 italics += 1;
6368 j = 1;
6369 }
6370 if (mem.length + name.length - (italics * 7) > 80) {
6371 output.push(mem + '<br>');
6372 mem = ' ';
6373 italics = j;
6374 }
6375 mem += name;
6376 j = 0;
6377 not_first = true;
6378 }
6379 }
6380 output.push(mem + '<br>*/</pre>');
6381 }
6382 output.push('</div>');
6383 }
6384 }
6385 return output.join('');
6386 };
6387 itself.jslint = itself;
6388
6389 itself.edition = '2012-02-23';
6390
6391 return itself;
6392}());