pre-commit 0.1.6 → 0.1.7

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