5 Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
7 Permission is hereby granted, free of charge, to any person obtaining a copy of
8 this software and associated documentation files (the "Software"), to deal in
9 the Software without restriction, including without limitation the rights to
10 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11 of the Software, and to permit persons to whom the Software is furnished to do
12 so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
17 The Software shall be used for Good, not Evil.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 JSLINT is a global function. It takes two parameters.
31 var myResult = JSLINT(source, option);
33 The first parameter is either a string or an array of strings. If it is a
34 string, it will be split on '\n' or '\r'. If it is an array of strings, it
35 is assumed that each string represents one line. The source can be a
36 JavaScript text, or HTML text, or a Konfabulator text.
38 The second parameter is an optional object of options which control the
39 operation of JSLINT. Most of the options are booleans: They are all are
40 optional and have a default value of false.
42 If it checks out, JSLINT returns true. Otherwise, it returns false.
44 If false, you can inspect JSLINT.errors to find out the problems.
45 JSLINT.errors is an array of objects containing these members:
48 line : The line (relative to 0) at which the lint was found
49 character : The character (relative to 0) at which the lint was found
51 evidence : The text line in which the problem occurred
52 raw : The raw message before the details were inserted
59 If a fatal error was found, a null will be the last element of the
62 You can request a Function Report, which shows all of the functions
63 and the parameters and vars that they use. This can be used to find
64 implied global variables and other problems. The report is in HTML and
65 can be inserted in an HTML <body>.
67 var myReport = JSLINT.report(limited);
69 If limited is true, then the report will be limited to only errors.
71 You can request a data structure which contains JSLint's results.
73 var myData = JSLINT.data();
75 It returns a structure with this form:
139 Empty arrays will not be included.
144 evil: true, nomen: false, onevar: false, regexp: false, strict: true
147 /*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%",
148 "(begin)", "(breakage)", "(context)", "(error)", "(global)",
149 "(identifier)", "(last)", "(line)", "(loopage)", "(name)", "(onevar)",
150 "(params)", "(scope)", "(verb)", "*", "+", "++", "-", "--", "\/",
151 "<", "<=", "==", "===", ">", ">=", ADSAFE, Array, Boolean,
152 COM, Canvas, CustomAnimation, Date, Debug, E, Error, EvalError,
153 FadeAnimation, Flash, FormField, Frame, Function, HotKey, Image, JSON,
154 LN10, LN2, LOG10E, LOG2E, MAX_VALUE, MIN_VALUE, Math, MenuItem,
155 MoveAnimation, NEGATIVE_INFINITY, Number, Object, Option, PI,
156 POSITIVE_INFINITY, Point, RangeError, Rectangle, ReferenceError, RegExp,
157 ResizeAnimation, RotateAnimation, SQRT1_2, SQRT2, ScrollBar, String,
158 Style, SyntaxError, System, Text, TextArea, Timer, TypeError, URIError,
159 URL, Web, Window, XMLDOM, XMLHttpRequest, "\\", a, abbr, acronym,
160 addEventListener, address, adsafe, alert, aliceblue, animator,
161 antiquewhite, appleScript, applet, apply, approved, aqua, aquamarine,
162 area, arguments, arity, autocomplete, azure, b, background,
163 "background-attachment", "background-color", "background-image",
164 "background-position", "background-repeat", base, bdo, beep, beige, big,
165 bisque, bitwise, black, blanchedalmond, block, blockquote, blue,
166 blueviolet, blur, body, border, "border-bottom", "border-bottom-color",
167 "border-bottom-style", "border-bottom-width", "border-collapse",
168 "border-color", "border-left", "border-left-color", "border-left-style",
169 "border-left-width", "border-right", "border-right-color",
170 "border-right-style", "border-right-width", "border-spacing",
171 "border-style", "border-top", "border-top-color", "border-top-style",
172 "border-top-width", "border-width", bottom, br, brown, browser,
173 burlywood, button, bytesToUIString, c, cadetblue, call, callee, caller,
174 canvas, cap, caption, "caption-side", cases, center, charAt, charCodeAt,
175 character, chartreuse, chocolate, chooseColor, chooseFile, chooseFolder,
176 cite, clear, clearInterval, clearTimeout, clip, close, closeWidget,
177 closed, closure, cm, code, col, colgroup, color, comment, condition,
178 confirm, console, constructor, content, convertPathToHFS,
179 convertPathToPlatform, coral, cornflowerblue, cornsilk,
180 "counter-increment", "counter-reset", create, crimson, css, cursor,
181 cyan, d, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen,
182 darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred,
183 darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise,
184 darkviolet, data, dd, debug, decodeURI, decodeURIComponent, deeppink,
185 deepskyblue, defaultStatus, defineClass, del, deserialize, devel, dfn,
186 dimension, dimgray, dir, direction, display, div, dl, document,
187 dodgerblue, dt, edition, else, em, embed, empty, "empty-cells",
188 encodeURI, encodeURIComponent, entityify, eqeqeq, errors, escape, eval,
189 event, evidence, evil, ex, exception, exec, exps, fieldset, filesystem,
190 firebrick, first, float, floor, floralwhite, focus, focusWidget, font,
191 "font-face", "font-family", "font-size", "font-size-adjust",
192 "font-stretch", "font-style", "font-variant", "font-weight",
193 forestgreen, forin, form, fragment, frame, frames, frameset, from,
194 fromCharCode, fuchsia, fud, funct, function, functions, g, gainsboro,
195 gc, getComputedStyle, ghostwhite, global, globals, gold, goldenrod,
196 gray, green, greenyellow, h1, h2, h3, h4, h5, h6, hasOwnProperty, head,
197 height, help, history, honeydew, hotpink, hr, html, i, iTunes, id,
198 identifier, iframe, img, immed, implieds, in, include, indent, indexOf,
199 indianred, indigo, init, input, ins, isAlpha, isApplicationRunning,
200 isDigit, isFinite, isNaN, ivory, join, jslint, json, kbd, khaki,
201 konfabulatorVersion, label, labelled, lang, last, lavender,
202 lavenderblush, lawngreen, laxbreak, lbp, led, left, legend,
203 lemonchiffon, length, "letter-spacing", li, lib, lightblue, lightcoral,
204 lightcyan, lightgoldenrodyellow, lightgreen, lightpink, lightsalmon,
205 lightseagreen, lightskyblue, lightslategray, lightsteelblue,
206 lightyellow, lime, limegreen, line, "line-height", linen, link,
207 "list-style", "list-style-image", "list-style-position",
208 "list-style-type", load, loadClass, location, log, m, magenta, map,
209 margin, "margin-bottom", "margin-left", "margin-right", "margin-top",
210 "marker-offset", maroon, match, "max-height", "max-width", maxerr, maxlen,
211 md5, media, mediumaquamarine, mediumblue, mediumorchid, mediumpurple,
212 mediumseagreen, mediumslateblue, mediumspringgreen, mediumturquoise,
213 mediumvioletred, member, menu, message, meta, midnightblue,
214 "min-height", "min-width", mintcream, mistyrose, mm, moccasin, moveBy,
215 moveTo, name, navajowhite, navigator, navy, new, newcap, noframes,
216 nomen, noscript, nud, object, ol, oldlace, olive, olivedrab, on,
217 onbeforeunload, onblur, onerror, onevar, onfocus, onload, onresize,
218 onunload, opacity, open, openURL, opener, opera, optgroup, option,
219 orange, orangered, orchid, outer, outline, "outline-color",
220 "outline-style", "outline-width", overflow, "overflow-x", "overflow-y",
221 p, padding, "padding-bottom", "padding-left", "padding-right",
222 "padding-top", page, "page-break-after", "page-break-before",
223 palegoldenrod, palegreen, paleturquoise, palevioletred, papayawhip,
224 param, parent, parseFloat, parseInt, passfail, pc, peachpuff, peru,
225 pink, play, plum, plusplus, pop, popupMenu, position, powderblue, pre,
226 predef, preferenceGroups, preferences, print, prompt, prototype, pt,
227 purple, push, px, q, quit, quotes, random, range, raw, reach, readFile,
228 readUrl, reason, red, regexp, reloadWidget, removeEventListener,
229 replace, report, reserved, resizeBy, resizeTo, resolvePath,
230 resumeUpdates, rhino, right, rosybrown, royalblue, runCommand,
231 runCommandInBg, saddlebrown, safe, salmon, samp, sandybrown, saveAs,
232 savePreferences, screen, script, scroll, scrollBy, scrollTo, seagreen,
233 seal, search, seashell, select, serialize, setInterval, setTimeout,
234 shift, showWidgetPreferences, sidebar, sienna, silver, skyblue,
235 slateblue, slategray, sleep, slice, small, snow, sort, span, spawn,
236 speak, split, springgreen, src, status, steelblue, strict, strong,
237 style, styleproperty, sub, substr, sup, supplant, suppressUpdates, sync,
238 system, table, "table-layout", tan, tbody, td, teal, tellWidget, test,
239 "text-align", "text-decoration", "text-indent", "text-shadow",
240 "text-transform", textarea, tfoot, th, thead, thistle, title,
241 toLowerCase, toString, toUpperCase, toint32, token, tomato, top, tr, tt,
242 turquoise, type, u, ul, undef, unescape, "unicode-bidi", unused,
243 unwatch, updateNow, urls, value, valueOf, var, version,
244 "vertical-align", violet, visibility, watch, wheat, white,
245 "white-space", whitesmoke, widget, width, "word-spacing", "word-wrap",
246 yahooCheckLogin, yahooLogin, yahooLogout, yellow, yellowgreen,
251 // We build the application inside a function so that we produce only a single
252 // global variable. The function will be invoked, its return value is the JSLINT
253 // application itself.
257 var JSLINT = (function () {
258 var adsafe_id, // The widget's ADsafe id.
259 adsafe_may, // The widget may load approved scripts.
260 adsafe_went, // ADSAFE.go has been called.
261 anonname, // The guessed name for anonymous functions.
262 approved, // ADsafe approved urls.
270 // These are operators that should not be used with the ! operator.
288 // These are members that should not be permitted in the safe subset.
290 banned = { // the member names that ADsafe prohibits.
303 // These are the JSLint boolean options.
306 adsafe : true, // if ADsafe should be enforced
307 bitwise : true, // if bitwise operators should not be allowed
308 browser : true, // if the standard browser globals should be predefined
309 cap : true, // if upper case HTML should be allowed
310 css : true, // if CSS workarounds should be tolerated
311 debug : true, // if debugger statements should be allowed
312 devel : true, // if logging should be allowed (console, alert, etc.)
313 eqeqeq : true, // if === should be required
314 evil : true, // if eval should be allowed
315 forin : true, // if for in statements must filter
316 fragment : true, // if HTML fragments should be allowed
317 immed : true, // if immediate invocations must be wrapped in parens
318 laxbreak : true, // if line breaks should not be checked
319 newcap : true, // if constructor names must be capitalized
320 nomen : true, // if names should be checked
321 on : true, // if HTML event handlers should be allowed
322 onevar : true, // if only one var statement per function should be allowed
323 passfail : true, // if the scan should stop on first error
324 plusplus : true, // if increment/decrement should not be allowed
325 regexp : true, // if the . should not be allowed in regexp literals
326 rhino : true, // if the Rhino environment globals should be predefined
327 undef : true, // if variables should be declared before used
328 safe : true, // if use of some browser features should be restricted
329 sidebar : true, // if the System object should be predefined
330 strict : true, // require the "use strict"; pragma
331 sub : true, // if all forms of subscript notation are tolerated
332 white : true, // if strict whitespace rules apply
333 widget : true // if the Yahoo Widgets globals should be predefined
336 // browser contains a set of global names which are commonly provided by a
337 // web browser environment.
340 addEventListener: false,
342 clearInterval : false,
343 clearTimeout : false,
346 defaultStatus : false,
351 getComputedStyle: false,
360 onbeforeunload : true,
372 removeEventListener: false,
383 XMLHttpRequest : false
391 "antiquewhite" : true,
398 "blanchedalmond" : true,
407 "cornflowerblue" : true,
413 "darkgoldenrod" : true,
417 "darkmagenta" : true,
418 "darkolivegreen" : true,
423 "darkseagreen" : true,
424 "darkslateblue" : true,
425 "darkslategray" : true,
426 "darkturquoise" : true,
429 "deepskyblue" : true,
433 "floralwhite" : true,
434 "forestgreen" : true,
442 "greenyellow" : true,
450 "lavenderblush" : true,
452 "lemonchiffon" : true,
456 "lightgoldenrodyellow" : true,
459 "lightsalmon" : true,
460 "lightseagreen" : true,
461 "lightskyblue" : true,
462 "lightslategray" : true,
463 "lightsteelblue" : true,
464 "lightyellow" : true,
470 "mediumaquamarine" : true,
472 "mediumorchid" : true,
473 "mediumpurple" : true,
474 "mediumseagreen" : true,
475 "mediumslateblue" : true,
476 "mediumspringgreen" : true,
477 "mediumturquoise" : true,
478 "mediumvioletred" : true,
479 "midnightblue" : true,
483 "navajowhite" : true,
491 "palegoldenrod" : true,
493 "paleturquoise" : true,
494 "palevioletred" : true,
505 "saddlebrown" : true,
516 "springgreen" : true,
568 funct, // The current function
571 'closure', 'exception', 'global', 'label',
572 'outer', 'unused', 'var'
575 functions, // All of the functions
577 global, // The global scope
584 area: {empty: true, parent: ' map '},
586 base: {empty: true, parent: ' head '},
590 body: {parent: ' html noframes '},
593 canvas: {parent: ' body p div th td '},
594 caption: {parent: ' table '},
598 col: {empty: true, parent: ' table colgroup '},
599 colgroup: {parent: ' table '},
600 dd: {parent: ' dl '},
606 dt: {parent: ' dl '},
612 frame: {empty: true, parent: ' frameset '},
613 frameset: {parent: ' html frameset '},
620 head: {parent: ' html '},
626 input: {empty: true},
630 legend: {parent: ' fieldset '},
631 li: {parent: ' dir menu ol ul '},
632 link: {empty: true, parent: ' head '},
635 meta: {empty: true, parent: ' head noframes noscript '},
636 noframes: {parent: ' html body '},
637 noscript: {parent: ' body head noframes '},
640 optgroup: {parent: ' select '},
641 option: {parent: ' optgroup select '},
643 param: {empty: true, parent: ' applet object '},
647 script: {empty: true, parent: ' body div frame head iframe p pre span '},
652 style: {parent: ' head ', empty: true},
656 tbody: {parent: ' table '},
657 td: {parent: ' tr '},
659 tfoot: {parent: ' table '},
660 th: {parent: ' tr '},
661 thead: {parent: ' table '},
662 title: {parent: ' head '},
663 tr: {parent: ' table tbody thead tfoot '},
671 implied, // Implied globals
682 predefined, // Global variables defined by option
706 scope, // The current scope
715 // standard contains the global names that are provided by the
716 // ECMAScript standard.
723 decodeURIComponent : false,
725 encodeURIComponent : false,
730 hasOwnProperty : false,
740 ReferenceError : false,
759 NEGATIVE_INFINITY : true,
760 POSITIVE_INFINITY : true
770 // widget contains the global names which are provided to a Yahoo
771 // (fna Konfabulator) widget.
778 bytesToUIString : true,
785 convertPathToHFS : true,
786 convertPathToPlatform : true,
787 CustomAnimation : true,
789 FadeAnimation : true,
799 isApplicationRunning : true,
801 konfabulatorVersion : true,
805 MoveAnimation : true,
810 preferenceGroups : true,
817 ResizeAnimation : true,
819 resumeUpdates : true,
820 RotateAnimation : true,
822 runCommandInBg : true,
824 savePreferences : true,
827 showWidgetPreferences : true,
831 suppressUpdates : true,
844 XMLHttpRequest : true,
845 yahooCheckLogin : true,
850 // xmode is used to adapt to the exceptions in html parsing.
851 // It can have these states:
852 // false .js script file
863 // unsafe comment or string
864 ax = /@cc|<\/?|script|\]*s\]|<\s*!|</i,
865 // unsafe characters that are silently deleted by one or more browsers
866 cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
868 tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jslint|members?|global)?|=|\/)?|\*[\/=]?|\+[+=]?|-[\-=]?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/,
870 //////// hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-]*|[0-9]+|--|.)/,
871 hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-]*|[0-9]+|--)/,
872 // characters in strings that need escapement
873 nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
874 nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
876 ox = /[>&]|<[\/!]?|--/,
880 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
882 jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i,
884 ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i,
886 sx = /^\s*([{:#%.=,>+\[\]@()"';]|\*=?|\$=|\|=|\^=|~=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/,
887 ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/,
888 // attributes characters
889 qx = /[^a-zA-Z0-9-_\/ ]/,
890 // query characters for ids
891 dx = /[\[\]\/\\"'*<>.&:(){}+=#]/,
902 if (typeof Object.create !== 'function') {
903 Object.create = function (o) {
910 function is_own(object, name) {
911 return Object.prototype.hasOwnProperty.call(object, name);
915 function combine(t, o) {
924 String.prototype.entityify = function () {
926 replace(/&/g, '&').
927 replace(/</g, '<').
928 replace(/>/g, '>');
931 String.prototype.isAlpha = function () {
932 return (this >= 'a' && this <= 'z\uffff') ||
933 (this >= 'A' && this <= 'Z\uffff');
937 String.prototype.isDigit = function () {
938 return (this >= '0' && this <= '9');
942 String.prototype.supplant = function (o) {
943 return this.replace(/\{([^{}]*)\}/g, function (a, b) {
945 return typeof r === 'string' || typeof r === 'number' ? r : a;
949 String.prototype.name = function () {
951 // If the string looks like an identifier, then we can return it as is.
952 // If the string contains no control characters, no quote characters, and no
953 // backslash characters, then we can simply slap some quotes around it.
954 // Otherwise we must also replace the offending characters with safe
961 return '"' + this.replace(nxg, function (a) {
966 return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
969 return '"' + this + '"';
976 combine(predefined, rhino);
979 combine(predefined, devel);
981 if (option.browser || option.sidebar) {
982 combine(predefined, browser);
984 if (option.sidebar) {
985 combine(predefined, sidebar);
988 combine(predefined, widget);
994 // Produce an error warning.
996 function quit(m, l, ch) {
1001 message: m + " (" + Math.floor((l / lines.length) * 100) +
1006 function warning(m, t, a, b, c, d) {
1009 if (t.id === '(end)') { // `~
1017 evidence: lines[l - 1] || '',
1025 w.reason = m.supplant(w);
1026 JSLINT.errors.push(w);
1027 if (option.passfail) {
1028 quit('Stopping. ', l, ch);
1031 if (warnings >= option.maxerr) {
1032 quit("Too many errors.", l, ch);
1037 function warningAt(m, l, ch, a, b, c, d) {
1044 function error(m, t, a, b, c, d) {
1045 var w = warning(m, t, a, b, c, d);
1046 quit("Stopping, unable to continue.", w.line, w.character);
1049 function errorAt(m, l, ch, a, b, c, d) {
1060 var lex = (function lex() {
1061 var character, from, line, s;
1063 // Private lex methods
1065 function nextLine() {
1067 if (line >= lines.length) {
1073 at = s.search(/ \t/);
1075 warningAt("Mixed spaces and tabs.", line, at + 1);
1077 s = s.replace(/\t/g, tab);
1080 warningAt("Unsafe character.", line, at);
1082 if (option.maxlen && option.maxlen < s.length) {
1083 warningAt("Line too long.", line, s.length);
1088 // Produce a token object. The token inherits from a syntax symbol.
1090 function it(type, value) {
1092 if (type === '(color)') {
1094 } else if (type === '(punctuator)' ||
1095 (type === '(identifier)' && is_own(syntax, value))) {
1096 t = syntax[value] || syntax['(error)'];
1100 t = Object.create(t);
1101 if (type === '(string)' || type === '(range)') {
1102 if (jx.test(value)) {
1103 warningAt("Script URL.", line, from);
1106 if (type === '(identifier)') {
1107 t.identifier = true;
1108 if (value === '__iterator__' || value === '__proto__') {
1109 errorAt("Reserved name '{a}'.",
1111 } else if (option.nomen &&
1112 (value.charAt(0) === '_' ||
1113 value.charAt(value.length - 1) === '_')) {
1114 warningAt("Unexpected {a} in '{b}'.", line, from,
1115 "dangling '_'", value);
1120 t.character = character;
1123 if (i !== '(endline)') {
1125 (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) ||
1131 // Public lex methods
1134 init: function (source) {
1135 if (typeof source === 'string') {
1137 replace(/\r\n/g, '\n').
1138 replace(/\r/g, '\n').
1148 range: function (begin, end) {
1151 if (s.charAt(0) !== begin) {
1152 errorAt("Expected '{a}' and instead saw '{b}'.",
1153 line, character, begin, s.charAt(0));
1161 errorAt("Missing '{a}'.", line, character, c);
1166 return it('(range)', value);
1169 warningAt("Unexpected '{a}'.", line, character, c);
1176 // token -- this is called by advance to get the next token.
1178 token: function () {
1179 var b, c, captures, d, depth, high, i, l, low, q, t;
1182 var r = x.exec(s), r1;
1188 from = character + l - r1.length;
1194 function string(x) {
1197 if (jsonmode && x !== '"') {
1198 warningAt("Strings must use doublequote.",
1202 if (xquote === x || (xmode === 'scriptstring' && !xquote)) {
1203 return it('(punctuator)', x);
1207 var i = parseInt(s.substr(j + 1, n), 16);
1209 if (i >= 32 && i <= 126 &&
1210 i !== 34 && i !== 92 && i !== 39) {
1211 warningAt("Unnecessary escapement.", line, character);
1214 c = String.fromCharCode(i);
1218 while (j >= s.length) {
1220 if (xmode !== 'html' || !nextLine()) {
1221 errorAt("Unclosed string.", line, from);
1227 s = s.substr(j + 1);
1228 return it('(string)', r, x);
1231 if (c === '\n' || c === '\r') {
1234 warningAt("Control character in string: {a}.",
1235 line, character + j, s.slice(0, j));
1236 } else if (c === xquote) {
1237 warningAt("Bad HTML string", line, character + j);
1238 } else if (c === '<') {
1239 if (option.safe && xmode === 'html') {
1240 warningAt("ADsafe string violation.",
1241 line, character + j);
1242 } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) {
1243 warningAt("Expected '<\\/' and instead saw '</'.", line, character);
1244 } else if (s.charAt(j + 1) === '!' && (xmode || option.safe)) {
1245 warningAt("Unexpected '<!' in a string.", line, character);
1247 } else if (c === '\\') {
1248 if (xmode === 'html') {
1250 warningAt("ADsafe string violation.",
1251 line, character + j);
1253 } else if (xmode === 'styleproperty') {
1258 warningAt("Escapement in style string.",
1259 line, character + j);
1267 warningAt("Bad HTML string", line,
1298 warningAt("Avoid \\x-.", line, character);
1303 warningAt("Bad escapement.", line, character);
1315 return it(nextLine() ? '(endline)' : '(end)', '');
1317 while (xmode === 'outer') {
1327 return it('(end)', '');
1331 // t = match(rx[xmode] || tx);
1333 // if (xmode === 'html') {
1334 // return it('(error)', s.charAt(0));
1338 // while (s && s < '!') {
1342 // errorAt("Unexpected '{a}'.",
1343 // line, character, s.substr(0, 1));
1346 t = match(rx[xmode] || tx);
1350 while (s && s < '!') {
1354 if (xmode === 'html') {
1355 return it('(error)', s.charAt(0));
1357 errorAt("Unexpected '{a}'.",
1358 line, character, s.substr(0, 1));
1365 if (c.isAlpha() || c === '_' || c === '$') {
1366 return it('(identifier)', t);
1372 if (xmode !== 'style' && !isFinite(Number(t))) {
1373 warningAt("Bad number '{a}'.",
1374 line, character, t);
1376 if (xmode !== 'style' &&
1377 xmode !== 'styleproperty' &&
1378 s.substr(0, 1).isAlpha()) {
1379 warningAt("Missing space after '{a}'.",
1380 line, character, t);
1385 if (token.id !== '.' && xmode !== 'styleproperty') {
1386 warningAt("Don't use extra leading zeros '{a}'.",
1387 line, character, t);
1389 } else if (jsonmode && (d === 'x' || d === 'X')) {
1390 warningAt("Avoid 0x-. '{a}'.",
1391 line, character, t);
1394 if (t.substr(t.length - 1) === '.') {
1396 "A trailing decimal point can be confused with a dot '{a}'.",
1397 line, character, t);
1399 return it('(number)', t);
1412 if (src || (xmode && xmode !== 'script')) {
1413 warningAt("Unexpected comment.", line, character);
1414 } else if (xmode === 'script' && /<\s*\//i.test(s)) {
1415 warningAt("Unexpected <\/ in comment.", line, character);
1416 } else if ((option.safe || xmode === 'script') && ax.test(s)) {
1417 warningAt("Dangerous comment.", line, character);
1420 token.comment = true;
1426 if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) {
1427 warningAt("Unexpected comment.", line, character);
1429 if (option.safe && ax.test(s)) {
1430 warningAt("ADsafe comment violation.", line, character);
1438 errorAt("Unclosed comment.", line, character);
1440 if (option.safe && ax.test(s)) {
1441 warningAt("ADsafe comment violation.", line, character);
1446 if (s.substr(i, 1) === '/') {
1447 errorAt("Nested comment.", line, character);
1449 s = s.substr(i + 2);
1450 token.comment = true;
1453 // /*members /*jslint /*global
1464 character: character,
1472 if (token.id === '/=') {
1474 "A regular expression literal can be confused with '/='.", line, from);
1486 errorAt("Unclosed regular expression.", line, from);
1490 warningAt("Unescaped '{a}'.", line, from + l, '/');
1492 c = s.substr(0, l - 1);
1498 while (q[s.charAt(l)] === true) {
1499 q[s.charAt(l)] = false;
1505 if (q === '/' || q === '*') {
1506 errorAt("Confusing regular expression.", line, from);
1508 return it('(regexp)', c);
1512 warningAt("Unexpected control character in regular expression.", line, from + l);
1513 } else if (c === '<') {
1514 warningAt("Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
1521 if (s.charAt(l) === '?') {
1523 switch (s.charAt(l)) {
1530 warningAt("Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l));
1541 warningAt("Unescaped '{a}'.", line, from + l, ')');
1548 while (s.charAt(l) === ' ') {
1553 warningAt("Spaces are hard to count. Use {{a}}.", line, from + l, q);
1560 if (option.regexp) {
1561 warningAt("Insecure '{a}'.", line, from + l, c);
1566 warningAt("Empty class.", line, from + l - 1);
1575 warningAt("Unescaped '{a}'.", line, from + l, c);
1582 warningAt("Unescaped '{a}'.", line, from + l, '-');
1588 warningAt("Unescaped '{a}'.", line, from + l - 1, '-');
1594 warningAt("Unexpected control character in regular expression.", line, from + l);
1595 } else if (c === '<') {
1596 warningAt("Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
1602 warningAt("Unescaped '{a}'.", line, from + l - 1, '/');
1606 if (xmode === 'script') {
1608 if (c === '!' || c === '/') {
1609 warningAt("HTML confusion in regular expression '<{a}'.", line, from + l, c);
1620 if (option.regexp) {
1621 warningAt("Insecure '{a}'.", line, from + l, c);
1630 warningAt("Unescaped '{a}'.", line, from + l, c);
1633 if (xmode === 'script') {
1635 if (c === '!' || c === '/') {
1636 warningAt("HTML confusion in regular expression '<{a}'.", line, from + l, c);
1641 switch (s.charAt(l)) {
1646 if (s.charAt(l) === '?') {
1653 if (c < '0' || c > '9') {
1654 warningAt("Expected a number and instead saw '{a}'.", line, from + l, c);
1660 if (c < '0' || c > '9') {
1664 low = +c + (low * 10);
1671 if (c >= '0' && c <= '9') {
1676 if (c < '0' || c > '9') {
1680 high = +c + (high * 10);
1684 if (s.charAt(l) !== '}') {
1685 warningAt("Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c);
1689 if (s.charAt(l) === '?') {
1693 warningAt("'{a}' should not be greater than '{b}'.", line, from + l, low, high);
1698 c = s.substr(0, l - 1);
1701 return it('(regexp)', c);
1703 return it('(punctuator)', t);
1711 i = s.indexOf('--');
1715 i = s.indexOf('<!');
1717 errorAt("Nested HTML comment.",
1718 line, character + i);
1721 errorAt("Unclosed HTML comment.", l, c);
1724 l = s.indexOf('<!');
1725 if (l >= 0 && l < i) {
1726 errorAt("Nested HTML comment.",
1727 line, character + l);
1730 if (s[i + 2] !== '>') {
1731 errorAt("Expected -->.", line, character);
1737 if (xmode === 'html' || xmode === 'styleproperty') {
1740 if ((c < '0' || c > '9') &&
1741 (c < 'a' || c > 'f') &&
1742 (c < 'A' || c > 'F')) {
1749 if (t.length !== 4 && t.length !== 7) {
1750 warningAt("Bad hex color '{a}'.", line,
1753 return it('(color)', t);
1755 return it('(punctuator)', t);
1757 if (xmode === 'outer' && c === '&') {
1767 if (!((c >= '0' && c <= '9') ||
1768 (c >= 'a' && c <= 'z') ||
1770 errorAt("Bad entity", line, from + l,
1776 return it('(punctuator)', t);
1785 function addlabel(t, type) {
1787 if (option.safe && funct['(global)'] && typeof predefined[t] !== 'boolean') {
1788 warning('ADsafe global: ' + t + '.', token);
1789 } else if (t === 'hasOwnProperty') {
1790 warning("'hasOwnProperty' is a really bad name.");
1793 // Define t in the current function in the current scope.
1795 if (is_own(funct, t) && !funct['(global)']) {
1796 warning(funct[t] === true ?
1797 "'{a}' was used before it was defined." :
1798 "'{a}' is already defined.",
1802 if (funct['(global)']) {
1804 if (is_own(implied, t)) {
1805 warning("'{a}' was used before it was defined.", nexttoken, t);
1814 function doOption() {
1815 var b, obj, filter, o = nexttoken.value, t, v;
1818 error("Unbegun comment.");
1830 warning("ADsafe restriction.");
1833 filter = boolOptions;
1837 warning("ADsafe restriction.");
1846 if (t.type === 'special' && t.value === '*/') {
1849 if (t.id !== '(endline)' && t.id !== ',') {
1854 if (t.type !== '(string)' && t.type !== '(identifier)' &&
1855 o !== '/*members') {
1856 error("Bad option.", t);
1861 if (obj === membersOnly) {
1862 error("Expected '{a}' and instead saw '{b}'.",
1865 if (t.value === 'indent' && o === '/*jslint') {
1867 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1868 Math.floor(b) !== b) {
1869 error("Expected a small integer and instead saw '{a}'.",
1874 } else if (t.value === 'maxerr' && o === '/*jslint') {
1876 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1877 Math.floor(b) !== b) {
1878 error("Expected a small integer and instead saw '{a}'.",
1882 } else if (t.value === 'maxlen' && o === '/*jslint') {
1884 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1885 Math.floor(b) !== b) {
1886 error("Expected a small integer and instead saw '{a}'.",
1890 } else if (v.value === 'true') {
1891 obj[t.value] = true;
1892 } else if (v.value === 'false') {
1893 obj[t.value] = false;
1895 error("Bad option value.", v);
1899 if (o === '/*jslint') {
1900 error("Missing option value.", t);
1902 obj[t.value] = false;
1912 // We need a peek function. If it has an argument, it peeks that much farther
1913 // ahead. It is used to distinguish
1914 // for ( var i in ...
1916 // for ( var i = ...
1919 var i = p || 0, j = 0, t;
1924 t = lookahead[j] = lex.token();
1933 // Produce the next token. It looks for programming errors.
1935 function advance(id, t) {
1938 if (nexttoken.id === '.') {
1940 "A dot following a number can be confused with a decimal point.", token);
1944 if (nexttoken.id === '-' || nexttoken.id === '--') {
1945 warning("Confusing minusses.");
1949 if (nexttoken.id === '+' || nexttoken.id === '++') {
1950 warning("Confusing plusses.");
1954 if (token.type === '(string)' || token.identifier) {
1955 anonname = token.value;
1958 if (id && nexttoken.id !== id) {
1960 if (nexttoken.id === '(end)') {
1961 warning("Unmatched '{a}'.", t, t.id);
1963 warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
1964 nexttoken, id, t.id, t.line, nexttoken.value);
1966 } else if (nexttoken.type !== '(identifier)' ||
1967 nexttoken.value !== id) {
1968 warning("Expected '{a}' and instead saw '{b}'.",
1969 nexttoken, id, nexttoken.value);
1975 nexttoken = lookahead.shift() || lex.token();
1976 if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {
1979 if (nexttoken.type === 'special') {
1982 if (nexttoken.id !== '(endline)') {
1990 // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
1991 // is looking for ad hoc lint patterns. We add to Pratt's model .fud, which is
1992 // like nud except that it is only used on the first token of a statement.
1993 // Having .fud makes it much easier to define JavaScript. I retained Pratt's
1996 // .nud Null denotation
1997 // .fud First null denotation
1998 // .led Left denotation
1999 // lbp Left binding power
2000 // rbp Right binding power
2002 // They are key to the parsing method called Top Down Operator Precedence.
2004 function parse(rbp, initial) {
2006 if (nexttoken.id === '(end)') {
2007 error("Unexpected early end of program.", token);
2010 if (option.safe && typeof predefined[token.value] === 'boolean' &&
2011 (nexttoken.id !== '(' && nexttoken.id !== '.')) {
2012 warning('ADsafe violation.', token);
2015 anonname = 'anonymous';
2016 funct['(verb)'] = token.value;
2018 if (initial === true && token.fud) {
2024 if (nexttoken.type === '(number)' && token.id === '.') {
2026 "A leading decimal point can be confused with a dot: '.{a}'.",
2027 token, nexttoken.value);
2031 error("Expected an identifier and instead saw '{a}'.",
2035 while (rbp < nexttoken.lbp) {
2038 left = token.led(left);
2040 error("Expected an operator and instead saw '{a}'.",
2049 // Functions for conformance of style.
2051 function adjacent(left, right) {
2052 left = left || token;
2053 right = right || nexttoken;
2054 if (option.white || xmode === 'styleproperty' || xmode === 'style') {
2055 if (left.character !== right.from && left.line === right.line) {
2056 warning("Unexpected space after '{a}'.", right, left.value);
2061 function nospace(left, right) {
2062 left = left || token;
2063 right = right || nexttoken;
2064 if (option.white && !left.comment) {
2065 if (left.line === right.line) {
2066 adjacent(left, right);
2072 function nonadjacent(left, right) {
2074 left = left || token;
2075 right = right || nexttoken;
2076 if (left.line === right.line && left.character === right.from) {
2077 warning("Missing space after '{a}'.",
2078 nexttoken, left.value);
2083 function nobreaknonadjacent(left, right) {
2084 left = left || token;
2085 right = right || nexttoken;
2086 if (!option.laxbreak && left.line !== right.line) {
2087 warning("Bad line breaking before '{a}'.", right, right.id);
2088 } else if (option.white) {
2089 left = left || token;
2090 right = right || nexttoken;
2091 if (left.character === right.from) {
2092 warning("Missing space after '{a}'.",
2093 nexttoken, left.value);
2098 function indentation(bias) {
2100 if (option.white && nexttoken.id !== '(end)') {
2101 i = indent + (bias || 0);
2102 if (nexttoken.from !== i) {
2103 warning("Expected '{a}' to have an indentation at {b} instead at {c}.",
2104 nexttoken, nexttoken.value, i, nexttoken.from);
2109 function nolinebreak(t) {
2111 if (t.line !== nexttoken.line) {
2112 warning("Line breaking error '{a}'.", t, t.value);
2118 if (token.line !== nexttoken.line) {
2119 if (!option.laxbreak) {
2120 warning("Bad line breaking before '{a}'.", token, nexttoken.id);
2122 } else if (token.character !== nexttoken.from && option.white) {
2123 warning("Unexpected space after '{a}'.", nexttoken, token.value);
2126 nonadjacent(token, nexttoken);
2130 // Functional constructors for making the symbols that will be inherited by
2133 function symbol(s, p) {
2135 if (!x || typeof x !== 'object') {
2147 return symbol(s, 0);
2151 function stmt(s, f) {
2153 x.identifier = x.reserved = true;
2159 function blockstmt(s, f) {
2166 function reserveName(x) {
2167 var c = x.id.charAt(0);
2168 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2169 x.identifier = x.reserved = true;
2175 function prefix(s, f) {
2176 var x = symbol(s, 150);
2178 x.nud = (typeof f === 'function') ? f : function () {
2179 this.right = parse(150);
2180 this.arity = 'unary';
2181 if (this.id === '++' || this.id === '--') {
2182 if (option.plusplus) {
2183 warning("Unexpected use of '{a}'.", this, this.id);
2184 } else if ((!this.right.identifier || this.right.reserved) &&
2185 this.right.id !== '.' && this.right.id !== '[') {
2186 warning("Bad operand.", this);
2195 function type(s, f) {
2203 function reserve(s, f) {
2205 x.identifier = x.reserved = true;
2210 function reservevar(s, v) {
2211 return reserve(s, function () {
2212 if (this.id === 'this' || this.id === 'arguments') {
2213 if (strict_mode && funct['(global)']) {
2214 warning("Strict violation.", this);
2215 } else if (option.safe) {
2216 warning("ADsafe violation.", this);
2224 function infix(s, f, p, w) {
2225 var x = symbol(s, p);
2227 x.led = function (left) {
2229 nobreaknonadjacent(prevtoken, token);
2230 nonadjacent(token, nexttoken);
2232 if (typeof f === 'function') {
2233 return f(left, this);
2236 this.right = parse(p);
2244 function relation(s, f) {
2245 var x = symbol(s, 100);
2246 x.led = function (left) {
2247 nobreaknonadjacent(prevtoken, token);
2248 nonadjacent(token, nexttoken);
2249 var right = parse(100);
2250 if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) {
2251 warning("Use the isNaN function to compare with NaN.", this);
2253 f.apply(this, [left, right]);
2255 if (left.id === '!') {
2256 warning("Confusing use of '{a}'.", left, '!');
2258 if (right.id === '!') {
2259 warning("Confusing use of '{a}'.", left, '!');
2269 function isPoorRelation(node) {
2271 ((node.type === '(number)' && +node.value === 0) ||
2272 (node.type === '(string)' && node.value === ' ') ||
2273 node.type === 'true' ||
2274 node.type === 'false' ||
2275 node.type === 'undefined' ||
2276 node.type === 'null');
2280 function assignop(s, f) {
2281 symbol(s, 20).exps = true;
2282 return infix(s, function (left, that) {
2285 if (predefined[left.value] === false &&
2286 scope[left.value]['(global)'] === true) {
2287 warning('Read only.', left);
2292 if (typeof predefined[l.value] === 'boolean') {
2293 warning('ADsafe violation.', l);
2299 if (left.id === '.' || left.id === '[') {
2300 if (!left.left || left.left.value === 'arguments') {
2301 warning('Bad assignment.', that);
2303 that.right = parse(19);
2305 } else if (left.identifier && !left.reserved) {
2306 if (funct[left.value] === 'exception') {
2307 warning("Do not assign to the exception parameter.", left);
2309 that.right = parse(19);
2312 if (left === syntax['function']) {
2314 "Expected an identifier in an assignment and instead saw a function invocation.",
2318 error("Bad assignment.", that);
2322 function bitwise(s, f, p) {
2323 var x = symbol(s, p);
2325 x.led = (typeof f === 'function') ? f : function (left) {
2326 if (option.bitwise) {
2327 warning("Unexpected use of '{a}'.", this, this.id);
2330 this.right = parse(p);
2336 function bitwiseassignop(s) {
2337 symbol(s, 20).exps = true;
2338 return infix(s, function (left, that) {
2339 if (option.bitwise) {
2340 warning("Unexpected use of '{a}'.", that, that.id);
2342 nonadjacent(prevtoken, token);
2343 nonadjacent(token, nexttoken);
2345 if (left.id === '.' || left.id === '[' ||
2346 (left.identifier && !left.reserved)) {
2350 if (left === syntax['function']) {
2352 "Expected an identifier in an assignment, and instead saw a function invocation.",
2357 error("Bad assignment.", that);
2362 function suffix(s, f) {
2363 var x = symbol(s, 150);
2364 x.led = function (left) {
2365 if (option.plusplus) {
2366 warning("Unexpected use of '{a}'.", this, this.id);
2367 } else if ((!left.identifier || left.reserved) && left.id !== '.' && left.id !== '[') {
2368 warning("Bad operand.", this);
2377 function optionalidentifier() {
2378 if (nexttoken.reserved) {
2379 warning("Expected an identifier and instead saw '{a}' (a reserved word).",
2380 nexttoken, nexttoken.id);
2382 if (nexttoken.identifier) {
2389 function identifier() {
2390 var i = optionalidentifier();
2394 if (token.id === 'function' && nexttoken.id === '(') {
2395 warning("Missing name in function statement.");
2397 error("Expected an identifier and instead saw '{a}'.",
2398 nexttoken, nexttoken.value);
2402 function reachable(s) {
2404 if (nexttoken.id !== ';' || noreach) {
2412 if (t.id !== '(endline)') {
2413 if (t.id === 'function') {
2415 "Inner functions should be listed at the top of the outer function.", t);
2418 warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
2426 function statement(noindent) {
2427 var i = indent, r, s = scope, t = nexttoken;
2429 // We don't like the empty statement.
2432 warning("Unnecessary semicolon.", t);
2437 // Is this a labelled statement?
2439 if (t.identifier && !t.reserved && peek().id === ':') {
2442 scope = Object.create(s);
2443 addlabel(t.value, 'label');
2444 if (!nexttoken.labelled) {
2445 warning("Label '{a}' on {b} statement.",
2446 nexttoken, t.value, nexttoken.value);
2448 if (jx.test(t.value + ':')) {
2449 warning("Label '{a}' looks like a javascript url.",
2452 nexttoken.label = t.value;
2456 // Parse the statement.
2463 // Look for the final semicolon.
2466 if (!r || !r.exps) {
2468 "Expected an assignment or function call and instead saw an expression.",
2470 } else if (r.id === '(' && r.left.id === 'new') {
2471 warning("Do not use 'new' for side effects.");
2473 if (nexttoken.id !== ';') {
2474 warningAt("Missing semicolon.", token.line,
2475 token.from + token.value.length);
2477 adjacent(token, nexttoken);
2479 nonadjacent(token, nexttoken);
2483 // Restore the indentation.
2491 function use_strict() {
2492 if (nexttoken.value === 'use strict') {
2503 function statements(begin) {
2505 if (begin && !use_strict() && option.strict) {
2506 warning('Missing "use strict" statement.', nexttoken);
2508 if (option.adsafe) {
2512 if (nexttoken.value !== 'ADSAFE' ||
2513 peek(0).id !== '.' ||
2514 (peek(1).value !== 'id' &&
2515 peek(1).value !== 'go')) {
2516 error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.',
2520 if (nexttoken.value === 'ADSAFE' &&
2521 peek(0).id === '.' &&
2522 peek(1).value === 'id') {
2524 error('ADsafe violation.', nexttoken);
2530 if (nexttoken.value !== adsafe_id) {
2531 error('ADsafe violation: id does not match.', nexttoken);
2533 advance('(string)');
2540 if (nexttoken.value === 'ADSAFE') {
2545 advance('(string)');
2548 if (f.id !== 'function') {
2549 error('The second argument to lib must be a function.', f);
2551 p = f.funct['(params)'];
2552 p = p && p.join(', ');
2553 if (p && p !== 'lib') {
2554 error("Expected '{a}' and instead saw '{b}'.",
2555 f, '(lib)', '(' + p + ')');
2561 error("ADsafe lib violation.");
2565 while (!nexttoken.reach && nexttoken.id !== '(end)') {
2566 if (nexttoken.id === ';') {
2567 warning("Unnecessary semicolon.");
2570 a.push(statement());
2578 var a, b = inblock, old_indent = indent, s = scope, t;
2580 scope = Object.create(scope);
2581 nonadjacent(token, nexttoken);
2583 if (nexttoken.id === '{') {
2585 if (nexttoken.id !== '}' || token.line !== nexttoken.line) {
2586 indent += option.indent;
2587 while (!f && nexttoken.from > indent) {
2588 indent += option.indent;
2594 indent -= option.indent;
2598 indent = old_indent;
2600 warning("Expected '{a}' and instead saw '{b}'.",
2601 nexttoken, '{', nexttoken.value);
2606 funct['(verb)'] = null;
2613 // An identity function, used by string and number tokens.
2615 function idValue() {
2620 function countMember(m) {
2621 if (membersOnly && typeof membersOnly[m] !== 'boolean') {
2622 warning("Unexpected /*member '{a}'.", token, m);
2624 if (typeof member[m] === 'number') {
2632 function note_implied(token) {
2633 var name = token.value, line = token.line, a = implied[name];
2634 if (typeof a === 'function') {
2640 } else if (a[a.length - 1] !== line) {
2648 function cssName() {
2649 if (nexttoken.identifier) {
2655 function cssNumber() {
2656 if (nexttoken.id === '-') {
2661 if (nexttoken.type === '(number)') {
2662 advance('(number)');
2667 function cssString() {
2668 if (nexttoken.type === '(string)') {
2674 function cssColor() {
2675 var i, number, value;
2676 if (nexttoken.identifier) {
2677 value = nexttoken.value;
2678 if (value === 'rgb' || value === 'rgba') {
2681 for (i = 0; i < 3; i += 1) {
2685 number = nexttoken.value;
2686 if (nexttoken.type !== '(number)' || number < 0) {
2687 warning("Expected a positive number and instead saw '{a}'",
2692 if (nexttoken.id === '%') {
2695 warning("Expected a percentage and instead saw '{a}'",
2700 warning("Expected a small number and instead saw '{a}'",
2706 if (value === 'rgba') {
2708 number = +nexttoken.value;
2709 if (nexttoken.type !== '(number)' || number < 0 || number > 1) {
2710 warning("Expected a number between 0 and 1 and instead saw '{a}'",
2714 if (nexttoken.id === '%') {
2715 warning("Unexpected '%'.");
2721 } else if (cssColorData[nexttoken.value] === true) {
2725 } else if (nexttoken.type === '(color)') {
2732 function cssLength() {
2733 if (nexttoken.id === '-') {
2738 if (nexttoken.type === '(number)') {
2740 if (nexttoken.type !== '(string)' &&
2741 cssLengthData[nexttoken.value] === true) {
2744 } else if (+token.value !== 0) {
2745 warning("Expected a linear unit and instead saw '{a}'.",
2746 nexttoken, nexttoken.value);
2753 function cssLineHeight() {
2754 if (nexttoken.id === '-') {
2758 if (nexttoken.type === '(number)') {
2760 if (nexttoken.type !== '(string)' &&
2761 cssLengthData[nexttoken.value] === true) {
2770 function cssWidth() {
2771 if (nexttoken.identifier) {
2772 switch (nexttoken.value) {
2784 function cssMargin() {
2785 if (nexttoken.identifier) {
2786 if (nexttoken.value === 'auto') {
2795 function cssAttr() {
2796 if (nexttoken.identifier && nexttoken.value === 'attr') {
2799 if (!nexttoken.identifier) {
2800 warning("Expected a name and instead saw '{a}'.",
2801 nexttoken, nexttoken.value);
2810 function cssCommaList() {
2811 while (nexttoken.id !== ';') {
2812 if (!cssName() && !cssString()) {
2813 warning("Expected a name and instead saw '{a}'.",
2814 nexttoken, nexttoken.value);
2816 if (nexttoken.id !== ',') {
2823 function cssCounter() {
2824 if (nexttoken.identifier && nexttoken.value === 'counter') {
2827 if (!nexttoken.identifier) {
2830 if (nexttoken.id === ',') {
2832 if (nexttoken.type !== '(string)') {
2833 warning("Expected a string and instead saw '{a}'.",
2834 nexttoken, nexttoken.value);
2841 if (nexttoken.identifier && nexttoken.value === 'counters') {
2844 if (!nexttoken.identifier) {
2845 warning("Expected a name and instead saw '{a}'.",
2846 nexttoken, nexttoken.value);
2849 if (nexttoken.id === ',') {
2851 if (nexttoken.type !== '(string)') {
2852 warning("Expected a string and instead saw '{a}'.",
2853 nexttoken, nexttoken.value);
2857 if (nexttoken.id === ',') {
2859 if (nexttoken.type !== '(string)') {
2860 warning("Expected a string and instead saw '{a}'.",
2861 nexttoken, nexttoken.value);
2872 function cssShape() {
2874 if (nexttoken.identifier && nexttoken.value === 'rect') {
2877 for (i = 0; i < 4; i += 1) {
2879 warning("Expected a number and instead saw '{a}'.",
2880 nexttoken, nexttoken.value);
2892 if (nexttoken.identifier && nexttoken.value === 'url') {
2893 nexttoken = lex.range('(', ')');
2894 url = nexttoken.value;
2896 if (c === '"' || c === '\'') {
2897 if (url.slice(-1) !== c) {
2898 warning("Bad url string.");
2900 url = url.slice(1, -1);
2901 if (url.indexOf(c) >= 0) {
2902 warning("Bad url string.");
2907 warning("Missing url.");
2910 if (option.safe && ux.test(url)) {
2911 error("ADsafe URL violation.");
2919 cssAny = [cssUrl, function () {
2921 if (nexttoken.identifier) {
2922 switch (nexttoken.value.toLowerCase()) {
2927 warning("Unexpected expression '{a}'.",
2928 nexttoken, nexttoken.value);
2935 if (nexttoken.id === ';' || nexttoken.id === '!' ||
2936 nexttoken.id === '(end)' || nexttoken.id === '}') {
2945 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'ridge',
2950 'auto', 'always', 'avoid', 'left', 'right'
2954 'auto', 'hidden', 'scroll', 'visible'
2957 cssAttributeData = {
2959 true, 'background-attachment', 'background-color',
2960 'background-image', 'background-position', 'background-repeat'
2962 'background-attachment': ['scroll', 'fixed'],
2963 'background-color': ['transparent', cssColor],
2964 'background-image': ['none', cssUrl],
2965 'background-position': [
2966 2, [cssLength, 'top', 'bottom', 'left', 'right', 'center']
2968 'background-repeat': [
2969 'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
2971 'border': [true, 'border-color', 'border-style', 'border-width'],
2973 true, 'border-bottom-color', 'border-bottom-style',
2974 'border-bottom-width'
2976 'border-bottom-color': cssColor,
2977 'border-bottom-style': cssBorderStyle,
2978 'border-bottom-width': cssWidth,
2979 'border-collapse': ['collapse', 'separate'],
2980 'border-color': ['transparent', 4, cssColor],
2982 true, 'border-left-color', 'border-left-style', 'border-left-width'
2984 'border-left-color': cssColor,
2985 'border-left-style': cssBorderStyle,
2986 'border-left-width': cssWidth,
2988 true, 'border-right-color', 'border-right-style',
2989 'border-right-width'
2991 'border-right-color': cssColor,
2992 'border-right-style': cssBorderStyle,
2993 'border-right-width': cssWidth,
2994 'border-spacing': [2, cssLength],
2995 'border-style': [4, cssBorderStyle],
2997 true, 'border-top-color', 'border-top-style', 'border-top-width'
2999 'border-top-color': cssColor,
3000 'border-top-style': cssBorderStyle,
3001 'border-top-width': cssWidth,
3002 'border-width': [4, cssWidth],
3003 bottom: [cssLength, 'auto'],
3004 'caption-side' : ['bottom', 'left', 'right', 'top'],
3005 clear: ['both', 'left', 'none', 'right'],
3006 clip: [cssShape, 'auto'],
3009 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote',
3010 cssString, cssUrl, cssCounter, cssAttr
3012 'counter-increment': [
3019 cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move',
3020 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize',
3021 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait'
3023 direction: ['ltr', 'rtl'],
3025 'block', 'compact', 'inline', 'inline-block', 'inline-table',
3026 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption',
3027 'table-cell', 'table-column', 'table-column-group',
3028 'table-footer-group', 'table-header-group', 'table-row',
3031 'empty-cells': ['show', 'hide'],
3032 'float': ['left', 'none', 'right'],
3034 'caption', 'icon', 'menu', 'message-box', 'small-caption',
3035 'status-bar', true, 'font-size', 'font-style', 'font-weight',
3038 'font-family': cssCommaList,
3040 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large',
3041 'xx-large', 'larger', 'smaller', cssLength
3043 'font-size-adjust': ['none', cssNumber],
3045 'normal', 'wider', 'narrower', 'ultra-condensed',
3046 'extra-condensed', 'condensed', 'semi-condensed',
3047 'semi-expanded', 'expanded', 'extra-expanded'
3050 'normal', 'italic', 'oblique'
3053 'normal', 'small-caps'
3056 'normal', 'bold', 'bolder', 'lighter', cssNumber
3058 height: [cssLength, 'auto'],
3059 left: [cssLength, 'auto'],
3060 'letter-spacing': ['normal', cssLength],
3061 'line-height': ['normal', cssLineHeight],
3063 true, 'list-style-image', 'list-style-position', 'list-style-type'
3065 'list-style-image': ['none', cssUrl],
3066 'list-style-position': ['inside', 'outside'],
3067 'list-style-type': [
3068 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero',
3069 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha',
3070 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana',
3071 'hiragana-iroha', 'katakana-oroha', 'none'
3073 margin: [4, cssMargin],
3074 'margin-bottom': cssMargin,
3075 'margin-left': cssMargin,
3076 'margin-right': cssMargin,
3077 'margin-top': cssMargin,
3078 'marker-offset': [cssLength, 'auto'],
3079 'max-height': [cssLength, 'none'],
3080 'max-width': [cssLength, 'none'],
3081 'min-height': cssLength,
3082 'min-width': cssLength,
3084 outline: [true, 'outline-color', 'outline-style', 'outline-width'],
3085 'outline-color': ['invert', cssColor],
3087 'dashed', 'dotted', 'double', 'groove', 'inset', 'none',
3088 'outset', 'ridge', 'solid'
3090 'outline-width': cssWidth,
3091 overflow: cssOverflow,
3092 'overflow-x': cssOverflow,
3093 'overflow-y': cssOverflow,
3094 padding: [4, cssLength],
3095 'padding-bottom': cssLength,
3096 'padding-left': cssLength,
3097 'padding-right': cssLength,
3098 'padding-top': cssLength,
3099 'page-break-after': cssBreak,
3100 'page-break-before': cssBreak,
3101 position: ['absolute', 'fixed', 'relative', 'static'],
3102 quotes: [8, cssString],
3103 right: [cssLength, 'auto'],
3104 'table-layout': ['auto', 'fixed'],
3105 'text-align': ['center', 'justify', 'left', 'right'],
3106 'text-decoration': [
3107 'none', 'underline', 'overline', 'line-through', 'blink'
3109 'text-indent': cssLength,
3110 'text-shadow': ['none', 4, [cssColor, cssLength]],
3111 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'],
3112 top: [cssLength, 'auto'],
3113 'unicode-bidi': ['normal', 'embed', 'bidi-override'],
3115 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle',
3116 'text-bottom', cssLength
3118 visibility: ['visible', 'hidden', 'collapse'],
3120 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit'
3122 width: [cssLength, 'auto'],
3123 'word-spacing': ['normal', cssLength],
3124 'word-wrap': ['break-word', 'normal'],
3125 'z-index': ['auto', cssNumber]
3128 function styleAttribute() {
3130 while (nexttoken.id === '*' || nexttoken.id === '#' ||
3131 nexttoken.value === '_') {
3133 warning("Unexpected '{a}'.", nexttoken, nexttoken.value);
3137 if (nexttoken.id === '-') {
3139 warning("Unexpected '{a}'.", nexttoken, nexttoken.value);
3142 if (!nexttoken.identifier) {
3144 "Expected a non-standard style attribute and instead saw '{a}'.",
3145 nexttoken, nexttoken.value);
3150 if (!nexttoken.identifier) {
3151 warning("Excepted a style attribute, and instead saw '{a}'.",
3152 nexttoken, nexttoken.value);
3154 if (is_own(cssAttributeData, nexttoken.value)) {
3155 v = cssAttributeData[nexttoken.value];
3159 warning("Unrecognized style attribute '{a}'.",
3160 nexttoken, nexttoken.value);
3169 function styleValue(v) {
3181 if (nexttoken.identifier && nexttoken.value === v) {
3188 if (i >= v.length) {
3195 } else if (typeof vi === 'number') {
3204 if (styleValue(vi)) {
3219 for (i = start; i < v.length; i += 1) {
3221 if (styleValue(cssAttributeData[v[i]])) {
3235 function styleChild() {
3236 if (nexttoken.id === '(number)') {
3238 if (nexttoken.value === 'n' && nexttoken.identifier) {
3241 if (nexttoken.id === '+') {
3245 advance('(number)');
3250 switch (nexttoken.value) {
3253 if (nexttoken.identifier) {
3259 warning("Unexpected token '{a}'.", nexttoken, nexttoken.value);
3262 function substyle() {
3265 if (nexttoken.id === '}' || nexttoken.id === '(end)' ||
3266 xquote && nexttoken.id === xquote) {
3269 while (nexttoken.id === ';') {
3270 warning("Misplaced ';'.");
3273 v = styleAttribute();
3275 if (nexttoken.identifier && nexttoken.value === 'inherit') {
3278 if (!styleValue(v)) {
3279 warning("Unexpected token '{a}'.", nexttoken,
3284 if (nexttoken.id === '!') {
3287 if (nexttoken.identifier && nexttoken.value === 'important') {
3290 warning("Expected '{a}' and instead saw '{b}'.",
3291 nexttoken, 'important', nexttoken.value);
3294 if (nexttoken.id === '}' || nexttoken.id === xquote) {
3295 warning("Missing '{a}'.", nexttoken, ';');
3302 function styleSelector() {
3303 if (nexttoken.identifier) {
3304 if (!is_own(htmltag, nexttoken.value)) {
3305 warning("Expected a tagName, and instead saw {a}.",
3306 nexttoken, nexttoken.value);
3310 switch (nexttoken.id) {
3318 switch (nexttoken.value) {
3327 case 'first-letter':
3329 case 'first-of-type':
3332 case 'last-of-type':
3334 case 'only-of-type':
3343 if (!nexttoken.identifier) {
3344 warning("Expected a lang code, and instead saw :{a}.",
3345 nexttoken, nexttoken.value);
3350 case 'nth-last-child':
3351 case 'nth-last-of-type':
3361 if (nexttoken.id === ':' && peek(0).value === 'not') {
3362 warning("Nested not.");
3368 warning("Expected a pseudo, and instead saw :{a}.",
3369 nexttoken, nexttoken.value);
3374 if (!nexttoken.identifier) {
3375 warning("Expected an id, and instead saw #{a}.",
3376 nexttoken, nexttoken.value);
3385 if (!nexttoken.identifier) {
3386 warning("Expected a class, and instead saw #.{a}.",
3387 nexttoken, nexttoken.value);
3393 if (!nexttoken.identifier) {
3394 warning("Expected an attribute, and instead saw [{a}].",
3395 nexttoken, nexttoken.value);
3398 if (nexttoken.id === '=' || nexttoken.value === '~=' ||
3399 nexttoken.value === '$=' ||
3400 nexttoken.value === '|=' ||
3401 nexttoken.id === '*=' ||
3402 nexttoken.id === '^=') {
3404 if (nexttoken.type !== '(string)') {
3405 warning("Expected a string, and instead saw {a}.",
3406 nexttoken, nexttoken.value);
3413 error("Expected a CSS selector, and instead saw {a}.",
3414 nexttoken, nexttoken.value);
3419 function stylePattern() {
3421 if (nexttoken.id === '{') {
3422 warning("Expected a style pattern, and instead saw '{a}'.", nexttoken,
3424 } else if (nexttoken.id === '@') {
3426 name = nexttoken.value;
3427 if (nexttoken.identifier && atrule[name] === true) {
3431 warning("Expected an at-rule, and instead saw @{a}.", nexttoken, name);
3435 if (nexttoken.id === '</' || nexttoken.id === '{' ||
3436 nexttoken.id === '(end)') {
3439 if (nexttoken.id === ',') {
3447 while (nexttoken.id === '@') {
3449 if (i.identifier && i.value === 'import') {
3453 warning("Expected '{a}' and instead saw '{b}'.", nexttoken,
3454 'url', nexttoken.value);
3462 while (nexttoken.id !== '</' && nexttoken.id !== '(end)') {
3464 xmode = 'styleproperty';
3465 if (nexttoken.id === ';') {
3479 function doBegin(n) {
3480 if (n !== 'html' && !option.fragment) {
3481 if (n === 'div' && option.adsafe) {
3482 error("ADSAFE: Use the fragment option.");
3484 error("Expected '{a}' and instead saw '{b}'.",
3488 if (option.adsafe) {
3491 "Currently, ADsafe does not operate on whole HTML documents. It operates on <div> fragments and .js files.", token);
3493 if (option.fragment) {
3495 error("ADsafe violation: Wrap the widget in a div.", token);
3498 error("Use the fragment option.", token);
3501 option.browser = true;
3505 function doAttribute(n, a, v) {
3508 u = typeof v === 'string' ? v.toUpperCase() : '';
3509 if (ids[u] === true) {
3510 warning("Duplicate id='{a}'.", nexttoken, v);
3512 if (!/^[A-Za-z][A-Za-z0-9._:\-]*$/.test(v)) {
3513 warning("Bad id: '{a}'.", nexttoken, v);
3514 } else if (option.adsafe) {
3516 if (v.slice(0, adsafe_id.length) !== adsafe_id) {
3517 warning("ADsafe violation: An id must have a '{a}' prefix",
3518 nexttoken, adsafe_id);
3519 } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {
3520 warning("ADSAFE violation: bad id.");
3524 if (!/^[A-Z]+_$/.test(v)) {
3525 warning("ADSAFE violation: bad id.");
3531 warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a);
3534 } else if (a === 'class' || a === 'type' || a === 'name') {
3537 warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a);
3540 } else if (a === 'href' || a === 'background' ||
3541 a === 'content' || a === 'data' ||
3542 a.indexOf('src') >= 0 || a.indexOf('url') >= 0) {
3543 if (option.safe && ux.test(v)) {
3544 error("ADsafe URL violation.");
3547 } else if (a === 'for') {
3548 if (option.adsafe) {
3550 if (v.slice(0, adsafe_id.length) !== adsafe_id) {
3551 warning("ADsafe violation: An id must have a '{a}' prefix",
3552 nexttoken, adsafe_id);
3553 } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {
3554 warning("ADSAFE violation: bad id.");
3557 warning("ADSAFE violation: bad id.");
3560 } else if (a === 'name') {
3561 if (option.adsafe && v.indexOf('_') >= 0) {
3562 warning("ADsafe name violation.");
3567 function doTag(n, a) {
3568 var i, t = htmltag[n], x;
3571 error("Unrecognized tag '<{a}>'.",
3573 n === n.toLowerCase() ? n :
3574 n + ' (capitalization error)');
3576 if (stack.length > 0) {
3578 error("Too many <html> tags.", token);
3582 if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) {
3583 error("A '<{a}>' must be within '<{b}>'.",
3586 } else if (!option.adsafe && !option.fragment) {
3590 error("A '<{a}>' must be within '<{b}>'.",
3594 } while (stack[i].name !== 'body');
3599 if (option.adsafe && stack.length === 1 && !adsafe_id) {
3600 warning("ADSAFE violation: missing ID_.");
3606 indent = nexttoken.from;
3608 warning("lang is deprecated.", token);
3610 if (option.adsafe && stack.length !== 1) {
3611 warning("ADsafe script placement violation.", token);
3614 if (option.adsafe && (!adsafe_may || !approved[a.src])) {
3615 warning("ADsafe unapproved script source.", token);
3618 warning("type is unnecessary.", token);
3622 error("ADsafe script violation.", token);
3624 statements('script');
3628 if (!nexttoken.identifier && nexttoken.value !== 'script') {
3629 warning("Expected '{a}' and instead saw '{b}'.",
3630 nexttoken, 'script', nexttoken.value);
3641 if (!nexttoken.identifier && nexttoken.value !== 'style') {
3642 warning("Expected '{a}' and instead saw '{b}'.",
3643 nexttoken, 'style', nexttoken.value);
3662 if (option.adsafe && a.autocomplete !== 'off') {
3663 warning("ADsafe autocomplete violation.");
3667 warning("Bad input type.");
3681 if (option.adsafe) {
3682 warning("ADsafe violation: Disallowed tag: " + n);
3689 function closetag(n) {
3690 return '</' + n + '>';
3694 var a, attributes, e, n, q, t, v, w = option.white, wmode;
3699 switch (nexttoken.value) {
3705 if (!t.identifier) {
3706 warning("Bad identifier {a}.", t, t.value);
3710 n = n.toLowerCase();
3719 if (typeof v !== 'object') {
3720 error("Unrecognized tag '<{a}>'.", t, n);
3725 if (nexttoken.id === '/') {
3727 if (nexttoken.id !== '>') {
3728 warning("Expected '{a}' and instead saw '{b}'.",
3729 nexttoken, '>', nexttoken.value);
3733 if (nexttoken.id && nexttoken.id.substr(0, 1) === '>') {
3736 if (!nexttoken.identifier) {
3737 if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {
3738 error("Missing '>'.", nexttoken);
3740 warning("Bad identifier.");
3742 option.white = true;
3743 nonadjacent(token, nexttoken);
3744 a = nexttoken.value;
3747 if (!option.cap && a !== a.toLowerCase()) {
3748 warning("Attribute '{a}' not all lower case.", nexttoken, a);
3750 a = a.toLowerCase();
3752 if (is_own(attributes, a)) {
3753 warning("Attribute '{a}' repeated.", nexttoken, a);
3755 if (a.slice(0, 2) === 'on') {
3757 warning("Avoid HTML event handlers.");
3759 xmode = 'scriptstring';
3762 if (q !== '"' && q !== "'") {
3763 error("Missing quote.");
3766 wmode = option.white;
3767 option.white = false;
3770 option.white = wmode;
3771 if (nexttoken.id !== q) {
3772 error("Missing close quote on script attribute.");
3778 } else if (a === 'style') {
3779 xmode = 'scriptstring';
3782 if (q !== '"' && q !== "'") {
3783 error("Missing quote.");
3785 xmode = 'styleproperty';
3794 if (nexttoken.id === '=') {
3796 v = nexttoken.value;
3797 if (!nexttoken.identifier &&
3798 nexttoken.id !== '"' &&
3799 nexttoken.id !== '\'' &&
3800 nexttoken.type !== '(string)' &&
3801 nexttoken.type !== '(number)' &&
3802 nexttoken.type !== '(color)') {
3803 warning("Expected an attribute value and instead saw '{a}'.", token, a);
3811 doAttribute(n, a, v);
3813 doTag(n, attributes);
3823 if (!nexttoken.identifier) {
3824 warning("Bad identifier.");
3826 n = nexttoken.value;
3828 n = n.toLowerCase();
3832 error("Unexpected '{a}'.", nexttoken, closetag(n));
3836 error("Unexpected '{a}'.", nexttoken, closetag(n));
3839 error("Expected '{a}' and instead saw '{b}'.",
3840 nexttoken, closetag(t.name), closetag(n));
3842 if (nexttoken.id !== '>') {
3843 error("Missing '{a}'.", nexttoken, '>');
3850 warning("ADsafe HTML violation.");
3855 if (nexttoken.id === '>' || nexttoken.id === '(end)') {
3858 if (nexttoken.value.indexOf('--') >= 0) {
3859 warning("Unexpected --.");
3861 if (nexttoken.value.indexOf('<') >= 0) {
3862 warning("Unexpected <.");
3864 if (nexttoken.value.indexOf('>') >= 0) {
3865 warning("Unexpected >.");
3874 if (nexttoken.id === '(end)') {
3875 error("Missing '{a}'.", nexttoken,
3876 '</' + stack[stack.length - 1].value + '>');
3881 if (stack && stack.length === 0 && (option.adsafe ||
3882 !option.fragment || nexttoken.id === '(end)')) {
3886 if (nexttoken.id !== '(end)') {
3887 error("Unexpected material after the end.");
3892 // Build the syntax table by declaring the syntactic elements of the language.
3894 type('(number)', idValue);
3895 type('(string)', idValue);
3897 syntax['(identifier)'] = {
3898 type: '(identifier)',
3905 if (typeof s === 'function') {
3907 } else if (typeof s === 'boolean') {
3909 funct = functions[0];
3915 // The name is in scope and defined in the current function.
3919 // Change 'unused' to 'var', and reject labels.
3926 warning("'{a}' is a statement label.", token, v);
3930 // The name is not defined in the function. If we are in the global scope,
3931 // then we have an undefined variable.
3933 } else if (funct['(global)']) {
3934 if (option.undef && predefined[v] !== 'boolean') {
3935 warning("'{a}' is not defined.", token, v);
3937 note_implied(token);
3939 // If the name is already defined in the current
3940 // function, but not as outer, then there is a scope error.
3948 warning("'{a}' used out of scope.", token, v);
3951 warning("'{a}' is a statement label.", token, v);
3958 // If the name is defined in an outer function, make an outer entry, and if
3959 // it was unused, make it var.
3963 } else if (s === null) {
3964 warning("'{a}' is not allowed.", token, v);
3965 note_implied(token);
3966 } else if (typeof s !== 'object') {
3968 warning("'{a}' is not defined.", token, v);
3972 note_implied(token);
3979 funct[v] = s['(global)'] ? 'global' : 'outer';
3983 funct[v] = s['(global)'] ? 'global' : 'outer';
3986 warning("'{a}' is a statement label.", token, v);
3994 error("Expected an operator and instead saw '{a}'.",
3995 nexttoken, nexttoken.value);
3999 type('(regexp)', function () {
4005 delim('(end)').reach = true;
4006 delim('</').reach = true;
4010 delim('(error)').reach = true;
4011 delim('}').reach = true;
4014 delim('"').reach = true;
4015 delim("'").reach = true;
4017 delim(':').reach = true;
4022 reserve('case').reach = true;
4024 reserve('default').reach = true;
4026 reservevar('arguments');
4028 reservevar('false');
4029 reservevar('Infinity');
4034 reservevar('undefined');
4035 assignop('=', 'assign', 20);
4036 assignop('+=', 'assignadd', 20);
4037 assignop('-=', 'assignsub', 20);
4038 assignop('*=', 'assignmult', 20);
4039 assignop('/=', 'assigndiv', 20).nud = function () {
4040 error("A regular expression literal can be confused with '/='.");
4042 assignop('%=', 'assignmod', 20);
4043 bitwiseassignop('&=', 'assignbitand', 20);
4044 bitwiseassignop('|=', 'assignbitor', 20);
4045 bitwiseassignop('^=', 'assignbitxor', 20);
4046 bitwiseassignop('<<=', 'assignshiftleft', 20);
4047 bitwiseassignop('>>=', 'assignshiftright', 20);
4048 bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20);
4049 infix('?', function (left, that) {
4051 that.right = parse(10);
4053 that['else'] = parse(10);
4057 infix('||', 'or', 40);
4058 infix('&&', 'and', 50);
4059 bitwise('|', 'bitor', 70);
4060 bitwise('^', 'bitxor', 80);
4061 bitwise('&', 'bitand', 90);
4062 relation('==', function (left, right) {
4063 if (option.eqeqeq) {
4064 warning("Expected '{a}' and instead saw '{b}'.",
4066 } else if (isPoorRelation(left)) {
4067 warning("Use '{a}' to compare with '{b}'.",
4068 this, '===', left.value);
4069 } else if (isPoorRelation(right)) {
4070 warning("Use '{a}' to compare with '{b}'.",
4071 this, '===', right.value);
4076 relation('!=', function (left, right) {
4077 if (option.eqeqeq) {
4078 warning("Expected '{a}' and instead saw '{b}'.",
4080 } else if (isPoorRelation(left)) {
4081 warning("Use '{a}' to compare with '{b}'.",
4082 this, '!==', left.value);
4083 } else if (isPoorRelation(right)) {
4084 warning("Use '{a}' to compare with '{b}'.",
4085 this, '!==', right.value);
4094 bitwise('<<', 'shiftleft', 120);
4095 bitwise('>>', 'shiftright', 120);
4096 bitwise('>>>', 'shiftrightunsigned', 120);
4097 infix('in', 'in', 120);
4098 infix('instanceof', 'instanceof', 120);
4099 infix('+', function (left, that) {
4100 var right = parse(130);
4101 if (left && right && left.id === '(string)' && right.id === '(string)') {
4102 left.value += right.value;
4103 left.character = right.character;
4104 if (jx.test(left.value)) {
4105 warning("JavaScript URL.", left);
4114 infix('-', 'sub', 130);
4116 infix('*', 'mult', 140);
4117 infix('/', 'div', 140);
4118 infix('%', 'mod', 140);
4120 suffix('++', 'postinc');
4121 prefix('++', 'preinc');
4122 syntax['++'].exps = true;
4124 suffix('--', 'postdec');
4125 prefix('--', 'predec');
4126 syntax['--'].exps = true;
4127 prefix('delete', function () {
4129 if (!p || (p.id !== '.' && p.id !== '[')) {
4130 warning("Expected '{a}' and instead saw '{b}'.",
4131 nexttoken, '.', nexttoken.value);
4138 prefix('~', function () {
4139 if (option.bitwise) {
4140 warning("Unexpected '{a}'.", this, '~');
4145 prefix('!', function () {
4146 this.right = parse(150);
4147 this.arity = 'unary';
4148 if (bang[this.right.id] === true) {
4149 warning("Confusing use of '{a}'.", this, '!');
4153 prefix('typeof', 'typeof');
4154 prefix('new', function () {
4155 var c = parse(155), i;
4156 if (c && c.id !== 'function') {
4161 warning("Use the object literal notation {}.", token);
4164 if (nexttoken.id !== '(') {
4165 warning("Use the array literal notation [].", token);
4168 if (nexttoken.id === ')') {
4169 warning("Use the array literal notation [].", token);
4173 if ((i.id === '(number)' && /[.+\-Ee]/.test(i.value)) ||
4174 (i.id === '-' && !i.right) ||
4175 i.id === '(string)' || i.id === '[' ||
4176 i.id === '{' || i.id === 'true' ||
4178 i.id === 'null' || i.id === 'undefined' ||
4179 i.id === 'Infinity') {
4180 warning("Use the array literal notation [].", token);
4182 if (nexttoken.id !== ')') {
4183 error("Use the array literal notation [].", token);
4195 warning("Do not use {a} as a constructor.", token, c.value);
4199 warning("The Function constructor is eval.");
4206 if (c.id !== 'function') {
4207 i = c.value.substr(0, 1);
4208 if (option.newcap && (i < 'A' || i > 'Z')) {
4210 "A constructor name should start with an uppercase letter.",
4216 if (c.id !== '.' && c.id !== '[' && c.id !== '(') {
4217 warning("Bad constructor.", token);
4221 warning("Weird construction. Delete 'new'.", this);
4223 adjacent(token, nexttoken);
4224 if (nexttoken.id !== '(') {
4225 warning("Missing '()' invoking a constructor.");
4230 syntax['new'].exps = true;
4232 infix('.', function (left, that) {
4233 adjacent(prevtoken, token);
4234 var m = identifier();
4235 if (typeof m === 'string') {
4240 if (!option.evil && left && left.value === 'document' &&
4241 (m === 'write' || m === 'writeln')) {
4242 warning("document.write can be a form of eval.", left);
4243 } else if (option.adsafe) {
4244 if (left && left.value === 'ADSAFE') {
4245 if (m === 'id' || m === 'lib') {
4246 warning("ADsafe violation.", that);
4247 } else if (m === 'go') {
4248 if (xmode !== 'script') {
4249 warning("ADsafe violation.", that);
4250 } else if (adsafe_went || nexttoken.id !== '(' ||
4251 peek(0).id !== '(string)' ||
4252 peek(0).value !== adsafe_id ||
4253 peek(1).id !== ',') {
4254 error("ADsafe violation: go.", that);
4261 if (!option.evil && (m === 'eval' || m === 'execScript')) {
4262 warning('eval is evil.');
4263 } else if (option.safe) {
4265 if (banned[m] === true) {
4266 warning("ADsafe restricted word '{a}'.", token, m);
4268 if (typeof predefined[left.value] !== 'boolean' ||
4269 nexttoken.id === '(') {
4272 if (standard_member[m] === true) {
4273 if (nexttoken.id === '.') {
4274 warning("ADsafe violation.", that);
4278 if (nexttoken.id !== '.') {
4279 warning("ADsafe violation.", that);
4287 if (typeof m === 'string') {
4295 infix('(', function (left, that) {
4296 adjacent(prevtoken, token);
4301 if (left.type === '(identifier)') {
4302 if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
4303 if (left.value !== 'Number' && left.value !== 'String' &&
4304 left.value !== 'Boolean' &&
4305 left.value !== 'Date') {
4306 if (left.value === 'Math') {
4307 warning("Math is not a function.", left);
4308 } else if (option.newcap) {
4310 "Missing 'new' prefix when invoking a constructor.", left);
4314 } else if (left.id === '.') {
4315 if (option.safe && left.left.value === 'Math' &&
4316 left.right === 'random') {
4317 warning("ADsafe violation.", left);
4321 if (nexttoken.id !== ')') {
4323 p[p.length] = parse(10);
4325 if (nexttoken.id !== ',') {
4332 if (option.immed && left.id === 'function' && nexttoken.id !== ')') {
4333 warning("Wrap the entire immediate function invocation in parens.",
4336 nospace(prevtoken, token);
4337 if (typeof left === 'object') {
4338 if (left.value === 'parseInt' && n === 1) {
4339 warning("Missing radix parameter.", left);
4342 if (left.value === 'eval' || left.value === 'Function' ||
4343 left.value === 'execScript') {
4344 warning("eval is evil.", left);
4345 } else if (p[0] && p[0].id === '(string)' &&
4346 (left.value === 'setTimeout' ||
4347 left.value === 'setInterval')) {
4349 "Implied eval is evil. Pass a function instead of a string.", left);
4352 if (!left.identifier && left.id !== '.' && left.id !== '[' &&
4353 left.id !== '(' && left.id !== '&&' && left.id !== '||' &&
4355 warning("Bad invocation.", left);
4360 }, 155, true).exps = true;
4362 prefix('(', function () {
4366 nospace(prevtoken, token);
4367 if (option.immed && v.id === 'function') {
4368 if (nexttoken.id === '(') {
4370 "Move the invocation into the parens that contain the function.", nexttoken);
4373 "Do not wrap function literals in parens unless they are to be immediately invoked.",
4380 infix('[', function (left, that) {
4382 var e = parse(0), s;
4383 if (e && e.type === '(string)') {
4384 if (option.safe && banned[e.value] === true) {
4385 warning("ADsafe restricted word '{a}'.", that, e.value);
4386 } else if (!option.evil &&
4387 (e.value === 'eval' || e.value === 'execScript')) {
4388 warning("eval is evil.", that);
4389 } else if (option.safe &&
4390 (e.value.charAt(0) === '_' || e.value.charAt(0) === '-')) {
4391 warning("ADsafe restricted subscript '{a}'.", that, e.value);
4393 countMember(e.value);
4394 if (!option.sub && ix.test(e.value)) {
4395 s = syntax[e.value];
4396 if (!s || !s.reserved) {
4397 warning("['{a}'] is better written in dot notation.",
4401 } else if (!e || e.type !== '(number)' || e.value < 0) {
4403 warning('ADsafe subscripting.');
4407 nospace(prevtoken, token);
4413 prefix('[', function () {
4414 var b = token.line !== nexttoken.line;
4417 indent += option.indent;
4418 if (nexttoken.from === indent + option.indent) {
4419 indent += option.indent;
4422 while (nexttoken.id !== '(end)') {
4423 while (nexttoken.id === ',') {
4424 warning("Extra comma.");
4427 if (nexttoken.id === ']') {
4430 if (b && token.line !== nexttoken.line) {
4433 this.first.push(parse(10));
4434 if (nexttoken.id === ',') {
4436 if (nexttoken.id === ']') {
4437 warning("Extra comma.", token);
4445 indent -= option.indent;
4453 x.nud = function () {
4454 var b, i, s, seen = {};
4455 b = token.line !== nexttoken.line;
4457 indent += option.indent;
4458 if (nexttoken.from === indent + option.indent) {
4459 indent += option.indent;
4463 if (nexttoken.id === '}') {
4469 i = optionalidentifier(true);
4471 if (nexttoken.id === '(string)') {
4472 i = nexttoken.value;
4477 } else if (nexttoken.id === '(number)') {
4478 i = nexttoken.value.toString();
4481 error("Expected '{a}' and instead saw '{b}'.",
4482 nexttoken, '}', nexttoken.value);
4485 if (seen[i] === true) {
4486 warning("Duplicate member '{a}'.", nexttoken, i);
4491 nonadjacent(token, nexttoken);
4493 if (nexttoken.id === ',') {
4495 if (nexttoken.id === ',' || nexttoken.id === '}') {
4496 warning("Extra comma.", token);
4503 indent -= option.indent;
4509 x.fud = function () {
4510 error("Expected to see a statement and instead saw a block.", token);
4515 function varstatement(prefix) {
4517 // JavaScript does not have block scope. It only has function scope. So,
4518 // declaring a variable in a block can have unexpected consequences.
4520 var id, name, value;
4522 if (funct['(onevar)'] && option.onevar) {
4523 warning("Too many var statements.");
4524 } else if (!funct['(global)']) {
4525 funct['(onevar)'] = true;
4529 nonadjacent(token, nexttoken);
4531 if (funct['(global)'] && predefined[id] === false) {
4532 warning("Redefinition of '{a}'.", token, id);
4534 addlabel(id, 'unused');
4539 this.first.push(token);
4540 if (nexttoken.id === '=') {
4541 nonadjacent(token, nexttoken);
4543 nonadjacent(token, nexttoken);
4544 if (nexttoken.id === 'undefined') {
4545 warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id);
4547 if (peek(0).id === '=' && nexttoken.identifier) {
4548 error("Variable {a} was not declared correctly.",
4549 nexttoken, nexttoken.value);
4554 if (nexttoken.id !== ',') {
4563 stmt('var', varstatement).exps = true;
4566 function functionparams() {
4567 var i, t = nexttoken, p = [];
4570 if (nexttoken.id === ')') {
4572 nospace(prevtoken, token);
4578 addlabel(i, 'parameter');
4579 if (nexttoken.id === ',') {
4583 nospace(prevtoken, token);
4589 function doFunction(i) {
4591 scope = Object.create(s);
4593 '(name)' : i || '"' + anonname + '"',
4594 '(line)' : nexttoken.line,
4595 '(context)' : funct,
4600 token.funct = funct;
4601 functions.push(funct);
4603 addlabel(i, 'function');
4605 funct['(params)'] = functionparams();
4609 funct['(last)'] = token.line;
4610 funct = funct['(context)'];
4614 blockstmt('function', function () {
4617 "Function statements cannot be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token);
4620 var i = identifier();
4621 adjacent(token, nexttoken);
4622 addlabel(i, 'unused');
4624 if (nexttoken.id === '(' && nexttoken.line === token.line) {
4626 "Function statements are not invocable. Wrap the whole function invocation in parens.");
4631 prefix('function', function () {
4632 var i = optionalidentifier();
4634 adjacent(token, nexttoken);
4636 nonadjacent(token, nexttoken);
4639 if (funct['(loopage)']) {
4640 warning("Don't make functions within a loop.");
4645 blockstmt('if', function () {
4648 nonadjacent(this, t);
4651 if (nexttoken.id === '=') {
4652 warning("Expected a conditional expression and instead saw an assignment.");
4657 nospace(prevtoken, token);
4659 if (nexttoken.id === 'else') {
4660 nonadjacent(token, nexttoken);
4662 if (nexttoken.id === 'if' || nexttoken.id === 'switch') {
4671 blockstmt('try', function () {
4673 if (option.adsafe) {
4674 warning("ADsafe try violation.", this);
4677 if (nexttoken.id === 'catch') {
4679 nonadjacent(token, nexttoken);
4682 scope = Object.create(s);
4683 e = nexttoken.value;
4684 if (nexttoken.type !== '(identifier)') {
4685 warning("Expected an identifier and instead saw '{a}'.",
4688 addlabel(e, 'exception');
4696 if (nexttoken.id === 'finally') {
4701 error("Expected '{a}' and instead saw '{b}'.",
4702 nexttoken, 'catch', nexttoken.value);
4707 blockstmt('while', function () {
4709 funct['(breakage)'] += 1;
4710 funct['(loopage)'] += 1;
4712 nonadjacent(this, t);
4715 if (nexttoken.id === '=') {
4716 warning("Expected a conditional expression and instead saw an assignment.");
4721 nospace(prevtoken, token);
4723 funct['(breakage)'] -= 1;
4724 funct['(loopage)'] -= 1;
4730 blockstmt('switch', function () {
4733 funct['(breakage)'] += 1;
4735 nonadjacent(this, t);
4737 this.condition = parse(20);
4739 nospace(prevtoken, token);
4740 nonadjacent(token, nexttoken);
4743 nonadjacent(token, nexttoken);
4744 indent += option.indent;
4747 switch (nexttoken.id) {
4749 switch (funct['(verb)']) {
4759 "Expected a 'break' statement before 'case'.",
4762 indentation(-option.indent);
4764 this.cases.push(parse(20));
4767 funct['(verb)'] = 'case';
4770 switch (funct['(verb)']) {
4778 "Expected a 'break' statement before 'default'.",
4781 indentation(-option.indent);
4787 indent -= option.indent;
4790 if (this.cases.length === 1 || this.condition.id === 'true' ||
4791 this.condition.id === 'false') {
4792 warning("This 'switch' should be an 'if'.", this);
4794 funct['(breakage)'] -= 1;
4795 funct['(verb)'] = undefined;
4798 error("Missing '{a}'.", nexttoken, '}');
4804 error("Each value should have its own case label.");
4810 error("Missing ':' on a case clause.", token);
4813 error("Expected '{a}' and instead saw '{b}'.",
4814 nexttoken, 'case', nexttoken.value);
4820 stmt('debugger', function () {
4821 if (!option.debug) {
4822 warning("All 'debugger' statements should be removed.");
4828 var x = stmt('do', function () {
4829 funct['(breakage)'] += 1;
4830 funct['(loopage)'] += 1;
4831 this.first = block(true);
4834 nonadjacent(token, t);
4838 if (nexttoken.id === '=') {
4839 warning("Expected a conditional expression and instead saw an assignment.");
4844 nospace(prevtoken, token);
4845 funct['(breakage)'] -= 1;
4846 funct['(loopage)'] -= 1;
4853 blockstmt('for', function () {
4854 var f = option.forin, s, t = nexttoken;
4855 funct['(breakage)'] += 1;
4856 funct['(loopage)'] += 1;
4858 nonadjacent(this, t);
4860 if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') {
4861 if (nexttoken.id === 'var') {
4865 switch (funct[nexttoken.value]) {
4867 funct[nexttoken.value] = 'var';
4872 warning("Bad for in variable '{a}'.",
4873 nexttoken, nexttoken.value);
4881 if (!f && (s.length > 1 || typeof s[0] !== 'object' ||
4882 s[0].value !== 'if')) {
4883 warning("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", this);
4885 funct['(breakage)'] -= 1;
4886 funct['(loopage)'] -= 1;
4889 if (nexttoken.id !== ';') {
4890 if (nexttoken.id === 'var') {
4896 if (nexttoken.id !== ',') {
4905 if (nexttoken.id !== ';') {
4907 if (nexttoken.id === '=') {
4908 warning("Expected a conditional expression and instead saw an assignment.");
4915 if (nexttoken.id === ';') {
4916 error("Expected '{a}' and instead saw '{b}'.",
4917 nexttoken, ')', ';');
4919 if (nexttoken.id !== ')') {
4922 if (nexttoken.id !== ',') {
4929 nospace(prevtoken, token);
4931 funct['(breakage)'] -= 1;
4932 funct['(loopage)'] -= 1;
4938 stmt('break', function () {
4939 var v = nexttoken.value;
4940 if (funct['(breakage)'] === 0) {
4941 warning("Unexpected '{a}'.", nexttoken, this.value);
4944 if (nexttoken.id !== ';') {
4945 if (token.line === nexttoken.line) {
4946 if (funct[v] !== 'label') {
4947 warning("'{a}' is not a statement label.", nexttoken, v);
4948 } else if (scope[v] !== funct) {
4949 warning("'{a}' is out of scope.", nexttoken, v);
4951 this.first = nexttoken;
4960 stmt('continue', function () {
4961 var v = nexttoken.value;
4962 if (funct['(breakage)'] === 0) {
4963 warning("Unexpected '{a}'.", nexttoken, this.value);
4966 if (nexttoken.id !== ';') {
4967 if (token.line === nexttoken.line) {
4968 if (funct[v] !== 'label') {
4969 warning("'{a}' is not a statement label.", nexttoken, v);
4970 } else if (scope[v] !== funct) {
4971 warning("'{a}' is out of scope.", nexttoken, v);
4973 this.first = nexttoken;
4976 } else if (!funct['(loopage)']) {
4977 warning("Unexpected '{a}'.", nexttoken, this.value);
4979 reachable('continue');
4984 stmt('return', function () {
4986 if (nexttoken.id === '(regexp)') {
4987 warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator.");
4989 if (nexttoken.id !== ';' && !nexttoken.reach) {
4990 nonadjacent(token, nexttoken);
4991 this.first = parse(20);
4993 reachable('return');
4998 stmt('throw', function () {
5000 nonadjacent(token, nexttoken);
5001 this.first = parse(20);
5008 // Superfluous reserved words
5020 reserve('implements');
5021 reserve('interface');
5024 reserve('protected');
5028 function jsonValue() {
5030 function jsonObject() {
5031 var o = {}, t = nexttoken;
5033 if (nexttoken.id !== '}') {
5035 if (nexttoken.id === '(end)') {
5036 error("Missing '}' to match '{' from line {a}.",
5038 } else if (nexttoken.id === '}') {
5039 warning("Unexpected comma.", token);
5041 } else if (nexttoken.id === ',') {
5042 error("Unexpected comma.", nexttoken);
5043 } else if (nexttoken.id !== '(string)') {
5044 warning("Expected a string and instead saw {a}.",
5045 nexttoken, nexttoken.value);
5047 if (o[nexttoken.value] === true) {
5048 warning("Duplicate key '{a}'.",
5049 nexttoken, nexttoken.value);
5050 } else if (nexttoken.value === '__proto__') {
5051 warning("Stupid key '{a}'.",
5052 nexttoken, nexttoken.value);
5054 o[nexttoken.value] = true;
5059 if (nexttoken.id !== ',') {
5068 function jsonArray() {
5071 if (nexttoken.id !== ']') {
5073 if (nexttoken.id === '(end)') {
5074 error("Missing ']' to match '[' from line {a}.",
5076 } else if (nexttoken.id === ']') {
5077 warning("Unexpected comma.", token);
5079 } else if (nexttoken.id === ',') {
5080 error("Unexpected comma.", nexttoken);
5083 if (nexttoken.id !== ',') {
5092 switch (nexttoken.id) {
5108 if (token.character !== nexttoken.from) {
5109 warning("Unexpected space after '-'.", token);
5111 adjacent(token, nexttoken);
5112 advance('(number)');
5115 error("Expected a JSON value.", nexttoken);
5120 // The actual JSLINT function itself.
5122 var itself = function (s, o) {
5125 predefined = Object.create(standard);
5128 if (a instanceof Array) {
5129 for (i = 0; i < a.length; i += 1) {
5130 predefined[a[i]] = true;
5153 predefined.Date = null;
5154 predefined['eval'] = null;
5155 predefined.Function = null;
5156 predefined.Object = null;
5157 predefined.ADSAFE = false;
5158 predefined.lib = false;
5164 option.indent = option.indent || 4;
5165 option.maxerr = option.maxerr || 50;
5168 adsafe_went = false;
5170 if (option.approved) {
5171 for (i = 0; i < option.approved.length; i += 1) {
5172 approved[option.approved[i]] = option.approved[i];
5175 approved.test = 'test';
5178 for (i = 0; i < option.indent; i += 1) {
5182 global = Object.create(predefined);
5186 '(name)': '(global)',
5191 functions = [funct];
5206 strict_mode = false;
5208 prevtoken = token = nexttoken = syntax['(begin)'];
5213 if (nexttoken.value.charAt(0) === '<') {
5215 if (option.adsafe && !adsafe_went) {
5216 warning("ADsafe violation: Missing ADSAFE.go.", this);
5219 switch (nexttoken.id) {
5222 option.laxbreak = true;
5233 if (token.id !== '@' || !nexttoken.identifier ||
5234 nexttoken.value !== 'charset' || token.line !== 1 ||
5236 error('A css file should begin with @charset "UTF-8";');
5239 if (nexttoken.type !== '(string)' &&
5240 nexttoken.value !== 'UTF-8') {
5241 error('A css file should begin with @charset "UTF-8";');
5249 if (option.adsafe && option.fragment) {
5250 error("Expected '{a}' and instead saw '{b}'.",
5251 nexttoken, '<div>', nexttoken.value);
5259 JSLINT.errors.push({
5261 line : e.line || nexttoken.line,
5262 character : e.character || nexttoken.from
5266 return JSLINT.errors.length === 0;
5269 function is_array(o) {
5270 return Object.prototype.toString.apply(o) === '[object Array]';
5273 function to_array(o) {
5285 itself.data = function () {
5287 var data = {functions: []}, fu, globals, implieds = [], f, i, j,
5288 members = [], n, unused = [], v;
5289 if (itself.errors.length) {
5290 data.errors = itself.errors;
5297 for (n in implied) {
5298 if (is_own(implied, n)) {
5305 if (implieds.length > 0) {
5306 data.implieds = implieds;
5309 if (urls.length > 0) {
5313 globals = to_array(scope);
5314 if (globals.length > 0) {
5315 data.globals = globals;
5318 for (i = 1; i < functions.length; i += 1) {
5321 for (j = 0; j < functionicity.length; j += 1) {
5322 fu[functionicity[j]] = [];
5325 if (is_own(f, n) && n.charAt(0) !== '(') {
5327 if (is_array(fu[v])) {
5329 if (v === 'unused') {
5333 'function': f['(name)']
5339 for (j = 0; j < functionicity.length; j += 1) {
5340 if (fu[functionicity[j]].length === 0) {
5341 delete fu[functionicity[j]];
5344 fu.name = f['(name)'];
5345 fu.param = f['(params)'];
5346 fu.line = f['(line)'];
5347 fu.last = f['(last)'];
5348 data.functions.push(fu);
5351 if (unused.length > 0) {
5352 data.unused = unused;
5357 if (typeof member[n] === 'number') {
5358 data.member = member;
5366 itself.report = function (option) {
5367 var data = itself.data();
5369 var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s;
5371 function detail(h, array) {
5372 var b, i, singularity;
5374 o.push('<div><i>' + h + '</i> ');
5375 array = array.sort();
5376 for (i = 0; i < array.length; i += 1) {
5377 if (array[i] !== singularity) {
5378 singularity = array[i];
5379 o.push((b ? ', ' : '') + singularity);
5388 if (data.errors || data.implieds || data.unused) {
5390 o.push('<div id=errors><i>Error:</i>');
5392 for (i = 0; i < data.errors.length; i += 1) {
5395 e = c.evidence || '';
5396 o.push('<p>Problem' + (isFinite(c.line) ? ' at line ' +
5397 c.line + ' character ' + c.character : '') +
5398 ': ' + c.reason.entityify() +
5399 '</p><p class=evidence>' +
5400 (e && (e.length > 80 ? e.slice(0, 77) + '...' :
5401 e).entityify()) + '</p>');
5406 if (data.implieds) {
5408 for (i = 0; i < data.implieds.length; i += 1) {
5409 s[i] = '<code>' + data.implieds[i].name + '</code> <i>' +
5410 data.implieds[i].line + '</i>';
5412 o.push('<p><i>Implied global:</i> ' + s.join(', ') + '</p>');
5417 for (i = 0; i < data.unused.length; i += 1) {
5418 s[i] = '<code><u>' + data.unused[i].name + '</u></code> <i>' +
5419 data.unused[i].line + '</i> <code>' +
5420 data.unused[i]['function'] + '</code>';
5422 o.push('<p><i>Unused variable:</i> ' + s.join(', ') + '</p>');
5425 o.push('<p>JSON: bad.</p>');
5432 o.push('<br><div id=functions>');
5435 detail("URLs<br>", data.urls, '<br>');
5438 if (xmode === 'style') {
5439 o.push('<p>CSS.</p>');
5440 } else if (data.json && !err) {
5441 o.push('<p>JSON: good.</p>');
5442 } else if (data.globals) {
5443 o.push('<div><i>Global</i> ' +
5444 data.globals.sort().join(', ') + '</div>');
5446 o.push('<div><i>No new global variables introduced.</i></div>');
5449 for (i = 0; i < data.functions.length; i += 1) {
5450 f = data.functions[i];
5452 o.push('<br><div class=function><i>' + f.line + '-' +
5453 f.last + '</i> ' + (f.name || '') + '(' +
5454 (f.param ? f.param.join(', ') : '') + ')</div>');
5455 detail('<big><b>Unused</b></big>', f.unused);
5456 detail('Closure', f.closure);
5457 detail('Variable', f['var']);
5458 detail('Exception', f.exception);
5459 detail('Outer', f.outer);
5460 detail('Global', f.global);
5461 detail('Label', f.label);
5465 a = to_array(data.member);
5468 m = '<br><pre id=members>/*members ';
5470 for (i = 0; i < a.length; i += 1) {
5473 if (l + n.length > 72) {
5479 if (data.member[k] === 1) {
5480 n = '<i>' + n + '</i>';
5482 if (i < a.length - 1) {
5487 o.push(m + '<br>*/</pre>');
5494 itself.jslint = itself;
5496 itself.edition = '2010-02-20';
5498 if (typeof exports !== "undefined") {
5499 exports.JSLINT = itself;