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('');
+ if (!nexttoken.identifier && nexttoken.value !== 'script') {
+ warning("Expected '{a}' and instead saw '{b}'.", nexttoken, 'script', nexttoken.value);
+ }
+ advance();
+ xmode = 'outer';
+ break;
+ case 'style':
+ xmode = 'style';
+ advance('>');
+ styles();
+ xmode = 'html';
+ advance('');
+ if (!nexttoken.identifier && nexttoken.value !== 'style') {
+ warning("Expected '{a}' and instead saw '{b}'.", nexttoken, 'style', nexttoken.value);
+ }
+ advance();
+ xmode = 'outer';
+ break;
+ case 'input':
+ switch (a.type) {
+ case 'radio':
+ case 'checkbox':
+ case 'text':
+ case 'button':
+ case 'file':
+ case 'reset':
+ case 'submit':
+ case 'password':
+ case 'file':
+ case 'hidden':
+ case 'image':
+ break;
+ default:
+ warning("Bad input type.");
+ }
+ if (option.adsafe && a.autocomplete !== 'off') {
+ warning("ADsafe autocomplete violation.");
+ }
+ break;
+ case 'applet':
+ case 'body':
+ case 'embed':
+ case 'frame':
+ case 'frameset':
+ case 'head':
+ case 'iframe':
+ case 'img':
+ case 'object':
+ case 'param':
+ if (option.adsafe) {
+ warning("ADsafe violation: Disallowed tag: " + n);
+ }
+ break;
+ }
+ }
+ function closetag(n) {
+ return '' + n + '>';
+ }
+ 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 '':
+ xmode = 'html';
+ advance('');
+ if (!nexttoken.identifier) {
+ warning("Bad identifier.");
+ }
+ n = nexttoken.value;
+ if (option.cap) {
+ n = n.toLowerCase();
+ }
+ advance();
+ if (!stack) {
+ error("Unexpected '{a}'.", nexttoken, closetag(n));
+ }
+ t = stack.pop();
+ if (!t) {
+ error("Unexpected '{a}'.", nexttoken, closetag(n));
+ }
+ if (t.name !== n) {
+ error("Expected '{a}' and instead saw '{b}'.", nexttoken, closetag(t.name), closetag(n));
+ }
+ if (nexttoken.id !== '>') {
+ 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('').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)