Add JSHint and make dygraphs pass its checks.
[dygraphs.git] / jshint / jshint.js
1 /*!
2 * JSHint, by JSHint Community.
3 *
4 * Licensed under the same slightly modified MIT license that JSLint is.
5 * It stops evil-doers everywhere.
6 *
7 * JSHint is a derivative work of JSLint:
8 *
9 * Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining
12 * a copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom
16 * the Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * The Software shall be used for Good, not Evil.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 * DEALINGS IN THE SOFTWARE.
30 *
31 * JSHint was forked from 2010-12-16 edition of JSLint.
32 *
33 */
34
35 /*
36 JSHINT is a global function. It takes two parameters.
37
38 var myResult = JSHINT(source, option);
39
40 The first parameter is either a string or an array of strings. If it is a
41 string, it will be split on '\n' or '\r'. If it is an array of strings, it
42 is assumed that each string represents one line. The source can be a
43 JavaScript text or a JSON text.
44
45 The second parameter is an optional object of options which control the
46 operation of JSHINT. Most of the options are booleans: They are all
47 optional and have a default value of false. One of the options, predef,
48 can be an array of names, which will be used to declare global variables,
49 or an object whose keys are used as global names, with a boolean value
50 that determines if they are assignable.
51
52 If it checks out, JSHINT returns true. Otherwise, it returns false.
53
54 If false, you can inspect JSHINT.errors to find out the problems.
55 JSHINT.errors is an array of objects containing these members:
56
57 {
58 line : The line (relative to 0) at which the lint was found
59 character : The character (relative to 0) at which the lint was found
60 reason : The problem
61 evidence : The text line in which the problem occurred
62 raw : The raw message before the details were inserted
63 a : The first detail
64 b : The second detail
65 c : The third detail
66 d : The fourth detail
67 }
68
69 If a fatal error was found, a null will be the last element of the
70 JSHINT.errors array.
71
72 You can request a Function Report, which shows all of the functions
73 and the parameters and vars that they use. This can be used to find
74 implied global variables and other problems. The report is in HTML and
75 can be inserted in an HTML <body>.
76
77 var myReport = JSHINT.report(limited);
78
79 If limited is true, then the report will be limited to only errors.
80
81 You can request a data structure which contains JSHint's results.
82
83 var myData = JSHINT.data();
84
85 It returns a structure with this form:
86
87 {
88 errors: [
89 {
90 line: NUMBER,
91 character: NUMBER,
92 reason: STRING,
93 evidence: STRING
94 }
95 ],
96 functions: [
97 name: STRING,
98 line: NUMBER,
99 last: NUMBER,
100 param: [
101 STRING
102 ],
103 closure: [
104 STRING
105 ],
106 var: [
107 STRING
108 ],
109 exception: [
110 STRING
111 ],
112 outer: [
113 STRING
114 ],
115 unused: [
116 STRING
117 ],
118 global: [
119 STRING
120 ],
121 label: [
122 STRING
123 ]
124 ],
125 globals: [
126 STRING
127 ],
128 member: {
129 STRING: NUMBER
130 },
131 unuseds: [
132 {
133 name: STRING,
134 line: NUMBER
135 }
136 ],
137 implieds: [
138 {
139 name: STRING,
140 line: NUMBER
141 }
142 ],
143 urls: [
144 STRING
145 ],
146 json: BOOLEAN
147 }
148
149 Empty arrays will not be included.
150
151 */
152
153 /*jshint
154 evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true,
155 undef: true, maxlen: 100, indent:4
156 */
157
158 /*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)",
159 "(breakage)", "(context)", "(error)", "(global)", "(identifier)", "(last)",
160 "(line)", "(loopage)", "(name)", "(onevar)", "(params)", "(scope)",
161 "(statement)", "(verb)", "*", "+", "++", "-", "--", "\/", "<", "<=", "==",
162 "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax,
163 __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio,
164 Autocompleter, Assets, Boolean, Builder, Buffer, Browser, COM, CScript, Canvas,
165 CustomAnimation, Class, Control, Chain, Color, Cookie, Core, DataView, Date,
166 Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMReady, Drag,
167 E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event,
168 Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form,
169 FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey,
170 HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement,
171 HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement,
172 HTMLDivElement, HTMLDListElement, HTMLFieldSetElement,
173 HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement,
174 HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement,
175 HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement,
176 HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement,
177 HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement,
178 HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement,
179 HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement,
180 HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement,
181 HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement,
182 HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement,
183 HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement,
184 Iframe, IframeShim, Image, Int16Array, Int32Array, Int8Array,
185 Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E,
186 MAX_VALUE, MIN_VALUE, Mask, Math, MenuItem, MoveAnimation, MooTools, Native,
187 NEGATIVE_INFINITY, Number, Object, ObjectRange, Option, Options, OverText, PI,
188 POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype, RangeError,
189 Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation,
190 SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion,
191 ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller,
192 Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables,
193 SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template,
194 Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL,
195 VBArray, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XPathEvaluator,
196 XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult, "\\", a,
197 addEventListener, address, alert, apply, applicationCache, arguments, arity,
198 asi, b, bitwise, block, blur, boolOptions, boss, browser, c, call, callee,
199 caller, cases, charAt, charCodeAt, character, clearInterval, clearTimeout,
200 close, closed, closure, comment, condition, confirm, console, constructor,
201 content, couch, create, css, curly, d, data, datalist, dd, debug, decodeURI,
202 decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
203 dojo, dijit, dojox, define, edition, else, emit, encodeURI, encodeURIComponent,
204 entityify, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil,
205 ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus,
206 forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions,
207 g, gc, getComputedStyle, getRow, getter, GLOBAL, global, globals, globalstrict,
208 hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include,
209 indent, indexOf, init, ins, instanceOf, isAlpha, isApplicationRunning, isArray,
210 isDigit, isFinite, isNaN, iterator, java, join, jshint,
211 JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastsemic, laxbreak,
212 latedef, lbp, led, left, length, line, load, loadClass, localStorage, location,
213 log, loopfunc, m, match, maxerr, maxlen, member,message, meta, module, moveBy,
214 moveTo, mootools, multistr, name, navigator, new, newcap, noarg, node, noempty, nomen,
215 nonew, nonstandard, nud, onbeforeunload, onblur, onerror, onevar, onecase, onfocus,
216 onload, onresize, onunload, open, openDatabase, openURL, opener, opera, options, outer, param,
217 parent, parseFloat, parseInt, passfail, plusplus, predef, print, process, prompt,
218 proto, prototype, prototypejs, provides, push, quit, range, raw, reach, reason, regexp,
219 readFile, readUrl, regexdash, removeEventListener, replace, report, require,
220 reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, respond, rhino, right,
221 runCommand, scroll, screen, scripturl, scrollBy, scrollTo, scrollbar, search, seal,
222 send, serialize, sessionStorage, setInterval, setTimeout, setter, setterToken, shift, slice,
223 smarttabs, sort, spawn, split, stack, status, start, strict, sub, substr, supernew, shadow,
224 supplant, sum, sync, test, toLowerCase, toString, toUpperCase, toint32, token, top, trailing,
225 type, typeOf, Uint16Array, Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis,
226 value, valueOf, var, version, WebSocket, white, window, Worker, wsh*/
227
228 /*global exports: false */
229
230 // We build the application inside a function so that we produce only a single
231 // global variable. That function will be invoked immediately, and its return
232 // value is the JSHINT function itself.
233
234 var JSHINT = (function () {
235 "use strict";
236
237 var anonname, // The guessed name for anonymous functions.
238
239 // These are operators that should not be used with the ! operator.
240
241 bang = {
242 '<' : true,
243 '<=' : true,
244 '==' : true,
245 '===': true,
246 '!==': true,
247 '!=' : true,
248 '>' : true,
249 '>=' : true,
250 '+' : true,
251 '-' : true,
252 '*' : true,
253 '/' : true,
254 '%' : true
255 },
256
257 // These are the JSHint boolean options.
258 boolOptions = {
259 asi : true, // if automatic semicolon insertion should be tolerated
260 bitwise : true, // if bitwise operators should not be allowed
261 boss : true, // if advanced usage of assignments should be allowed
262 browser : true, // if the standard browser globals should be predefined
263 couch : true, // if CouchDB globals should be predefined
264 curly : true, // if curly braces around all blocks should be required
265 debug : true, // if debugger statements should be allowed
266 devel : true, // if logging globals should be predefined (console,
267 // alert, etc.)
268 dojo : true, // if Dojo Toolkit globals should be predefined
269 eqeqeq : true, // if === should be required
270 eqnull : true, // if == null comparisons should be tolerated
271 es5 : true, // if ES5 syntax should be allowed
272 esnext : true, // if es.next specific syntax should be allowed
273 evil : true, // if eval should be allowed
274 expr : true, // if ExpressionStatement should be allowed as Programs
275 forin : true, // if for in statements must filter
276 funcscope : true, // if only function scope should be used for scope tests
277 globalstrict: true, // if global "use strict"; should be allowed (also
278 // enables 'strict')
279 immed : true, // if immediate invocations must be wrapped in parens
280 iterator : true, // if the `__iterator__` property should be allowed
281 jquery : true, // if jQuery globals should be predefined
282 lastsemic : true, // if semicolons may be ommitted for the trailing
283 // statements inside of a one-line blocks.
284 latedef : true, // if the use before definition should not be tolerated
285 laxbreak : true, // if line breaks should not be checked
286 loopfunc : true, // if functions should be allowed to be defined within
287 // loops
288 mootools : true, // if MooTools globals should be predefined
289 multistr : true, // allow multiline strings
290 newcap : true, // if constructor names must be capitalized
291 noarg : true, // if arguments.caller and arguments.callee should be
292 // disallowed
293 node : true, // if the Node.js environment globals should be
294 // predefined
295 noempty : true, // if empty blocks should be disallowed
296 nonew : true, // if using `new` for side-effects should be disallowed
297 nonstandard : true, // if non-standard (but widely adopted) globals should
298 // be predefined
299 nomen : true, // if names should be checked
300 onevar : true, // if only one var statement per function should be
301 // allowed
302 onecase : true, // if one case switch statements should be allowed
303 passfail : true, // if the scan should stop on first error
304 plusplus : true, // if increment/decrement should not be allowed
305 proto : true, // if the `__proto__` property should be allowed
306 prototypejs : true, // if Prototype and Scriptaculous globals should be
307 // predefined
308 regexdash : true, // if unescaped first/last dash (-) inside brackets
309 // should be tolerated
310 regexp : true, // if the . should not be allowed in regexp literals
311 rhino : true, // if the Rhino environment globals should be predefined
312 undef : true, // if variables should be declared before used
313 scripturl : true, // if script-targeted URLs should be tolerated
314 shadow : true, // if variable shadowing should be tolerated
315 smarttabs : true, // if smarttabs should be tolerated
316 // (http://www.emacswiki.org/emacs/SmartTabs)
317 strict : true, // require the "use strict"; pragma
318 sub : true, // if all forms of subscript notation are tolerated
319 supernew : true, // if `new function () { ... };` and `new Object;`
320 // should be tolerated
321 trailing : true, // if trailing whitespace rules apply
322 validthis : true, // if 'this' inside a non-constructor function is valid.
323 // This is a function scoped option only.
324 white : true, // if strict whitespace rules apply
325 wsh : true // if the Windows Scripting Host environment globals
326 // should be predefined
327 },
328
329 // browser contains a set of global names which are commonly provided by a
330 // web browser environment.
331 browser = {
332 ArrayBuffer : false,
333 ArrayBufferView : false,
334 Audio : false,
335 addEventListener : false,
336 applicationCache : false,
337 blur : false,
338 clearInterval : false,
339 clearTimeout : false,
340 close : false,
341 closed : false,
342 DataView : false,
343 defaultStatus : false,
344 document : false,
345 event : false,
346 FileReader : false,
347 Float32Array : false,
348 Float64Array : false,
349 FormData : false,
350 focus : false,
351 frames : false,
352 getComputedStyle : false,
353 HTMLElement : false,
354 HTMLAnchorElement : false,
355 HTMLBaseElement : false,
356 HTMLBlockquoteElement : false,
357 HTMLBodyElement : false,
358 HTMLBRElement : false,
359 HTMLButtonElement : false,
360 HTMLCanvasElement : false,
361 HTMLDirectoryElement : false,
362 HTMLDivElement : false,
363 HTMLDListElement : false,
364 HTMLFieldSetElement : false,
365 HTMLFontElement : false,
366 HTMLFormElement : false,
367 HTMLFrameElement : false,
368 HTMLFrameSetElement : false,
369 HTMLHeadElement : false,
370 HTMLHeadingElement : false,
371 HTMLHRElement : false,
372 HTMLHtmlElement : false,
373 HTMLIFrameElement : false,
374 HTMLImageElement : false,
375 HTMLInputElement : false,
376 HTMLIsIndexElement : false,
377 HTMLLabelElement : false,
378 HTMLLayerElement : false,
379 HTMLLegendElement : false,
380 HTMLLIElement : false,
381 HTMLLinkElement : false,
382 HTMLMapElement : false,
383 HTMLMenuElement : false,
384 HTMLMetaElement : false,
385 HTMLModElement : false,
386 HTMLObjectElement : false,
387 HTMLOListElement : false,
388 HTMLOptGroupElement : false,
389 HTMLOptionElement : false,
390 HTMLParagraphElement : false,
391 HTMLParamElement : false,
392 HTMLPreElement : false,
393 HTMLQuoteElement : false,
394 HTMLScriptElement : false,
395 HTMLSelectElement : false,
396 HTMLStyleElement : false,
397 HTMLTableCaptionElement : false,
398 HTMLTableCellElement : false,
399 HTMLTableColElement : false,
400 HTMLTableElement : false,
401 HTMLTableRowElement : false,
402 HTMLTableSectionElement : false,
403 HTMLTextAreaElement : false,
404 HTMLTitleElement : false,
405 HTMLUListElement : false,
406 HTMLVideoElement : false,
407 history : false,
408 Int16Array : false,
409 Int32Array : false,
410 Int8Array : false,
411 Image : false,
412 length : false,
413 localStorage : false,
414 location : false,
415 moveBy : false,
416 moveTo : false,
417 name : false,
418 navigator : false,
419 onbeforeunload : true,
420 onblur : true,
421 onerror : true,
422 onfocus : true,
423 onload : true,
424 onresize : true,
425 onunload : true,
426 open : false,
427 openDatabase : false,
428 opener : false,
429 Option : false,
430 parent : false,
431 print : false,
432 removeEventListener : false,
433 resizeBy : false,
434 resizeTo : false,
435 screen : false,
436 scroll : false,
437 scrollBy : false,
438 scrollTo : false,
439 sessionStorage : false,
440 setInterval : false,
441 setTimeout : false,
442 SharedWorker : false,
443 status : false,
444 top : false,
445 Uint16Array : false,
446 Uint32Array : false,
447 Uint8Array : false,
448 WebSocket : false,
449 window : false,
450 Worker : false,
451 XMLHttpRequest : false,
452 XPathEvaluator : false,
453 XPathException : false,
454 XPathExpression : false,
455 XPathNamespace : false,
456 XPathNSResolver : false,
457 XPathResult : false
458 },
459
460 couch = {
461 "require" : false,
462 respond : false,
463 getRow : false,
464 emit : false,
465 send : false,
466 start : false,
467 sum : false,
468 log : false,
469 exports : false,
470 module : false,
471 provides : false
472 },
473
474 devel = {
475 alert : false,
476 confirm : false,
477 console : false,
478 Debug : false,
479 opera : false,
480 prompt : false
481 },
482
483 dojo = {
484 dojo : false,
485 dijit : false,
486 dojox : false,
487 define : false,
488 "require" : false
489 },
490
491 escapes = {
492 '\b': '\\b',
493 '\t': '\\t',
494 '\n': '\\n',
495 '\f': '\\f',
496 '\r': '\\r',
497 '"' : '\\"',
498 '/' : '\\/',
499 '\\': '\\\\'
500 },
501
502 funct, // The current function
503
504 functionicity = [
505 'closure', 'exception', 'global', 'label',
506 'outer', 'unused', 'var'
507 ],
508
509 functions, // All of the functions
510
511 global, // The global scope
512 implied, // Implied globals
513 inblock,
514 indent,
515 jsonmode,
516
517 jquery = {
518 '$' : false,
519 jQuery : false
520 },
521
522 lines,
523 lookahead,
524 member,
525 membersOnly,
526
527 mootools = {
528 '$' : false,
529 '$$' : false,
530 Assets : false,
531 Browser : false,
532 Chain : false,
533 Class : false,
534 Color : false,
535 Cookie : false,
536 Core : false,
537 Document : false,
538 DomReady : false,
539 DOMReady : false,
540 Drag : false,
541 Element : false,
542 Elements : false,
543 Event : false,
544 Events : false,
545 Fx : false,
546 Group : false,
547 Hash : false,
548 HtmlTable : false,
549 Iframe : false,
550 IframeShim : false,
551 InputValidator : false,
552 instanceOf : false,
553 Keyboard : false,
554 Locale : false,
555 Mask : false,
556 MooTools : false,
557 Native : false,
558 Options : false,
559 OverText : false,
560 Request : false,
561 Scroller : false,
562 Slick : false,
563 Slider : false,
564 Sortables : false,
565 Spinner : false,
566 Swiff : false,
567 Tips : false,
568 Type : false,
569 typeOf : false,
570 URI : false,
571 Window : false
572 },
573
574 nexttoken,
575
576 node = {
577 __filename : false,
578 __dirname : false,
579 Buffer : false,
580 console : false,
581 exports : false,
582 GLOBAL : false,
583 global : false,
584 module : false,
585 process : false,
586 require : false,
587 setTimeout : false,
588 clearTimeout : false,
589 setInterval : false,
590 clearInterval : false
591 },
592
593 noreach,
594 option,
595 predefined, // Global variables defined by option
596 prereg,
597 prevtoken,
598
599 prototypejs = {
600 '$' : false,
601 '$$' : false,
602 '$A' : false,
603 '$F' : false,
604 '$H' : false,
605 '$R' : false,
606 '$break' : false,
607 '$continue' : false,
608 '$w' : false,
609 Abstract : false,
610 Ajax : false,
611 Class : false,
612 Enumerable : false,
613 Element : false,
614 Event : false,
615 Field : false,
616 Form : false,
617 Hash : false,
618 Insertion : false,
619 ObjectRange : false,
620 PeriodicalExecuter: false,
621 Position : false,
622 Prototype : false,
623 Selector : false,
624 Template : false,
625 Toggle : false,
626 Try : false,
627 Autocompleter : false,
628 Builder : false,
629 Control : false,
630 Draggable : false,
631 Draggables : false,
632 Droppables : false,
633 Effect : false,
634 Sortable : false,
635 SortableObserver : false,
636 Sound : false,
637 Scriptaculous : false
638 },
639
640 rhino = {
641 defineClass : false,
642 deserialize : false,
643 gc : false,
644 help : false,
645 importPackage: false,
646 "java" : false,
647 load : false,
648 loadClass : false,
649 print : false,
650 quit : false,
651 readFile : false,
652 readUrl : false,
653 runCommand : false,
654 seal : false,
655 serialize : false,
656 spawn : false,
657 sync : false,
658 toint32 : false,
659 version : false
660 },
661
662 scope, // The current scope
663 stack,
664
665 // standard contains the global names that are provided by the
666 // ECMAScript standard.
667 standard = {
668 Array : false,
669 Boolean : false,
670 Date : false,
671 decodeURI : false,
672 decodeURIComponent : false,
673 encodeURI : false,
674 encodeURIComponent : false,
675 Error : false,
676 'eval' : false,
677 EvalError : false,
678 Function : false,
679 hasOwnProperty : false,
680 isFinite : false,
681 isNaN : false,
682 JSON : false,
683 Math : false,
684 Number : false,
685 Object : false,
686 parseInt : false,
687 parseFloat : false,
688 RangeError : false,
689 ReferenceError : false,
690 RegExp : false,
691 String : false,
692 SyntaxError : false,
693 TypeError : false,
694 URIError : false
695 },
696
697 // widely adopted global names that are not part of ECMAScript standard
698 nonstandard = {
699 escape : false,
700 unescape : false
701 },
702
703 standard_member = {
704 E : true,
705 LN2 : true,
706 LN10 : true,
707 LOG2E : true,
708 LOG10E : true,
709 MAX_VALUE : true,
710 MIN_VALUE : true,
711 NEGATIVE_INFINITY : true,
712 PI : true,
713 POSITIVE_INFINITY : true,
714 SQRT1_2 : true,
715 SQRT2 : true
716 },
717
718 directive,
719 syntax = {},
720 tab,
721 token,
722 urls,
723 useESNextSyntax,
724 warnings,
725
726 wsh = {
727 ActiveXObject : true,
728 Enumerator : true,
729 GetObject : true,
730 ScriptEngine : true,
731 ScriptEngineBuildVersion : true,
732 ScriptEngineMajorVersion : true,
733 ScriptEngineMinorVersion : true,
734 VBArray : true,
735 WSH : true,
736 WScript : true,
737 XDomainRequest : true
738 };
739
740 // Regular expressions. Some of these are stupidly long.
741 var ax, cx, tx, nx, nxg, lx, ix, jx, ft;
742 (function () {
743 /*jshint maxlen:300 */
744
745 // unsafe comment or string
746 ax = /@cc|<\/?|script|\]\s*\]|<\s*!|&lt/i;
747
748 // unsafe characters that are silently deleted by one or more browsers
749 cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
750
751 // token
752 tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint|jslint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/;
753
754 // characters in strings that need escapement
755 nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
756 nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
757
758 // star slash
759 lx = /\*\/|\/\*/;
760
761 // identifier
762 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
763
764 // javascript url
765 jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i;
766
767 // catches /* falls through */ comments
768 ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/;
769 }());
770
771 function F() {} // Used by Object.create
772
773 function is_own(object, name) {
774
775 // The object.hasOwnProperty method fails when the property under consideration
776 // is named 'hasOwnProperty'. So we have to use this more convoluted form.
777
778 return Object.prototype.hasOwnProperty.call(object, name);
779 }
780
781 // Provide critical ES5 functions to ES3.
782
783 if (typeof Array.isArray !== 'function') {
784 Array.isArray = function (o) {
785 return Object.prototype.toString.apply(o) === '[object Array]';
786 };
787 }
788
789 if (typeof Object.create !== 'function') {
790 Object.create = function (o) {
791 F.prototype = o;
792 return new F();
793 };
794 }
795
796 if (typeof Object.keys !== 'function') {
797 Object.keys = function (o) {
798 var a = [], k;
799 for (k in o) {
800 if (is_own(o, k)) {
801 a.push(k);
802 }
803 }
804 return a;
805 };
806 }
807
808 // Non standard methods
809
810 if (typeof String.prototype.entityify !== 'function') {
811 String.prototype.entityify = function () {
812 return this
813 .replace(/&/g, '&amp;')
814 .replace(/</g, '&lt;')
815 .replace(/>/g, '&gt;');
816 };
817 }
818
819 if (typeof String.prototype.isAlpha !== 'function') {
820 String.prototype.isAlpha = function () {
821 return (this >= 'a' && this <= 'z\uffff') ||
822 (this >= 'A' && this <= 'Z\uffff');
823 };
824 }
825
826 if (typeof String.prototype.isDigit !== 'function') {
827 String.prototype.isDigit = function () {
828 return (this >= '0' && this <= '9');
829 };
830 }
831
832 if (typeof String.prototype.supplant !== 'function') {
833 String.prototype.supplant = function (o) {
834 return this.replace(/\{([^{}]*)\}/g, function (a, b) {
835 var r = o[b];
836 return typeof r === 'string' || typeof r === 'number' ? r : a;
837 });
838 };
839 }
840
841 if (typeof String.prototype.name !== 'function') {
842 String.prototype.name = function () {
843
844 // If the string looks like an identifier, then we can return it as is.
845 // If the string contains no control characters, no quote characters, and no
846 // backslash characters, then we can simply slap some quotes around it.
847 // Otherwise we must also replace the offending characters with safe
848 // sequences.
849
850 if (ix.test(this)) {
851 return this;
852 }
853 if (nx.test(this)) {
854 return '"' + this.replace(nxg, function (a) {
855 var c = escapes[a];
856 if (c) {
857 return c;
858 }
859 return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
860 }) + '"';
861 }
862 return '"' + this + '"';
863 };
864 }
865
866
867 function combine(t, o) {
868 var n;
869 for (n in o) {
870 if (is_own(o, n)) {
871 t[n] = o[n];
872 }
873 }
874 }
875
876 function assume() {
877 if (option.couch) {
878 combine(predefined, couch);
879 }
880
881 if (option.rhino) {
882 combine(predefined, rhino);
883 }
884
885 if (option.prototypejs) {
886 combine(predefined, prototypejs);
887 }
888
889 if (option.node) {
890 combine(predefined, node);
891 }
892
893 if (option.devel) {
894 combine(predefined, devel);
895 }
896
897 if (option.dojo) {
898 combine(predefined, dojo);
899 }
900
901 if (option.browser) {
902 combine(predefined, browser);
903 }
904
905 if (option.nonstandard) {
906 combine(predefined, nonstandard);
907 }
908
909 if (option.jquery) {
910 combine(predefined, jquery);
911 }
912
913 if (option.mootools) {
914 combine(predefined, mootools);
915 }
916
917 if (option.wsh) {
918 combine(predefined, wsh);
919 }
920
921 if (option.esnext) {
922 useESNextSyntax();
923 }
924
925 if (option.globalstrict && option.strict !== false) {
926 option.strict = true;
927 }
928 }
929
930
931 // Produce an error warning.
932 function quit(message, line, chr) {
933 var percentage = Math.floor((line / lines.length) * 100);
934
935 throw {
936 name: 'JSHintError',
937 line: line,
938 character: chr,
939 message: message + " (" + percentage + "% scanned).",
940 raw: message
941 };
942 }
943
944 function isundef(scope, m, t, a) {
945 return JSHINT.undefs.push([scope, m, t, a]);
946 }
947
948 function warning(m, t, a, b, c, d) {
949 var ch, l, w;
950 t = t || nexttoken;
951 if (t.id === '(end)') { // `~
952 t = token;
953 }
954 l = t.line || 0;
955 ch = t.from || 0;
956 w = {
957 id: '(error)',
958 raw: m,
959 evidence: lines[l - 1] || '',
960 line: l,
961 character: ch,
962 a: a,
963 b: b,
964 c: c,
965 d: d
966 };
967 w.reason = m.supplant(w);
968 JSHINT.errors.push(w);
969 if (option.passfail) {
970 quit('Stopping. ', l, ch);
971 }
972 warnings += 1;
973 if (warnings >= option.maxerr) {
974 quit("Too many errors.", l, ch);
975 }
976 return w;
977 }
978
979 function warningAt(m, l, ch, a, b, c, d) {
980 return warning(m, {
981 line: l,
982 from: ch
983 }, a, b, c, d);
984 }
985
986 function error(m, t, a, b, c, d) {
987 var w = warning(m, t, a, b, c, d);
988 }
989
990 function errorAt(m, l, ch, a, b, c, d) {
991 return error(m, {
992 line: l,
993 from: ch
994 }, a, b, c, d);
995 }
996
997
998
999 // lexical analysis and token construction
1000
1001 var lex = (function lex() {
1002 var character, from, line, s;
1003
1004 // Private lex methods
1005
1006 function nextLine() {
1007 var at,
1008 tw; // trailing whitespace check
1009
1010 if (line >= lines.length)
1011 return false;
1012
1013 character = 1;
1014 s = lines[line];
1015 line += 1;
1016
1017 // If smarttabs option is used check for spaces followed by tabs only.
1018 // Otherwise check for any occurence of mixed tabs and spaces.
1019 if (option.smarttabs)
1020 at = s.search(/ \t/);
1021 else
1022 at = s.search(/ \t|\t /);
1023
1024 if (at >= 0)
1025 warningAt("Mixed spaces and tabs.", line, at + 1);
1026
1027 s = s.replace(/\t/g, tab);
1028 at = s.search(cx);
1029
1030 if (at >= 0)
1031 warningAt("Unsafe character.", line, at);
1032
1033 if (option.maxlen && option.maxlen < s.length)
1034 warningAt("Line too long.", line, s.length);
1035
1036 // Check for trailing whitespaces
1037 tw = /\s+$/.test(s);
1038 if (option.trailing && tw && !/^\s+$/.test(s)) {
1039 warningAt("Trailing whitespace.", line, tw);
1040 }
1041 return true;
1042 }
1043
1044 // Produce a token object. The token inherits from a syntax symbol.
1045
1046 function it(type, value) {
1047 var i, t;
1048 if (type === '(color)' || type === '(range)') {
1049 t = {type: type};
1050 } else if (type === '(punctuator)' ||
1051 (type === '(identifier)' && is_own(syntax, value))) {
1052 t = syntax[value] || syntax['(error)'];
1053 } else {
1054 t = syntax[type];
1055 }
1056 t = Object.create(t);
1057 if (type === '(string)' || type === '(range)') {
1058 if (!option.scripturl && jx.test(value)) {
1059 warningAt("Script URL.", line, from);
1060 }
1061 }
1062 if (type === '(identifier)') {
1063 t.identifier = true;
1064 if (value === '__proto__' && !option.proto) {
1065 warningAt("The '{a}' property is deprecated.",
1066 line, from, value);
1067 } else if (value === '__iterator__' && !option.iterator) {
1068 warningAt("'{a}' is only available in JavaScript 1.7.",
1069 line, from, value);
1070 } else if (option.nomen && (value.charAt(0) === '_' ||
1071 value.charAt(value.length - 1) === '_')) {
1072 if (!option.node || token.id === '.' ||
1073 (value !== '__dirname' && value !== '__filename')) {
1074 warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", value);
1075 }
1076 }
1077 }
1078 t.value = value;
1079 t.line = line;
1080 t.character = character;
1081 t.from = from;
1082 i = t.id;
1083 if (i !== '(endline)') {
1084 prereg = i &&
1085 (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) ||
1086 i === 'return' ||
1087 i === 'case');
1088 }
1089 return t;
1090 }
1091
1092 // Public lex methods
1093 return {
1094 init: function (source) {
1095 if (typeof source === 'string') {
1096 lines = source
1097 .replace(/\r\n/g, '\n')
1098 .replace(/\r/g, '\n')
1099 .split('\n');
1100 } else {
1101 lines = source;
1102 }
1103
1104 // If the first line is a shebang (#!), make it a blank and move on.
1105 // Shebangs are used by Node scripts.
1106 if (lines[0] && lines[0].substr(0, 2) === '#!')
1107 lines[0] = '';
1108
1109 line = 0;
1110 nextLine();
1111 from = 1;
1112 },
1113
1114 range: function (begin, end) {
1115 var c, value = '';
1116 from = character;
1117 if (s.charAt(0) !== begin) {
1118 errorAt("Expected '{a}' and instead saw '{b}'.",
1119 line, character, begin, s.charAt(0));
1120 }
1121 for (;;) {
1122 s = s.slice(1);
1123 character += 1;
1124 c = s.charAt(0);
1125 switch (c) {
1126 case '':
1127 errorAt("Missing '{a}'.", line, character, c);
1128 break;
1129 case end:
1130 s = s.slice(1);
1131 character += 1;
1132 return it('(range)', value);
1133 case '\\':
1134 warningAt("Unexpected '{a}'.", line, character, c);
1135 }
1136 value += c;
1137 }
1138
1139 },
1140
1141
1142 // token -- this is called by advance to get the next token
1143 token: function () {
1144 var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange;
1145
1146 function match(x) {
1147 var r = x.exec(s), r1;
1148 if (r) {
1149 l = r[0].length;
1150 r1 = r[1];
1151 c = r1.charAt(0);
1152 s = s.substr(l);
1153 from = character + l - r1.length;
1154 character += l;
1155 return r1;
1156 }
1157 }
1158
1159 function string(x) {
1160 var c, j, r = '', allowNewLine = false;
1161
1162 if (jsonmode && x !== '"') {
1163 warningAt("Strings must use doublequote.",
1164 line, character);
1165 }
1166
1167 function esc(n) {
1168 var i = parseInt(s.substr(j + 1, n), 16);
1169 j += n;
1170 if (i >= 32 && i <= 126 &&
1171 i !== 34 && i !== 92 && i !== 39) {
1172 warningAt("Unnecessary escapement.", line, character);
1173 }
1174 character += n;
1175 c = String.fromCharCode(i);
1176 }
1177 j = 0;
1178 unclosedString: for (;;) {
1179 while (j >= s.length) {
1180 j = 0;
1181
1182 var cl = line, cf = from;
1183 if (!nextLine()) {
1184 errorAt("Unclosed string.", cl, cf);
1185 break unclosedString;
1186 }
1187
1188 if (allowNewLine) {
1189 allowNewLine = false;
1190 } else {
1191 warningAt("Unclosed string.", cl, cf);
1192 }
1193 }
1194 c = s.charAt(j);
1195 if (c === x) {
1196 character += 1;
1197 s = s.substr(j + 1);
1198 return it('(string)', r, x);
1199 }
1200 if (c < ' ') {
1201 if (c === '\n' || c === '\r') {
1202 break;
1203 }
1204 warningAt("Control character in string: {a}.",
1205 line, character + j, s.slice(0, j));
1206 } else if (c === '\\') {
1207 j += 1;
1208 character += 1;
1209 c = s.charAt(j);
1210 switch (c) {
1211 case '\\':
1212 case '"':
1213 case '/':
1214 break;
1215 case '\'':
1216 if (jsonmode) {
1217 warningAt("Avoid \\'.", line, character);
1218 }
1219 break;
1220 case 'b':
1221 c = '\b';
1222 break;
1223 case 'f':
1224 c = '\f';
1225 break;
1226 case 'n':
1227 c = '\n';
1228 break;
1229 case 'r':
1230 c = '\r';
1231 break;
1232 case 't':
1233 c = '\t';
1234 break;
1235 case 'u':
1236 esc(4);
1237 break;
1238 case 'v':
1239 if (jsonmode) {
1240 warningAt("Avoid \\v.", line, character);
1241 }
1242 c = '\v';
1243 break;
1244 case 'x':
1245 if (jsonmode) {
1246 warningAt("Avoid \\x-.", line, character);
1247 }
1248 esc(2);
1249 break;
1250 case '':
1251 // last character is escape character
1252 // always allow new line if escaped, but show
1253 // warning if option is not set
1254 allowNewLine = true;
1255 if (option.multistr) {
1256 if (jsonmode) {
1257 warningAt("Avoid EOL escapement.", line, character);
1258 }
1259 c = '';
1260 character -= 1;
1261 break;
1262 }
1263 warningAt("Bad escapement of EOL. Use option multistr if needed.",
1264 line, character);
1265 break;
1266 default:
1267 warningAt("Bad escapement.", line, character);
1268 }
1269 }
1270 r += c;
1271 character += 1;
1272 j += 1;
1273 }
1274 }
1275
1276 for (;;) {
1277 if (!s) {
1278 return it(nextLine() ? '(endline)' : '(end)', '');
1279 }
1280 t = match(tx);
1281 if (!t) {
1282 t = '';
1283 c = '';
1284 while (s && s < '!') {
1285 s = s.substr(1);
1286 }
1287 if (s) {
1288 errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1));
1289 s = '';
1290 }
1291 } else {
1292
1293 // identifier
1294
1295 if (c.isAlpha() || c === '_' || c === '$') {
1296 return it('(identifier)', t);
1297 }
1298
1299 // number
1300
1301 if (c.isDigit()) {
1302 if (!isFinite(Number(t))) {
1303 warningAt("Bad number '{a}'.",
1304 line, character, t);
1305 }
1306 if (s.substr(0, 1).isAlpha()) {
1307 warningAt("Missing space after '{a}'.",
1308 line, character, t);
1309 }
1310 if (c === '0') {
1311 d = t.substr(1, 1);
1312 if (d.isDigit()) {
1313 if (token.id !== '.') {
1314 warningAt("Don't use extra leading zeros '{a}'.",
1315 line, character, t);
1316 }
1317 } else if (jsonmode && (d === 'x' || d === 'X')) {
1318 warningAt("Avoid 0x-. '{a}'.",
1319 line, character, t);
1320 }
1321 }
1322 if (t.substr(t.length - 1) === '.') {
1323 warningAt(
1324 "A trailing decimal point can be confused with a dot '{a}'.", line, character, t);
1325 }
1326 return it('(number)', t);
1327 }
1328 switch (t) {
1329
1330 // string
1331
1332 case '"':
1333 case "'":
1334 return string(t);
1335
1336 // // comment
1337
1338 case '//':
1339 s = '';
1340 token.comment = true;
1341 break;
1342
1343 // /* comment
1344
1345 case '/*':
1346 for (;;) {
1347 i = s.search(lx);
1348 if (i >= 0) {
1349 break;
1350 }
1351 if (!nextLine()) {
1352 errorAt("Unclosed comment.", line, character);
1353 }
1354 }
1355 character += i + 2;
1356 if (s.substr(i, 1) === '/') {
1357 errorAt("Nested comment.", line, character);
1358 }
1359 s = s.substr(i + 2);
1360 token.comment = true;
1361 break;
1362
1363 // /*members /*jshint /*global
1364
1365 case '/*members':
1366 case '/*member':
1367 case '/*jshint':
1368 case '/*jslint':
1369 case '/*global':
1370 case '*/':
1371 return {
1372 value: t,
1373 type: 'special',
1374 line: line,
1375 character: character,
1376 from: from
1377 };
1378
1379 case '':
1380 break;
1381 // /
1382 case '/':
1383 if (token.id === '/=') {
1384 errorAt("A regular expression literal can be confused with '/='.",
1385 line, from);
1386 }
1387 if (prereg) {
1388 depth = 0;
1389 captures = 0;
1390 l = 0;
1391 for (;;) {
1392 b = true;
1393 c = s.charAt(l);
1394 l += 1;
1395 switch (c) {
1396 case '':
1397 errorAt("Unclosed regular expression.", line, from);
1398 return quit('Stopping.', line, from);
1399 case '/':
1400 if (depth > 0) {
1401 warningAt("{a} unterminated regular expression " +
1402 "group(s).", line, from + l, depth);
1403 }
1404 c = s.substr(0, l - 1);
1405 q = {
1406 g: true,
1407 i: true,
1408 m: true
1409 };
1410 while (q[s.charAt(l)] === true) {
1411 q[s.charAt(l)] = false;
1412 l += 1;
1413 }
1414 character += l;
1415 s = s.substr(l);
1416 q = s.charAt(0);
1417 if (q === '/' || q === '*') {
1418 errorAt("Confusing regular expression.",
1419 line, from);
1420 }
1421 return it('(regexp)', c);
1422 case '\\':
1423 c = s.charAt(l);
1424 if (c < ' ') {
1425 warningAt(
1426 "Unexpected control character in regular expression.", line, from + l);
1427 } else if (c === '<') {
1428 warningAt(
1429 "Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
1430 }
1431 l += 1;
1432 break;
1433 case '(':
1434 depth += 1;
1435 b = false;
1436 if (s.charAt(l) === '?') {
1437 l += 1;
1438 switch (s.charAt(l)) {
1439 case ':':
1440 case '=':
1441 case '!':
1442 l += 1;
1443 break;
1444 default:
1445 warningAt(
1446 "Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l));
1447 }
1448 } else {
1449 captures += 1;
1450 }
1451 break;
1452 case '|':
1453 b = false;
1454 break;
1455 case ')':
1456 if (depth === 0) {
1457 warningAt("Unescaped '{a}'.",
1458 line, from + l, ')');
1459 } else {
1460 depth -= 1;
1461 }
1462 break;
1463 case ' ':
1464 q = 1;
1465 while (s.charAt(l) === ' ') {
1466 l += 1;
1467 q += 1;
1468 }
1469 if (q > 1) {
1470 warningAt(
1471 "Spaces are hard to count. Use {{a}}.", line, from + l, q);
1472 }
1473 break;
1474 case '[':
1475 c = s.charAt(l);
1476 if (c === '^') {
1477 l += 1;
1478 if (option.regexp) {
1479 warningAt("Insecure '{a}'.",
1480 line, from + l, c);
1481 } else if (s.charAt(l) === ']') {
1482 errorAt("Unescaped '{a}'.",
1483 line, from + l, '^');
1484 }
1485 }
1486 if (c === ']') {
1487 warningAt("Empty class.", line,
1488 from + l - 1);
1489 }
1490 isLiteral = false;
1491 isInRange = false;
1492 klass: do {
1493 c = s.charAt(l);
1494 l += 1;
1495 switch (c) {
1496 case '[':
1497 case '^':
1498 warningAt("Unescaped '{a}'.",
1499 line, from + l, c);
1500 if (isInRange) {
1501 isInRange = false;
1502 } else {
1503 isLiteral = true;
1504 }
1505 break;
1506 case '-':
1507 if (isLiteral && !isInRange) {
1508 isLiteral = false;
1509 isInRange = true;
1510 } else if (isInRange) {
1511 isInRange = false;
1512 } else if (s.charAt(l) === ']') {
1513 isInRange = true;
1514 } else {
1515 if (option.regexdash !== (l === 2 || (l === 3 &&
1516 s.charAt(1) === '^'))) {
1517 warningAt("Unescaped '{a}'.",
1518 line, from + l - 1, '-');
1519 }
1520 isLiteral = true;
1521 }
1522 break;
1523 case ']':
1524 if (isInRange && !option.regexdash) {
1525 warningAt("Unescaped '{a}'.",
1526 line, from + l - 1, '-');
1527 }
1528 break klass;
1529 case '\\':
1530 c = s.charAt(l);
1531 if (c < ' ') {
1532 warningAt(
1533 "Unexpected control character in regular expression.", line, from + l);
1534 } else if (c === '<') {
1535 warningAt(
1536 "Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
1537 }
1538 l += 1;
1539
1540 // \w, \s and \d are never part of a character range
1541 if (/[wsd]/i.test(c)) {
1542 if (isInRange) {
1543 warningAt("Unescaped '{a}'.",
1544 line, from + l, '-');
1545 isInRange = false;
1546 }
1547 isLiteral = false;
1548 } else if (isInRange) {
1549 isInRange = false;
1550 } else {
1551 isLiteral = true;
1552 }
1553 break;
1554 case '/':
1555 warningAt("Unescaped '{a}'.",
1556 line, from + l - 1, '/');
1557
1558 if (isInRange) {
1559 isInRange = false;
1560 } else {
1561 isLiteral = true;
1562 }
1563 break;
1564 case '<':
1565 if (isInRange) {
1566 isInRange = false;
1567 } else {
1568 isLiteral = true;
1569 }
1570 break;
1571 default:
1572 if (isInRange) {
1573 isInRange = false;
1574 } else {
1575 isLiteral = true;
1576 }
1577 }
1578 } while (c);
1579 break;
1580 case '.':
1581 if (option.regexp) {
1582 warningAt("Insecure '{a}'.", line,
1583 from + l, c);
1584 }
1585 break;
1586 case ']':
1587 case '?':
1588 case '{':
1589 case '}':
1590 case '+':
1591 case '*':
1592 warningAt("Unescaped '{a}'.", line,
1593 from + l, c);
1594 }
1595 if (b) {
1596 switch (s.charAt(l)) {
1597 case '?':
1598 case '+':
1599 case '*':
1600 l += 1;
1601 if (s.charAt(l) === '?') {
1602 l += 1;
1603 }
1604 break;
1605 case '{':
1606 l += 1;
1607 c = s.charAt(l);
1608 if (c < '0' || c > '9') {
1609 warningAt(
1610 "Expected a number and instead saw '{a}'.", line, from + l, c);
1611 }
1612 l += 1;
1613 low = +c;
1614 for (;;) {
1615 c = s.charAt(l);
1616 if (c < '0' || c > '9') {
1617 break;
1618 }
1619 l += 1;
1620 low = +c + (low * 10);
1621 }
1622 high = low;
1623 if (c === ',') {
1624 l += 1;
1625 high = Infinity;
1626 c = s.charAt(l);
1627 if (c >= '0' && c <= '9') {
1628 l += 1;
1629 high = +c;
1630 for (;;) {
1631 c = s.charAt(l);
1632 if (c < '0' || c > '9') {
1633 break;
1634 }
1635 l += 1;
1636 high = +c + (high * 10);
1637 }
1638 }
1639 }
1640 if (s.charAt(l) !== '}') {
1641 warningAt(
1642 "Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c);
1643 } else {
1644 l += 1;
1645 }
1646 if (s.charAt(l) === '?') {
1647 l += 1;
1648 }
1649 if (low > high) {
1650 warningAt(
1651 "'{a}' should not be greater than '{b}'.", line, from + l, low, high);
1652 }
1653 }
1654 }
1655 }
1656 c = s.substr(0, l - 1);
1657 character += l;
1658 s = s.substr(l);
1659 return it('(regexp)', c);
1660 }
1661 return it('(punctuator)', t);
1662
1663 // punctuator
1664
1665 case '#':
1666 return it('(punctuator)', t);
1667 default:
1668 return it('(punctuator)', t);
1669 }
1670 }
1671 }
1672 }
1673 };
1674 }());
1675
1676
1677 function addlabel(t, type) {
1678
1679 if (t === 'hasOwnProperty') {
1680 warning("'hasOwnProperty' is a really bad name.");
1681 }
1682
1683 // Define t in the current function in the current scope.
1684 if (is_own(funct, t) && !funct['(global)']) {
1685 if (funct[t] === true) {
1686 if (option.latedef)
1687 warning("'{a}' was used before it was defined.", nexttoken, t);
1688 } else {
1689 if (!option.shadow && type !== "exception")
1690 warning("'{a}' is already defined.", nexttoken, t);
1691 }
1692 }
1693
1694 funct[t] = type;
1695 if (funct['(global)']) {
1696 global[t] = funct;
1697 if (is_own(implied, t)) {
1698 if (option.latedef)
1699 warning("'{a}' was used before it was defined.", nexttoken, t);
1700 delete implied[t];
1701 }
1702 } else {
1703 scope[t] = funct;
1704 }
1705 }
1706
1707
1708 function doOption() {
1709 var b, obj, filter, o = nexttoken.value, t, v;
1710 switch (o) {
1711 case '*/':
1712 error("Unbegun comment.");
1713 break;
1714 case '/*members':
1715 case '/*member':
1716 o = '/*members';
1717 if (!membersOnly) {
1718 membersOnly = {};
1719 }
1720 obj = membersOnly;
1721 break;
1722 case '/*jshint':
1723 case '/*jslint':
1724 obj = option;
1725 filter = boolOptions;
1726 break;
1727 case '/*global':
1728 obj = predefined;
1729 break;
1730 default:
1731 error("What?");
1732 }
1733 t = lex.token();
1734 loop: for (;;) {
1735 for (;;) {
1736 if (t.type === 'special' && t.value === '*/') {
1737 break loop;
1738 }
1739 if (t.id !== '(endline)' && t.id !== ',') {
1740 break;
1741 }
1742 t = lex.token();
1743 }
1744 if (t.type !== '(string)' && t.type !== '(identifier)' &&
1745 o !== '/*members') {
1746 error("Bad option.", t);
1747 }
1748 v = lex.token();
1749 if (v.id === ':') {
1750 v = lex.token();
1751 if (obj === membersOnly) {
1752 error("Expected '{a}' and instead saw '{b}'.",
1753 t, '*/', ':');
1754 }
1755 if (t.value === 'indent' && (o === '/*jshint' || o === '/*jslint')) {
1756 b = +v.value;
1757 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1758 Math.floor(b) !== b) {
1759 error("Expected a small integer and instead saw '{a}'.",
1760 v, v.value);
1761 }
1762 obj.white = true;
1763 obj.indent = b;
1764 } else if (t.value === 'maxerr' && (o === '/*jshint' || o === '/*jslint')) {
1765 b = +v.value;
1766 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1767 Math.floor(b) !== b) {
1768 error("Expected a small integer and instead saw '{a}'.",
1769 v, v.value);
1770 }
1771 obj.maxerr = b;
1772 } else if (t.value === 'maxlen' && (o === '/*jshint' || o === '/*jslint')) {
1773 b = +v.value;
1774 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1775 Math.floor(b) !== b) {
1776 error("Expected a small integer and instead saw '{a}'.",
1777 v, v.value);
1778 }
1779 obj.maxlen = b;
1780 } else if (t.value === 'validthis') {
1781 if (funct['(global)']) {
1782 error("Option 'validthis' can't be used in a global scope.");
1783 } else {
1784 if (v.value === 'true' || v.value === 'false')
1785 obj[t.value] = v.value === 'true';
1786 else
1787 error("Bad option value.", v);
1788 }
1789 } else if (v.value === 'true') {
1790 obj[t.value] = true;
1791 } else if (v.value === 'false') {
1792 obj[t.value] = false;
1793 } else {
1794 error("Bad option value.", v);
1795 }
1796 t = lex.token();
1797 } else {
1798 if (o === '/*jshint' || o === '/*jslint') {
1799 error("Missing option value.", t);
1800 }
1801 obj[t.value] = false;
1802 t = v;
1803 }
1804 }
1805 if (filter) {
1806 assume();
1807 }
1808 }
1809
1810
1811 // We need a peek function. If it has an argument, it peeks that much farther
1812 // ahead. It is used to distinguish
1813 // for ( var i in ...
1814 // from
1815 // for ( var i = ...
1816
1817 function peek(p) {
1818 var i = p || 0, j = 0, t;
1819
1820 while (j <= i) {
1821 t = lookahead[j];
1822 if (!t) {
1823 t = lookahead[j] = lex.token();
1824 }
1825 j += 1;
1826 }
1827 return t;
1828 }
1829
1830
1831
1832 // Produce the next token. It looks for programming errors.
1833
1834 function advance(id, t) {
1835 switch (token.id) {
1836 case '(number)':
1837 if (nexttoken.id === '.') {
1838 warning("A dot following a number can be confused with a decimal point.", token);
1839 }
1840 break;
1841 case '-':
1842 if (nexttoken.id === '-' || nexttoken.id === '--') {
1843 warning("Confusing minusses.");
1844 }
1845 break;
1846 case '+':
1847 if (nexttoken.id === '+' || nexttoken.id === '++') {
1848 warning("Confusing plusses.");
1849 }
1850 break;
1851 }
1852
1853 if (token.type === '(string)' || token.identifier) {
1854 anonname = token.value;
1855 }
1856
1857 if (id && nexttoken.id !== id) {
1858 if (t) {
1859 if (nexttoken.id === '(end)') {
1860 warning("Unmatched '{a}'.", t, t.id);
1861 } else {
1862 warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
1863 nexttoken, id, t.id, t.line, nexttoken.value);
1864 }
1865 } else if (nexttoken.type !== '(identifier)' ||
1866 nexttoken.value !== id) {
1867 warning("Expected '{a}' and instead saw '{b}'.",
1868 nexttoken, id, nexttoken.value);
1869 }
1870 }
1871
1872 prevtoken = token;
1873 token = nexttoken;
1874 for (;;) {
1875 nexttoken = lookahead.shift() || lex.token();
1876 if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {
1877 return;
1878 }
1879 if (nexttoken.type === 'special') {
1880 doOption();
1881 } else {
1882 if (nexttoken.id !== '(endline)') {
1883 break;
1884 }
1885 }
1886 }
1887 }
1888
1889
1890 // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it
1891 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
1892 // like .nud except that it is only used on the first token of a statement.
1893 // Having .fud makes it much easier to define statement-oriented languages like
1894 // JavaScript. I retained Pratt's nomenclature.
1895
1896 // .nud Null denotation
1897 // .fud First null denotation
1898 // .led Left denotation
1899 // lbp Left binding power
1900 // rbp Right binding power
1901
1902 // They are elements of the parsing method called Top Down Operator Precedence.
1903
1904 function expression(rbp, initial) {
1905 var left, isArray = false;
1906
1907 if (nexttoken.id === '(end)')
1908 error("Unexpected early end of program.", token);
1909
1910 advance();
1911 if (initial) {
1912 anonname = 'anonymous';
1913 funct['(verb)'] = token.value;
1914 }
1915 if (initial === true && token.fud) {
1916 left = token.fud();
1917 } else {
1918 if (token.nud) {
1919 left = token.nud();
1920 } else {
1921 if (nexttoken.type === '(number)' && token.id === '.') {
1922 warning("A leading decimal point can be confused with a dot: '.{a}'.",
1923 token, nexttoken.value);
1924 advance();
1925 return token;
1926 } else {
1927 error("Expected an identifier and instead saw '{a}'.",
1928 token, token.id);
1929 }
1930 }
1931 while (rbp < nexttoken.lbp) {
1932 isArray = token.value === 'Array';
1933 advance();
1934 if (isArray && token.id === '(' && nexttoken.id === ')')
1935 warning("Use the array literal notation [].", token);
1936 if (token.led) {
1937 left = token.led(left);
1938 } else {
1939 error("Expected an operator and instead saw '{a}'.",
1940 token, token.id);
1941 }
1942 }
1943 }
1944 return left;
1945 }
1946
1947
1948 // Functions for conformance of style.
1949
1950 function adjacent(left, right) {
1951 left = left || token;
1952 right = right || nexttoken;
1953 if (option.white) {
1954 if (left.character !== right.from && left.line === right.line) {
1955 left.from += (left.character - left.from);
1956 warning("Unexpected space after '{a}'.", left, left.value);
1957 }
1958 }
1959 }
1960
1961 function nobreak(left, right) {
1962 left = left || token;
1963 right = right || nexttoken;
1964 if (option.white && (left.character !== right.from || left.line !== right.line)) {
1965 warning("Unexpected space before '{a}'.", right, right.value);
1966 }
1967 }
1968
1969 function nospace(left, right) {
1970 left = left || token;
1971 right = right || nexttoken;
1972 if (option.white && !left.comment) {
1973 if (left.line === right.line) {
1974 adjacent(left, right);
1975 }
1976 }
1977 }
1978
1979 function nonadjacent(left, right) {
1980 if (option.white) {
1981 left = left || token;
1982 right = right || nexttoken;
1983 if (left.line === right.line && left.character === right.from) {
1984 left.from += (left.character - left.from);
1985 warning("Missing space after '{a}'.",
1986 left, left.value);
1987 }
1988 }
1989 }
1990
1991 function nobreaknonadjacent(left, right) {
1992 left = left || token;
1993 right = right || nexttoken;
1994 if (!option.laxbreak && left.line !== right.line) {
1995 warning("Bad line breaking before '{a}'.", right, right.id);
1996 } else if (option.white) {
1997 left = left || token;
1998 right = right || nexttoken;
1999 if (left.character === right.from) {
2000 left.from += (left.character - left.from);
2001 warning("Missing space after '{a}'.",
2002 left, left.value);
2003 }
2004 }
2005 }
2006
2007 function indentation(bias) {
2008 var i;
2009 if (option.white && nexttoken.id !== '(end)') {
2010 i = indent + (bias || 0);
2011 if (nexttoken.from !== i) {
2012 warning(
2013 "Expected '{a}' to have an indentation at {b} instead at {c}.",
2014 nexttoken, nexttoken.value, i, nexttoken.from);
2015 }
2016 }
2017 }
2018
2019 function nolinebreak(t) {
2020 t = t || token;
2021 if (t.line !== nexttoken.line) {
2022 warning("Line breaking error '{a}'.", t, t.value);
2023 }
2024 }
2025
2026
2027 function comma() {
2028 if (token.line !== nexttoken.line) {
2029 if (!option.laxbreak) {
2030 warning("Bad line breaking before '{a}'.", token, nexttoken.id);
2031 }
2032 } else if (!token.comment && token.character !== nexttoken.from && option.white) {
2033 token.from += (token.character - token.from);
2034 warning("Unexpected space after '{a}'.", token, token.value);
2035 }
2036 advance(',');
2037 nonadjacent(token, nexttoken);
2038 }
2039
2040
2041 // Functional constructors for making the symbols that will be inherited by
2042 // tokens.
2043
2044 function symbol(s, p) {
2045 var x = syntax[s];
2046 if (!x || typeof x !== 'object') {
2047 syntax[s] = x = {
2048 id: s,
2049 lbp: p,
2050 value: s
2051 };
2052 }
2053 return x;
2054 }
2055
2056
2057 function delim(s) {
2058 return symbol(s, 0);
2059 }
2060
2061
2062 function stmt(s, f) {
2063 var x = delim(s);
2064 x.identifier = x.reserved = true;
2065 x.fud = f;
2066 return x;
2067 }
2068
2069
2070 function blockstmt(s, f) {
2071 var x = stmt(s, f);
2072 x.block = true;
2073 return x;
2074 }
2075
2076
2077 function reserveName(x) {
2078 var c = x.id.charAt(0);
2079 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2080 x.identifier = x.reserved = true;
2081 }
2082 return x;
2083 }
2084
2085
2086 function prefix(s, f) {
2087 var x = symbol(s, 150);
2088 reserveName(x);
2089 x.nud = (typeof f === 'function') ? f : function () {
2090 this.right = expression(150);
2091 this.arity = 'unary';
2092 if (this.id === '++' || this.id === '--') {
2093 if (option.plusplus) {
2094 warning("Unexpected use of '{a}'.", this, this.id);
2095 } else if ((!this.right.identifier || this.right.reserved) &&
2096 this.right.id !== '.' && this.right.id !== '[') {
2097 warning("Bad operand.", this);
2098 }
2099 }
2100 return this;
2101 };
2102 return x;
2103 }
2104
2105
2106 function type(s, f) {
2107 var x = delim(s);
2108 x.type = s;
2109 x.nud = f;
2110 return x;
2111 }
2112
2113
2114 function reserve(s, f) {
2115 var x = type(s, f);
2116 x.identifier = x.reserved = true;
2117 return x;
2118 }
2119
2120
2121 function reservevar(s, v) {
2122 return reserve(s, function () {
2123 if (typeof v === 'function') {
2124 v(this);
2125 }
2126 return this;
2127 });
2128 }
2129
2130
2131 function infix(s, f, p, w) {
2132 var x = symbol(s, p);
2133 reserveName(x);
2134 x.led = function (left) {
2135 if (!w) {
2136 nobreaknonadjacent(prevtoken, token);
2137 nonadjacent(token, nexttoken);
2138 }
2139 if (s === "in" && left.id === "!") {
2140 warning("Confusing use of '{a}'.", left, '!');
2141 }
2142 if (typeof f === 'function') {
2143 return f(left, this);
2144 } else {
2145 this.left = left;
2146 this.right = expression(p);
2147 return this;
2148 }
2149 };
2150 return x;
2151 }
2152
2153
2154 function relation(s, f) {
2155 var x = symbol(s, 100);
2156 x.led = function (left) {
2157 nobreaknonadjacent(prevtoken, token);
2158 nonadjacent(token, nexttoken);
2159 var right = expression(100);
2160 if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) {
2161 warning("Use the isNaN function to compare with NaN.", this);
2162 } else if (f) {
2163 f.apply(this, [left, right]);
2164 }
2165 if (left.id === '!') {
2166 warning("Confusing use of '{a}'.", left, '!');
2167 }
2168 if (right.id === '!') {
2169 warning("Confusing use of '{a}'.", right, '!');
2170 }
2171 this.left = left;
2172 this.right = right;
2173 return this;
2174 };
2175 return x;
2176 }
2177
2178
2179 function isPoorRelation(node) {
2180 return node &&
2181 ((node.type === '(number)' && +node.value === 0) ||
2182 (node.type === '(string)' && node.value === '') ||
2183 (node.type === 'null' && !option.eqnull) ||
2184 node.type === 'true' ||
2185 node.type === 'false' ||
2186 node.type === 'undefined');
2187 }
2188
2189
2190 function assignop(s, f) {
2191 symbol(s, 20).exps = true;
2192 return infix(s, function (left, that) {
2193 var l;
2194 that.left = left;
2195 if (predefined[left.value] === false &&
2196 scope[left.value]['(global)'] === true) {
2197 warning("Read only.", left);
2198 } else if (left['function']) {
2199 warning("'{a}' is a function.", left, left.value);
2200 }
2201 if (left) {
2202 if (option.esnext && funct[left.value] === 'const') {
2203 warning("Attempting to override '{a}' which is a constant", left, left.value);
2204 }
2205 if (left.id === '.' || left.id === '[') {
2206 if (!left.left || left.left.value === 'arguments') {
2207 warning('Bad assignment.', that);
2208 }
2209 that.right = expression(19);
2210 return that;
2211 } else if (left.identifier && !left.reserved) {
2212 if (funct[left.value] === 'exception') {
2213 warning("Do not assign to the exception parameter.", left);
2214 }
2215 that.right = expression(19);
2216 return that;
2217 }
2218 if (left === syntax['function']) {
2219 warning(
2220 "Expected an identifier in an assignment and instead saw a function invocation.",
2221 token);
2222 }
2223 }
2224 error("Bad assignment.", that);
2225 }, 20);
2226 }
2227
2228
2229 function bitwise(s, f, p) {
2230 var x = symbol(s, p);
2231 reserveName(x);
2232 x.led = (typeof f === 'function') ? f : function (left) {
2233 if (option.bitwise) {
2234 warning("Unexpected use of '{a}'.", this, this.id);
2235 }
2236 this.left = left;
2237 this.right = expression(p);
2238 return this;
2239 };
2240 return x;
2241 }
2242
2243
2244 function bitwiseassignop(s) {
2245 symbol(s, 20).exps = true;
2246 return infix(s, function (left, that) {
2247 if (option.bitwise) {
2248 warning("Unexpected use of '{a}'.", that, that.id);
2249 }
2250 nonadjacent(prevtoken, token);
2251 nonadjacent(token, nexttoken);
2252 if (left) {
2253 if (left.id === '.' || left.id === '[' ||
2254 (left.identifier && !left.reserved)) {
2255 expression(19);
2256 return that;
2257 }
2258 if (left === syntax['function']) {
2259 warning(
2260 "Expected an identifier in an assignment, and instead saw a function invocation.",
2261 token);
2262 }
2263 return that;
2264 }
2265 error("Bad assignment.", that);
2266 }, 20);
2267 }
2268
2269
2270 function suffix(s, f) {
2271 var x = symbol(s, 150);
2272 x.led = function (left) {
2273 if (option.plusplus) {
2274 warning("Unexpected use of '{a}'.", this, this.id);
2275 } else if ((!left.identifier || left.reserved) &&
2276 left.id !== '.' && left.id !== '[') {
2277 warning("Bad operand.", this);
2278 }
2279 this.left = left;
2280 return this;
2281 };
2282 return x;
2283 }
2284
2285
2286 // fnparam means that this identifier is being defined as a function
2287 // argument (see identifier())
2288 function optionalidentifier(fnparam) {
2289 if (nexttoken.identifier) {
2290 advance();
2291 if (token.reserved && !option.es5) {
2292 // `undefined` as a function param is a common pattern to protect
2293 // against the case when somebody does `undefined = true` and
2294 // help with minification. More info: https://gist.github.com/315916
2295 if (!fnparam || token.value !== 'undefined') {
2296 warning("Expected an identifier and instead saw '{a}' (a reserved word).",
2297 token, token.id);
2298 }
2299 }
2300 return token.value;
2301 }
2302 }
2303
2304 // fnparam means that this identifier is being defined as a function
2305 // argument
2306 function identifier(fnparam) {
2307 var i = optionalidentifier(fnparam);
2308 if (i) {
2309 return i;
2310 }
2311 if (token.id === 'function' && nexttoken.id === '(') {
2312 warning("Missing name in function declaration.");
2313 } else {
2314 error("Expected an identifier and instead saw '{a}'.",
2315 nexttoken, nexttoken.value);
2316 }
2317 }
2318
2319
2320 function reachable(s) {
2321 var i = 0, t;
2322 if (nexttoken.id !== ';' || noreach) {
2323 return;
2324 }
2325 for (;;) {
2326 t = peek(i);
2327 if (t.reach) {
2328 return;
2329 }
2330 if (t.id !== '(endline)') {
2331 if (t.id === 'function') {
2332 warning(
2333 "Inner functions should be listed at the top of the outer function.", t);
2334 break;
2335 }
2336 warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
2337 break;
2338 }
2339 i += 1;
2340 }
2341 }
2342
2343
2344 function statement(noindent) {
2345 var i = indent, r, s = scope, t = nexttoken;
2346
2347 if (t.id === ";") {
2348 advance(";");
2349 return;
2350 }
2351
2352 // Is this a labelled statement?
2353
2354 if (t.identifier && !t.reserved && peek().id === ':') {
2355 advance();
2356 advance(':');
2357 scope = Object.create(s);
2358 addlabel(t.value, 'label');
2359 if (!nexttoken.labelled) {
2360 warning("Label '{a}' on {b} statement.",
2361 nexttoken, t.value, nexttoken.value);
2362 }
2363 if (jx.test(t.value + ':')) {
2364 warning("Label '{a}' looks like a javascript url.",
2365 t, t.value);
2366 }
2367 nexttoken.label = t.value;
2368 t = nexttoken;
2369 }
2370
2371 // Parse the statement.
2372
2373 if (!noindent) {
2374 indentation();
2375 }
2376 r = expression(0, true);
2377
2378 // Look for the final semicolon.
2379 if (!t.block) {
2380 if (!option.expr && (!r || !r.exps)) {
2381 warning("Expected an assignment or function call and instead saw an expression.",
2382 token);
2383 } else if (option.nonew && r.id === '(' && r.left.id === 'new') {
2384 warning("Do not use 'new' for side effects.");
2385 }
2386
2387 if (nexttoken.id !== ';') {
2388 if (!option.asi) {
2389 // If this is the last statement in a block that ends on
2390 // the same line *and* option lastsemic is on, ignore the warning.
2391 // Otherwise, complain about missing semicolon.
2392 if (!option.lastsemic || nexttoken.id !== '}' ||
2393 nexttoken.line !== token.line) {
2394 warningAt("Missing semicolon.", token.line, token.character);
2395 }
2396 }
2397 } else {
2398 adjacent(token, nexttoken);
2399 advance(';');
2400 nonadjacent(token, nexttoken);
2401 }
2402 }
2403
2404 // Restore the indentation.
2405
2406 indent = i;
2407 scope = s;
2408 return r;
2409 }
2410
2411
2412 function statements(startLine) {
2413 var a = [], f, p;
2414
2415 while (!nexttoken.reach && nexttoken.id !== '(end)') {
2416 if (nexttoken.id === ';') {
2417 warning("Unnecessary semicolon.");
2418 advance(';');
2419 } else {
2420 a.push(statement(startLine === nexttoken.line));
2421 }
2422 }
2423 return a;
2424 }
2425
2426
2427 /*
2428 * read all directives
2429 * recognizes a simple form of asi, but always
2430 * warns, if it is used
2431 */
2432 function directives() {
2433 var i, p, pn;
2434
2435 for (;;) {
2436 if (nexttoken.id === "(string)") {
2437 p = peek(0);
2438 if (p.id === "(endline)") {
2439 i = 1;
2440 do {
2441 pn = peek(i);
2442 i = i + 1;
2443 } while (pn.id === "(endline)");
2444
2445 if (pn.id !== ";") {
2446 if (pn.id !== "(string)" && pn.id !== "(number)" &&
2447 pn.id !== "(regexp)" && pn.identifier !== true &&
2448 pn.id !== "}") {
2449 break;
2450 }
2451 warning("Missing semicolon.", nexttoken);
2452 } else {
2453 p = pn;
2454 }
2455 } else if (p.id === "}") {
2456 // directive with no other statements, warn about missing semicolon
2457 warning("Missing semicolon.", p);
2458 } else if (p.id !== ";") {
2459 break;
2460 }
2461
2462 indentation();
2463 advance();
2464 if (directive[token.value]) {
2465 warning("Unnecessary directive \"{a}\".", token, token.value);
2466 }
2467
2468 if (token.value === "use strict") {
2469 option.newcap = true;
2470 option.undef = true;
2471 }
2472
2473 // there's no directive negation, so always set to true
2474 directive[token.value] = true;
2475
2476 if (p.id === ";") {
2477 advance(";");
2478 }
2479 continue;
2480 }
2481 break;
2482 }
2483 }
2484
2485
2486 /*
2487 * Parses a single block. A block is a sequence of statements wrapped in
2488 * braces.
2489 *
2490 * ordinary - true for everything but function bodies and try blocks.
2491 * stmt - true if block can be a single statement (e.g. in if/for/while).
2492 * isfunc - true if block is a function body
2493 */
2494 function block(ordinary, stmt, isfunc) {
2495 var a,
2496 b = inblock,
2497 old_indent = indent,
2498 m,
2499 s = scope,
2500 t,
2501 line,
2502 d;
2503
2504 inblock = ordinary;
2505 if (!ordinary || !option.funcscope) scope = Object.create(scope);
2506 nonadjacent(token, nexttoken);
2507 t = nexttoken;
2508
2509 if (nexttoken.id === '{') {
2510 advance('{');
2511 line = token.line;
2512 if (nexttoken.id !== '}') {
2513 indent += option.indent;
2514 while (!ordinary && nexttoken.from > indent) {
2515 indent += option.indent;
2516 }
2517
2518 if (isfunc) {
2519 m = {};
2520 for (d in directive) {
2521 if (is_own(directive, d)) {
2522 m[d] = directive[d];
2523 }
2524 }
2525 directives();
2526
2527 if (option.strict && funct['(context)']['(global)']) {
2528 if (!m["use strict"] && !directive["use strict"]) {
2529 warning("Missing \"use strict\" statement.");
2530 }
2531 }
2532 }
2533
2534 a = statements(line);
2535
2536 if (isfunc) {
2537 directive = m;
2538 }
2539
2540 indent -= option.indent;
2541 if (line !== nexttoken.line) {
2542 indentation();
2543 }
2544 } else if (line !== nexttoken.line) {
2545 indentation();
2546 }
2547 advance('}', t);
2548 indent = old_indent;
2549 } else if (!ordinary) {
2550 error("Expected '{a}' and instead saw '{b}'.",
2551 nexttoken, '{', nexttoken.value);
2552 } else {
2553 if (!stmt || option.curly)
2554 warning("Expected '{a}' and instead saw '{b}'.",
2555 nexttoken, '{', nexttoken.value);
2556
2557 noreach = true;
2558 indent += option.indent;
2559 // test indentation only if statement is in new line
2560 a = [statement(nexttoken.line === token.line)];
2561 indent -= option.indent;
2562 noreach = false;
2563 }
2564 funct['(verb)'] = null;
2565 if (!ordinary || !option.funcscope) scope = s;
2566 inblock = b;
2567 if (ordinary && option.noempty && (!a || a.length === 0)) {
2568 warning("Empty block.");
2569 }
2570 return a;
2571 }
2572
2573
2574 function countMember(m) {
2575 if (membersOnly && typeof membersOnly[m] !== 'boolean') {
2576 warning("Unexpected /*member '{a}'.", token, m);
2577 }
2578 if (typeof member[m] === 'number') {
2579 member[m] += 1;
2580 } else {
2581 member[m] = 1;
2582 }
2583 }
2584
2585
2586 function note_implied(token) {
2587 var name = token.value, line = token.line, a = implied[name];
2588 if (typeof a === 'function') {
2589 a = false;
2590 }
2591 if (!a) {
2592 a = [line];
2593 implied[name] = a;
2594 } else if (a[a.length - 1] !== line) {
2595 a.push(line);
2596 }
2597 }
2598
2599
2600 // Build the syntax table by declaring the syntactic elements of the language.
2601
2602 type('(number)', function () {
2603 return this;
2604 });
2605
2606 type('(string)', function () {
2607 return this;
2608 });
2609
2610 syntax['(identifier)'] = {
2611 type: '(identifier)',
2612 lbp: 0,
2613 identifier: true,
2614 nud: function () {
2615 var v = this.value,
2616 s = scope[v],
2617 f;
2618
2619 if (typeof s === 'function') {
2620 // Protection against accidental inheritance.
2621 s = undefined;
2622 } else if (typeof s === 'boolean') {
2623 f = funct;
2624 funct = functions[0];
2625 addlabel(v, 'var');
2626 s = funct;
2627 funct = f;
2628 }
2629
2630 // The name is in scope and defined in the current function.
2631 if (funct === s) {
2632 // Change 'unused' to 'var', and reject labels.
2633 switch (funct[v]) {
2634 case 'unused':
2635 funct[v] = 'var';
2636 break;
2637 case 'unction':
2638 funct[v] = 'function';
2639 this['function'] = true;
2640 break;
2641 case 'function':
2642 this['function'] = true;
2643 break;
2644 case 'label':
2645 warning("'{a}' is a statement label.", token, v);
2646 break;
2647 }
2648 } else if (funct['(global)']) {
2649 // The name is not defined in the function. If we are in the global
2650 // scope, then we have an undefined variable.
2651 //
2652 // Operators typeof and delete do not raise runtime errors even if
2653 // the base object of a reference is null so no need to display warning
2654 // if we're inside of typeof or delete.
2655 if (anonname !== 'typeof' && anonname !== 'delete' &&
2656 option.undef && typeof predefined[v] !== 'boolean') {
2657 isundef(funct, "'{a}' is not defined.", token, v);
2658 }
2659 note_implied(token);
2660 } else {
2661 // If the name is already defined in the current
2662 // function, but not as outer, then there is a scope error.
2663
2664 switch (funct[v]) {
2665 case 'closure':
2666 case 'function':
2667 case 'var':
2668 case 'unused':
2669 warning("'{a}' used out of scope.", token, v);
2670 break;
2671 case 'label':
2672 warning("'{a}' is a statement label.", token, v);
2673 break;
2674 case 'outer':
2675 case 'global':
2676 break;
2677 default:
2678 // If the name is defined in an outer function, make an outer entry,
2679 // and if it was unused, make it var.
2680 if (s === true) {
2681 funct[v] = true;
2682 } else if (s === null) {
2683 warning("'{a}' is not allowed.", token, v);
2684 note_implied(token);
2685 } else if (typeof s !== 'object') {
2686 // Operators typeof and delete do not raise runtime errors even
2687 // if the base object of a reference is null so no need to
2688 // display warning if we're inside of typeof or delete.
2689 if (anonname !== 'typeof' && anonname !== 'delete' && option.undef) {
2690 isundef(funct, "'{a}' is not defined.", token, v);
2691 }
2692 funct[v] = true;
2693 note_implied(token);
2694 } else {
2695 switch (s[v]) {
2696 case 'function':
2697 case 'unction':
2698 this['function'] = true;
2699 s[v] = 'closure';
2700 funct[v] = s['(global)'] ? 'global' : 'outer';
2701 break;
2702 case 'var':
2703 case 'unused':
2704 s[v] = 'closure';
2705 funct[v] = s['(global)'] ? 'global' : 'outer';
2706 break;
2707 case 'closure':
2708 case 'parameter':
2709 funct[v] = s['(global)'] ? 'global' : 'outer';
2710 break;
2711 case 'label':
2712 warning("'{a}' is a statement label.", token, v);
2713 }
2714 }
2715 }
2716 }
2717 return this;
2718 },
2719 led: function () {
2720 error("Expected an operator and instead saw '{a}'.",
2721 nexttoken, nexttoken.value);
2722 }
2723 };
2724
2725 type('(regexp)', function () {
2726 return this;
2727 });
2728
2729
2730 // ECMAScript parser
2731
2732 delim('(endline)');
2733 delim('(begin)');
2734 delim('(end)').reach = true;
2735 delim('</').reach = true;
2736 delim('<!');
2737 delim('<!--');
2738 delim('-->');
2739 delim('(error)').reach = true;
2740 delim('}').reach = true;
2741 delim(')');
2742 delim(']');
2743 delim('"').reach = true;
2744 delim("'").reach = true;
2745 delim(';');
2746 delim(':').reach = true;
2747 delim(',');
2748 delim('#');
2749 delim('@');
2750 reserve('else');
2751 reserve('case').reach = true;
2752 reserve('catch');
2753 reserve('default').reach = true;
2754 reserve('finally');
2755 reservevar('arguments', function (x) {
2756 if (directive['use strict'] && funct['(global)']) {
2757 warning("Strict violation.", x);
2758 }
2759 });
2760 reservevar('eval');
2761 reservevar('false');
2762 reservevar('Infinity');
2763 reservevar('NaN');
2764 reservevar('null');
2765 reservevar('this', function (x) {
2766 if (directive['use strict'] && !option.validthis && ((funct['(statement)'] &&
2767 funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) {
2768 warning("Possible strict violation.", x);
2769 }
2770 });
2771 reservevar('true');
2772 reservevar('undefined');
2773 assignop('=', 'assign', 20);
2774 assignop('+=', 'assignadd', 20);
2775 assignop('-=', 'assignsub', 20);
2776 assignop('*=', 'assignmult', 20);
2777 assignop('/=', 'assigndiv', 20).nud = function () {
2778 error("A regular expression literal can be confused with '/='.");
2779 };
2780 assignop('%=', 'assignmod', 20);
2781 bitwiseassignop('&=', 'assignbitand', 20);
2782 bitwiseassignop('|=', 'assignbitor', 20);
2783 bitwiseassignop('^=', 'assignbitxor', 20);
2784 bitwiseassignop('<<=', 'assignshiftleft', 20);
2785 bitwiseassignop('>>=', 'assignshiftright', 20);
2786 bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20);
2787 infix('?', function (left, that) {
2788 that.left = left;
2789 that.right = expression(10);
2790 advance(':');
2791 that['else'] = expression(10);
2792 return that;
2793 }, 30);
2794
2795 infix('||', 'or', 40);
2796 infix('&&', 'and', 50);
2797 bitwise('|', 'bitor', 70);
2798 bitwise('^', 'bitxor', 80);
2799 bitwise('&', 'bitand', 90);
2800 relation('==', function (left, right) {
2801 var eqnull = option.eqnull && (left.value === 'null' || right.value === 'null');
2802
2803 if (!eqnull && option.eqeqeq)
2804 warning("Expected '{a}' and instead saw '{b}'.", this, '===', '==');
2805 else if (isPoorRelation(left))
2806 warning("Use '{a}' to compare with '{b}'.", this, '===', left.value);
2807 else if (isPoorRelation(right))
2808 warning("Use '{a}' to compare with '{b}'.", this, '===', right.value);
2809
2810 return this;
2811 });
2812 relation('===');
2813 relation('!=', function (left, right) {
2814 var eqnull = option.eqnull &&
2815 (left.value === 'null' || right.value === 'null');
2816
2817 if (!eqnull && option.eqeqeq) {
2818 warning("Expected '{a}' and instead saw '{b}'.",
2819 this, '!==', '!=');
2820 } else if (isPoorRelation(left)) {
2821 warning("Use '{a}' to compare with '{b}'.",
2822 this, '!==', left.value);
2823 } else if (isPoorRelation(right)) {
2824 warning("Use '{a}' to compare with '{b}'.",
2825 this, '!==', right.value);
2826 }
2827 return this;
2828 });
2829 relation('!==');
2830 relation('<');
2831 relation('>');
2832 relation('<=');
2833 relation('>=');
2834 bitwise('<<', 'shiftleft', 120);
2835 bitwise('>>', 'shiftright', 120);
2836 bitwise('>>>', 'shiftrightunsigned', 120);
2837 infix('in', 'in', 120);
2838 infix('instanceof', 'instanceof', 120);
2839 infix('+', function (left, that) {
2840 var right = expression(130);
2841 if (left && right && left.id === '(string)' && right.id === '(string)') {
2842 left.value += right.value;
2843 left.character = right.character;
2844 if (!option.scripturl && jx.test(left.value)) {
2845 warning("JavaScript URL.", left);
2846 }
2847 return left;
2848 }
2849 that.left = left;
2850 that.right = right;
2851 return that;
2852 }, 130);
2853 prefix('+', 'num');
2854 prefix('+++', function () {
2855 warning("Confusing pluses.");
2856 this.right = expression(150);
2857 this.arity = 'unary';
2858 return this;
2859 });
2860 infix('+++', function (left) {
2861 warning("Confusing pluses.");
2862 this.left = left;
2863 this.right = expression(130);
2864 return this;
2865 }, 130);
2866 infix('-', 'sub', 130);
2867 prefix('-', 'neg');
2868 prefix('---', function () {
2869 warning("Confusing minuses.");
2870 this.right = expression(150);
2871 this.arity = 'unary';
2872 return this;
2873 });
2874 infix('---', function (left) {
2875 warning("Confusing minuses.");
2876 this.left = left;
2877 this.right = expression(130);
2878 return this;
2879 }, 130);
2880 infix('*', 'mult', 140);
2881 infix('/', 'div', 140);
2882 infix('%', 'mod', 140);
2883
2884 suffix('++', 'postinc');
2885 prefix('++', 'preinc');
2886 syntax['++'].exps = true;
2887
2888 suffix('--', 'postdec');
2889 prefix('--', 'predec');
2890 syntax['--'].exps = true;
2891 prefix('delete', function () {
2892 var p = expression(0);
2893 if (!p || (p.id !== '.' && p.id !== '[')) {
2894 warning("Variables should not be deleted.");
2895 }
2896 this.first = p;
2897 return this;
2898 }).exps = true;
2899
2900 prefix('~', function () {
2901 if (option.bitwise) {
2902 warning("Unexpected '{a}'.", this, '~');
2903 }
2904 expression(150);
2905 return this;
2906 });
2907
2908 prefix('!', function () {
2909 this.right = expression(150);
2910 this.arity = 'unary';
2911 if (bang[this.right.id] === true) {
2912 warning("Confusing use of '{a}'.", this, '!');
2913 }
2914 return this;
2915 });
2916 prefix('typeof', 'typeof');
2917 prefix('new', function () {
2918 var c = expression(155), i;
2919 if (c && c.id !== 'function') {
2920 if (c.identifier) {
2921 c['new'] = true;
2922 switch (c.value) {
2923 case 'Object':
2924 warning("Use the object literal notation {}.", token);
2925 break;
2926 case 'Number':
2927 case 'String':
2928 case 'Boolean':
2929 case 'Math':
2930 case 'JSON':
2931 warning("Do not use {a} as a constructor.", token, c.value);
2932 break;
2933 case 'Function':
2934 if (!option.evil) {
2935 warning("The Function constructor is eval.");
2936 }
2937 break;
2938 case 'Date':
2939 case 'RegExp':
2940 break;
2941 default:
2942 if (c.id !== 'function') {
2943 i = c.value.substr(0, 1);
2944 if (option.newcap && (i < 'A' || i > 'Z')) {
2945 warning("A constructor name should start with an uppercase letter.",
2946 token);
2947 }
2948 }
2949 }
2950 } else {
2951 if (c.id !== '.' && c.id !== '[' && c.id !== '(') {
2952 warning("Bad constructor.", token);
2953 }
2954 }
2955 } else {
2956 if (!option.supernew)
2957 warning("Weird construction. Delete 'new'.", this);
2958 }
2959 adjacent(token, nexttoken);
2960 if (nexttoken.id !== '(' && !option.supernew) {
2961 warning("Missing '()' invoking a constructor.");
2962 }
2963 this.first = c;
2964 return this;
2965 });
2966 syntax['new'].exps = true;
2967
2968 prefix('void').exps = true;
2969
2970 infix('.', function (left, that) {
2971 adjacent(prevtoken, token);
2972 nobreak();
2973 var m = identifier();
2974 if (typeof m === 'string') {
2975 countMember(m);
2976 }
2977 that.left = left;
2978 that.right = m;
2979 if (left && left.value === 'arguments' && (m === 'callee' || m === 'caller')) {
2980 if (option.noarg)
2981 warning("Avoid arguments.{a}.", left, m);
2982 else if (directive['use strict'])
2983 error('Strict violation.');
2984 } else if (!option.evil && left && left.value === 'document' &&
2985 (m === 'write' || m === 'writeln')) {
2986 warning("document.write can be a form of eval.", left);
2987 }
2988 if (!option.evil && (m === 'eval' || m === 'execScript')) {
2989 warning('eval is evil.');
2990 }
2991 return that;
2992 }, 160, true);
2993
2994 infix('(', function (left, that) {
2995 if (prevtoken.id !== '}' && prevtoken.id !== ')') {
2996 nobreak(prevtoken, token);
2997 }
2998 nospace();
2999 if (option.immed && !left.immed && left.id === 'function') {
3000 warning("Wrap an immediate function invocation in parentheses " +
3001 "to assist the reader in understanding that the expression " +
3002 "is the result of a function, and not the function itself.");
3003 }
3004 var n = 0,
3005 p = [];
3006 if (left) {
3007 if (left.type === '(identifier)') {
3008 if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
3009 if (left.value !== 'Number' && left.value !== 'String' &&
3010 left.value !== 'Boolean' &&
3011 left.value !== 'Date') {
3012 if (left.value === 'Math') {
3013 warning("Math is not a function.", left);
3014 } else if (option.newcap) {
3015 warning(
3016 "Missing 'new' prefix when invoking a constructor.", left);
3017 }
3018 }
3019 }
3020 }
3021 }
3022 if (nexttoken.id !== ')') {
3023 for (;;) {
3024 p[p.length] = expression(10);
3025 n += 1;
3026 if (nexttoken.id !== ',') {
3027 break;
3028 }
3029 comma();
3030 }
3031 }
3032 advance(')');
3033 nospace(prevtoken, token);
3034 if (typeof left === 'object') {
3035 if (left.value === 'parseInt' && n === 1) {
3036 warning("Missing radix parameter.", left);
3037 }
3038 if (!option.evil) {
3039 if (left.value === 'eval' || left.value === 'Function' ||
3040 left.value === 'execScript') {
3041 warning("eval is evil.", left);
3042 } else if (p[0] && p[0].id === '(string)' &&
3043 (left.value === 'setTimeout' ||
3044 left.value === 'setInterval')) {
3045 warning(
3046 "Implied eval is evil. Pass a function instead of a string.", left);
3047 }
3048 }
3049 if (!left.identifier && left.id !== '.' && left.id !== '[' &&
3050 left.id !== '(' && left.id !== '&&' && left.id !== '||' &&
3051 left.id !== '?') {
3052 warning("Bad invocation.", left);
3053 }
3054 }
3055 that.left = left;
3056 return that;
3057 }, 155, true).exps = true;
3058
3059 prefix('(', function () {
3060 nospace();
3061 if (nexttoken.id === 'function') {
3062 nexttoken.immed = true;
3063 }
3064 var v = expression(0);
3065 advance(')', this);
3066 nospace(prevtoken, token);
3067 if (option.immed && v.id === 'function') {
3068 if (nexttoken.id === '(') {
3069 warning(
3070 "Move the invocation into the parens that contain the function.", nexttoken);
3071 } else {
3072 warning(
3073 "Do not wrap function literals in parens unless they are to be immediately invoked.",
3074 this);
3075 }
3076 }
3077 return v;
3078 });
3079
3080 infix('[', function (left, that) {
3081 nobreak(prevtoken, token);
3082 nospace();
3083 var e = expression(0), s;
3084 if (e && e.type === '(string)') {
3085 if (!option.evil && (e.value === 'eval' || e.value === 'execScript')) {
3086 warning("eval is evil.", that);
3087 }
3088 countMember(e.value);
3089 if (!option.sub && ix.test(e.value)) {
3090 s = syntax[e.value];
3091 if (!s || !s.reserved) {
3092 warning("['{a}'] is better written in dot notation.",
3093 e, e.value);
3094 }
3095 }
3096 }
3097 advance(']', that);
3098 nospace(prevtoken, token);
3099 that.left = left;
3100 that.right = e;
3101 return that;
3102 }, 160, true);
3103
3104 prefix('[', function () {
3105 var b = token.line !== nexttoken.line;
3106 this.first = [];
3107 if (b) {
3108 indent += option.indent;
3109 if (nexttoken.from === indent + option.indent) {
3110 indent += option.indent;
3111 }
3112 }
3113 while (nexttoken.id !== '(end)') {
3114 while (nexttoken.id === ',') {
3115 warning("Extra comma.");
3116 advance(',');
3117 }
3118 if (nexttoken.id === ']') {
3119 break;
3120 }
3121 if (b && token.line !== nexttoken.line) {
3122 indentation();
3123 }
3124 this.first.push(expression(10));
3125 if (nexttoken.id === ',') {
3126 comma();
3127 if (nexttoken.id === ']' && !option.es5) {
3128 warning("Extra comma.", token);
3129 break;
3130 }
3131 } else {
3132 break;
3133 }
3134 }
3135 if (b) {
3136 indent -= option.indent;
3137 indentation();
3138 }
3139 advance(']', this);
3140 return this;
3141 }, 160);
3142
3143
3144 function property_name() {
3145 var id = optionalidentifier(true);
3146 if (!id) {
3147 if (nexttoken.id === '(string)') {
3148 id = nexttoken.value;
3149 advance();
3150 } else if (nexttoken.id === '(number)') {
3151 id = nexttoken.value.toString();
3152 advance();
3153 }
3154 }
3155 return id;
3156 }
3157
3158
3159 function functionparams() {
3160 var i, t = nexttoken, p = [];
3161 advance('(');
3162 nospace();
3163 if (nexttoken.id === ')') {
3164 advance(')');
3165 return;
3166 }
3167 for (;;) {
3168 i = identifier(true);
3169 p.push(i);
3170 addlabel(i, 'parameter');
3171 if (nexttoken.id === ',') {
3172 comma();
3173 } else {
3174 advance(')', t);
3175 nospace(prevtoken, token);
3176 return p;
3177 }
3178 }
3179 }
3180
3181
3182 function doFunction(i, statement) {
3183 var f,
3184 oldOption = option,
3185 oldScope = scope;
3186
3187 option = Object.create(option);
3188 scope = Object.create(scope);
3189
3190 funct = {
3191 '(name)' : i || '"' + anonname + '"',
3192 '(line)' : nexttoken.line,
3193 '(context)' : funct,
3194 '(breakage)' : 0,
3195 '(loopage)' : 0,
3196 '(scope)' : scope,
3197 '(statement)': statement
3198 };
3199 f = funct;
3200 token.funct = funct;
3201 functions.push(funct);
3202 if (i) {
3203 addlabel(i, 'function');
3204 }
3205 funct['(params)'] = functionparams();
3206
3207 block(false, false, true);
3208 scope = oldScope;
3209 option = oldOption;
3210 funct['(last)'] = token.line;
3211 funct = funct['(context)'];
3212 return f;
3213 }
3214
3215
3216 (function (x) {
3217 x.nud = function () {
3218 var b, f, i, j, p, seen = {}, t;
3219 var prop, acc = {}; // Accessor methods
3220
3221 function saveSetter(name, token) {
3222 if (!acc[name]) {
3223 acc[name] = {};
3224 }
3225 acc[name].setter = true;
3226 acc[name].setterToken = token;
3227 }
3228
3229 function saveGetter(name) {
3230 if (!acc[name]) {
3231 acc[name] = {};
3232 }
3233 acc[name].getter = true;
3234 }
3235
3236 b = token.line !== nexttoken.line;
3237 if (b) {
3238 indent += option.indent;
3239 if (nexttoken.from === indent + option.indent) {
3240 indent += option.indent;
3241 }
3242 }
3243 for (;;) {
3244 if (nexttoken.id === '}') {
3245 break;
3246 }
3247 if (b) {
3248 indentation();
3249 }
3250 if (nexttoken.value === 'get' && peek().id !== ':') {
3251 advance('get');
3252 if (!option.es5) {
3253 error("get/set are ES5 features.");
3254 }
3255 i = property_name();
3256 if (!i) {
3257 error("Missing property name.");
3258 }
3259 saveGetter(i);
3260 t = nexttoken;
3261 adjacent(token, nexttoken);
3262 f = doFunction();
3263 if (!option.loopfunc && funct['(loopage)']) {
3264 warning("Don't make functions within a loop.", t);
3265 }
3266 p = f['(params)'];
3267 if (p) {
3268 warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i);
3269 }
3270 adjacent(token, nexttoken);
3271 } else if (nexttoken.value === 'set' && peek().id !== ':') {
3272 advance('set');
3273 if (!option.es5) {
3274 error("get/set are ES5 features.");
3275 }
3276 i = property_name();
3277 if (!i) {
3278 error("Missing property name.");
3279 }
3280 if (acc[i] && acc[i].setter) {
3281 warning("Duplicate member '{a}'.", nexttoken, i);
3282 }
3283 saveSetter(i, nexttoken);
3284 seen[i] = false;
3285 t = nexttoken;
3286 adjacent(token, nexttoken);
3287 f = doFunction();
3288 p = f['(params)'];
3289 if (!p || p.length !== 1 || p[0] !== 'value') {
3290 warning("Expected (value) in set {a} function.", t, i);
3291 }
3292 } else {
3293 i = property_name();
3294 if (typeof i !== 'string') {
3295 break;
3296 }
3297 advance(':');
3298 nonadjacent(token, nexttoken);
3299 expression(10);
3300 }
3301 if (seen[i] === true) {
3302 warning("Duplicate member '{a}'.", nexttoken, i);
3303 }
3304 seen[i] = true;
3305 countMember(i);
3306 if (nexttoken.id === ',') {
3307 comma();
3308 if (nexttoken.id === ',') {
3309 warning("Extra comma.", token);
3310 } else if (nexttoken.id === '}' && !option.es5) {
3311 warning("Extra comma.", token);
3312 }
3313 } else {
3314 break;
3315 }
3316 }
3317 if (b) {
3318 indent -= option.indent;
3319 indentation();
3320 }
3321 advance('}', this);
3322
3323 // Check for lonely setters if in the ES5 mode.
3324 if (option.es5) {
3325 for (prop in acc) {
3326 if (acc.hasOwnProperty(prop) && acc[prop].setter && !acc[prop].getter) {
3327 warning("Setter is defined without getter.", acc[prop].setterToken);
3328 }
3329 }
3330 }
3331 return this;
3332 };
3333 x.fud = function () {
3334 error("Expected to see a statement and instead saw a block.", token);
3335 };
3336 }(delim('{')));
3337
3338 // This Function is called when esnext option is set to true
3339 // it adds the `const` statement to JSHINT
3340
3341 useESNextSyntax = function () {
3342 var conststatement = stmt('const', function (prefix) {
3343 var id, name, value;
3344
3345 this.first = [];
3346 for (;;) {
3347 nonadjacent(token, nexttoken);
3348 id = identifier();
3349 if (funct[id] === "const") {
3350 warning("const '" + id + "' has already been declared");
3351 }
3352 if (funct['(global)'] && predefined[id] === false) {
3353 warning("Redefinition of '{a}'.", token, id);
3354 }
3355 addlabel(id, 'const');
3356 if (prefix) {
3357 break;
3358 }
3359 name = token;
3360 this.first.push(token);
3361
3362 if (nexttoken.id !== "=") {
3363 warning("const " +
3364 "'{a}' is initialized to 'undefined'.", token, id);
3365 }
3366
3367 if (nexttoken.id === '=') {
3368 nonadjacent(token, nexttoken);
3369 advance('=');
3370 nonadjacent(token, nexttoken);
3371 if (nexttoken.id === 'undefined') {
3372 warning("It is not necessary to initialize " +
3373 "'{a}' to 'undefined'.", token, id);
3374 }
3375 if (peek(0).id === '=' && nexttoken.identifier) {
3376 error("Constant {a} was not declared correctly.",
3377 nexttoken, nexttoken.value);
3378 }
3379 value = expression(0);
3380 name.first = value;
3381 }
3382
3383 if (nexttoken.id !== ',') {
3384 break;
3385 }
3386 comma();
3387 }
3388 return this;
3389 });
3390 conststatement.exps = true;
3391 };
3392
3393 var varstatement = stmt('var', function (prefix) {
3394 // JavaScript does not have block scope. It only has function scope. So,
3395 // declaring a variable in a block can have unexpected consequences.
3396 var id, name, value;
3397
3398 if (funct['(onevar)'] && option.onevar) {
3399 warning("Too many var statements.");
3400 } else if (!funct['(global)']) {
3401 funct['(onevar)'] = true;
3402 }
3403 this.first = [];
3404 for (;;) {
3405 nonadjacent(token, nexttoken);
3406 id = identifier();
3407 if (option.esnext && funct[id] === "const") {
3408 warning("const '" + id + "' has already been declared");
3409 }
3410 if (funct['(global)'] && predefined[id] === false) {
3411 warning("Redefinition of '{a}'.", token, id);
3412 }
3413 addlabel(id, 'unused');
3414 if (prefix) {
3415 break;
3416 }
3417 name = token;
3418 this.first.push(token);
3419 if (nexttoken.id === '=') {
3420 nonadjacent(token, nexttoken);
3421 advance('=');
3422 nonadjacent(token, nexttoken);
3423 if (nexttoken.id === 'undefined') {
3424 warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id);
3425 }
3426 if (peek(0).id === '=' && nexttoken.identifier) {
3427 error("Variable {a} was not declared correctly.",
3428 nexttoken, nexttoken.value);
3429 }
3430 value = expression(0);
3431 name.first = value;
3432 }
3433 if (nexttoken.id !== ',') {
3434 break;
3435 }
3436 comma();
3437 }
3438 return this;
3439 });
3440 varstatement.exps = true;
3441
3442 blockstmt('function', function () {
3443 if (inblock) {
3444 warning("Function declarations should not be placed in blocks. " +
3445 "Use a function expression or move the statement to the top of " +
3446 "the outer function.", token);
3447
3448 }
3449 var i = identifier();
3450 if (option.esnext && funct[i] === "const") {
3451 warning("const '" + i + "' has already been declared");
3452 }
3453 adjacent(token, nexttoken);
3454 addlabel(i, 'unction');
3455 doFunction(i, true);
3456 if (nexttoken.id === '(' && nexttoken.line === token.line) {
3457 error(
3458 "Function declarations are not invocable. Wrap the whole function invocation in parens.");
3459 }
3460 return this;
3461 });
3462
3463 prefix('function', function () {
3464 var i = optionalidentifier();
3465 if (i) {
3466 adjacent(token, nexttoken);
3467 } else {
3468 nonadjacent(token, nexttoken);
3469 }
3470 doFunction(i);
3471 if (!option.loopfunc && funct['(loopage)']) {
3472 warning("Don't make functions within a loop.");
3473 }
3474 return this;
3475 });
3476
3477 blockstmt('if', function () {
3478 var t = nexttoken;
3479 advance('(');
3480 nonadjacent(this, t);
3481 nospace();
3482 expression(20);
3483 if (nexttoken.id === '=') {
3484 if (!option.boss)
3485 warning("Expected a conditional expression and instead saw an assignment.");
3486 advance('=');
3487 expression(20);
3488 }
3489 advance(')', t);
3490 nospace(prevtoken, token);
3491 block(true, true);
3492 if (nexttoken.id === 'else') {
3493 nonadjacent(token, nexttoken);
3494 advance('else');
3495 if (nexttoken.id === 'if' || nexttoken.id === 'switch') {
3496 statement(true);
3497 } else {
3498 block(true, true);
3499 }
3500 }
3501 return this;
3502 });
3503
3504 blockstmt('try', function () {
3505 var b, e, s;
3506
3507 block(false);
3508 if (nexttoken.id === 'catch') {
3509 advance('catch');
3510 nonadjacent(token, nexttoken);
3511 advance('(');
3512 s = scope;
3513 scope = Object.create(s);
3514 e = nexttoken.value;
3515 if (nexttoken.type !== '(identifier)') {
3516 warning("Expected an identifier and instead saw '{a}'.",
3517 nexttoken, e);
3518 } else {
3519 addlabel(e, 'exception');
3520 }
3521 advance();
3522 advance(')');
3523 block(false);
3524 b = true;
3525 scope = s;
3526 }
3527 if (nexttoken.id === 'finally') {
3528 advance('finally');
3529 block(false);
3530 return;
3531 } else if (!b) {
3532 error("Expected '{a}' and instead saw '{b}'.",
3533 nexttoken, 'catch', nexttoken.value);
3534 }
3535 return this;
3536 });
3537
3538 blockstmt('while', function () {
3539 var t = nexttoken;
3540 funct['(breakage)'] += 1;
3541 funct['(loopage)'] += 1;
3542 advance('(');
3543 nonadjacent(this, t);
3544 nospace();
3545 expression(20);
3546 if (nexttoken.id === '=') {
3547 if (!option.boss)
3548 warning("Expected a conditional expression and instead saw an assignment.");
3549 advance('=');
3550 expression(20);
3551 }
3552 advance(')', t);
3553 nospace(prevtoken, token);
3554 block(true, true);
3555 funct['(breakage)'] -= 1;
3556 funct['(loopage)'] -= 1;
3557 return this;
3558 }).labelled = true;
3559
3560 reserve('with');
3561
3562 blockstmt('switch', function () {
3563 var t = nexttoken,
3564 g = false;
3565 funct['(breakage)'] += 1;
3566 advance('(');
3567 nonadjacent(this, t);
3568 nospace();
3569 this.condition = expression(20);
3570 advance(')', t);
3571 nospace(prevtoken, token);
3572 nonadjacent(token, nexttoken);
3573 t = nexttoken;
3574 advance('{');
3575 nonadjacent(token, nexttoken);
3576 indent += option.indent;
3577 this.cases = [];
3578 for (;;) {
3579 switch (nexttoken.id) {
3580 case 'case':
3581 switch (funct['(verb)']) {
3582 case 'break':
3583 case 'case':
3584 case 'continue':
3585 case 'return':
3586 case 'switch':
3587 case 'throw':
3588 break;
3589 default:
3590 // You can tell JSHint that you don't use break intentionally by
3591 // adding a comment /* falls through */ on a line just before
3592 // the next `case`.
3593 if (!ft.test(lines[nexttoken.line - 2])) {
3594 warning(
3595 "Expected a 'break' statement before 'case'.",
3596 token);
3597 }
3598 }
3599 indentation(-option.indent);
3600 advance('case');
3601 this.cases.push(expression(20));
3602 g = true;
3603 advance(':');
3604 funct['(verb)'] = 'case';
3605 break;
3606 case 'default':
3607 switch (funct['(verb)']) {
3608 case 'break':
3609 case 'continue':
3610 case 'return':
3611 case 'throw':
3612 break;
3613 default:
3614 if (!ft.test(lines[nexttoken.line - 2])) {
3615 warning(
3616 "Expected a 'break' statement before 'default'.",
3617 token);
3618 }
3619 }
3620 indentation(-option.indent);
3621 advance('default');
3622 g = true;
3623 advance(':');
3624 break;
3625 case '}':
3626 indent -= option.indent;
3627 indentation();
3628 advance('}', t);
3629 if (this.cases.length === 1 || this.condition.id === 'true' ||
3630 this.condition.id === 'false') {
3631 if (!option.onecase)
3632 warning("This 'switch' should be an 'if'.", this);
3633 }
3634 funct['(breakage)'] -= 1;
3635 funct['(verb)'] = undefined;
3636 return;
3637 case '(end)':
3638 error("Missing '{a}'.", nexttoken, '}');
3639 return;
3640 default:
3641 if (g) {
3642 switch (token.id) {
3643 case ',':
3644 error("Each value should have its own case label.");
3645 return;
3646 case ':':
3647 g = false;
3648 statements();
3649 break;
3650 default:
3651 error("Missing ':' on a case clause.", token);
3652 return;
3653 }
3654 } else {
3655 if (token.id === ':') {
3656 advance(':');
3657 error("Unexpected '{a}'.", token, ':');
3658 statements();
3659 } else {
3660 error("Expected '{a}' and instead saw '{b}'.",
3661 nexttoken, 'case', nexttoken.value);
3662 return;
3663 }
3664 }
3665 }
3666 }
3667 }).labelled = true;
3668
3669 stmt('debugger', function () {
3670 if (!option.debug) {
3671 warning("All 'debugger' statements should be removed.");
3672 }
3673 return this;
3674 }).exps = true;
3675
3676 (function () {
3677 var x = stmt('do', function () {
3678 funct['(breakage)'] += 1;
3679 funct['(loopage)'] += 1;
3680 this.first = block(true);
3681 advance('while');
3682 var t = nexttoken;
3683 nonadjacent(token, t);
3684 advance('(');
3685 nospace();
3686 expression(20);
3687 if (nexttoken.id === '=') {
3688 if (!option.boss)
3689 warning("Expected a conditional expression and instead saw an assignment.");
3690 advance('=');
3691 expression(20);
3692 }
3693 advance(')', t);
3694 nospace(prevtoken, token);
3695 funct['(breakage)'] -= 1;
3696 funct['(loopage)'] -= 1;
3697 return this;
3698 });
3699 x.labelled = true;
3700 x.exps = true;
3701 }());
3702
3703 blockstmt('for', function () {
3704 var s, t = nexttoken;
3705 funct['(breakage)'] += 1;
3706 funct['(loopage)'] += 1;
3707 advance('(');
3708 nonadjacent(this, t);
3709 nospace();
3710 if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') {
3711 if (nexttoken.id === 'var') {
3712 advance('var');
3713 varstatement.fud.call(varstatement, true);
3714 } else {
3715 switch (funct[nexttoken.value]) {
3716 case 'unused':
3717 funct[nexttoken.value] = 'var';
3718 break;
3719 case 'var':
3720 break;
3721 default:
3722 warning("Bad for in variable '{a}'.",
3723 nexttoken, nexttoken.value);
3724 }
3725 advance();
3726 }
3727 advance('in');
3728 expression(20);
3729 advance(')', t);
3730 s = block(true, true);
3731 if (option.forin && s && (s.length > 1 || typeof s[0] !== 'object' ||
3732 s[0].value !== 'if')) {
3733 warning("The body of a for in should be wrapped in an if statement to filter " +
3734 "unwanted properties from the prototype.", this);
3735 }
3736 funct['(breakage)'] -= 1;
3737 funct['(loopage)'] -= 1;
3738 return this;
3739 } else {
3740 if (nexttoken.id !== ';') {
3741 if (nexttoken.id === 'var') {
3742 advance('var');
3743 varstatement.fud.call(varstatement);
3744 } else {
3745 for (;;) {
3746 expression(0, 'for');
3747 if (nexttoken.id !== ',') {
3748 break;
3749 }
3750 comma();
3751 }
3752 }
3753 }
3754 nolinebreak(token);
3755 advance(';');
3756 if (nexttoken.id !== ';') {
3757 expression(20);
3758 if (nexttoken.id === '=') {
3759 if (!option.boss)
3760 warning("Expected a conditional expression and instead saw an assignment.");
3761 advance('=');
3762 expression(20);
3763 }
3764 }
3765 nolinebreak(token);
3766 advance(';');
3767 if (nexttoken.id === ';') {
3768 error("Expected '{a}' and instead saw '{b}'.",
3769 nexttoken, ')', ';');
3770 }
3771 if (nexttoken.id !== ')') {
3772 for (;;) {
3773 expression(0, 'for');
3774 if (nexttoken.id !== ',') {
3775 break;
3776 }
3777 comma();
3778 }
3779 }
3780 advance(')', t);
3781 nospace(prevtoken, token);
3782 block(true, true);
3783 funct['(breakage)'] -= 1;
3784 funct['(loopage)'] -= 1;
3785 return this;
3786 }
3787 }).labelled = true;
3788
3789
3790 stmt('break', function () {
3791 var v = nexttoken.value;
3792
3793 if (funct['(breakage)'] === 0)
3794 warning("Unexpected '{a}'.", nexttoken, this.value);
3795
3796 if (!option.asi)
3797 nolinebreak(this);
3798
3799 if (nexttoken.id !== ';') {
3800 if (token.line === nexttoken.line) {
3801 if (funct[v] !== 'label') {
3802 warning("'{a}' is not a statement label.", nexttoken, v);
3803 } else if (scope[v] !== funct) {
3804 warning("'{a}' is out of scope.", nexttoken, v);
3805 }
3806 this.first = nexttoken;
3807 advance();
3808 }
3809 }
3810 reachable('break');
3811 return this;
3812 }).exps = true;
3813
3814
3815 stmt('continue', function () {
3816 var v = nexttoken.value;
3817
3818 if (funct['(breakage)'] === 0)
3819 warning("Unexpected '{a}'.", nexttoken, this.value);
3820
3821 if (!option.asi)
3822 nolinebreak(this);
3823
3824 if (nexttoken.id !== ';') {
3825 if (token.line === nexttoken.line) {
3826 if (funct[v] !== 'label') {
3827 warning("'{a}' is not a statement label.", nexttoken, v);
3828 } else if (scope[v] !== funct) {
3829 warning("'{a}' is out of scope.", nexttoken, v);
3830 }
3831 this.first = nexttoken;
3832 advance();
3833 }
3834 } else if (!funct['(loopage)']) {
3835 warning("Unexpected '{a}'.", nexttoken, this.value);
3836 }
3837 reachable('continue');
3838 return this;
3839 }).exps = true;
3840
3841
3842 stmt('return', function () {
3843 if (this.line === nexttoken.line) {
3844 if (nexttoken.id === '(regexp)')
3845 warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator.");
3846
3847 if (nexttoken.id !== ';' && !nexttoken.reach) {
3848 nonadjacent(token, nexttoken);
3849 if (peek().value === "=" && !option.boss) {
3850 warningAt("Did you mean to return a conditional instead of an assignment?",
3851 token.line, token.character + 1);
3852 }
3853 this.first = expression(0);
3854 }
3855 } else if (!option.asi) {
3856 nolinebreak(this); // always warn (Line breaking error)
3857 }
3858 reachable('return');
3859 return this;
3860 }).exps = true;
3861
3862
3863 stmt('throw', function () {
3864 nolinebreak(this);
3865 nonadjacent(token, nexttoken);
3866 this.first = expression(20);
3867 reachable('throw');
3868 return this;
3869 }).exps = true;
3870
3871 // Superfluous reserved words
3872
3873 reserve('class');
3874 reserve('const');
3875 reserve('enum');
3876 reserve('export');
3877 reserve('extends');
3878 reserve('import');
3879 reserve('super');
3880
3881 reserve('let');
3882 reserve('yield');
3883 reserve('implements');
3884 reserve('interface');
3885 reserve('package');
3886 reserve('private');
3887 reserve('protected');
3888 reserve('public');
3889 reserve('static');
3890
3891
3892 // Parse JSON
3893
3894 function jsonValue() {
3895
3896 function jsonObject() {
3897 var o = {}, t = nexttoken;
3898 advance('{');
3899 if (nexttoken.id !== '}') {
3900 for (;;) {
3901 if (nexttoken.id === '(end)') {
3902 error("Missing '}' to match '{' from line {a}.",
3903 nexttoken, t.line);
3904 } else if (nexttoken.id === '}') {
3905 warning("Unexpected comma.", token);
3906 break;
3907 } else if (nexttoken.id === ',') {
3908 error("Unexpected comma.", nexttoken);
3909 } else if (nexttoken.id !== '(string)') {
3910 warning("Expected a string and instead saw {a}.",
3911 nexttoken, nexttoken.value);
3912 }
3913 if (o[nexttoken.value] === true) {
3914 warning("Duplicate key '{a}'.",
3915 nexttoken, nexttoken.value);
3916 } else if ((nexttoken.value === '__proto__' &&
3917 !option.proto) || (nexttoken.value === '__iterator__' &&
3918 !option.iterator)) {
3919 warning("The '{a}' key may produce unexpected results.",
3920 nexttoken, nexttoken.value);
3921 } else {
3922 o[nexttoken.value] = true;
3923 }
3924 advance();
3925 advance(':');
3926 jsonValue();
3927 if (nexttoken.id !== ',') {
3928 break;
3929 }
3930 advance(',');
3931 }
3932 }
3933 advance('}');
3934 }
3935
3936 function jsonArray() {
3937 var t = nexttoken;
3938 advance('[');
3939 if (nexttoken.id !== ']') {
3940 for (;;) {
3941 if (nexttoken.id === '(end)') {
3942 error("Missing ']' to match '[' from line {a}.",
3943 nexttoken, t.line);
3944 } else if (nexttoken.id === ']') {
3945 warning("Unexpected comma.", token);
3946 break;
3947 } else if (nexttoken.id === ',') {
3948 error("Unexpected comma.", nexttoken);
3949 }
3950 jsonValue();
3951 if (nexttoken.id !== ',') {
3952 break;
3953 }
3954 advance(',');
3955 }
3956 }
3957 advance(']');
3958 }
3959
3960 switch (nexttoken.id) {
3961 case '{':
3962 jsonObject();
3963 break;
3964 case '[':
3965 jsonArray();
3966 break;
3967 case 'true':
3968 case 'false':
3969 case 'null':
3970 case '(number)':
3971 case '(string)':
3972 advance();
3973 break;
3974 case '-':
3975 advance('-');
3976 if (token.character !== nexttoken.from) {
3977 warning("Unexpected space after '-'.", token);
3978 }
3979 adjacent(token, nexttoken);
3980 advance('(number)');
3981 break;
3982 default:
3983 error("Expected a JSON value.", nexttoken);
3984 }
3985 }
3986
3987
3988 // The actual JSHINT function itself.
3989
3990 var itself = function (s, o, g) {
3991 var a, i, k;
3992 JSHINT.errors = [];
3993 JSHINT.undefs = [];
3994 predefined = Object.create(standard);
3995 combine(predefined, g || {});
3996 if (o) {
3997 a = o.predef;
3998 if (a) {
3999 if (Array.isArray(a)) {
4000 for (i = 0; i < a.length; i += 1) {
4001 predefined[a[i]] = true;
4002 }
4003 } else if (typeof a === 'object') {
4004 k = Object.keys(a);
4005 for (i = 0; i < k.length; i += 1) {
4006 predefined[k[i]] = !!a[k[i]];
4007 }
4008 }
4009 }
4010 option = o;
4011 } else {
4012 option = {};
4013 }
4014 option.indent = option.indent || 4;
4015 option.maxerr = option.maxerr || 50;
4016
4017 tab = '';
4018 for (i = 0; i < option.indent; i += 1) {
4019 tab += ' ';
4020 }
4021 indent = 1;
4022 global = Object.create(predefined);
4023 scope = global;
4024 funct = {
4025 '(global)': true,
4026 '(name)': '(global)',
4027 '(scope)': scope,
4028 '(breakage)': 0,
4029 '(loopage)': 0
4030 };
4031 functions = [funct];
4032 urls = [];
4033 stack = null;
4034 member = {};
4035 membersOnly = null;
4036 implied = {};
4037 inblock = false;
4038 lookahead = [];
4039 jsonmode = false;
4040 warnings = 0;
4041 lex.init(s);
4042 prereg = true;
4043 directive = {};
4044
4045 prevtoken = token = nexttoken = syntax['(begin)'];
4046 assume();
4047
4048 // combine the passed globals after we've assumed all our options
4049 combine(predefined, g || {});
4050
4051 try {
4052 advance();
4053 switch (nexttoken.id) {
4054 case '{':
4055 case '[':
4056 option.laxbreak = true;
4057 jsonmode = true;
4058 jsonValue();
4059 break;
4060 default:
4061 directives();
4062 if (directive["use strict"] && !option.globalstrict) {
4063 warning("Use the function form of \"use strict\".", prevtoken);
4064 }
4065
4066 statements();
4067 }
4068 advance('(end)');
4069 } catch (e) {
4070 if (e) {
4071 var nt = nexttoken || {};
4072 JSHINT.errors.push({
4073 raw : e.raw,
4074 reason : e.message,
4075 line : e.line || nt.line,
4076 character : e.character || nt.from
4077 }, null);
4078 }
4079 }
4080
4081 for (i = 0; i < JSHINT.undefs.length; i += 1) {
4082 k = JSHINT.undefs[i].slice(0);
4083 scope = k.shift();
4084 a = k[2];
4085
4086 if (typeof scope[a] !== 'string' && typeof funct[a] !== 'string') {
4087 warning.apply(warning, k);
4088 }
4089 }
4090
4091 return JSHINT.errors.length === 0;
4092 };
4093
4094 // Data summary.
4095 itself.data = function () {
4096
4097 var data = { functions: [], options: option }, fu, globals, implieds = [], f, i, j,
4098 members = [], n, unused = [], v;
4099 if (itself.errors.length) {
4100 data.errors = itself.errors;
4101 }
4102
4103 if (jsonmode) {
4104 data.json = true;
4105 }
4106
4107 for (n in implied) {
4108 if (is_own(implied, n)) {
4109 implieds.push({
4110 name: n,
4111 line: implied[n]
4112 });
4113 }
4114 }
4115 if (implieds.length > 0) {
4116 data.implieds = implieds;
4117 }
4118
4119 if (urls.length > 0) {
4120 data.urls = urls;
4121 }
4122
4123 globals = Object.keys(scope);
4124 if (globals.length > 0) {
4125 data.globals = globals;
4126 }
4127
4128 for (i = 1; i < functions.length; i += 1) {
4129 f = functions[i];
4130 fu = {};
4131 for (j = 0; j < functionicity.length; j += 1) {
4132 fu[functionicity[j]] = [];
4133 }
4134 for (n in f) {
4135 if (is_own(f, n) && n.charAt(0) !== '(') {
4136 v = f[n];
4137 if (v === 'unction') {
4138 v = 'unused';
4139 }
4140 if (Array.isArray(fu[v])) {
4141 fu[v].push(n);
4142 if (v === 'unused') {
4143 unused.push({
4144 name: n,
4145 line: f['(line)'],
4146 'function': f['(name)']
4147 });
4148 }
4149 }
4150 }
4151 }
4152 for (j = 0; j < functionicity.length; j += 1) {
4153 if (fu[functionicity[j]].length === 0) {
4154 delete fu[functionicity[j]];
4155 }
4156 }
4157 fu.name = f['(name)'];
4158 fu.param = f['(params)'];
4159 fu.line = f['(line)'];
4160 fu.last = f['(last)'];
4161 data.functions.push(fu);
4162 }
4163
4164 if (unused.length > 0) {
4165 data.unused = unused;
4166 }
4167
4168 members = [];
4169 for (n in member) {
4170 if (typeof member[n] === 'number') {
4171 data.member = member;
4172 break;
4173 }
4174 }
4175
4176 return data;
4177 };
4178
4179 itself.report = function (option) {
4180 var data = itself.data();
4181
4182 var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s;
4183
4184 function detail(h, array) {
4185 var b, i, singularity;
4186 if (array) {
4187 o.push('<div><i>' + h + '</i> ');
4188 array = array.sort();
4189 for (i = 0; i < array.length; i += 1) {
4190 if (array[i] !== singularity) {
4191 singularity = array[i];
4192 o.push((b ? ', ' : '') + singularity);
4193 b = true;
4194 }
4195 }
4196 o.push('</div>');
4197 }
4198 }
4199
4200
4201 if (data.errors || data.implieds || data.unused) {
4202 err = true;
4203 o.push('<div id=errors><i>Error:</i>');
4204 if (data.errors) {
4205 for (i = 0; i < data.errors.length; i += 1) {
4206 c = data.errors[i];
4207 if (c) {
4208 e = c.evidence || '';
4209 o.push('<p>Problem' + (isFinite(c.line) ? ' at line ' +
4210 c.line + ' character ' + c.character : '') +
4211 ': ' + c.reason.entityify() +
4212 '</p><p class=evidence>' +
4213 (e && (e.length > 80 ? e.slice(0, 77) + '...' :
4214 e).entityify()) + '</p>');
4215 }
4216 }
4217 }
4218
4219 if (data.implieds) {
4220 s = [];
4221 for (i = 0; i < data.implieds.length; i += 1) {
4222 s[i] = '<code>' + data.implieds[i].name + '</code>&nbsp;<i>' +
4223 data.implieds[i].line + '</i>';
4224 }
4225 o.push('<p><i>Implied global:</i> ' + s.join(', ') + '</p>');
4226 }
4227
4228 if (data.unused) {
4229 s = [];
4230 for (i = 0; i < data.unused.length; i += 1) {
4231 s[i] = '<code><u>' + data.unused[i].name + '</u></code>&nbsp;<i>' +
4232 data.unused[i].line + '</i> <code>' +
4233 data.unused[i]['function'] + '</code>';
4234 }
4235 o.push('<p><i>Unused variable:</i> ' + s.join(', ') + '</p>');
4236 }
4237 if (data.json) {
4238 o.push('<p>JSON: bad.</p>');
4239 }
4240 o.push('</div>');
4241 }
4242
4243 if (!option) {
4244
4245 o.push('<br><div id=functions>');
4246
4247 if (data.urls) {
4248 detail("URLs<br>", data.urls, '<br>');
4249 }
4250
4251 if (data.json && !err) {
4252 o.push('<p>JSON: good.</p>');
4253 } else if (data.globals) {
4254 o.push('<div><i>Global</i> ' +
4255 data.globals.sort().join(', ') + '</div>');
4256 } else {
4257 o.push('<div><i>No new global variables introduced.</i></div>');
4258 }
4259
4260 for (i = 0; i < data.functions.length; i += 1) {
4261 f = data.functions[i];
4262
4263 o.push('<br><div class=function><i>' + f.line + '-' +
4264 f.last + '</i> ' + (f.name || '') + '(' +
4265 (f.param ? f.param.join(', ') : '') + ')</div>');
4266 detail('<big><b>Unused</b></big>', f.unused);
4267 detail('Closure', f.closure);
4268 detail('Variable', f['var']);
4269 detail('Exception', f.exception);
4270 detail('Outer', f.outer);
4271 detail('Global', f.global);
4272 detail('Label', f.label);
4273 }
4274
4275 if (data.member) {
4276 a = Object.keys(data.member);
4277 if (a.length) {
4278 a = a.sort();
4279 m = '<br><pre id=members>/*members ';
4280 l = 10;
4281 for (i = 0; i < a.length; i += 1) {
4282 k = a[i];
4283 n = k.name();
4284 if (l + n.length > 72) {
4285 o.push(m + '<br>');
4286 m = ' ';
4287 l = 1;
4288 }
4289 l += n.length + 2;
4290 if (data.member[k] === 1) {
4291 n = '<i>' + n + '</i>';
4292 }
4293 if (i < a.length - 1) {
4294 n += ', ';
4295 }
4296 m += n;
4297 }
4298 o.push(m + '<br>*/</pre>');
4299 }
4300 o.push('</div>');
4301 }
4302 }
4303 return o.join('');
4304 };
4305
4306 itself.jshint = itself;
4307 itself.edition = '2011-04-16';
4308
4309 return itself;
4310 }());
4311
4312 // Make JSHINT a Node module, if possible.
4313 if (typeof exports === 'object' && exports)
4314 exports.JSHINT = JSHINT;