#!/usr/bin/js // (C)2002 Douglas Crockford // www.JSLint.com // Spidermonkey hacks by Andy Walker >?>?=?|<([\/=]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/, hx = /^\s*(['"=>\/&#]|<[\/!]?|[a-zA-Z][a-zA-Z0-9_\-]*|--)/, ox = /[>&]|<[\/!]?|--/, lx = /\*\/|\/\*/, ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i, sx = /^\s*([{:#*%.=,>+\[\]@()"';*]|[a-zA-Z0-9_][a-zA-Z0-9_\-]*|<\/|\/\*)/, ssx = /^\s*([@#!"'};:\-\/%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\d+(?:\.\d+)?|<\/)/, rx = { outer: hx, html: hx, style: sx, styleproperty: ssx }; function F() {} if (typeof Object.create !== 'function') { Object.create = function(o) { F.prototype = o; return new F(); }; } Object.prototype.union = function(o) { var n; for (n in o) { if (o.hasOwnProperty(n)) { this[n] = o[n]; } } }; String.prototype.entityify = function() { return this.replace(/&/g, '&').replace(//g, '>'); }; String.prototype.isAlpha = function() { return (this >= 'a' && this <= 'z\uffff') || (this >= 'A' && this <= 'Z\uffff'); }; String.prototype.isDigit = function() { return (this >= '0' && this <= '9'); }; String.prototype.supplant = function(o) { return this.replace(/\{([^{}]*)\}/g, function(a, b) { var r = o[b]; return typeof r === 'string' || typeof r === 'number' ? r: a; }); }; String.prototype.name = function() { if (ix.test(this)) { return this; } if (/[&<"\/\\\x00-\x1f]/.test(this)) { return '"' + this.replace(/[&<"\/\\\x00-\x1f]/g, function(a) { var c = escapes[a]; if (c) { return c; } c = a.charCodeAt(); return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); }) + '"'; } return '"' + this + '"'; }; function assume() { if (!option.safe) { if (option.rhino) { predefined.union(rhino); } if (option.browser || option.sidebar) { predefined.union(browser); } if (option.sidebar) { predefined.union(sidebar); } if (option.widget) { predefined.union(widget); } } } function quit(m, l, ch) { throw { name: 'JSLintError', line: l, character: ch, message: m + " (" + Math.floor((l / lines.length) * 100) + "% scanned)." }; } function warning(m, t, a, b, c, d) { var ch, l, w; t = t || nexttoken; if (t.id === '(end)') { t = token; } l = t.line || 0; ch = t.from || 0; w = { id: '(error)', raw: m, evidence: lines[l] || '', line: l, character: ch, a: a, b: b, c: c, d: d }; w.reason = m.supplant(w); JSLINT.errors.push(w); if (option.passfail) { quit('Stopping. ', l, ch); } warnings += 1; if (warnings === 50) { quit("Too many errors.", l, ch); } return w; } function warningAt(m, l, ch, a, b, c, d) { return warning(m, { line: l, from: ch }, a, b, c, d); } function error(m, t, a, b, c, d) { var w = warning(m, t, a, b, c, d); quit("Stopping, unable to continue.", w.line, w.character); } function errorAt(m, l, ch, a, b, c, d) { return error(m, { line: l, from: ch }, a, b, c, d); } var lex = function lex() { var character, from, line, s; function nextLine() { var at; line += 1; if (line >= lines.length) { return false; } character = 0; s = lines[line].replace(/\t/g, tab); at = s.search(cx); if (at >= 0) { warningAt("Unsafe character.", line, at); } return true; } function it(type, value) { var i, t; if (type === '(color)') { t = { type: type }; } else if (type === '(punctuator)' || (type === '(identifier)' && syntax.hasOwnProperty(value))) { t = syntax[value]; if (!t.id) { t = syntax[type]; } } else { t = syntax[type]; } t = Object.create(t); if (type === '(string)' || type === '(range)') { if (jx.test(value)) { warningAt("Script URL.", line, from); } } if (type === '(identifier)') { t.identifier = true; if (option.nomen && value.charAt(0) === '_') { warningAt("Unexpected '_' in '{a}'.", line, from, value); } } t.value = value; t.line = line; t.character = character; t.from = from; i = t.id; if (i !== '(endline)') { prereg = i && (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) || i === 'return'); } return t; } return { init: function(source) { if (typeof source === 'string') { lines = source.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n'); } else { lines = source; } line = -1; nextLine(); from = 0; }, range: function(begin, end) { var c, value = ''; from = character; if (s.charAt(0) !== begin) { errorAt("Expected '{a}' and instead saw '{b}'.", line, character, begin, s.charAt(0)); } for (;;) { s = s.slice(1); character += 1; c = s.charAt(0); switch (c) { case '': errorAt("Missing '{a}'.", line, character, c); break; case end: s = s.slice(1); character += 1; return it('(range)', value); case xquote: case '\\': case '\'': case '"': warningAt("Unexpected '{a}'.", line, character, c); } value += c; } }, token: function() { var b, c, captures, d, depth, high, i, l, low, q, t; function match(x) { var r = x.exec(s), r1; if (r) { l = r[0].length; r1 = r[1]; c = r1.charAt(0); s = s.substr(l); character += l; from = character - r1.length; return r1; } } function string(x) { var c, j, r = ''; if (jsonmode && x !== '"') { warningAt("Strings must use doublequote.", line, character); } if (xquote === x || (xmode === 'scriptstring' && !xquote)) { return it('(punctuator)', x); } function esc(n) { var i = parseInt(s.substr(j + 1, n), 16); j += n; if (i >= 32 && i <= 127 && i !== 34 && i !== 92 && i !== 39) { warningAt("Unnecessary escapement.", line, character); } character += n; c = String.fromCharCode(i); } j = 0; for (;;) { while (j >= s.length) { j = 0; if (xmode !== 'html' || !nextLine()) { errorAt("Unclosed string.", line, from); } } c = s.charAt(j); if (c === x) { character += 1; s = s.substr(j + 1); return it('(string)', r, x); } if (c < ' ') { if (c === '\n' || c === '\r') { break; } warningAt("Control character in string: {a}.", line, character + j, s.slice(0, j)); } else if (c === xquote) { warningAt("Bad HTML string", line, character + j); } else if (c === '<') { if (option.safe && xmode === 'html') { warningAt("ADsafe string violation.", line, character + j); } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) { warningAt("Expected '<\\/' and instead saw ' 0) { character += 1; s = s.slice(i); break; } else { if (!nextLine()) { return it('(end)', ''); } } } t = match(rx[xmode] || tx); if (!t) { if (xmode === 'html') { return it('(error)', s.charAt(0)); } else { t = ''; c = ''; while (s && s < '!') { s = s.substr(1); } if (s) { errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); } } } else { if (c.isAlpha() || c === '_' || c === '$') { return it('(identifier)', t); } if (c.isDigit()) { if (xmode !== 'style' && !isFinite(Number(t))) { warningAt("Bad number '{a}'.", line, character, t); } if (xmode !== 'styleproperty' && s.substr(0, 1).isAlpha()) { warningAt("Missing space after '{a}'.", line, character, t); } if (c === '0') { d = t.substr(1, 1); if (d.isDigit()) { if (token.id !== '.' && xmode !== 'styleproperty') { warningAt("Don't use extra leading zeros '{a}'.", line, character, t); } } else if (jsonmode && (d === 'x' || d === 'X')) { warningAt("Avoid 0x-. '{a}'.", line, character, t); } } if (t.substr(t.length - 1) === '.') { warningAt("A trailing decimal point can be confused with a dot '{a}'.", line, character, t); } return it('(number)', t); } switch (t) { case '"': case "'": return string(t); case '//': if (src || (xmode && xmode !== 'script')) { warningAt("Unexpected comment.", line, character); } else if (xmode === 'script' && /<\s*\//i.test(s)) { warningAt("Unexpected <\/ in comment.", line, character); } else if ((option.safe || xmode === 'script') && ax.test(s)) { warningAt("Dangerous comment.", line, character); } s = ''; token.comment = true; break; case '/*': if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) { warningAt("Unexpected comment.", line, character); } if (option.safe && ax.test(s)) { warningAt("ADsafe comment violation.", line, character); } for (;;) { i = s.search(lx); if (i >= 0) { break; } if (!nextLine()) { errorAt("Unclosed comment.", line, character); } else { if (option.safe && ax.test(s)) { warningAt("ADsafe comment violation.", line, character); } } } character += i + 2; if (s.substr(i, 1) === '/') { errorAt("Nested comment.", line, character); } s = s.substr(i + 2); token.comment = true; break; case '/*global': case '/*extern': case '/*members': case '/*member': case '/*jslint': case '*/': return { value: t, type: 'special', line: line, character: character, from: from }; case '': break; case '/': if (prereg) { depth = 0; captures = 0; l = 0; for (;;) { b = true; c = s.charAt(l); l += 1; switch (c) { case '': errorAt("Unclosed regular expression.", line, from); return; case '/': if (depth > 0) { warningAt("Unescaped '{a}'.", line, from + l, '/'); } c = s.substr(0, l - 1); q = { g: true, i: true, m: true }; while (q[s.charAt(l)] === true) { q[s.charAt(l)] = false; l += 1; } character += l; s = s.substr(l); return it('(regexp)', c); case '\\': c = s.charAt(l); if (c < ' ') { warningAt("Unexpected control character in regular expression.", line, from + l); } else if (c === '<') { warningAt("Unexpected escaped character '{a}' in regular expression.", line, from + l, c); } l += 1; break; case '(': depth += 1; b = false; if (s.charAt(l) === '?') { l += 1; switch (s.charAt(l)) { case ':': case '=': case '!': l += 1; break; default: warningAt("Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l)); } } else { captures += 1; } break; case ')': if (depth === 0) { warningAt("Unescaped '{a}'.", line, from + l, ')'); } else { depth -= 1; } break; case ' ': q = 1; while (s.charAt(l) === ' ') { l += 1; q += 1; } if (q > 1) { warningAt("Spaces are hard to count. Use {{a}}.", line, from + l, q); } break; case '[': if (s.charAt(l) === '^') { l += 1; } q = false; klass: do { c = s.charAt(l); l += 1; switch (c) { case '[': case '^': warningAt("Unescaped '{a}'.", line, from + l, c); q = true; break; case '-': if (q) { q = false; } else { warningAt("Unescaped '{a}'.", line, from + l, '-'); q = true; } break; case ']': if (!q) { warningAt("Unescaped '{a}'.", line, from + l - 1, '-'); } break klass; case '\\': c = s.charAt(l); if (c < ' ') { warningAt("Unexpected control character in regular expression.", line, from + l); } else if (c === '<') { warningAt("Unexpected escaped character '{a}' in regular expression.", line, from + l, c); } l += 1; q = true; break; case '/': warningAt("Unescaped '{a}'.", line, from + l - 1, '/'); q = true; break; case '<': if (xmode === 'script') { c = s.charAt(l); if (c === '!' || c === '/') { warningAt("HTML confusion in regular expression '<{a}'.", line, from + l, c); } } q = true; break; default: q = true; } } while ( c ); break; case '.': if (option.regexp) { warningAt("Unexpected '{a}'.", line, from + l, c); } break; case ']': case '?': case '{': case '}': case '+': case '*': warningAt("Unescaped '{a}'.", line, from + l, c); break; case '<': if (xmode === 'script') { c = s.charAt(l); if (c === '!' || c === '/') { warningAt("HTML confusion in regular expression '<{a}'.", line, from + l, c); } } } if (b) { switch (s.charAt(l)) { case '?': case '+': case '*': l += 1; if (s.charAt(l) === '?') { l += 1; } break; case '{': l += 1; c = s.charAt(l); if (c < '0' || c > '9') { warningAt("Expected a number and instead saw '{a}'.", line, from + l, c); } l += 1; low = +c; for (;;) { c = s.charAt(l); if (c < '0' || c > '9') { break; } l += 1; low = +c + (low * 10); } high = low; if (c === ',') { l += 1; high = Infinity; c = s.charAt(l); if (c >= '0' && c <= '9') { l += 1; high = +c; for (;;) { c = s.charAt(l); if (c < '0' || c > '9') { break; } l += 1; high = +c + (high * 10); } } } if (s.charAt(l) !== '}') { warningAt("Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c); } else { l += 1; } if (s.charAt(l) === '?') { l += 1; } if (low > high) { warningAt("'{a}' should not be greater than '{b}'.", line, from + l, low, high); } } } } c = s.substr(0, l - 1); character += l; s = s.substr(l); return it('(regexp)', c); } return it('(punctuator)', t); case '#': if (xmode === 'html' || xmode === 'styleproperty') { for (;;) { c = s.charAt(0); if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) { break; } character += 1; s = s.substr(1); t += c; } if (t.length !== 4 && t.length !== 7) { warningAt("Bad hex color '{a}'.", line, from + l, t); } return it('(color)', t); } return it('(punctuator)', t); default: if (xmode === 'outer' && c === '&') { character += 1; s = s.substr(1); for (;;) { c = s.charAt(0); character += 1; s = s.substr(1); if (c === ';') { break; } if (! ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || c === '#')) { errorAt("Bad entity", line, from + l, character); } } break; } return it('(punctuator)', t); } } } } }; } (); function addlabel(t, type) { if (t === 'hasOwnProperty') { error("'hasOwnProperty' is a really bad name."); } if (option.safe && funct['(global)']) { warning('ADsafe global: ' + t + '.', token); } if (funct.hasOwnProperty(t)) { warning(funct[t] === true ? "'{a}' was used before it was defined.": "'{a}' is already defined.", nexttoken, t); } funct[t] = type; if (type === 'label') { scope[t] = funct; } else if (funct['(global)']) { global[t] = funct; if (implied.hasOwnProperty(t)) { warning("'{a}' was used before it was defined.", nexttoken, t); delete implied[t]; } } else { funct['(scope)'][t] = funct; } } function doOption() { var b, obj, filter, o = nexttoken.value, t, v; switch (o) { case '*/': error("Unbegun comment."); break; case '/*global': case '/*extern': if (option.safe) { warning("ADsafe restriction."); } obj = predefined; break; case '/*members': case '/*member': o = '/*members'; if (!membersOnly) { membersOnly = {}; } obj = membersOnly; break; case '/*jslint': if (option.safe) { warning("ADsafe restriction."); } obj = option; filter = boolOptions; } for (;;) { t = lex.token(); if (t.id === ',') { t = lex.token(); } while (t.id === '(endline)') { t = lex.token(); } if (t.type === 'special' && t.value === '*/') { break; } if (t.type !== '(string)' && t.type !== '(identifier)' && o !== '/*members') { error("Bad option.", t); } if (filter) { if (filter[t.value] !== true) { error("Bad option.", t); } v = lex.token(); if (v.id !== ':') { error("Expected '{a}' and instead saw '{b}'.", t, ':', t.value); } v = lex.token(); if (v.value === 'true') { b = true; } else if (v.value === 'false') { b = false; } else { error("Expected '{a}' and instead saw '{b}'.", t, 'true', t.value); } } else { b = true; } obj[t.value] = b; } if (filter) { assume(); } } function peek(p) { var i = p || 0, j = 0, t; while (j <= i) { t = lookahead[j]; if (!t) { t = lookahead[j] = lex.token(); } j += 1; } return t; } function advance(id, t) { var l; switch (token.id) { case '(number)': if (nexttoken.id === '.') { warning("A dot following a number can be confused with a decimal point.", token); } break; case '-': if (nexttoken.id === '-' || nexttoken.id === '--') { warning("Confusing minusses."); } break; case '+': if (nexttoken.id === '+' || nexttoken.id === '++') { warning("Confusing plusses."); } break; } if (token.type === '(string)' || token.identifier) { anonname = token.value; } if (id && nexttoken.id !== id) { if (t) { if (nexttoken.id === '(end)') { warning("Unmatched '{a}'.", t, t.id); } else { warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", nexttoken, id, t.id, t.line + 1, nexttoken.value); } } else if (nexttoken.type !== '(identifier)' || nexttoken.value !== id) { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, id, nexttoken.value); } } prevtoken = token; token = nexttoken; for (;;) { nexttoken = lookahead.shift() || lex.token(); if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { return; } if (nexttoken.type === 'special') { doOption(); } else { if (nexttoken.id !== '(endline)') { break; } l = !xmode && !option.laxbreak && (token.type === '(string)' || token.type === '(number)' || token.type === '(identifier)' || badbreak[token.id]); } } if (l) { switch (nexttoken.id) { case '{': case '}': case ']': case '.': break; case ')': switch (token.id) { case ')': case '}': case ']': break; default: warning("Line breaking error '{a}'.", token, ')'); } break; default: warning("Line breaking error '{a}'.", token, token.value); } } } function parse(rbp, initial) { var left, o; if (nexttoken.id === '(end)') { error("Unexpected early end of program.", token); } advance(); if (option.safe && predefined[token.value] === true && (nexttoken.id !== '(' && nexttoken.id !== '.')) { warning('ADsafe violation.', token); } if (initial) { anonname = 'anonymous'; funct['(verb)'] = token.value; } if (initial === true && token.fud) { left = token.fud(); } else { if (token.nud) { o = token.exps; left = token.nud(); } else { if (nexttoken.type === '(number)' && token.id === '.') { warning("A leading decimal point can be confused with a dot: '.{a}'.", token, nexttoken.value); advance(); return token; } else { error("Expected an identifier and instead saw '{a}'.", token, token.id); } } while (rbp < nexttoken.lbp) { o = nexttoken.exps; advance(); if (token.led) { left = token.led(left); } else { error("Expected an operator and instead saw '{a}'.", token, token.id); } } if (initial && !o) { warning("Expected an assignment or function call and instead saw an expression.", token); } } if (!option.evil && left && left.value === 'eval') { warning("eval is evil.", left); } return left; } function adjacent(left, right) { left = left || token; right = right || nexttoken; if (option.white || xmode === 'styleproperty' || xmode === 'style') { if (left.character !== right.from && left.line === right.line) { warning("Unexpected space after '{a}'.", nexttoken, left.value); } } } function nospace(left, right) { left = left || token; right = right || nexttoken; if (option.white && !left.comment) { if (left.line === right.line) { adjacent(left, right); } } } function nonadjacent(left, right) { left = left || token; right = right || nexttoken; if (option.white) { if (left.character === right.from) { warning("Missing space after '{a}'.", nexttoken, left.value); } } } function indentation(bias) { var i; if (option.white && nexttoken.id !== '(end)') { i = indent + (bias || 0); if (nexttoken.from !== i) { warning("Expected '{a}' to have an indentation of {b} instead of {c}.", nexttoken, nexttoken.value, i, nexttoken.from); } } } function nolinebreak(t) { if (t.line !== nexttoken.line) { warning("Line breaking error '{a}'.", t, t.id); } } function symbol(s, p) { var x = syntax[s]; if (!x || typeof x !== 'object') { syntax[s] = x = { id: s, lbp: p, value: s }; } return x; } function delim(s) { return symbol(s, 0); } function stmt(s, f) { var x = delim(s); x.identifier = x.reserved = true; x.fud = f; return x; } function blockstmt(s, f) { var x = stmt(s, f); x.block = true; return x; } function reserveName(x) { var c = x.id.charAt(0); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { x.identifier = x.reserved = true; } return x; } function prefix(s, f) { var x = symbol(s, 150); reserveName(x); x.nud = (typeof f === 'function') ? f: function() { if (option.plusplus && (this.id === '++' || this.id === '--')) { warning("Unexpected use of '{a}'.", this, this.id); } this.right = parse(150); this.arity = 'unary'; return this; }; return x; } function type(s, f) { var x = delim(s); x.type = s; x.nud = f; return x; } function reserve(s, f) { var x = type(s, f); x.identifier = x.reserved = true; return x; } function reservevar(s) { return reserve(s, function() { if (this.id === 'this') { if (option.safe) { warning("ADsafe violation.", this); } } return this; }); } function infix(s, f, p) { var x = symbol(s, p); reserveName(x); x.led = (typeof f === 'function') ? f: function(left) { nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); this.left = left; this.right = parse(p); return this; }; return x; } function relation(s, f) { var x = symbol(s, 100); x.led = function(left) { nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); var right = parse(100); if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) { warning("Use the isNaN function to compare with NaN.", this); } else if (f) { f.apply(this, [left, right]); } this.left = left; this.right = right; return this; }; return x; } function isPoorRelation(node) { var n = +node.value; return (node.type === '(number)' && !n) || (node.type === '(string)' && !node.value) || node.type === 'true' || node.type === 'false' || node.type === 'undefined' || node.type === 'null'; } function assignop(s, f) { symbol(s, 20).exps = true; return infix(s, function(left) { var l; this.left = left; nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); if (option.safe) { l = left; do { if (predefined[l.value] === true) { warning('ADsafe violation.', l); } l = l.left; } while ( l ); } if (left) { if (left.id === '.' || left.id === '[') { if (left.left.value === 'arguments') { warning('Bad assignment.', this); } this.right = parse(19); return this; } else if (left.identifier && !left.reserved) { this.right = parse(19); return this; } if (left === syntax['function']) { warning("Expected an identifier in an assignment and instead saw a function invocation.", token); } } error("Bad assignment.", this); }, 20); } function bitwise(s, f, p) { var x = symbol(s, p); reserveName(x); x.led = (typeof f === 'function') ? f: function(left) { if (option.bitwise) { warning("Unexpected use of '{a}'.", this, this.id); } nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); this.left = left; this.right = parse(p); return this; }; return x; } function bitwiseassignop(s) { symbol(s, 20).exps = true; return infix(s, function(left) { if (option.bitwise) { warning("Unexpected use of '{a}'.", this, this.id); } nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); if (left) { if (left.id === '.' || left.id === '[' || (left.identifier && !left.reserved)) { parse(19); return left; } if (left === syntax['function']) { warning("Expected an identifier in an assignment, and instead saw a function invocation.", token); } } error("Bad assignment.", this); }, 20); } function suffix(s, f) { var x = symbol(s, 150); x.led = function(left) { if (option.plusplus) { warning("Unexpected use of '{a}'.", this, this.id); } this.left = left; return this; }; return x; } function optionalidentifier() { if (nexttoken.reserved) { warning("Expected an identifier and instead saw '{a}' (a reserved word).", nexttoken, nexttoken.id); } if (nexttoken.identifier) { advance(); return token.value; } } function identifier() { var i = optionalidentifier(); if (i) { return i; } if (token.id === 'function' && nexttoken.id === '(') { warning("Missing name in function statement."); } else { error("Expected an identifier and instead saw '{a}'.", nexttoken, nexttoken.value); } } function reachable(s) { var i = 0, t; if (nexttoken.id !== ';' || noreach) { return; } for (;;) { t = peek(i); if (t.reach) { return; } if (t.id !== '(endline)') { if (t.id === 'function') { warning("Inner functions should be listed at the top of the outer function.", t); break; } warning("Unreachable '{a}' after '{b}'.", t, t.value, s); break; } i += 1; } } function statement(noindent) { var i = indent, r, s = scope, t = nexttoken; if (t.id === ';') { warning("Unnecessary semicolon.", t); advance(';'); return; } if (t.identifier && !t.reserved && peek().id === ':') { advance(); advance(':'); scope = Object.create(s); addlabel(t.value, 'label'); if (!nexttoken.labelled) { warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); } if (jx.test(t.value + ':')) { warning("Label '{a}' looks like a javascript url.", t, t.value); } nexttoken.label = t.value; t = nexttoken; } if (!noindent) { indentation(); } r = parse(0, true); if (!t.block) { if (nexttoken.id !== ';') { warningAt("Missing semicolon.", token.line, token.from + token.value.length); } else { adjacent(token, nexttoken); advance(';'); nonadjacent(token, nexttoken); } } indent = i; scope = s; return r; } function statements(begin) { var a = []; if (begin) { if (option.strict && nexttoken.type !== '(string)') { warning('Missing "use strict" statement.', nexttoken); } if (nexttoken.type === '(string)' && nexttoken.value === 'use strict') { advance(); advance(';'); } } if (option.adsafe) { switch (begin) { case 'script': if (!adsafe_may) { if (nexttoken.value !== 'ADSAFE' || peek(0).id !== '.' || (peek(1).value !== 'id' && peek(1).value !== 'go')) { error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.', nexttoken); } } if (nexttoken.value === 'ADSAFE' && peek(0).id === '.' && peek(1).value === 'id') { if (adsafe_may) { error('ADsafe violation.', nexttoken); } advance('ADSAFE'); advance('.'); advance('id'); advance('('); if (nexttoken.value !== adsafe_id) { error('ADsafe violation: id does not match.', nexttoken); } advance('(string)'); advance(')'); advance(';'); adsafe_may = true; } break; case 'lib': if (nexttoken.value === 'ADSAFE') { advance('ADSAFE'); advance('.'); advance('lib'); advance('('); advance('(string)'); advance(','); parse(0); advance(')'); advance(';'); return a; } else { error("ADsafe lib violation."); } } } while (!nexttoken.reach && nexttoken.id !== '(end)') { if (nexttoken.id === ';') { warning("Unnecessary semicolon."); advance(';'); } else { a.push(statement()); } } return a; } function block(f) { var a, b = inblock, s = scope, t; inblock = f; if (f) { scope = Object.create(scope); } nonadjacent(token, nexttoken); t = nexttoken; if (nexttoken.id === '{') { advance('{'); if (nexttoken.id !== '}' || token.line !== nexttoken.line) { indent += option.indent; if (!f && nexttoken.from === indent + option.indent) { indent += option.indent; } a = statements(); indent -= option.indent; indentation(); } advance('}', t); } else { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, '{', nexttoken.value); noreach = true; a = [statement()]; noreach = false; } funct['(verb)'] = null; scope = s; inblock = b; return a; } function idValue() { return this; } function countMember(m) { if (membersOnly && membersOnly[m] !== true) { warning("Unexpected /*member '{a}'.", nexttoken, m); } if (typeof member[m] === 'number') { member[m] += 1; } else { member[m] = 1; } } function note_implied(token) { var name = token.value, line = token.line + 1, a = implied[name]; if (!a) { a = [line]; implied[name] = a; } else if (a[a.length - 1] !== line) { a.push(line); } } function cssName() { if (nexttoken.identifier) { advance(); return true; } } function cssNumber() { if (nexttoken.id === '-') { advance('-'); advance('(number)'); } if (nexttoken.type === '(number)') { advance(); return true; } } function cssString() { if (nexttoken.type === '(string)') { advance(); return true; } } function cssColor() { var i, number; if (nexttoken.identifier) { if (nexttoken.value === 'rgb') { advance(); advance('('); for (i = 0; i < 3; i += 1) { number = nexttoken.value; if (nexttoken.type !== '(number)' || number < 0) { warning("Expected a positive number and instead saw '{a}'", nexttoken, number); advance(); } else { advance(); if (nexttoken.id === '%') { advance('%'); if (number > 100) { warning("Expected a percentage and instead saw '{a}'", token, number); } } else { if (number > 255) { warning("Expected a small number and instead saw '{a}'", token, number); } } } } advance(')'); return true; } else if (cssColorData[nexttoken.value] === true) { advance(); return true; } } else if (nexttoken.type === '(color)') { advance(); return true; } return false; } function cssLength() { if (nexttoken.id === '-') { advance('-'); adjacent(); } if (nexttoken.type === '(number)') { advance(); if (nexttoken.type !== '(string)' && cssLengthData[nexttoken.value] === true) { adjacent(); advance(); } else if ( + token.value !== 0) { warning("Expected a linear unit and instead saw '{a}'.", nexttoken, nexttoken.value); } return true; } return false; } function cssLineHeight() { if (nexttoken.id === '-') { advance('-'); adjacent(); } if (nexttoken.type === '(number)') { advance(); if (nexttoken.type !== '(string)' && cssLengthData[nexttoken.value] === true) { adjacent(); advance(); } return true; } return false; } function cssWidth() { if (nexttoken.identifier) { switch (nexttoken.value) { case 'thin': case 'medium': case 'thick': advance(); return true; } } else { return cssLength(); } } function cssMargin() { if (nexttoken.identifier) { if (nexttoken.value === 'auto') { advance(); return true; } } else { return cssLength(); } } function cssAttr() { if (nexttoken.identifier && nexttoken.value === 'attr') { advance(); advance('('); if (!nexttoken.identifier) { warning("Expected a name and instead saw '{a}'.", nexttoken, nexttoken.value); } advance(); advance(')'); return true; } return false; } function cssCommaList() { while (nexttoken.id !== ';') { if (!cssName() && !cssString()) { warning("Expected a name and instead saw '{a}'.", nexttoken, nexttoken.value); } if (nexttoken.id !== ',') { return true; } advance(','); } } function cssCounter() { if (nexttoken.identifier && nexttoken.value === 'counter') { advance(); advance('('); if (!nexttoken.identifier) {} advance(); if (nexttoken.id === ',') { advance(','); if (nexttoken.type !== '(string)') { warning("Expected a string and instead saw '{a}'.", nexttoken, nexttoken.value); } advance(); } advance(')'); return true; } if (nexttoken.identifier && nexttoken.value === 'counters') { advance(); advance('('); if (!nexttoken.identifier) { warning("Expected a name and instead saw '{a}'.", nexttoken, nexttoken.value); } advance(); if (nexttoken.id === ',') { advance(','); if (nexttoken.type !== '(string)') { warning("Expected a string and instead saw '{a}'.", nexttoken, nexttoken.value); } advance(); } if (nexttoken.id === ',') { advance(','); if (nexttoken.type !== '(string)') { warning("Expected a string and instead saw '{a}'.", nexttoken, nexttoken.value); } advance(); } advance(')'); return true; } return false; } function cssShape() { var i; if (nexttoken.identifier && nexttoken.value === 'rect') { advance(); advance('('); for (i = 0; i < 4; i += 1) { if (!cssLength()) { warning("Expected a number and instead saw '{a}'.", nexttoken, nexttoken.value); break; } } advance(')'); return true; } return false; } function cssUrl() { var url; if (nexttoken.identifier && nexttoken.value === 'url') { nexttoken = lex.range('(', ')'); url = nexttoken.value; advance(); if (option.safe && ux.test(url)) { error("ADsafe URL violation."); } urls.push(url); return true; } return false; } cssAny = [cssUrl, function() { for (;;) { if (nexttoken.identifier) { switch (nexttoken.value.toLowerCase()) { case 'url': cssUrl(); break; case 'expression': warning("Unexpected expression '{a}'.", nexttoken, nexttoken.value); advance(); break; default: advance(); } } else { if (nexttoken.id === ';' || nexttoken.id === '!' || nexttoken.id === '(end)' || nexttoken.id === '}') { return true; } advance(); } } }]; cssBorderStyle = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'ridge', 'inset', 'outset']; cssAttributeData = { background: [true, 'background-attachment', 'background-color', 'background-image', 'background-position', 'background-repeat'], 'background-attachment': ['scroll', 'fixed'], 'background-color': ['transparent', cssColor], 'background-image': ['none', cssUrl], 'background-position': [2, [cssLength, 'top', 'bottom', 'left', 'right', 'center']], 'background-repeat': ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'], 'border': [true, 'border-color', 'border-style', 'border-width'], 'border-bottom': [true, 'border-bottom-color', 'border-bottom-style', 'border-bottom-width'], 'border-bottom-color': cssColor, 'border-bottom-style': cssBorderStyle, 'border-bottom-width': cssWidth, 'border-collapse': ['collapse', 'separate'], 'border-color': ['transparent', 4, cssColor], 'border-left': [true, 'border-left-color', 'border-left-style', 'border-left-width'], 'border-left-color': cssColor, 'border-left-style': cssBorderStyle, 'border-left-width': cssWidth, 'border-right': [true, 'border-right-color', 'border-right-style', 'border-right-width'], 'border-right-color': cssColor, 'border-right-style': cssBorderStyle, 'border-right-width': cssWidth, 'border-spacing': [2, cssLength], 'border-style': [4, cssBorderStyle], 'border-top': [true, 'border-top-color', 'border-top-style', 'border-top-width'], 'border-top-color': cssColor, 'border-top-style': cssBorderStyle, 'border-top-width': cssWidth, 'border-width': [4, cssWidth], bottom: [cssLength, 'auto'], 'caption-side': ['bottom', 'left', 'right', 'top'], clear: ['both', 'left', 'none', 'right'], clip: [cssShape, 'auto'], color: cssColor, content: ['open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', cssString, cssUrl, cssCounter, cssAttr], 'counter-increment': [cssName, 'none'], 'counter-reset': [cssName, 'none'], cursor: [cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move', 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize', 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait'], direction: ['ltr', 'rtl'], display: ['block', 'compact', 'inline', 'inline-block', 'inline-table', 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row', 'table-row-group'], 'empty-cells': ['show', 'hide'], 'float': ['left', 'none', 'right'], font: ['caption', 'icon', 'menu', 'message-box', 'small-caption', 'status-bar', true, 'font-size', 'font-style', 'font-weight', 'font-family'], 'font-family': cssCommaList, 'font-size': ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'larger', 'smaller', cssLength], 'font-size-adjust': ['none', cssNumber], 'font-stretch': ['normal', 'wider', 'narrower', 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'semi-expanded', 'expanded', 'extra-expanded'], 'font-style': ['normal', 'italic', 'oblique'], 'font-variant': ['normal', 'small-caps'], 'font-weight': ['normal', 'bold', 'bolder', 'lighter', cssNumber], height: [cssLength, 'auto'], left: [cssLength, 'auto'], 'letter-spacing': ['normal', cssLength], 'line-height': ['normal', cssLineHeight], 'list-style': [true, 'list-style-image', 'list-style-position', 'list-style-type'], 'list-style-image': ['none', cssUrl], 'list-style-position': ['inside', 'outside'], 'list-style-type': ['circle', 'disc', 'square', 'decimal', 'decimal-leading-zero', 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana', 'hiragana-iroha', 'katakana-oroha', 'none'], margin: [4, cssMargin], 'margin-bottom': cssMargin, 'margin-left': cssMargin, 'margin-right': cssMargin, 'margin-top': cssMargin, 'marker-offset': [cssLength, 'auto'], 'max-height': [cssLength, 'none'], 'max-width': [cssLength, 'none'], 'min-height': cssLength, 'min-width': cssLength, opacity: cssNumber, outline: [true, 'outline-color', 'outline-style', 'outline-width'], 'outline-color': ['invert', cssColor], 'outline-style': ['dashed', 'dotted', 'double', 'groove', 'inset', 'none', 'outset', 'ridge', 'solid'], 'outline-width': cssWidth, overflow: ['auto', 'hidden', 'scroll', 'visible'], padding: [4, cssLength], 'padding-bottom': cssLength, 'padding-left': cssLength, 'padding-right': cssLength, 'padding-top': cssLength, position: ['absolute', 'fixed', 'relative', 'static'], quotes: [8, cssString], right: [cssLength, 'auto'], 'table-layout': ['auto', 'fixed'], 'text-align': ['center', 'justify', 'left', 'right'], 'text-decoration': ['none', 'underline', 'overline', 'line-through', 'blink'], 'text-indent': cssLength, 'text-shadow': ['none', 4, [cssColor, cssLength]], 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], top: [cssLength, 'auto'], 'unicode-bidi': ['normal', 'embed', 'bidi-override'], 'vertical-align': ['baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', 'text-bottom', cssLength], visibility: ['visible', 'hidden', 'collapse'], 'white-space': ['normal', 'pre', 'nowrap'], width: [cssLength, 'auto'], 'word-spacing': ['normal', cssLength], 'z-index': ['auto', cssNumber] }; function styleAttribute() { var v; while (nexttoken.id === '*' || nexttoken.id === '#' || nexttoken.value === '_') { if (!option.css) { warning("Unexpected '{a}'.", nexttoken, nexttoken.value); } advance(); } if (nexttoken.id === '-') { if (!option.css) { warning("Unexpected '{a}'.", nexttoken, nexttoken.value); } advance('-'); if (!nexttoken.identifier) { warning("Expected a non-standard style attribute and instead saw '{a}'.", nexttoken, nexttoken.value); } advance(); return cssAny; } else { if (!nexttoken.identifier) { warning("Excepted a style attribute, and instead saw '{a}'.", nexttoken, nexttoken.value); } else { if (cssAttributeData.hasOwnProperty(nexttoken.value)) { v = cssAttributeData[nexttoken.value]; } else { v = cssAny; if (!option.css) { warning("Unrecognized style attribute '{a}'.", nexttoken, nexttoken.value); } } } advance(); return v; } } function styleValue(v) { var i = 0, n, once, match, round, start = 0, vi; switch (typeof v) { case 'function': return v(); case 'string': if (nexttoken.identifier && nexttoken.value === v) { advance(); return true; } return false; } for (;;) { if (i >= v.length) { return false; } vi = v[i]; i += 1; if (vi === true) { break; } else if (typeof vi === 'number') { n = vi; vi = v[i]; i += 1; } else { n = 1; } match = false; while (n > 0) { if (styleValue(vi)) { match = true; n -= 1; } else { break; } } if (match) { return true; } } start = i; once = []; for (;;) { round = false; for (i = start; i < v.length; i += 1) { if (!once[i]) { if (styleValue(cssAttributeData[v[i]])) { match = true; round = true; once[i] = true; break; } } } if (!round) { return match; } } } function substyle() { var v; for (;;) { if (nexttoken.id === '}' || nexttoken.id === '(end)' || xquote && nexttoken.id === xquote) { return; } while (nexttoken.id === ';') { warning("Misplaced ';'."); advance(';'); } v = styleAttribute(); advance(':'); if (nexttoken.identifier && nexttoken.value === 'inherit') { advance(); } else { styleValue(v); } while (nexttoken.id !== ';' && nexttoken.id !== '!' && nexttoken.id !== '}' && nexttoken.id !== '(end)' && nexttoken.id !== xquote) { warning("Unexpected token '{a}'.", nexttoken, nexttoken.value); advance(); } if (nexttoken.id === '!') { advance('!'); adjacent(); if (nexttoken.identifier && nexttoken.value === 'important') { advance(); } else { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, 'important', nexttoken.value); } } if (nexttoken.id === '}' || nexttoken.id === xquote) { warning("Missing '{a}'.", nexttoken, ';'); } else { advance(';'); } } } function stylePattern() { var name; if (nexttoken.id === '{') { warning("Expected a style pattern, and instead saw '{a}'.", nexttoken, nexttoken.id); } else if (nexttoken.id === '@') { advance('@'); name = nexttoken.value; if (nexttoken.identifier && atrule[name] === true) { advance(); return name; } warning("Expected an at-rule, and instead saw @{a}.", nexttoken, name); } for (;;) { if (nexttoken.identifier) { if (!htmltag.hasOwnProperty(nexttoken.value)) { warning("Expected a tagName, and instead saw {a}.", nexttoken, nexttoken.value); } advance(); } else { switch (nexttoken.id) { case '>': case '+': advance(); if (!nexttoken.identifier || !htmltag.hasOwnProperty(nexttoken.value)) { warning("Expected a tagName, and instead saw {a}.", nexttoken, nexttoken.value); } advance(); break; case ':': advance(':'); if (pseudorule[nexttoken.value] !== true) { warning("Expected a pseudo, and instead saw :{a}.", nexttoken, nexttoken.value); } advance(); if (nexttoken.value === 'lang') { advance('('); if (!nexttoken.identifier) { warning("Expected a lang code, and instead saw :{a}.", nexttoken, nexttoken.value); } advance(')'); } break; case '#': advance('#'); if (!nexttoken.identifier) { warning("Expected an id, and instead saw #{a}.", nexttoken, nexttoken.value); } advance(); break; case '*': advance('*'); break; case '.': advance('.'); if (!nexttoken.identifier) { warning("Expected a class, and instead saw #.{a}.", nexttoken, nexttoken.value); } advance(); break; case '[': advance('['); if (!nexttoken.identifier) { warning("Expected an attribute, and instead saw [{a}].", nexttoken, nexttoken.value); } advance(); if (nexttoken.id === '=' || nexttoken.id === '~=' || nexttoken.id === '|=') { advance(); if (nexttoken.type !== '(string)') { warning("Expected a string, and instead saw {a}.", nexttoken, nexttoken.value); } advance(); } advance(']'); break; default: error("Expected a CSS selector, and instead saw {a}.", nexttoken, nexttoken.value); } } if (nexttoken.id === ' fragments and .js files.", token); } if (option.fragment) { if (n !== 'div') { error("ADsafe violation: Wrap the widget in a div.", token); } } else { error("Use the fragment option.", token); } } option.browser = true; assume(); } function doAttribute(n, a, v) { var u; if (a === 'id') { u = typeof v === 'string' ? v.toUpperCase() : ''; if (ids[u] === true) { warning("Duplicate id='{a}'.", nexttoken, v); } ids[u] = true; if (option.adsafe) { if (adsafe_id) { if (v.slice(0, adsafe_id.length) !== adsafe_id) { warning("ADsafe violation: An id must have a '{a}' prefix", nexttoken, adsafe_id); } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { warning("ADSAFE violation: bad id."); } } else { adsafe_id = v; if (!/^[A-Z]+_$/.test(v)) { warning("ADSAFE violation: bad id."); } } } } else if (a === 'href' || a === 'background' || a === 'content' || a === 'data' || a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { if (option.safe && ux.test(v)) { error("ADsafe URL violation."); } urls.push(v); } else if (a === 'for') { if (option.adsafe) { if (adsafe_id) { if (v.slice(0, adsafe_id.length) !== adsafe_id) { warning("ADsafe violation: An id must have a '{a}' prefix", nexttoken, adsafe_id); } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { warning("ADSAFE violation: bad id."); } } else { warning("ADSAFE violation: bad id."); } } } else if (a === 'name') { if (option.adsafe && v.indexOf('_') >= 0) { warning("ADsafe name violation."); } } } function doTag(n, a) { var i, t = htmltag[n], x; src = false; if (!t) { error("Unrecognized tag '<{a}>'.", nexttoken, n === n.toLowerCase() ? n: n + ' (capitalization error)'); } if (stack.length > 0) { if (n === 'html') { error("Too many tags.", token); } x = t.parent; if (x) { if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { error("A '<{a}>' must be within '<{b}>'.", token, n, x); } } else if (!option.adsafe || !option.fragment) { i = stack.length; do { if (i <= 0) { error("A '<{a}>' must be within '<{b}>'.", token, n, 'body'); } i -= 1; } while ( stack [ i ].name !== 'body'); } } switch (n) { case 'div': if (option.adsafe && stack.length === 1 && !adsafe_id) { warning("ADSAFE violation: missing ID_."); } break; case 'script': xmode = 'script'; advance('>'); indent = nexttoken.from; if (a.lang) { warning("lang is deprecated.", token); } if (option.adsafe && stack.length !== 1) { warning("ADsafe script placement violation.", token); } if (a.src) { if (option.adsafe && (!adsafe_may || !approved[a.src])) { warning("ADsafe unapproved script source.", token); } if (a.type) { warning("type is unnecessary.", token); } } else { if (adsafe_went) { error("ADsafe script violation.", token); } statements('script'); } xmode = 'html'; advance(''); styles(); xmode = 'html'; advance(''; } function html() { var a, attributes, e, n, q, t, v, wmode; xmode = 'html'; xquote = ''; stack = null; for (;;) { switch (nexttoken.value) { case '<': xmode = 'html'; advance('<'); attributes = {}; t = nexttoken; if (!t.identifier) { warning("Bad identifier {a}.", t, t.value); } n = t.value; if (option.cap) { n = n.toLowerCase(); } t.name = n; advance(); if (!stack) { stack = []; doBegin(n); } v = htmltag[n]; if (typeof v !== 'object') { error("Unrecognized tag '<{a}>'.", t, n); } e = v.empty; t.type = n; for (;;) { if (nexttoken.id === '/') { advance('/'); if (nexttoken.id !== '>') { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, '>', nexttoken.value); } break; } if (nexttoken.id && nexttoken.id.substr(0, 1) === '>') { break; } if (!nexttoken.identifier) { if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { error("Missing '>'.", nexttoken); } warning("Bad identifier."); } a = nexttoken.value; advance(); if (!option.cap && a !== a.toLowerCase()) { warning("Attribute '{a}' not all lower case.", nexttoken, a); } a = a.toLowerCase(); xquote = ''; if (attributes.hasOwnProperty(a)) { warning("Attribute '{a}' repeated.", nexttoken, a); } if (a.slice(0, 2) === 'on') { if (!option.on) { warning("Avoid HTML event handlers."); } xmode = 'scriptstring'; advance('='); q = nexttoken.id; if (q !== '"' && q !== "'") { error("Missing quote."); } xquote = q; wmode = option.white; option.white = false; advance(q); statements('on'); option.white = wmode; if (nexttoken.id !== q) { error("Missing close quote on script attribute."); } xmode = 'html'; xquote = ''; advance(q); v = false; } else if (a === 'style') { xmode = 'scriptstring'; advance('='); q = nexttoken.id; if (q !== '"' && q !== "'") { error("Missing quote."); } xmode = 'styleproperty'; xquote = q; advance(q); substyle(); xmode = 'html'; xquote = ''; advance(q); v = false; } else { if (nexttoken.id === '=') { advance('='); v = nexttoken.value; if (!nexttoken.identifier && nexttoken.id !== '"' && nexttoken.id !== '\'' && nexttoken.type !== '(string)' && nexttoken.type !== '(number)' && nexttoken.type !== '(color)') { warning("Expected an attribute value and instead saw '{a}'.", token, a); } advance(); } else { v = true; } } attributes[a] = v; doAttribute(n, a, v); } doTag(n, attributes); if (!e) { stack.push(t); } xmode = 'outer'; advance('>'); break; case '') { error("Missing '{a}'.", nexttoken, '>'); } xmode = 'outer'; advance('>'); break; case '') { break; } if (nexttoken.id === '<' || nexttoken.id === '(end)') { error("Missing '{a}'.", token, '>'); } if (nexttoken.id === '--') { v = !v; } } if (v) { warning("Misshapen HTML comment."); } xmode = 'html'; advance('>'); break; case '(end)': return; default: if (nexttoken.id === '(end)') { error("Missing '{a}'.", nexttoken, ''); } else if (nexttoken.id !== '--' && nexttoken.id !== '#') { error("Unexpected '{a}'.", nexttoken, nexttoken.value); } else { advance(); } } if (stack && stack.length === 0) { break; } } if (nexttoken.id !== '(end)') { error("Unexpected material after the end."); } } type('(number)', idValue); type('(string)', idValue); syntax['(identifier)'] = { type: '(identifier)', lbp: 0, identifier: true, nud: function() { var v = this.value, s = scope[v]; if (s && (s === funct || s === funct['(global)'])) { if (!funct['(global)']) { switch (funct[v]) { case 'unused': funct[v] = 'var'; break; case 'label': warning("'{a}' is a statement label.", token, v); break; } } } else if (funct['(global)']) { if (option.undef) { warning("'{a}' is undefined.", token, v); } note_implied(token); } else { switch (funct[v]) { case 'closure': case 'function': case 'var': case 'unused': warning("'{a}' used out of scope.", token, v); break; case 'label': warning("'{a}' is a statement label.", token, v); break; case 'outer': case true: break; default: if (s === true) { funct[v] = true; } else if (typeof s !== 'object') { if (option.undef) { warning("'{a}' is undefined.", token, v); } else { funct[v] = true; } note_implied(token); } else { switch (s[v]) { case 'function': case 'var': case 'unused': s[v] = 'closure'; funct[v] = 'outer'; break; case 'closure': case 'parameter': funct[v] = 'outer'; break; case 'label': warning("'{a}' is a statement label.", token, v); } } } } return this; }, led: function() { error("Expected an operator and instead saw '{a}'.", nexttoken, nexttoken.value); } }; type('(regexp)', function() { return this; }); delim('(endline)'); delim('(begin)'); delim('(end)').reach = true; delim('>=', 'assignshiftright', 20); bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20); infix('?', function(left) { parse(10); advance(':'); parse(10); }, 30); infix('||', 'or', 40); infix('&&', 'and', 50); bitwise('|', 'bitor', 70); bitwise('^', 'bitxor', 80); bitwise('&', 'bitand', 90); relation('==', function(left, right) { if (option.eqeqeq) { warning("Expected '{a}' and instead saw '{b}'.", this, '===', '=='); } else if (isPoorRelation(left)) { warning("Use '{a}' to compare with '{b}'.", this, '===', left.value); } else if (isPoorRelation(right)) { warning("Use '{a}' to compare with '{b}'.", this, '===', right.value); } return this; }); relation('==='); relation('!=', function(left, right) { if (option.eqeqeq) { warning("Expected '{a}' and instead saw '{b}'.", this, '!==', '!='); } else if (isPoorRelation(left)) { warning("Use '{a}' to compare with '{b}'.", this, '!==', left.value); } else if (isPoorRelation(right)) { warning("Use '{a}' to compare with '{b}'.", this, '!==', right.value); } return this; }); relation('!=='); relation('<'); relation('>'); relation('<='); relation('>='); bitwise('<<', 'shiftleft', 120); bitwise('>>', 'shiftright', 120); bitwise('>>>', 'shiftrightunsigned', 120); infix('in', 'in', 120); infix('instanceof', 'instanceof', 120); infix('+', function(left) { nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); var right = parse(130); if (left && right && left.id === '(string)' && right.id === '(string)') { left.value += right.value; left.character = right.character; if (jx.test(left.value)) { warning("JavaScript URL.", left); } return left; } this.left = left; this.right = right; return this; }, 130); prefix('+', 'num'); infix('-', 'sub', 130); prefix('-', 'neg'); infix('*', 'mult', 140); infix('/', 'div', 140); infix('%', 'mod', 140); suffix('++', 'postinc'); prefix('++', 'preinc'); syntax['++'].exps = true; suffix('--', 'postdec'); prefix('--', 'predec'); syntax['--'].exps = true; prefix('delete', function() { var p = parse(0); if (p.id !== '.' && p.id !== '[') { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, '.', nexttoken.value); } }).exps = true; prefix('~', function() { if (option.bitwise) { warning("Unexpected '{a}'.", this, '~'); } parse(150); return this; }); prefix('!', 'not'); prefix('typeof', 'typeof'); prefix('new', function() { var c = parse(155), i; if (c && c.id !== 'function') { if (c.identifier) { c['new'] = true; switch (c.value) { case 'Object': warning("Use the object literal notation {}.", token); break; case 'Array': warning("Use the array literal notation [].", token); break; case 'Number': case 'String': case 'Boolean': case 'Math': warning("Do not use the {a} function as a constructor.", token, c.value); break; case 'Function': if (!option.evil) { warning("The Function constructor is eval."); } break; case 'Date': case 'RegExp': break; default: if (c.id !== 'function') { i = c.value.substr(0, 1); if (i < 'A' || i > 'Z') { warning("A constructor name should start with an uppercase letter.", token); } } } } else { if (c.id !== '.' && c.id !== '[' && c.id !== '(') { warning("Bad constructor.", token); } } } else { warning("Weird construction. Delete 'new'.", this); } adjacent(token, nexttoken); if (nexttoken.id !== '(') { warning("Missing '()' invoking a constructor."); } this.first = c; return this; }); syntax['new'].exps = true; infix('.', function(left) { adjacent(prevtoken, token); var t = this, m = identifier(); if (typeof m === 'string') { countMember(m); } t.left = left; t.right = m; if (!option.evil && left && left.value === 'document' && (m === 'write' || m === 'writeln')) { warning("document.write can be a form of eval.", left); } if (option.adsafe) { if (left && left.value === 'ADSAFE') { if (m === 'id' || m === 'lib') { warning("ADsafe violation.", this); } else if (m === 'go') { if (xmode !== 'script') { warning("ADsafe violation.", this); } else if (adsafe_went || nexttoken.id !== '(' || peek(0).id !== '(string)' || peek(0).value !== adsafe_id || peek(1).id !== ',') { error("ADsafe violation: go.", this); } adsafe_went = true; adsafe_may = false; } } for (;;) { if (banned[m] === true) { warning("ADsafe restricted word '{a}'.", token, m); } if (predefined[left.value] !== true || nexttoken.id === '(') { break; } if (standard_member[m] === true) { if (nexttoken.id === '.') { warning("ADsafe violation.", this); } break; } if (nexttoken.id !== '.') { warning("ADsafe violation.", this); break; } advance('.'); token.left = t; token.right = m; t = token; m = identifier(); if (typeof m === 'string') { countMember(m); } } } return t; }, 160); infix('(', function(left) { adjacent(prevtoken, token); nospace(); var n = 0, p = []; if (left) { if (left.type === '(identifier)') { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { if (left.value !== 'Number' && left.value !== 'String' && left.value !== 'Boolean' && left.value !== 'Date') { if (left.value === 'Math') { warning("Math is not a function.", left); } else { warning("Missing 'new' prefix when invoking a constructor.", left); } } } } else if (left.id === '.') { if (option.safe && left.left.value === 'Math' && left.right === 'random') { warning("ADsafe violation.", left); } } } if (nexttoken.id !== ')') { for (;;) { p[p.length] = parse(10); n += 1; if (nexttoken.id !== ',') { break; } advance(','); nonadjacent(token, nexttoken); } } advance(')'); nospace(prevtoken, token); if (typeof left === 'object') { if (left.value === 'parseInt' && n === 1) { warning("Missing radix parameter.", left); } if (!option.evil) { if (left.value === 'eval' || left.value === 'Function' || left.value === 'execScript') { warning("eval is evil.", left); } else if (p[0] && p[0].id === '(string)' && (left.value === 'setTimeout' || left.value === 'setInterval')) { warning("Implied eval is evil. Pass a function instead of a string.", left); } } if (!left.identifier && left.id !== '.' && left.id !== '[' && left.id !== '(' && left.id !== '&&' && left.id !== '||' && left.id !== '?') { warning("Bad invocation.", left); } } this.left = left; return this; }, 155).exps = true; prefix('(', function() { nospace(); var t = nexttoken, v = parse(0); advance(')', this); nospace(prevtoken, token); return v; }); infix('[', function(left) { nospace(); var e = parse(0), s; if (e && e.type === '(string)') { if (option.safe && banned[e.value] === true) { warning("ADsafe restricted word '{a}'.", this, e.value); } countMember(e.value); if (!option.sub && ix.test(e.value)) { s = syntax[e.value]; if (!s || !s.reserved) { warning("['{a}'] is better written in dot notation.", e, e.value); } } } else if (!e || (e.type !== '(number)' && (e.id !== '+' || e.arity !== 'unary'))) { if (option.safe) { warning('ADsafe subscripting.'); } } advance(']', this); nospace(prevtoken, token); this.left = left; this.right = e; return this; }, 160); prefix('[', function() { if (nexttoken.id === ']') { advance(']'); return; } var b = token.line !== nexttoken.line; if (b) { indent += option.indent; if (nexttoken.from === indent + option.indent) { indent += option.indent; } } for (;;) { if (b && token.line !== nexttoken.line) { indentation(); } parse(10); if (nexttoken.id === ',') { adjacent(token, nexttoken); advance(','); if (nexttoken.id === ',') { warning("Extra comma.", token); } else if (nexttoken.id === ']') { warning("Extra comma.", token); break; } nonadjacent(token, nexttoken); } else { if (b) { indent -= option.indent; indentation(); } break; } } advance(']', this); return; }, 160); (function(x) { x.nud = function() { var b, i, s; if (nexttoken.id === '}') { advance('}'); return; } b = token.line !== nexttoken.line; if (b) { indent += option.indent; if (nexttoken.from === indent + option.indent) { indent += option.indent; } } for (;;) { if (b) { indentation(); } i = optionalidentifier(true); if (!i) { if (nexttoken.id === '(string)') { i = nexttoken.value; if (ix.test(i)) { s = syntax[i]; } advance(); } else if (nexttoken.id === '(number)') { i = nexttoken.value.toString(); advance(); } else { error("Expected '{a}' and instead saw '{b}'.", nexttoken, '}', nexttoken.value); } } countMember(i); advance(':'); nonadjacent(token, nexttoken); parse(10); if (nexttoken.id === ',') { adjacent(token, nexttoken); advance(','); if (nexttoken.id === ',' || nexttoken.id === '}') { warning("Extra comma.", token); } nonadjacent(token, nexttoken); } else { if (b) { indent -= option.indent; indentation(); } advance('}', this); return; } } }; x.fud = function() { error("Expected to see a statement and instead saw a block.", token); }; })(delim('{')); function varstatement(prefix) { if (funct['(onevar)'] && option.onevar) { warning("Too many var statements."); } else if (!funct['(global)']) { funct['(onevar)'] = true; } for (;;) { nonadjacent(token, nexttoken); addlabel(identifier(), 'unused'); if (prefix) { return; } if (nexttoken.id === '=') { nonadjacent(token, nexttoken); advance('='); nonadjacent(token, nexttoken); if (peek(0).id === '=') { error("Variable {a} was not declared correctly.", nexttoken, nexttoken.value); } parse(20); } if (nexttoken.id !== ',') { return; } adjacent(token, nexttoken); advance(','); nonadjacent(token, nexttoken); } } stmt('var', varstatement); stmt('new', function() { error("'new' should not be used as a statement."); }); function functionparams() { var i, t = nexttoken, p = []; advance('('); nospace(); if (nexttoken.id === ')') { advance(')'); nospace(prevtoken, token); return; } for (;;) { i = identifier(); p.push(i); addlabel(i, 'parameter'); if (nexttoken.id === ',') { advance(','); nonadjacent(token, nexttoken); } else { advance(')', t); nospace(prevtoken, token); return p.join(', '); } } } function doFunction(i) { var s = scope; scope = Object.create(s); funct = { '(name)': i || '"' + anonname + '"', '(line)': nexttoken.line + 1, '(context)': funct, '(breakage)': 0, '(loopage)': 0, '(scope)': scope }; functions.push(funct); if (i) { addlabel(i, 'function'); } funct['(params)'] = functionparams(); block(false); scope = s; funct = funct['(context)']; } blockstmt('function', function() { if (inblock) { warning("Function statements cannot be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token); } var i = identifier(); adjacent(token, nexttoken); addlabel(i, 'unused'); doFunction(i); if (nexttoken.id === '(' && nexttoken.line === token.line) { error("Function statements are not invocable. Wrap the function expression in parens."); } }); prefix('function', function() { var i = optionalidentifier(); if (i) { adjacent(token, nexttoken); } else { nonadjacent(token, nexttoken); } doFunction(i); if (funct['(loopage)'] && nexttoken.id !== '(') { warning("Be careful when making functions within a loop. Consider putting the function in a closure."); } return this; }); blockstmt('if', function() { var t = nexttoken; advance('('); nonadjacent(this, t); nospace(); parse(20); if (nexttoken.id === '=') { warning("Expected a conditional expression and instead saw an assignment."); advance('='); parse(20); } advance(')', t); nospace(prevtoken, token); block(true); if (nexttoken.id === 'else') { nonadjacent(token, nexttoken); advance('else'); if (nexttoken.id === 'if' || nexttoken.id === 'switch') { statement(true); } else { block(true); } } return this; }); blockstmt('try', function() { var b, e, s; if (option.adsafe) { warning("ADsafe try violation.", this); } block(false); if (nexttoken.id === 'catch') { advance('catch'); nonadjacent(token, nexttoken); advance('('); s = scope; scope = Object.create(s); e = nexttoken.value; if (nexttoken.type !== '(identifier)') { warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); } else { addlabel(e, 'unused'); } advance(); advance(')'); block(false); b = true; scope = s; } if (nexttoken.id === 'finally') { advance('finally'); block(false); return; } else if (!b) { error("Expected '{a}' and instead saw '{b}'.", nexttoken, 'catch', nexttoken.value); } }); blockstmt('while', function() { var t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); nonadjacent(this, t); nospace(); parse(20); if (nexttoken.id === '=') { warning("Expected a conditional expression and instead saw an assignment."); advance('='); parse(20); } advance(')', t); nospace(prevtoken, token); block(true); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; }).labelled = true; reserve('with'); blockstmt('switch', function() { var t = nexttoken, g = false; funct['(breakage)'] += 1; advance('('); nonadjacent(this, t); nospace(); this.condition = parse(20); advance(')', t); nospace(prevtoken, token); nonadjacent(token, nexttoken); t = nexttoken; advance('{'); nonadjacent(token, nexttoken); indent += option.indent; this.cases = []; for (;;) { switch (nexttoken.id) { case 'case': switch (funct['(verb)']) { case 'break': case 'case': case 'continue': case 'return': case 'switch': case 'throw': break; default: warning("Expected a 'break' statement before 'case'.", token); } indentation( - option.indent); advance('case'); this.cases.push(parse(20)); g = true; advance(':'); funct['(verb)'] = 'case'; break; case 'default': switch (funct['(verb)']) { case 'break': case 'continue': case 'return': case 'throw': break; default: warning("Expected a 'break' statement before 'default'.", token); } indentation( - option.indent); advance('default'); g = true; advance(':'); break; case '}': indent -= option.indent; indentation(); advance('}', t); if (this.cases.length === 1 || this.condition.id === 'true' || this.condition.id === 'false') { warning("This 'switch' should be an 'if'.", this); } funct['(breakage)'] -= 1; funct['(verb)'] = undefined; return; case '(end)': error("Missing '{a}'.", nexttoken, '}'); return; default: if (g) { switch (token.id) { case ',': error("Each value should have its own case label."); return; case ':': statements(); break; default: error("Missing ':' on a case clause.", token); } } else { error("Expected '{a}' and instead saw '{b}'.", nexttoken, 'case', nexttoken.value); } } } }).labelled = true; stmt('debugger', function() { if (!option.debug) { warning("All 'debugger' statements should be removed."); } }); stmt('do', function() { funct['(breakage)'] += 1; funct['(loopage)'] += 1; block(true); advance('while'); var t = nexttoken; nonadjacent(token, t); advance('('); nospace(); parse(20); if (nexttoken.id === '=') { warning("Expected a conditional expression and instead saw an assignment."); advance('='); parse(20); } advance(')', t); nospace(prevtoken, token); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; }).labelled = true; blockstmt('for', function() { var s, t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); nonadjacent(this, t); nospace(); if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') { if (nexttoken.id === 'var') { advance('var'); varstatement(true); } else { advance(); } advance('in'); parse(20); advance(')', t); s = block(true); if (!option.forin && (s.length > 1 || typeof s[0] !== 'object' || s[0].value !== 'if')) { warning("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", this); } funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; } else { if (nexttoken.id !== ';') { if (nexttoken.id === 'var') { advance('var'); varstatement(); } else { for (;;) { parse(0, 'for'); if (nexttoken.id !== ',') { break; } advance(','); } } } advance(';'); if (nexttoken.id !== ';') { parse(20); if (nexttoken.id === '=') { warning("Expected a conditional expression and instead saw an assignment."); advance('='); parse(20); } } advance(';'); if (nexttoken.id === ';') { error("Expected '{a}' and instead saw '{b}'.", nexttoken, ')', ';'); } if (nexttoken.id !== ')') { for (;;) { parse(0, 'for'); if (nexttoken.id !== ',') { break; } advance(','); } } advance(')', t); nospace(prevtoken, token); block(true); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; } }).labelled = true; stmt('break', function() { var v = nexttoken.value; if (funct['(breakage)'] === 0) { warning("Unexpected '{a}'.", nexttoken, this.value); } nolinebreak(this); if (nexttoken.id !== ';') { if (token.line === nexttoken.line) { if (funct[v] !== 'label') { warning("'{a}' is not a statement label.", nexttoken, v); } else if (scope[v] !== funct) { warning("'{a}' is out of scope.", nexttoken, v); } advance(); } } reachable('break'); }); stmt('continue', function() { var v = nexttoken.value; if (funct['(breakage)'] === 0) { warning("Unexpected '{a}'.", nexttoken, this.value); } nolinebreak(this); if (nexttoken.id !== ';') { if (token.line === nexttoken.line) { if (funct[v] !== 'label') { warning("'{a}' is not a statement label.", nexttoken, v); } else if (scope[v] !== funct) { warning("'{a}' is out of scope.", nexttoken, v); } advance(); } } reachable('continue'); }); stmt('return', function() { nolinebreak(this); if (nexttoken.id === '(regexp)') { warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); } if (nexttoken.id !== ';' && !nexttoken.reach) { nonadjacent(token, nexttoken); parse(20); } reachable('return'); }); stmt('throw', function() { nolinebreak(this); nonadjacent(token, nexttoken); parse(20); reachable('throw'); }); reserve('void'); reserve('class'); reserve('const'); reserve('enum'); reserve('export'); reserve('extends'); reserve('float'); reserve('goto'); reserve('import'); reserve('let'); reserve('super'); function jsonValue() { function jsonObject() { var t = nexttoken; advance('{'); if (nexttoken.id !== '}') { for (;;) { if (nexttoken.id === '(end)') { error("Missing '}' to match '{' from line {a}.", nexttoken, t.line + 1); } else if (nexttoken.id === '}') { warning("Unexpected comma.", token); break; } else if (nexttoken.id === ',') { error("Unexpected comma.", nexttoken); } else if (nexttoken.id !== '(string)') { warning("Expected a string and instead saw {a}.", nexttoken, nexttoken.value); } advance(); advance(':'); jsonValue(); if (nexttoken.id !== ',') { break; } advance(','); } } advance('}'); } function jsonArray() { var t = nexttoken; advance('['); if (nexttoken.id !== ']') { for (;;) { if (nexttoken.id === '(end)') { error("Missing ']' to match '[' from line {a}.", nexttoken, t.line + 1); } else if (nexttoken.id === ']') { warning("Unexpected comma.", token); break; } else if (nexttoken.id === ',') { error("Unexpected comma.", nexttoken); } jsonValue(); if (nexttoken.id !== ',') { break; } advance(','); } } advance(']'); } switch (nexttoken.id) { case '{': jsonObject(); break; case '[': jsonArray(); break; case 'true': case 'false': case 'null': case '(number)': case '(string)': advance(); break; case '-': advance('-'); if (token.character !== nexttoken.from) { warning("Unexpected space after '-'.", token); } adjacent(token, nexttoken); advance('(number)'); break; default: error("Expected a JSON value.", nexttoken); } } var itself = function(s, o) { var a, i; JSLINT.errors = []; predefined = Object.create(standard); if (o) { a = o.predef; if (a instanceof Array) { for (i = 0; i < a.length; i += 1) { predefined[a[i]] = true; } } if (o.adsafe) { o.safe = true; } if (o.safe) { o.browser = false; o.css = false; o.debug = false; o.eqeqeq = true; o.evil = false; o.forin = false; o.nomen = true; o.on = false; o.rhino = false; o.safe = true; o.sidebar = false; o.strict = true; o.sub = false; o.undef = true; o.widget = false; predefined.Date = false; predefined['eval'] = false; predefined.Function = false; predefined.Object = false; predefined.ADSAFE = true; } option = o; } else { option = {}; } option.indent = option.indent || 4; adsafe_id = ''; adsafe_may = false; adsafe_went = false; approved = {}; if (option.approved) { for (i = 0; i < option.approved.length; i += 1) { approved[option.approved[i]] = option.approved[i]; } } approved.test = 'test'; tab = ''; for (i = 0; i < option.indent; i += 1) { tab += ' '; } indent = 0; global = Object.create(predefined); scope = global; funct = { '(global)': true, '(name)': '(global)', '(scope)': scope, '(breakage)': 0, '(loopage)': 0 }; functions = []; ids = {}; urls = []; src = false; xmode = false; stack = null; member = {}; membersOnly = null; implied = {}; inblock = false; lookahead = []; jsonmode = false; warnings = 0; lex.init(s); prereg = true; prevtoken = token = nexttoken = syntax['(begin)']; assume(); try { advance(); if (nexttoken.value.charAt(0) === '<') { html(); if (option.adsafe && !adsafe_went) { warning("ADsafe violation: Missing ADSAFE.go.", this); } } else { switch (nexttoken.id) { case '{': case '[': option.laxbreak = true; jsonmode = true; jsonValue(); break; case '@': case '*': case '#': case '.': case ':': xmode = 'style'; advance(); if (token.id !== '@' || !nexttoken.identifier || nexttoken.value !== 'charset') { error('A css file should begin with @charset "UTF-8";'); } advance(); if (nexttoken.type !== '(string)' && nexttoken.value !== 'UTF-8') { error('A css file should begin with @charset "UTF-8";'); } advance(); advance(';'); styles(); break; default: if (option.adsafe && option.fragment) { warning("ADsafe violation.", this); } statements('lib'); } } advance('(end)'); } catch(e) { if (e) { JSLINT.errors.push({ reason: e.message, line: e.line || nexttoken.line, character: e.character || nexttoken.from }, null); } } return JSLINT.errors.length === 0; }; function to_array(o) { var a = [], k; for (k in o) { if (o.hasOwnProperty(k)) { a.push(k); } } return a; } itself.report = function(option, sep) { var a = [], c, e, f, i, k, l, m = '', n, o = [], s, v, cl, va, un, ou, gl, la; function detail(h, s, sep) { if (s.length) { o.push('
' + h + ' ' + s.sort().join(sep || ', ') + '
'); } } s = to_array(implied); k = JSLINT.errors.length; if (k || s.length > 0) { o.push('
Error:'); if (s.length > 0) { s.sort(); for (i = 0; i < s.length; i += 1) { s[i] = '' + s[i] + ' ' + implied[s[i]].join(' ') + ''; } o.push('

Implied global: ' + s.join(', ') + '

'); c = true; } for (i = 0; i < k; i += 1) { c = JSLINT.errors[i]; if (c) { e = c.evidence || ''; o.push('

Problem' + (isFinite(c.line) ? ' at line ' + (c.line + 1) + ' character ' + (c.character + 1) : '') + ': ' + c.reason.entityify() + '

' + (e && (e.length > 80 ? e.slice(0, 77) + '...': e).entityify()) + '

'); } } o.push('
'); if (!c) { return o.join(''); } } if (!option) { o.push('
'); if (urls.length > 0) { detail("URLs
", urls, '
'); } s = to_array(scope); if (s.length === 0) { if (jsonmode) { if (k === 0) { o.push('

JSON: good.

'); } else { o.push('

JSON: bad.

'); } } else { o.push('
No new global variables introduced.
'); } } else { o.push('
Global ' + s.sort().join(', ') + '
'); } for (i = 0; i < functions.length; i += 1) { f = functions[i]; cl = []; va = []; un = []; ou = []; gl = []; la = []; for (k in f) { if (f.hasOwnProperty(k) && k.charAt(0) !== '(') { v = f[k]; switch (v) { case 'closure': cl.push(k); break; case 'var': va.push(k); break; case 'unused': un.push(k); break; case 'label': la.push(k); break; case 'outer': ou.push(k); break; case true: gl.push(k); break; } } } o.push('
' + f['(line)'] + ' ' + (f['(name)'] || '') + '(' + (f['(params)'] || '') + ')
'); detail('Closure', cl); detail('Variable', va); detail('Unused', un); detail('Label', la); detail('Outer', ou); detail('Global', gl); } a = []; for (k in member) { if (typeof member[k] === 'number') { a.push(k); } } if (a.length) { a = a.sort(); m = '
/*members ';
                l = 10;
                for (i = 0; i < a.length; i += 1) {
                    k = a[i];
                    n = k.name();
                    if (l + n.length > 72) {
                        o.push(m + '
'); m = ' '; l = 1; } l += n.length + 2; if (member[k] === 1) { n = '' + n + ''; } if (i < a.length - 1) { n += ', '; } m += n; } o.push(m + '
*/
'); } o.push('
'); } return o.join(''); }; return itself; } (); (function(a) { var input=""; var line=""; var blankcount="0"; while (blankcount < 10){ line=readline(); if (line=="") blankcount++; else blankcount=0; if (line=="END") break; input += line; input += "\n"; } input = input.substring(0, input.length-blankcount); if (!input) { print("No input!"); quit(1); } if (!JSLINT(input, { rhino: true, passfail: false })) { for (var i = 0; i < JSLINT.errors.length; i += 1) { var e = JSLINT.errors[i]; if (e) { print('Lint at line ' + (e.line + 1) + ' character ' + (e.character + 1) + ': ' + e.reason); print((e.evidence || '').replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); print(''); } } } else { print("jslint: No problems found."); quit(); } })(arguments);