diff --git a/j/dip3.js b/j/dip3.js index a762d47..d4b6637 100644 --- a/j/dip3.js +++ b/j/dip3.js @@ -16,26 +16,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -function getSearchTerms() { - var highlighterParameters = 'q as_q as_epq as_oq query search'; - var a = new Array(); - var params = getParamValues(document.referrer/*document.location.href*/, highlighterParameters); - var terms; - for (i = 0; i < params.length; i++) { - terms = parseTerms(params[i]); - for (j = 0; j < terms.length; j++) { - if (terms[j] != '') { - a.push(terms[j].toLowerCase()); - } - } - } - return a; -} - function parseTerms(query) { var s = query + ''; s = s.replace(/(^|\s)(site|related|link|info|cache):[^\s]*(\s|$)/ig, ' '); - s = s.replace(/[^a-z0-9_-]/ig, ' '); // word chars only. + s = s.replace(/[^a-z0-9_\-]/ig, ' '); // word chars only. s = s.replace(/(^|\s)-/g, ' '); // +required -excluded ~synonyms s = s.replace(/\b(and|not|or)\b/ig, ' '); s = s.replace(/\b[a-z0-9]\b/ig, ' '); // one char terms @@ -43,14 +27,14 @@ function parseTerms(query) { } function getParamValues(url, parameters) { - var params = new Array(); + var params = []; var p = parameters.replace(/,/, ' ').split(/\s+/); if (url.indexOf('?') > 0) { var qs = url.substr(url.indexOf('?') + 1); var qsa = qs.split('&'); for (i = 0; i < qsa.length; i++) { nameValue = qsa[i].split('='); - if (nameValue.length != 2) continue; + if (nameValue.length != 2) { continue; } for (j = 0; j < p.length; j++) { if (nameValue[0] == p[j]) { params.push(unescape(nameValue[1]).toLowerCase().replace(/\+/g, ' ')); @@ -61,6 +45,22 @@ function getParamValues(url, parameters) { return params; } +function getSearchTerms() { + var highlighterParameters = 'q as_q as_epq as_oq query search'; + var a = []; + var params = getParamValues(document.referrer/*document.location.href*/, highlighterParameters); + var terms; + for (i = 0; i < params.length; i++) { + terms = parseTerms(params[i]); + for (j = 0; j < terms.length; j++) { + if (terms[j] !== '') { + a.push(terms[j].toLowerCase()); + } + } + } + return a; +} + /* The rest of this script is @@ -89,6 +89,33 @@ POSSIBILITY OF SUCH DAMAGE. */ var HS = {'visible': 'hide', 'hidden': 'show'}; + +function hideTOC() { + var toc = ' show table of contents'; + $("#toc").html(toc); +} + +function showTOC() { + var toc = ''; + var old_level = 1; + $('h2,h3').each(function(i, h) { + level = parseInt(h.tagName.substring(1), 10); + if (level < old_level) { + toc += ''; + } else if (level > old_level) { + toc += '
    '; + } + toc += '
  1. ' + h.innerHTML + ''; + old_level = level; + }); + while (level > 1) { + toc += '
'; + level -= 1; + } + toc = ' hide table of contents
  1. Full table of contents
  2. ' + toc.substring(4); + $("#toc").html(toc); +} + $(document).ready(function() { hideTOC(); prettyPrint(); @@ -103,7 +130,7 @@ $(document).ready(function() { /* "hide", "open in new window", and (optionally) "download" widgets on code & screen blocks */ $("pre > code").each(function(i) { var pre = $(this.parentNode); - if (pre.parents("table").length == 0) { + if (pre.parents("table").length === 0) { pre.addClass("code"); } }); @@ -113,7 +140,7 @@ $(document).ready(function() { /* wrap code block in a div and insert widget block */ $(this).wrapInner('
    '); - $(this).prepend('
    [' + HS['visible'] + '] [open in new window]
    '); + $(this).prepend('
    [' + HS.visible + '] [open in new window]
    '); /* move download link into widget block */ $(this).prev("p.d").each(function(i) { @@ -189,7 +216,7 @@ $(document).ready(function() { function toggleCodeBlock(id) { $("#" + id).find("div.b").toggle(); var a = $("#" + id).find("a.toggle"); - a.text(a.text() == HS['visible'] ? HS['hidden'] : HS['visible']); + a.text(a.text() == HS.visible ? HS.hidden : HS.visible); } function plainTextOnClick(id) { @@ -200,29 +227,3 @@ function plainTextOnClick(id) { win.document.write('
    ' + clone.html());
         win.document.close();
     }
    -
    -function hideTOC() {
    -    var toc = ' show table of contents';
    -    $("#toc").html(toc);
    -}
    -
    -function showTOC() {
    -    var toc = '';
    -    var old_level = 1;
    -    $('h2,h3').each(function(i, h) {
    -	    level = parseInt(h.tagName.substring(1));
    -	    if (level < old_level) {
    -		toc += '
'; - } else if (level > old_level) { - toc += '
    '; - } - toc += '
  1. ' + h.innerHTML + ''; - old_level = level; - }); - while (level > 1) { - toc += '
'; - level -= 1; - } - toc = ' hide table of contents
  1. Full table of contents
  2. ' + toc.substring(4); - $("#toc").html(toc); -} diff --git a/j/jslint.js b/j/jslint.js new file mode 100644 index 0000000..dd5832c --- /dev/null +++ b/j/jslint.js @@ -0,0 +1,4271 @@ +#!/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); diff --git a/publish b/publish index 0b0eb62..019c021 100755 --- a/publish +++ b/publish @@ -18,6 +18,12 @@ rm -f examples/*.pyc cp -R examples build/ cp .htaccess build/ +echo "linting JS" + +[ -n "$(which js 2>/dev/null)" ] || die "SpiderMonkey (js or js.exe) not found" +js_lint_results=`js j/jslint.js < build/j/dip3.js 2>/dev/null` +[ "$js_lint_results" = "jslint: No problems found." ] || die "$js_lint_results" + echo "minimizing HTML" # minimize HTML (NB: this script is quite fragile and relies on knowledge of how I write HTML)