diff --git a/.eslintignore b/.eslintignore index c08ca08a05..d6f8f427bd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ static/js/bundle.js static/js/blueslip.js puppet/zulip_ops/files/statsd/local.js -tools/jslint/check-all.js diff --git a/docs/THIRDPARTY b/docs/THIRDPARTY index fa98eaa9c2..e905ccc22f 100644 --- a/docs/THIRDPARTY +++ b/docs/THIRDPARTY @@ -218,10 +218,6 @@ Files: tools/inject-messages/othello Copyright: Shakespeare License: public-domain -Files: tools/jslint/jslint.js -Copyright: 2002 Douglas Crockford -License: XXX-good-not-evil - Files: tools/review Copyright: 2010 Ksplice, Inc. License: Apache-2.0 diff --git a/docs/code-style.md b/docs/code-style.md index 7bd1bc3af1..887c961b22 100644 --- a/docs/code-style.md +++ b/docs/code-style.md @@ -23,12 +23,7 @@ The Vagrant setup process runs this for you. `lint-all` runs many lint checks in parallel, including -- JavaScript ([JSLint](http://www.jslint.com/)) - - > `tools/jslint/check-all.js` contains a pretty fine-grained set of - > JSLint options, rule exceptions, and allowed global variables. If - > you add a new global, you'll need to add it to the list. - +- JavaScript ([ESLint](http://eslint.org/)) - Python ([Pyflakes](http://pypi.python.org/pypi/pyflakes)) - templates - Puppet configuration diff --git a/docs/linters.md b/docs/linters.md index aa228dcce3..c12324727c 100644 --- a/docs/linters.md +++ b/docs/linters.md @@ -17,9 +17,10 @@ prevent common coding errors. We borrow some open source tools for much of our linting, and the links below will direct you to the official documentation for these projects. -- [jslint](https://github.com/douglascrockford/JSLint) +- [eslint](http://eslint.org) - [mypy](http://mypy-lang.org/) -- [puppet](https://puppet.com/) (puppet provides its own mechanism for validating manifests) +- [puppet](https://puppet.com/) (puppet provides its own mechanism for + validating manifests) - [pyflakes](https://pypi.python.org/pypi/pyflakes) Zulip also uses some home-grown code to perform tasks like validating @@ -81,7 +82,7 @@ Most of our lint checks get performed by `./tools/lint-all`. These include the following checks: - Check Python code with pyflakes. -- Check JavaScript code with jslint. +- Check JavaScript code with eslint. - Check Python code for custom Zulip rules. - Check non-Python code for custom Zulip rules. - Check puppet manifests with the puppet validator. @@ -102,7 +103,7 @@ The rest of this document pertains to the checks that occur in `./tools/lint-all Zulip has a script called `lint-all` that lives in our "tools" directory. It is the workhorse of our linting system, although in some cases it dispatches the heavy lifting to other components such as pyflakes, -jslint, and other home grown tools. +eslint, and other home grown tools. You can find the source code [here](https://github.com/zulip/zulip/blob/master/tools/lint-all). @@ -161,7 +162,7 @@ that we exempt may be deemed not worthwhile to fix. #### JavaScript code We check our JavaScript code in a few different ways: -- We run jslint. +- We run eslint. - We perform custom Zulip regex checks on the code. - We verify that all addClass calls, with a few exceptions, explicitly contain a CSS class. diff --git a/static/js/debug.js b/static/js/debug.js index 0b0f2717ec..b0ee440c24 100644 --- a/static/js/debug.js +++ b/static/js/debug.js @@ -9,9 +9,6 @@ The file may still be accessible under other circumstances, so do not put sensitive information here. */ -// It's fine to use console.log etc. in this file. -/*jslint devel: true */ - /* print_elapsed_time("foo", foo) diff --git a/static/js/message_list.js b/static/js/message_list.js index 8f9a7e5284..a6c75c4477 100644 --- a/static/js/message_list.js +++ b/static/js/message_list.js @@ -1,4 +1,3 @@ -/*jslint nomen: true */ var message_list = (function () { var exports = {}; @@ -642,7 +641,6 @@ $(document).on('message_selected.zulip zuliphashchange.zulip mousewheel', functi return exports; }()); -/*jslint nomen: false */ if (typeof module !== 'undefined') { module.exports = message_list; } diff --git a/tools/jslint/check-all.js b/tools/jslint/check-all.js deleted file mode 100644 index e6beafe7c7..0000000000 --- a/tools/jslint/check-all.js +++ /dev/null @@ -1,166 +0,0 @@ -"use strict"; - -// Global variables, categorized by place of definition. -var globals = - // Third-party libraries - ' $ _ jQuery Spinner Handlebars XDate zxcvbn Intl Notification' - + ' LazyLoad Dropbox SockJS marked i18n' - - // Node-based unit tests - + ' module require' - - // Cocoa<-> Javascript bridge - + ' bridge' - - // index.html - + ' page_params' - - // common.js - + ' status_classes password_quality' - - // setup.js - + ' csrf_token' - - // Modules, defined in their respective files. - + ' compose compose_fade rows hotkeys narrow reload search subs pointer' - + ' composebox_typeahead server_events typeahead_helper notifications hashchange' - + ' invite ui viewport util activity timerender message_list MessageListView blueslip unread stream_list' - + ' message_edit tab_bar emoji popovers navigate people settings alert_words_ui message_store' - + ' avatar feature_flags search_suggestion referral stream_color Dict' - + ' Filter summary admin stream_data muting WinChan muting_ui Socket channel gear_menu' - + ' message_flags bot_data loading favicon resize scroll_bar condense floating_recipient_bar' - + ' copy_and_paste click_handlers topic_list pm_list unread_ui components' - - // colorspace.js - + ' colorspace' - - // tutorial.js - + ' tutorial' - - // templates.js - + ' templates' - - // alert_words.js - + ' alert_words' - - // fenced_code.js - + ' fenced_code' - - // echo.js - + ' echo' - - // localstorage.js - + ' localstorage' - - // zulip.js - + ' home_msg_list current_msg_list' - ; - - -var options = { - vars: true, // Allow multiple 'var' per function - sloppy: true, // Don't require "use strict" - white: true, // Lenient whitespace rules - plusplus: true, // Allow increment/decrement operators - regexp: true, // Allow . and [^...] in regular expressions - todo: true, // Allow "TODO" comments. - newcap: true, // Don't assume that capitalized functions are - // constructors (and the converse) - nomen: true, // Tolerate underscore at the beginning of a name - stupid: true // Allow synchronous methods -}; - - -// For each error.raw message, we can return 'true' to ignore -// the error. -var exceptions = { - "Unexpected 'else' after 'return'." : function () { - return true; - }, - - "Don't make functions within a loop." : function () { - return true; - }, - - // We use typeof to test if a variable exists at all. - "Unexpected 'typeof'. Use '===' to compare directly with {a}.": function (error) { - return error.a === 'undefined'; - } -}; - - -var fs = require('fs'); -var path = require('path'); -var JSLINT = require(path.join(__dirname, 'jslint')).JSLINT; - -var cwd = process.cwd(); - -var exit_code = 0; -var i; - -// Drop 'node' and the script name from args. -for (i=0; i<2; i++) { - process.argv.shift(); -} - -process.argv.forEach(function (filepath) { - var contents = fs.readFileSync(filepath, 'utf8'); - var messages = []; - - // We mutate 'options' so be sure to clear everything. - if (filepath.indexOf('static/js/') !== -1) { - // Frontend browser code - options.browser = true; - options.node = false; - options.predef = globals.split(/\s+/); - } else { - // Backend code for Node.js - options.browser = false; - options.node = true; - - if (filepath.indexOf('frontend_tests/') !== -1) { - // Include '$' and browser globals because we use them inside - // casper.evaluate - options.predef = ['casper', '$', 'document', 'window', 'set_global', 'add_dependencies', 'patch_builtin', 'assert', 'current_msg_list']; - } else { - options.predef = []; - } - } - - if (!JSLINT(contents, options)) { - JSLINT.errors.forEach(function (error) { - if (error === null) { - // JSLint stopping error - messages.push(' (JSLint giving up)'); - return; - } - - var exn = exceptions[error.raw]; - if (exn && exn(error)) { - // Ignore this error. - return; - } - - // NB: this will break on a 10,000 line file - var line = (' ' + error.line).slice(-4); - - messages.push(' ' + line + ' ' + error.reason); - }); - - if (messages.length > 0) { - exit_code = 1; - - console.log(path.relative(cwd, filepath)); - - // Something very wacky happens if we do - // .forEach(console.log) directly. - messages.forEach(function (msg) { - console.log(msg); - }); - - console.log(''); - } - } -}); - -process.exit(exit_code); diff --git a/tools/jslint/jslint.js b/tools/jslint/jslint.js deleted file mode 100644 index 2d98b22f18..0000000000 --- a/tools/jslint/jslint.js +++ /dev/null @@ -1,6460 +0,0 @@ -// jslint.js -// 2012-10-01 - -// Copyright (c) 2002 Douglas Crockford (www.JSLint.com) - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// The Software shall be used for Good, not Evil. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// WARNING: JSLint will hurt your feelings. - -// JSLINT is a global function. It takes two parameters. - -// var myResult = JSLINT(source, option); - -// The first parameter is either a string or an array of strings. If it is a -// string, it will be split on '\n' or '\r'. If it is an array of strings, it -// is assumed that each string represents one line. The source can be a -// JavaScript text, or HTML text, or a JSON text, or a CSS text. - -// The second parameter is an optional object of options that control the -// operation of JSLINT. Most of the options are booleans: They are all -// optional and have a default value of false. One of the options, predef, -// can be an array of names, which will be used to declare global variables, -// or an object whose keys are used as global names, with a boolean value -// that determines if they are assignable. - -// If it checks out, JSLINT returns true. Otherwise, it returns false. - -// If false, you can inspect JSLINT.errors to find out the problems. -// JSLINT.errors is an array of objects containing these properties: - -// { -// line : The line (relative to 0) at which the lint was found -// character : The character (relative to 0) at which the lint was found -// reason : The problem -// evidence : The text line in which the problem occurred -// raw : The raw message before the details were inserted -// a : The first detail -// b : The second detail -// c : The third detail -// d : The fourth detail -// } - -// If a stopping error was found, a null will be the last element of the -// JSLINT.errors array. A stopping error means that JSLint was not confident -// enough to continue. It does not necessarily mean that the error was -// especially heinous. - -// You can request a data structure that contains JSLint's results. - -// var myData = JSLINT.data(); - -// It returns a structure with this form: - -// { -// errors: [ -// { -// line: NUMBER, -// character: NUMBER, -// reason: STRING, -// evidence: STRING -// } -// ], -// functions: [ -// { -// name: STRING, -// line: NUMBER, -// last: NUMBER, -// params: [ -// { -// string: STRING -// } -// ], -// closure: [ -// STRING -// ], -// var: [ -// STRING -// ], -// exception: [ -// STRING -// ], -// outer: [ -// STRING -// ], -// unused: [ -// STRING -// ], -// undef: [ -// STRING -// ], -// global: [ -// STRING -// ], -// label: [ -// STRING -// ] -// } -// ], -// globals: [ -// STRING -// ], -// member: { -// STRING: NUMBER -// }, -// urls: [ -// STRING -// ], -// json: BOOLEAN -// } - -// Empty arrays will not be included. - -// You can request a Function Report, which shows all of the functions -// and the parameters and vars that they use. This can be used to find -// implied global variables and other problems. The report is in HTML and -// can be inserted in an HTML . It should be given the result of the -// JSLINT.data function. - -// var myReport = JSLINT.report(data); - -// You can request an HTML error report. - -// var myErrorReport = JSLINT.error_report(data); - -// You can request a properties report, which produces a list of the program's -// properties in the form of a /*properties*/ declaration. - -// var myPropertyReport = JSLINT.properties_report(JSLINT.property); - -// You can obtain the parse tree that JSLint constructed while parsing. The -// latest tree is kept in JSLINT.tree. A nice stringification can be produced -// with - -// JSON.stringify(JSLINT.tree, [ -// 'string', 'arity', 'name', 'first', -// 'second', 'third', 'block', 'else' -// ], 4)); - -// JSLint provides three directives. They look like slashstar comments, and -// allow for setting options, declaring global variables, and establishing a -// set of allowed property names. - -// These directives respect function scope. - -// The jslint directive is a special comment that can set one or more options. -// For example: - -/*jslint - evil: true, nomen: true, regexp: true, todo: true -*/ - -// The current option set is - -// anon true, if the space may be omitted in anonymous function declarations -// bitwise true, if bitwise operators should be allowed -// browser true, if the standard browser globals should be predefined -// cap true, if upper case HTML should be allowed -// 'continue' true, if the continuation statement should be tolerated -// css true, if CSS workarounds should be tolerated -// debug true, if debugger statements should be allowed -// devel true, if logging should be allowed (console, alert, etc.) -// eqeq true, if == should be allowed -// es5 true, if ES5 syntax should be allowed -// evil true, if eval should be allowed -// forin true, if for in statements need not filter -// fragment true, if HTML fragments should be allowed -// indent the indentation factor -// maxerr the maximum number of errors to allow -// maxlen the maximum length of a source line -// newcap true, if constructor names capitalization is ignored -// node true, if Node.js globals should be predefined -// nomen true, if names may have dangling _ -// on true, if HTML event handlers should be allowed -// passfail true, if the scan should stop on first error -// plusplus true, if increment/decrement should be allowed -// properties true, if all property names must be declared with /*properties*/ -// regexp true, if the . should be allowed in regexp literals -// rhino true, if the Rhino environment globals should be predefined -// undef true, if variables can be declared out of order -// unparam true, if unused parameters should be tolerated -// sloppy true, if the 'use strict'; pragma is optional -// stupid true, if really stupid practices are tolerated -// sub true, if all forms of subscript notation are tolerated -// todo true, if TODO comments are tolerated -// vars true, if multiple var statements per function should be allowed -// white true, if sloppy whitespace is tolerated -// windows true, if MS Windows-specific globals should be predefined - -// The properties directive declares an exclusive list of property names. -// Any properties named in the program that are not in the list will -// produce a warning. - -// For example: - -/*properties - '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', - '(arguments)', '(begin)', '(breakage)', '(context)', '(error)', - '(identifier)', '(line)', '(loopage)', '(name)', '(params)', '(scope)', - '(token)', '(vars)', '(verb)', '*', '+', '-', '/', '<', '<=', '==', '===', - '>', '>=', ADSAFE, Array, Date, Function, Object, '\\', a, a_label, - a_not_allowed, a_not_defined, a_scope, abbr, acronym, address, adsafe, - adsafe_a, adsafe_autocomplete, adsafe_bad_id, adsafe_div, adsafe_fragment, - adsafe_go, adsafe_html, adsafe_id, adsafe_id_go, adsafe_lib, - adsafe_lib_second, adsafe_missing_id, adsafe_name_a, adsafe_placement, - adsafe_prefix_a, adsafe_script, adsafe_source, adsafe_subscript_a, - adsafe_tag, all, already_defined, and, anon, applet, apply, approved, area, - arity, article, aside, assign, assign_exception, - assignment_function_expression, at, attribute_case_a, audio, autocomplete, - avoid_a, b, background, 'background-attachment', 'background-color', - 'background-image', 'background-position', 'background-repeat', - bad_assignment, bad_color_a, bad_constructor, bad_entity, bad_html, bad_id_a, - bad_in_a, bad_invocation, bad_name_a, bad_new, bad_number, bad_operand, - bad_style, bad_type, bad_url_a, bad_wrap, base, bdo, big, bitwise, block, - blockquote, body, border, 'border-bottom', 'border-bottom-color', - 'border-bottom-left-radius', 'border-bottom-right-radius', - 'border-bottom-style', 'border-bottom-width', 'border-collapse', - 'border-color', 'border-left', 'border-left-color', 'border-left-style', - 'border-left-width', 'border-radius', 'border-right', 'border-right-color', - 'border-right-style', 'border-right-width', 'border-spacing', 'border-style', - 'border-top', 'border-top-color', 'border-top-left-radius', - 'border-top-right-radius', 'border-top-style', 'border-top-width', - 'border-width', bottom, 'box-shadow', br, braille, browser, button, c, call, - canvas, cap, caption, 'caption-side', center, charAt, charCodeAt, character, - cite, clear, clip, closure, cm, code, col, colgroup, color, combine_var, - command, conditional_assignment, confusing_a, confusing_regexp, - constructor_name_a, content, continue, control_a, 'counter-increment', - 'counter-reset', create, css, cursor, d, dangerous_comment, dangling_a, data, - datalist, dd, debug, del, deleted, details, devel, dfn, dialog, dir, - direction, display, disrupt, div, dl, dt, duplicate_a, edge, edition, else, - em, embed, embossed, empty, 'empty-cells', empty_block, empty_case, - empty_class, entityify, eqeq, error_report, errors, es5, eval, evidence, - evil, ex, exception, exec, expected_a, expected_a_at_b_c, expected_a_b, - expected_a_b_from_c_d, expected_at_a, expected_attribute_a, - expected_attribute_value_a, expected_class_a, expected_fraction_a, - expected_id_a, expected_identifier_a, expected_identifier_a_reserved, - expected_lang_a, expected_linear_a, expected_media_a, expected_name_a, - expected_nonstandard_style_attribute, expected_number_a, expected_operator_a, - expected_percent_a, expected_positive_a, expected_pseudo_a, - expected_selector_a, expected_small_a, expected_space_a_b, expected_string_a, - expected_style_attribute, expected_style_pattern, expected_tagname_a, - expected_type_a, f, fieldset, figure, filter, first, flag, float, floor, - font, 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', - 'font-style', 'font-variant', 'font-weight', footer, forEach, for_if, forin, - form, fragment, frame, frameset, from, fromCharCode, fud, funct, function, - function_block, function_eval, function_loop, function_statement, - function_strict, functions, global, globals, h1, h2, h3, h4, h5, h6, - handheld, hasOwnProperty, head, header, height, hgroup, hr, - 'hta:application', html, html_confusion_a, html_handlers, i, id, identifier, - identifier_function, iframe, img, immed, implied_evil, in, indent, indexOf, - infix_in, init, input, ins, insecure_a, isAlpha, isArray, isDigit, isNaN, - join, jslint, json, kbd, keygen, keys, label, labeled, lang, lbp, - leading_decimal_a, led, left, legend, length, 'letter-spacing', li, lib, - line, 'line-height', link, 'list-style', 'list-style-image', - 'list-style-position', 'list-style-type', map, margin, 'margin-bottom', - 'margin-left', 'margin-right', 'margin-top', mark, 'marker-offset', match, - 'max-height', 'max-width', maxerr, maxlen, menu, message, meta, meter, - 'min-height', 'min-width', missing_a, missing_a_after_b, missing_option, - missing_property, missing_space_a_b, missing_url, missing_use_strict, mixed, - mm, mode, move_invocation, move_var, n, name, name_function, nav, - nested_comment, newcap, node, noframes, nomen, noscript, not, - not_a_constructor, not_a_defined, not_a_function, not_a_label, not_a_scope, - not_greater, nud, number, object, octal_a, ol, on, opacity, open, optgroup, - option, outer, outline, 'outline-color', 'outline-style', 'outline-width', - output, overflow, 'overflow-x', 'overflow-y', p, padding, 'padding-bottom', - 'padding-left', 'padding-right', 'padding-top', 'page-break-after', - 'page-break-before', param, parameter_a_get_b, parameter_arguments_a, - parameter_set_a, params, paren, parent, passfail, pc, plusplus, pop, - position, postscript, pre, predef, print, progress, projection, properties, - properties_report, property, prototype, pt, push, px, q, quote, quotes, r, - radix, range, raw, read_only, reason, redefinition_a, regexp, replace, - report, reserved, reserved_a, rhino, right, rp, rt, ruby, safe, samp, - scanned_a_b, screen, script, search, second, section, select, shift, - slash_equal, slice, sloppy, small, sort, source, span, speech, split, src, - statement_block, stopping, strange_loop, strict, string, strong, stupid, - style, styleproperty, sub, subscript, substr, sup, supplant, sync_a, t, - table, 'table-layout', tag_a_in_b, tbody, td, test, 'text-align', - 'text-decoration', 'text-indent', 'text-shadow', 'text-transform', textarea, - tfoot, th, thead, third, thru, time, title, todo, todo_comment, toLowerCase, - toString, toUpperCase, token, too_long, too_many, top, tr, - trailing_decimal_a, tree, tt, tty, tv, type, u, ul, unclosed, - unclosed_comment, unclosed_regexp, undef, undefined, unescaped_a, - unexpected_a, unexpected_char_a_b, unexpected_comment, unexpected_else, - unexpected_label_a, unexpected_property_a, unexpected_space_a_b, - unexpected_typeof_a, 'unicode-bidi', unnecessary_initialize, - unnecessary_use, unparam, unreachable_a_b, unrecognized_style_attribute_a, - unrecognized_tag_a, unsafe, unused, url, urls, use_array, use_braces, - use_charAt, use_object, use_or, use_param, used_before_a, var, var_a_not, - vars, 'vertical-align', video, visibility, was, weird_assignment, - weird_condition, weird_new, weird_program, weird_relation, weird_ternary, - white, 'white-space', width, windows, 'word-spacing', 'word-wrap', wrap, - wrap_immediate, wrap_regexp, write_is_wrong, writeable, 'z-index' -*/ - -// The global directive is used to declare global variables that can -// be accessed by the program. If a declaration is true, then the variable -// is writeable. Otherwise, it is read-only. - -// We build the application inside a function so that we produce only a single -// global variable. That function will be invoked immediately, and its return -// value is the JSLINT function itself. That function is also an object that -// can contain data and other functions. - -var JSLINT = (function () { - 'use strict'; - - function array_to_object(array, value) { - -// Make an object from an array of keys and a common value. - - var i, length = array.length, object = {}; - for (i = 0; i < length; i += 1) { - object[array[i]] = value; - } - return object; - } - - - var adsafe_id, // The widget's ADsafe id. - adsafe_may, // The widget may load approved scripts. - adsafe_top, // At the top of the widget script. - adsafe_went, // ADSAFE.go has been called. - allowed_option = { - anon : true, - bitwise : true, - browser : true, - cap : true, - 'continue': true, - css : true, - debug : true, - devel : true, - eqeq : true, - es5 : true, - evil : true, - forin : true, - fragment : true, - indent : 10, - maxerr : 1000, - maxlen : 256, - newcap : true, - node : true, - nomen : true, - on : true, - passfail : true, - plusplus : true, - properties: true, - regexp : true, - rhino : true, - undef : true, - unparam : true, - sloppy : true, - stupid : true, - sub : true, - todo : true, - vars : true, - white : true, - windows : true - }, - anonname, // The guessed name for anonymous functions. - approved, // ADsafe approved urls. - -// These are operators that should not be used with the ! operator. - - bang = { - '<' : true, - '<=' : true, - '==' : true, - '===': true, - '!==': true, - '!=' : true, - '>' : true, - '>=' : true, - '+' : true, - '-' : true, - '*' : true, - '/' : true, - '%' : true - }, - -// These are property names that should not be permitted in the safe subset. - - banned = array_to_object([ - 'arguments', 'callee', 'caller', 'constructor', 'eval', 'prototype', - 'stack', 'unwatch', 'valueOf', 'watch' - ], true), - begin, // The root token - -// browser contains a set of global names that are commonly provided by a -// web browser environment. - - browser = array_to_object([ - 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData', - 'frames', 'history', 'Image', 'localStorage', 'location', 'name', - 'navigator', 'Option', 'parent', 'screen', 'sessionStorage', - 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest' - ], false), - -// bundle contains the text messages. - - bundle = { - a_label: "'{a}' is a statement label.", - a_not_allowed: "'{a}' is not allowed.", - a_not_defined: "'{a}' is not defined.", - a_scope: "'{a}' used out of scope.", - adsafe_a: "ADsafe violation: '{a}'.", - adsafe_autocomplete: "ADsafe autocomplete violation.", - adsafe_bad_id: "ADSAFE violation: bad id.", - adsafe_div: "ADsafe violation: Wrap the widget in a div.", - adsafe_fragment: "ADSAFE: Use the fragment option.", - adsafe_go: "ADsafe violation: Misformed ADSAFE.go.", - adsafe_html: "Currently, ADsafe does not operate on whole HTML " + - "documents. It operates on
fragments and .js files.", - adsafe_id: "ADsafe violation: id does not match.", - adsafe_id_go: "ADsafe violation: Missing ADSAFE.id or ADSAFE.go.", - adsafe_lib: "ADsafe lib violation.", - adsafe_lib_second: "ADsafe: The second argument to lib must be a function.", - adsafe_missing_id: "ADSAFE violation: missing ID_.", - adsafe_name_a: "ADsafe name violation: '{a}'.", - adsafe_placement: "ADsafe script placement violation.", - adsafe_prefix_a: "ADsafe violation: An id must have a '{a}' prefix", - adsafe_script: "ADsafe script violation.", - adsafe_source: "ADsafe unapproved script source.", - adsafe_subscript_a: "ADsafe subscript '{a}'.", - adsafe_tag: "ADsafe violation: Disallowed tag '{a}'.", - already_defined: "'{a}' is already defined.", - and: "The '&&' subexpression should be wrapped in parens.", - assign_exception: "Do not assign to the exception parameter.", - assignment_function_expression: "Expected an assignment or " + - "function call and instead saw an expression.", - attribute_case_a: "Attribute '{a}' not all lower case.", - avoid_a: "Avoid '{a}'.", - bad_assignment: "Bad assignment.", - bad_color_a: "Bad hex color '{a}'.", - bad_constructor: "Bad constructor.", - bad_entity: "Bad entity.", - bad_html: "Bad HTML string", - bad_id_a: "Bad id: '{a}'.", - bad_in_a: "Bad for in variable '{a}'.", - bad_invocation: "Bad invocation.", - bad_name_a: "Bad name: '{a}'.", - bad_new: "Do not use 'new' for side effects.", - bad_number: "Bad number '{a}'.", - bad_operand: "Bad operand.", - bad_style: "Bad style.", - bad_type: "Bad type.", - bad_url_a: "Bad url '{a}'.", - bad_wrap: "Do not wrap function literals in parens unless they " + - "are to be immediately invoked.", - combine_var: "Combine this with the previous 'var' statement.", - conditional_assignment: "Expected a conditional expression and " + - "instead saw an assignment.", - confusing_a: "Confusing use of '{a}'.", - confusing_regexp: "Confusing regular expression.", - constructor_name_a: "A constructor name '{a}' should start with " + - "an uppercase letter.", - control_a: "Unexpected control character '{a}'.", - css: "A css file should begin with @charset 'UTF-8';", - dangling_a: "Unexpected dangling '_' in '{a}'.", - dangerous_comment: "Dangerous comment.", - deleted: "Only properties should be deleted.", - duplicate_a: "Duplicate '{a}'.", - empty_block: "Empty block.", - empty_case: "Empty case.", - empty_class: "Empty class.", - es5: "This is an ES5 feature.", - evil: "eval is evil.", - expected_a: "Expected '{a}'.", - expected_a_b: "Expected '{a}' and instead saw '{b}'.", - expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " + - "{c} and instead saw '{d}'.", - expected_at_a: "Expected an at-rule, and instead saw @{a}.", - expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", - expected_attribute_a: "Expected an attribute, and instead saw [{a}].", - expected_attribute_value_a: "Expected an attribute value and " + - "instead saw '{a}'.", - expected_class_a: "Expected a class, and instead saw .{a}.", - expected_fraction_a: "Expected a number between 0 and 1 and " + - "instead saw '{a}'", - expected_id_a: "Expected an id, and instead saw #{a}.", - expected_identifier_a: "Expected an identifier and instead saw '{a}'.", - expected_identifier_a_reserved: "Expected an identifier and " + - "instead saw '{a}' (a reserved word).", - expected_linear_a: "Expected a linear unit and instead saw '{a}'.", - expected_lang_a: "Expected a lang code, and instead saw :{a}.", - expected_media_a: "Expected a CSS media type, and instead saw '{a}'.", - expected_name_a: "Expected a name and instead saw '{a}'.", - expected_nonstandard_style_attribute: "Expected a non-standard " + - "style attribute and instead saw '{a}'.", - expected_number_a: "Expected a number and instead saw '{a}'.", - expected_operator_a: "Expected an operator and instead saw '{a}'.", - expected_percent_a: "Expected a percentage and instead saw '{a}'", - expected_positive_a: "Expected a positive number and instead saw '{a}'", - expected_pseudo_a: "Expected a pseudo, and instead saw :{a}.", - expected_selector_a: "Expected a CSS selector, and instead saw {a}.", - expected_small_a: "Expected a small positive integer and instead saw '{a}'", - expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.", - expected_string_a: "Expected a string and instead saw '{a}'.", - expected_style_attribute: "Excepted a style attribute, and instead saw '{a}'.", - expected_style_pattern: "Expected a style pattern, and instead saw '{a}'.", - expected_tagname_a: "Expected a tagName, and instead saw {a}.", - expected_type_a: "Expected a type, and instead saw {a}.", - for_if: "The body of a for in should be wrapped in an if " + - "statement to filter unwanted properties from the prototype.", - function_block: "Function statements should not be placed in blocks." + - "Use a function expression or move the statement to the top of " + - "the outer function.", - function_eval: "The Function constructor is eval.", - function_loop: "Don't make functions within a loop.", - function_statement: "Function statements are not invocable." + - "Wrap the whole function invocation in parens.", - function_strict: "Use the function form of 'use strict'.", - html_confusion_a: "HTML confusion in regular expression '<{a}'.", - html_handlers: "Avoid HTML event handlers.", - identifier_function: "Expected an identifier in an assignment " + - "and instead saw a function invocation.", - implied_evil: "Implied eval is evil. Pass a function instead of a string.", - infix_in: "Unexpected 'in'. Compare with undefined, or use the " + - "hasOwnProperty method instead.", - insecure_a: "Insecure '{a}'.", - isNaN: "Use the isNaN function to compare with NaN.", - lang: "lang is deprecated.", - leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.", - missing_a: "Missing '{a}'.", - missing_a_after_b: "Missing '{a}' after '{b}'.", - missing_option: "Missing option value.", - missing_property: "Missing property name.", - missing_space_a_b: "Missing space between '{a}' and '{b}'.", - missing_url: "Missing url.", - missing_use_strict: "Missing 'use strict' statement.", - mixed: "Mixed spaces and tabs.", - move_invocation: "Move the invocation into the parens that " + - "contain the function.", - move_var: "Move 'var' declarations to the top of the function.", - name_function: "Missing name in function statement.", - nested_comment: "Nested comment.", - not: "Nested not.", - not_a_constructor: "Do not use {a} as a constructor.", - not_a_defined: "'{a}' has not been fully defined yet.", - not_a_function: "'{a}' is not a function.", - not_a_label: "'{a}' is not a label.", - not_a_scope: "'{a}' is out of scope.", - not_greater: "'{a}' should not be greater than '{b}'.", - octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.", - parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.", - parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.", - parameter_set_a: "Expected parameter (value) in set {a} function.", - radix: "Missing radix parameter.", - read_only: "Read only.", - redefinition_a: "Redefinition of '{a}'.", - reserved_a: "Reserved name '{a}'.", - scanned_a_b: "{a} ({b}% scanned).", - slash_equal: "A regular expression literal can be confused with '/='.", - statement_block: "Expected to see a statement and instead saw a block.", - stopping: "Stopping.", - strange_loop: "Strange loop.", - strict: "Strict violation.", - subscript: "['{a}'] is better written in dot notation.", - sync_a: "Unexpected sync method: '{a}'.", - tag_a_in_b: "A '<{a}>' must be within '<{b}>'.", - todo_comment: "Unexpected TODO comment.", - too_long: "Line too long.", - too_many: "Too many errors.", - trailing_decimal_a: "A trailing decimal point can be confused " + - "with a dot: '.{a}'.", - type: "type is unnecessary.", - unclosed: "Unclosed string.", - unclosed_comment: "Unclosed comment.", - unclosed_regexp: "Unclosed regular expression.", - unescaped_a: "Unescaped '{a}'.", - unexpected_a: "Unexpected '{a}'.", - unexpected_char_a_b: "Unexpected character '{a}' in {b}.", - unexpected_comment: "Unexpected comment.", - unexpected_else: "Unexpected 'else' after 'return'.", - unexpected_label_a: "Unexpected label '{a}'.", - unexpected_property_a: "Unexpected /*property*/ '{a}'.", - unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", - unexpected_typeof_a: "Unexpected 'typeof'. " + - "Use '===' to compare directly with {a}.", - unnecessary_initialize: "It is not necessary to initialize '{a}' " + - "to 'undefined'.", - unnecessary_use: "Unnecessary 'use strict'.", - unreachable_a_b: "Unreachable '{a}' after '{b}'.", - unrecognized_style_attribute_a: "Unrecognized style attribute '{a}'.", - unrecognized_tag_a: "Unrecognized tag '<{a}>'.", - unsafe: "Unsafe character.", - url: "JavaScript URL.", - use_array: "Use the array literal notation [].", - use_braces: "Spaces are hard to count. Use {{a}}.", - use_charAt: "Use the charAt method.", - use_object: "Use the object literal notation {}.", - use_or: "Use the || operator.", - use_param: "Use a named parameter.", - used_before_a: "'{a}' was used before it was defined. (If you " + - "have added a new module, you need to add it to the whitelist " + - "in tools/jslint/check-all.js; if it's a new global you should " + - "change it to be an export of the appropriate module)", - var_a_not: "Variable {a} was not declared correctly.", - weird_assignment: "Weird assignment.", - weird_condition: "Weird condition.", - weird_new: "Weird construction. Delete 'new'.", - weird_program: "Weird program.", - weird_relation: "Weird relation.", - weird_ternary: "Weird ternary.", - wrap_immediate: "Wrap an immediate function invocation in parentheses " + - "to assist the reader in understanding that the expression " + - "is the result of a function, and not the function itself.", - wrap_regexp: "Wrap the /regexp/ literal in parens to " + - "disambiguate the slash operator.", - write_is_wrong: "document.write can be a form of eval." - }, - comments_off, - css_attribute_data, - css_any, - - css_colorData = array_to_object([ - "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", - "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", - "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", - "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", - "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", - "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", - "darkred", "darksalmon", "darkseagreen", "darkslateblue", - "darkslategray", "darkturquoise", "darkviolet", "deeppink", - "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", - "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", - "goldenrod", "gray", "green", "greenyellow", "honeydew", "hotpink", - "indianred", "indigo", "ivory", "khaki", "lavender", - "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", - "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgreen", - "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", - "lightslategray", "lightsteelblue", "lightyellow", "lime", - "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", - "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", - "mediumslateblue", "mediumspringgreen", "mediumturquoise", - "mediumvioletred", "midnightblue", "mintcream", "mistyrose", - "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab", - "orange", "orangered", "orchid", "palegoldenrod", "palegreen", - "paleturquoise", "palevioletred", "papayawhip", "peachpuff", - "peru", "pink", "plum", "powderblue", "purple", "red", "rosybrown", - "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", - "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", - "snow", "springgreen", "steelblue", "tan", "teal", "thistle", - "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", - "yellow", "yellowgreen", - - "activeborder", "activecaption", "appworkspace", "background", - "buttonface", "buttonhighlight", "buttonshadow", "buttontext", - "captiontext", "graytext", "highlight", "highlighttext", - "inactiveborder", "inactivecaption", "inactivecaptiontext", - "infobackground", "infotext", "menu", "menutext", "scrollbar", - "threeddarkshadow", "threedface", "threedhighlight", - "threedlightshadow", "threedshadow", "window", "windowframe", - "windowtext" - ], true), - - css_border_style, - css_break, - - css_lengthData = { - '%': true, - 'cm': true, - 'em': true, - 'ex': true, - 'in': true, - 'mm': true, - 'pc': true, - 'pt': true, - 'px': true - }, - - css_media, - css_overflow, - - descapes = { - 'b': '\b', - 't': '\t', - 'n': '\n', - 'f': '\f', - 'r': '\r', - '"': '"', - '/': '/', - '\\': '\\', - '!': '!' - }, - - devel = array_to_object([ - 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH' - ], false), - directive, - escapes = { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '\'': '\\\'', - '"' : '\\"', - '/' : '\\/', - '\\': '\\\\' - }, - - funct, // The current function, including the labels used in - // the function, as well as (breakage), - // (context), (loopage), (name), (params), (token), - // (vars), (verb) - - functionicity = [ - 'closure', 'exception', 'global', 'label', 'outer', 'undef', - 'unused', 'var' - ], - - functions, // All of the functions - global_funct, // The global body - global_scope, // The global scope - html_tag = { - a: {}, - abbr: {}, - acronym: {}, - address: {}, - applet: {}, - area: {empty: true, parent: ' map '}, - article: {}, - aside: {}, - audio: {}, - b: {}, - base: {empty: true, parent: ' head '}, - bdo: {}, - big: {}, - blockquote: {}, - body: {parent: ' html noframes '}, - br: {empty: true}, - button: {}, - canvas: {parent: ' body p div th td '}, - caption: {parent: ' table '}, - center: {}, - cite: {}, - code: {}, - col: {empty: true, parent: ' table colgroup '}, - colgroup: {parent: ' table '}, - command: {parent: ' menu '}, - datalist: {}, - dd: {parent: ' dl '}, - del: {}, - details: {}, - dialog: {}, - dfn: {}, - dir: {}, - div: {}, - dl: {}, - dt: {parent: ' dl '}, - em: {}, - embed: {}, - fieldset: {}, - figure: {}, - font: {}, - footer: {}, - form: {}, - frame: {empty: true, parent: ' frameset '}, - frameset: {parent: ' html frameset '}, - h1: {}, - h2: {}, - h3: {}, - h4: {}, - h5: {}, - h6: {}, - head: {parent: ' html '}, - header: {}, - hgroup: {}, - hr: {empty: true}, - 'hta:application': - {empty: true, parent: ' head '}, - html: {parent: '*'}, - i: {}, - iframe: {}, - img: {empty: true}, - input: {empty: true}, - ins: {}, - kbd: {}, - keygen: {}, - label: {}, - legend: {parent: ' details fieldset figure '}, - li: {parent: ' dir menu ol ul '}, - link: {empty: true, parent: ' head '}, - map: {}, - mark: {}, - menu: {}, - meta: {empty: true, parent: ' head noframes noscript '}, - meter: {}, - nav: {}, - noframes: {parent: ' html body '}, - noscript: {parent: ' body head noframes '}, - object: {}, - ol: {}, - optgroup: {parent: ' select '}, - option: {parent: ' optgroup select '}, - output: {}, - p: {}, - param: {empty: true, parent: ' applet object '}, - pre: {}, - progress: {}, - q: {}, - rp: {}, - rt: {}, - ruby: {}, - samp: {}, - script: {empty: true, parent: ' body div frame head iframe p pre span '}, - section: {}, - select: {}, - small: {}, - span: {}, - source: {}, - strong: {}, - style: {parent: ' head ', empty: true}, - sub: {}, - sup: {}, - table: {}, - tbody: {parent: ' table '}, - td: {parent: ' tr '}, - textarea: {}, - tfoot: {parent: ' table '}, - th: {parent: ' tr '}, - thead: {parent: ' table '}, - time: {}, - title: {parent: ' head '}, - tr: {parent: ' table tbody thead tfoot '}, - tt: {}, - u: {}, - ul: {}, - 'var': {}, - video: {} - }, - - ids, // HTML ids - in_block, - indent, - itself, // JSLint itself - json_mode, - lex, // the tokenizer - lines, - lookahead, - node = array_to_object([ - 'Buffer', 'clearInterval', 'clearTimeout', 'console', 'exports', - 'global', 'module', 'process', 'querystring', 'require', - 'setInterval', 'setTimeout', '__dirname', '__filename' - ], false), - node_js, - numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true), - next_token, - option, - predefined, // Global variables defined by option - prereg, - prev_token, - property, - regexp_flag = array_to_object(['g', 'i', 'm'], true), - return_this = function return_this() { - return this; - }, - rhino = array_to_object([ - 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass', - 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal', - 'serialize', 'spawn', 'sync', 'toint32', 'version' - ], false), - - scope, // An object containing an object for each variable in scope - semicolon_coda = array_to_object([';', '"', '\'', ')'], true), - src, - stack, - -// standard contains the global names that are provided by the -// ECMAScript standard. - - standard = array_to_object([ - 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent', - 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError', - 'Function', 'isFinite', 'isNaN', 'JSON', 'Math', 'Number', - 'Object', 'parseInt', 'parseFloat', 'RangeError', 'ReferenceError', - 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError' - ], false), - - strict_mode, - syntax = {}, - tab, - token, - urls, - var_mode, - warnings, - - windows = array_to_object([ - 'ActiveXObject', 'CScript', 'Debug', 'Enumerator', 'System', - 'VBArray', 'WScript', 'WSH' - ], false), - -// xmode is used to adapt to the exceptions in html parsing. -// It can have these states: -// '' .js script file -// 'html' -// 'outer' -// 'script' -// 'style' -// 'scriptstring' -// 'styleproperty' - - xmode, - xquote, - -// Regular expressions. Some of these are stupidly long. - -// unsafe comment or string - ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i, -// carriage return, carriage return linefeed, or linefeed - crlfx = /\r\n?|\n/, -// unsafe characters that are silently deleted by one or more browsers - cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, -// query characters for ids - dx = /[\[\]\/\\"'*<>.&:(){}+=#]/, -// html token - hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/, -// identifier - ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, -// javascript url - jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, -// star slash - lx = /\*\/|\/\*/, -// characters in strings that need escapement - nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, -// outer html token - ox = /[>&]|<[\/!]?|--/, -// attributes characters - qx = /[^a-zA-Z0-9+\-_\/. ]/, -// style - ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/, - sx = /^\s*([{}:#%.=,>+\[\]@()"';]|[*$\^~]=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/, - -// sync - syx = /Sync$/, -// comment todo - tox = /^\W*to\s*do(?:\W|$)/i, -// token - tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!(\!|==?)?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/, -// url badness - ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto|script/i, - - rx = { - outer: hx, - html: hx, - style: sx, - styleproperty: ssx - }; - - - function F() {} // Used by Object.create - -// Provide critical ES5 functions to ES3. - - if (typeof Array.prototype.filter !== 'function') { - Array.prototype.filter = function (f) { - var i, length = this.length, result = [], value; - for (i = 0; i < length; i += 1) { - try { - value = this[i]; - if (f(value)) { - result.push(value); - } - } catch (ignore) { - } - } - return result; - }; - } - - if (typeof Array.prototype.forEach !== 'function') { - Array.prototype.forEach = function (f) { - var i, length = this.length; - for (i = 0; i < length; i += 1) { - try { - f(this[i]); - } catch (ignore) { - } - } - }; - } - - if (typeof Array.isArray !== 'function') { - Array.isArray = function (o) { - return Object.prototype.toString.apply(o) === '[object Array]'; - }; - } - - if (!Object.prototype.hasOwnProperty.call(Object, 'create')) { - Object.create = function (o) { - F.prototype = o; - return new F(); - }; - } - - if (typeof Object.keys !== 'function') { - Object.keys = function (o) { - var array = [], key; - for (key in o) { - if (Object.prototype.hasOwnProperty.call(o, key)) { - array.push(key); - } - } - return array; - }; - } - - if (typeof String.prototype.entityify !== 'function') { - String.prototype.entityify = function () { - return this - .replace(/&/g, '&') - .replace(//g, '>'); - }; - } - - if (typeof String.prototype.isAlpha !== 'function') { - String.prototype.isAlpha = function () { - return (this >= 'a' && this <= 'z\uffff') || - (this >= 'A' && this <= 'Z\uffff'); - }; - } - - if (typeof String.prototype.isDigit !== 'function') { - String.prototype.isDigit = function () { - return (this >= '0' && this <= '9'); - }; - } - - if (typeof String.prototype.supplant !== 'function') { - String.prototype.supplant = function (o) { - return this.replace(/\{([^{}]*)\}/g, function (a, b) { - var replacement = o[b]; - return typeof replacement === 'string' || - typeof replacement === 'number' ? replacement : a; - }); - }; - } - - - function sanitize(a) { - -// Escapify a troublesome character. - - return escapes[a] || - '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); - } - - - function add_to_predefined(group) { - Object.keys(group).forEach(function (name) { - predefined[name] = group[name]; - }); - } - - - function assume() { - if (!option.safe) { - if (option.rhino) { - add_to_predefined(rhino); - option.rhino = false; - } - if (option.devel) { - add_to_predefined(devel); - option.devel = false; - } - if (option.browser) { - add_to_predefined(browser); - option.browser = false; - } - if (option.windows) { - add_to_predefined(windows); - option.windows = false; - } - if (option.node) { - add_to_predefined(node); - option.node = false; - node_js = true; - } - } - } - - -// Produce an error warning. - - function artifact(tok) { - if (!tok) { - tok = next_token; - } - return tok.number || tok.string; - } - - function quit(message, line, character) { - throw { - name: 'JSLintError', - line: line, - character: character, - message: bundle.scanned_a_b.supplant({ - a: message, - b: Math.floor((line / lines.length) * 100) - }) - }; - } - - function warn(message, offender, a, b, c, d) { - var character, line, warning; - offender = offender || next_token; // ~~ - line = offender.line || 0; - character = offender.from || 0; - warning = { - id: '(error)', - raw: bundle[message] || message, - evidence: lines[line - 1] || '', - line: line, - character: character, - a: a || (offender.id === '(number)' - ? String(offender.number) - : offender.string), - b: b, - c: c, - d: d - }; - warning.reason = warning.raw.supplant(warning); - JSLINT.errors.push(warning); - if (option.passfail) { - quit(bundle.stopping, line, character); - } - warnings += 1; - if (warnings >= option.maxerr) { - quit(bundle.too_many, line, character); - } - return warning; - } - - function warn_at(message, line, character, a, b, c, d) { - return warn(message, { - line: line, - from: character - }, a, b, c, d); - } - - function stop(message, offender, a, b, c, d) { - var warning = warn(message, offender, a, b, c, d); - quit(bundle.stopping, warning.line, warning.character); - } - - function stop_at(message, line, character, a, b, c, d) { - return stop(message, { - line: line, - from: character - }, a, b, c, d); - } - - function expected_at(at) { - if (!option.white && next_token.from !== at) { - warn('expected_a_at_b_c', next_token, '', at, - next_token.from); - } - } - - function aint(it, name, expected) { - if (it[name] !== expected) { - warn('expected_a_b', it, expected, it[name]); - return true; - } - return false; - } - - -// lexical analysis and token construction - - lex = (function lex() { - var character, c, from, length, line, pos, source_row; - -// Private lex methods - - function next_line() { - var at; - if (line >= lines.length) { - return false; - } - character = 1; - source_row = lines[line]; - line += 1; - at = source_row.search(/ \t/); - if (at >= 0) { - warn_at('mixed', line, at + 1); - } - source_row = source_row.replace(/\t/g, tab); - at = source_row.search(cx); - if (at >= 0) { - warn_at('unsafe', line, at); - } - if (option.maxlen && option.maxlen < source_row.length) { - warn_at('too_long', line, source_row.length); - } - return true; - } - -// Produce a token object. The token inherits from a syntax symbol. - - function it(type, value) { - var id, the_token; - if (type === '(string)' || type === '(range)') { - if (jx.test(value)) { - warn_at('url', line, from); - } - } - the_token = Object.create(syntax[( - type === '(punctuator)' || (type === '(identifier)' && - Object.prototype.hasOwnProperty.call(syntax, value)) - ? value - : type - )] || syntax['(error)']); - if (type === '(identifier)') { - the_token.identifier = true; - if (value === '__iterator__' || value === '__proto__') { - stop_at('reserved_a', line, from, value); - } else if (!option.nomen && - (value.charAt(0) === '_' || - value.charAt(value.length - 1) === '_')) { - warn_at('dangling_a', line, from, value); - } - } - if (type === '(number)') { - the_token.number = +value; - } else if (value !== undefined) { - the_token.string = String(value); - } - the_token.line = line; - the_token.from = from; - the_token.thru = character; - id = the_token.id; - prereg = id && ( - ('(,=:[!&|?{};~+-*%^<>'.indexOf(id.charAt(id.length - 1)) >= 0) || - id === 'return' || id === 'case' - ); - return the_token; - } - - function match(x) { - var exec = x.exec(source_row), first; - if (exec) { - length = exec[0].length; - first = exec[1]; - c = first.charAt(0); - source_row = source_row.slice(length); - from = character + length - first.length; - character += length; - return first; - } - } - - function string(x) { - var c, pos = 0, r = '', result; - - function hex(n) { - var i = parseInt(source_row.substr(pos + 1, n), 16); - pos += n; - if (i >= 32 && i <= 126 && - i !== 34 && i !== 92 && i !== 39) { - warn_at('unexpected_a', line, character, '\\'); - } - character += n; - c = String.fromCharCode(i); - } - - if (json_mode && x !== '"') { - warn_at('expected_a', line, character, '"'); - } - - if (xquote === x || (xmode === 'scriptstring' && !xquote)) { - return it('(punctuator)', x); - } - - for (;;) { - while (pos >= source_row.length) { - pos = 0; - if (xmode !== 'html' || !next_line()) { - stop_at('unclosed', line, from); - } - } - c = source_row.charAt(pos); - if (c === x) { - character += 1; - source_row = source_row.slice(pos + 1); - result = it('(string)', r); - result.quote = x; - return result; - } - if (c < ' ') { - if (c === '\n' || c === '\r') { - break; - } - warn_at('control_a', line, character + pos, - source_row.slice(0, pos)); - } else if (c === xquote) { - warn_at('bad_html', line, character + pos); - } else if (c === '<') { - if (option.safe && xmode === 'html') { - warn_at('adsafe_a', line, character + pos, c); - } else if (source_row.charAt(pos + 1) === '/' && (xmode || option.safe)) { - warn_at('expected_a_b', line, character, - '<\\/', '= '0' && c <= '7' ? 'octal_a' : 'unexpected_a', - line, character, '\\' + c); - } else { - c = descapes[c]; - } - } - } - } - r += c; - character += 1; - pos += 1; - } - } - - function number(snippet) { - var digit; - if (xmode !== 'style' && xmode !== 'styleproperty' && - source_row.charAt(0).isAlpha()) { - warn_at('expected_space_a_b', - line, character, c, source_row.charAt(0)); - } - if (c === '0') { - digit = snippet.charAt(1); - if (digit.isDigit()) { - if (token.id !== '.' && xmode !== 'styleproperty') { - warn_at('unexpected_a', line, character, snippet); - } - } else if (json_mode && (digit === 'x' || digit === 'X')) { - warn_at('unexpected_a', line, character, '0x'); - } - } - if (snippet.slice(snippet.length - 1) === '.') { - warn_at('trailing_decimal_a', line, character, snippet); - } - if (xmode !== 'style') { - digit = +snippet; - if (!isFinite(digit)) { - warn_at('bad_number', line, character, snippet); - } - snippet = digit; - } - return it('(number)', snippet); - } - - function comment(snippet) { - if (comments_off || src || (xmode && xmode !== 'script' && - xmode !== 'style' && xmode !== 'styleproperty')) { - warn_at('unexpected_comment', line, character); - } else if (xmode === 'script' && /<\//i.test(source_row)) { - warn_at('unexpected_a', line, character, '<\/'); - } else if (option.safe && ax.test(snippet)) { - warn_at('dangerous_comment', line, character); - } else if (!option.todo && tox.test(snippet)) { - warn_at('todo_comment', line, character); - } - } - - function regexp() { - var b, - bit, - captures = 0, - depth = 0, - flag = '', - high, - letter, - length = 0, - low, - potential, - quote, - result; - for (;;) { - b = true; - c = source_row.charAt(length); - length += 1; - switch (c) { - case '': - stop_at('unclosed_regexp', line, from); - return; - case '/': - if (depth > 0) { - warn_at('unescaped_a', line, from + length, '/'); - } - c = source_row.slice(0, length - 1); - potential = Object.create(regexp_flag); - for (;;) { - letter = source_row.charAt(length); - if (potential[letter] !== true) { - break; - } - potential[letter] = false; - length += 1; - flag += letter; - } - if (source_row.charAt(length).isAlpha()) { - stop_at('unexpected_a', line, from, source_row.charAt(length)); - } - character += length; - source_row = source_row.slice(length); - quote = source_row.charAt(0); - if (quote === '/' || quote === '*') { - stop_at('confusing_regexp', line, from); - } - result = it('(regexp)', c); - result.flag = flag; - return result; - case '\\': - c = source_row.charAt(length); - if (c < ' ') { - warn_at('control_a', line, from + length, String(c)); - } else if (c === '<') { - warn_at(bundle.unexpected_a, line, from + length, '\\'); - } - length += 1; - break; - case '(': - depth += 1; - b = false; - if (source_row.charAt(length) === '?') { - length += 1; - switch (source_row.charAt(length)) { - case ':': - case '=': - case '!': - length += 1; - break; - default: - warn_at(bundle.expected_a_b, line, from + length, - ':', source_row.charAt(length)); - } - } else { - captures += 1; - } - break; - case '|': - b = false; - break; - case ')': - if (depth === 0) { - warn_at('unescaped_a', line, from + length, ')'); - } else { - depth -= 1; - } - break; - case ' ': - pos = 1; - while (source_row.charAt(length) === ' ') { - length += 1; - pos += 1; - } - if (pos > 1) { - warn_at('use_braces', line, from + length, pos); - } - break; - case '[': - c = source_row.charAt(length); - if (c === '^') { - length += 1; - if (!option.regexp) { - warn_at('insecure_a', line, from + length, c); - } else if (source_row.charAt(length) === ']') { - stop_at('unescaped_a', line, from + length, '^'); - } - } - bit = false; - if (c === ']') { - warn_at('empty_class', line, from + length - 1); - bit = true; - } -klass: do { - c = source_row.charAt(length); - length += 1; - switch (c) { - case '[': - case '^': - warn_at('unescaped_a', line, from + length, c); - bit = true; - break; - case '-': - if (bit) { - bit = false; - } else { - warn_at('unescaped_a', line, from + length, '-'); - bit = true; - } - break; - case ']': - if (!bit) { - warn_at('unescaped_a', line, from + length - 1, '-'); - } - break klass; - case '\\': - c = source_row.charAt(length); - if (c < ' ') { - warn_at(bundle.control_a, line, from + length, String(c)); - } else if (c === '<') { - warn_at(bundle.unexpected_a, line, from + length, '\\'); - } - length += 1; - bit = true; - break; - case '/': - warn_at('unescaped_a', line, from + length - 1, '/'); - bit = true; - break; - case '<': - if (xmode === 'script') { - c = source_row.charAt(length); - if (c === '!' || c === '/') { - warn_at(bundle.html_confusion_a, line, - from + length, c); - } - } - bit = true; - break; - default: - bit = true; - } - } while (c); - break; - case '.': - if (!option.regexp) { - warn_at('insecure_a', line, from + length, c); - } - break; - case ']': - case '?': - case '{': - case '}': - case '+': - case '*': - warn_at('unescaped_a', line, from + length, c); - break; - case '<': - if (xmode === 'script') { - c = source_row.charAt(length); - if (c === '!' || c === '/') { - warn_at(bundle.html_confusion_a, line, from + length, c); - } - } - break; - } - if (b) { - switch (source_row.charAt(length)) { - case '?': - case '+': - case '*': - length += 1; - if (source_row.charAt(length) === '?') { - length += 1; - } - break; - case '{': - length += 1; - c = source_row.charAt(length); - if (c < '0' || c > '9') { - warn_at(bundle.expected_number_a, line, - from + length, c); - } - length += 1; - low = +c; - for (;;) { - c = source_row.charAt(length); - if (c < '0' || c > '9') { - break; - } - length += 1; - low = +c + (low * 10); - } - high = low; - if (c === ',') { - length += 1; - high = Infinity; - c = source_row.charAt(length); - if (c >= '0' && c <= '9') { - length += 1; - high = +c; - for (;;) { - c = source_row.charAt(length); - if (c < '0' || c > '9') { - break; - } - length += 1; - high = +c + (high * 10); - } - } - } - if (source_row.charAt(length) !== '}') { - warn_at(bundle.expected_a_b, line, from + length, - '}', c); - } else { - length += 1; - } - if (source_row.charAt(length) === '?') { - length += 1; - } - if (low > high) { - warn_at(bundle.not_greater, line, from + length, - low, high); - } - break; - } - } - } - c = source_row.slice(0, length - 1); - character += length; - source_row = source_row.slice(length); - return it('(regexp)', c); - } - -// Public lex methods - - return { - init: function (source) { - if (typeof source === 'string') { - lines = source.split(crlfx); - } else { - lines = source; - } - line = 0; - next_line(); - from = 1; - }, - - range: function (begin, end) { - var c, value = ''; - from = character; - if (source_row.charAt(0) !== begin) { - stop_at('expected_a_b', line, character, begin, - source_row.charAt(0)); - } - for (;;) { - source_row = source_row.slice(1); - character += 1; - c = source_row.charAt(0); - switch (c) { - case '': - stop_at('missing_a', line, character, c); - break; - case end: - source_row = source_row.slice(1); - character += 1; - return it('(range)', value); - case xquote: - case '\\': - warn_at('unexpected_a', line, character, c); - break; - } - value += c; - } - }, - -// token -- this is called by advance to get the next token. - - token: function () { - var c, i, snippet; - - for (;;) { - while (!source_row) { - if (!next_line()) { - return it('(end)'); - } - } - while (xmode === 'outer') { - i = source_row.search(ox); - if (i === 0) { - break; - } else if (i > 0) { - character += 1; - source_row = source_row.slice(i); - break; - } else { - if (!next_line()) { - return it('(end)', ''); - } - } - } - snippet = match(rx[xmode] || tx); - if (!snippet) { - if (source_row) { - if (source_row.charAt(0) === ' ') { - if (!option.white) { - warn_at('unexpected_a', line, character, - '(space)'); - } - character += 1; - source_row = ''; - } else { - stop_at('unexpected_a', line, character, - source_row.charAt(0)); - } - } - } else { - -// identifier - - c = snippet.charAt(0); - if (c.isAlpha() || c === '_' || c === '$') { - return it('(identifier)', snippet); - } - -// number - - if (c.isDigit()) { - return number(snippet); - } - switch (snippet) { - -// string - - case '"': - case "'": - return string(snippet); - -// // comment - - case '//': - comment(source_row); - source_row = ''; - break; - -// /* comment - - case '/*': - for (;;) { - i = source_row.search(lx); - if (i >= 0) { - break; - } - comment(source_row); - if (!next_line()) { - stop_at('unclosed_comment', line, character); - } - } - comment(source_row.slice(0, i)); - character += i + 2; - if (source_row.charAt(i) === '/') { - stop_at('nested_comment', line, character); - } - source_row = source_row.slice(i + 2); - break; - - case '': - break; -// / - case '/': - if (token.id === '/=') { - stop_at( - bundle.slash_equal, - line, - from - ); - } - return prereg - ? regexp() - : it('(punctuator)', snippet); - -// punctuator - - case ''); - } - character += 3; - source_row = source_row.slice(i + 3); - break; - case '#': - if (xmode === 'html' || xmode === 'styleproperty') { - for (;;) { - c = source_row.charAt(0); - if ((c < '0' || c > '9') && - (c < 'a' || c > 'f') && - (c < 'A' || c > 'F')) { - break; - } - character += 1; - source_row = source_row.slice(1); - snippet += c; - } - if (snippet.length !== 4 && snippet.length !== 7) { - warn_at('bad_color_a', line, - from + length, snippet); - } - return it('(color)', snippet); - } - return it('(punctuator)', snippet); - - default: - if (xmode === 'outer' && c === '&') { - character += 1; - source_row = source_row.slice(1); - for (;;) { - c = source_row.charAt(0); - character += 1; - source_row = source_row.slice(1); - if (c === ';') { - break; - } - if (!((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - c === '#')) { - stop_at('bad_entity', line, from + length, - character); - } - } - break; - } - return it('(punctuator)', snippet); - } - } - } - } - }; - }()); - - - function add_label(token, kind, name) { - -// Define the symbol in the current function in the current scope. - - name = name || token.string; - -// Global variables cannot be created in the safe subset. If a global variable -// already exists, do nothing. If it is predefined, define it. - - if (funct === global_funct) { - if (option.safe) { - warn('adsafe_a', token, name); - } - if (typeof global_funct[name] !== 'string') { - token.writeable = typeof predefined[name] === 'boolean' - ? predefined[name] - : true; - token.funct = funct; - global_scope[name] = token; - } - if (kind === 'becoming') { - kind = 'var'; - } - -// Ordinary variables. - - } else { - -// Warn if the variable already exists. - - if (typeof funct[name] === 'string') { - if (funct[name] === 'undef') { - if (!option.undef) { - warn('used_before_a', token, name); - } - kind = 'var'; - } else { - warn('already_defined', token, name); - } - } else { - -// Add the symbol to the current function. - - token.funct = funct; - token.writeable = true; - scope[name] = token; - } - } - funct[name] = kind; - } - - - function peek(distance) { - -// Peek ahead to a future token. The distance is how far ahead to look. The -// default is the next token. - - var found, slot = 0; - - distance = distance || 0; - while (slot <= distance) { - found = lookahead[slot]; - if (!found) { - found = lookahead[slot] = lex.token(); - } - slot += 1; - } - return found; - } - - - function advance(id, match) { - -// Produce the next token, also looking for programming errors. - - if (indent) { - -// If indentation checking was requested, then inspect all of the line breakings. -// The var statement is tricky because the names might be aligned or not. We -// look at the first line break after the var to determine the programmer's -// intention. - - if (var_mode && next_token.line !== token.line) { - if ((var_mode !== indent || !next_token.edge) && - next_token.from === indent.at - - (next_token.edge ? option.indent : 0)) { - var dent = indent; - for (;;) { - dent.at -= option.indent; - if (dent === var_mode) { - break; - } - dent = dent.was; - } - dent.open = false; - } - var_mode = null; - } - if (next_token.id === '?' && indent.mode === ':' && - token.line !== next_token.line) { - indent.at -= option.indent; - } - if (indent.open) { - -// If the token is an edge. - - if (next_token.edge) { - if (next_token.edge === 'label') { - expected_at(1); - } else if (next_token.edge === 'case' || indent.mode === 'statement') { - expected_at(indent.at - option.indent); - } else if (indent.mode !== 'array' || next_token.line !== token.line) { - expected_at(indent.at); - } - -// If the token is not an edge, but is the first token on the line. - - } else if (next_token.line !== token.line) { - if (next_token.from < indent.at + (indent.mode === - 'expression' ? 0 : option.indent)) { - expected_at(indent.at + option.indent); - } - indent.wrap = true; - } - } else if (next_token.line !== token.line) { - if (next_token.edge) { - expected_at(indent.at); - } else { - indent.wrap = true; - if (indent.mode === 'statement' || indent.mode === 'var') { - expected_at(indent.at + option.indent); - } else if (next_token.from < indent.at + (indent.mode === - 'expression' ? 0 : option.indent)) { - expected_at(indent.at + option.indent); - } - } - } - } - - switch (token.id) { - case '(number)': - if (next_token.id === '.') { - warn('trailing_decimal_a'); - } - break; - case '-': - if (next_token.id === '-' || next_token.id === '--') { - warn('confusing_a'); - } - break; - case '+': - if (next_token.id === '+' || next_token.id === '++') { - warn('confusing_a'); - } - break; - } - if (token.id === '(string)' || token.identifier) { - anonname = token.string; - } - - if (id && next_token.id !== id) { - if (match) { - warn('expected_a_b_from_c_d', next_token, id, - match.id, match.line, artifact()); - } else if (!next_token.identifier || next_token.string !== id) { - warn('expected_a_b', next_token, id, artifact()); - } - } - prev_token = token; - token = next_token; - next_token = lookahead.shift() || lex.token(); - } - - - function advance_identifier(string) { - if (next_token.identifier && next_token.string === string) { - advance(); - } else { - warn('expected_a_b', next_token, string, artifact()); - } - } - - - function do_safe() { - if (option.adsafe) { - option.safe = true; - } - if (option.safe) { - option.browser = - option['continue'] = - option.css = - option.debug = - option.devel = - option.evil = - option.forin = - option.newcap = - option.nomen = - option.on = - option.rhino = - option.sloppy = - option.sub = - option.undef = - option.windows = false; - - - delete predefined.Array; - delete predefined.Date; - delete predefined.Function; - delete predefined.Object; - delete predefined['eval']; - - add_to_predefined({ - ADSAFE: false, - lib: false - }); - } - } - - - function do_globals() { - var name, writeable; - for (;;) { - if (next_token.id !== '(string)' && !next_token.identifier) { - return; - } - name = next_token.string; - advance(); - writeable = false; - if (next_token.id === ':') { - advance(':'); - switch (next_token.id) { - case 'true': - writeable = predefined[name] !== false; - advance('true'); - break; - case 'false': - advance('false'); - break; - default: - stop('unexpected_a'); - } - } - predefined[name] = writeable; - if (next_token.id !== ',') { - return; - } - advance(','); - } - } - - - function do_jslint() { - var name, value; - while (next_token.id === '(string)' || next_token.identifier) { - name = next_token.string; - if (!allowed_option[name]) { - stop('unexpected_a'); - } - advance(); - if (next_token.id !== ':') { - stop('expected_a_b', next_token, ':', artifact()); - } - advance(':'); - if (typeof allowed_option[name] === 'number') { - value = next_token.number; - if (value > allowed_option[name] || value <= 0 || - Math.floor(value) !== value) { - stop('expected_small_a'); - } - option[name] = value; - } else { - if (next_token.id === 'true') { - option[name] = true; - } else if (next_token.id === 'false') { - option[name] = false; - } else { - stop('unexpected_a'); - } - } - advance(); - if (next_token.id === ',') { - advance(','); - } - } - assume(); - } - - - function do_properties() { - var name; - option.properties = true; - for (;;) { - if (next_token.id !== '(string)' && !next_token.identifier) { - return; - } - name = next_token.string; - advance(); - if (next_token.id === ':') { - for (;;) { - advance(); - if (next_token.id !== '(string)' && !next_token.identifier) { - break; - } - } - } - property[name] = 0; - if (next_token.id !== ',') { - return; - } - advance(','); - } - } - - - directive = function directive() { - var command = this.id, - old_comments_off = comments_off, - old_indent = indent; - comments_off = true; - indent = null; - if (next_token.line === token.line && next_token.from === token.thru) { - warn('missing_space_a_b', next_token, artifact(token), artifact()); - } - if (lookahead.length > 0) { - warn('unexpected_a', this); - } - switch (command) { - case '/*properties': - case '/*property': - case '/*members': - case '/*member': - do_properties(); - break; - case '/*jslint': - if (option.safe) { - warn('adsafe_a', this); - } - do_jslint(); - break; - case '/*globals': - case '/*global': - if (option.safe) { - warn('adsafe_a', this); - } - do_globals(); - break; - default: - stop('unexpected_a', this); - } - comments_off = old_comments_off; - advance('*/'); - indent = old_indent; - }; - - -// Indentation intention - - function edge(mode) { - next_token.edge = indent ? indent.open && (mode || 'edge') : ''; - } - - - function step_in(mode) { - var open; - if (typeof mode === 'number') { - indent = { - at: +mode, - open: true, - was: indent - }; - } else if (!indent) { - indent = { - at: 1, - mode: 'statement', - open: true - }; - } else if (mode === 'statement') { - indent = { - at: indent.at, - open: true, - was: indent - }; - } else { - open = mode === 'var' || next_token.line !== token.line; - indent = { - at: (open || mode === 'control' - ? indent.at + option.indent - : indent.at) + (indent.wrap ? option.indent : 0), - mode: mode, - open: open, - was: indent - }; - if (mode === 'var' && open) { - var_mode = indent; - } - } - } - - function step_out(id, symbol) { - if (id) { - if (indent && indent.open) { - indent.at -= option.indent; - edge(); - } - advance(id, symbol); - } - if (indent) { - indent = indent.was; - } - } - -// Functions for conformance of whitespace. - - function one_space(left, right) { - left = left || token; - right = right || next_token; - if (right.id !== '(end)' && !option.white && - (token.line !== right.line || - token.thru + 1 !== right.from)) { - warn('expected_space_a_b', right, artifact(token), artifact(right)); - } - } - - function one_space_only(left, right) { - left = left || token; - right = right || next_token; - if (right.id !== '(end)' && (left.line !== right.line || - (!option.white && left.thru + 1 !== right.from))) { - warn('expected_space_a_b', right, artifact(left), artifact(right)); - } - } - - function no_space(left, right) { - left = left || token; - right = right || next_token; - if ((!option.white || xmode === 'styleproperty' || xmode === 'style') && - left.thru !== right.from && left.line === right.line) { - warn('unexpected_space_a_b', right, artifact(left), artifact(right)); - } - } - - function no_space_only(left, right) { - left = left || token; - right = right || next_token; - if (right.id !== '(end)' && (left.line !== right.line || - (!option.white && left.thru !== right.from))) { - warn('unexpected_space_a_b', right, artifact(left), artifact(right)); - } - } - - function spaces(left, right) { - if (!option.white) { - left = left || token; - right = right || next_token; - if (left.thru === right.from && left.line === right.line) { - warn('missing_space_a_b', right, artifact(left), artifact(right)); - } - } - } - - function comma() { - if (next_token.id !== ',') { - warn_at('expected_a_b', token.line, token.thru, ',', artifact()); - } else { - if (!option.white) { - no_space_only(); - } - advance(','); - spaces(); - } - } - - - function semicolon() { - if (next_token.id !== ';') { - warn_at('expected_a_b', token.line, token.thru, ';', artifact()); - } else { - if (!option.white) { - no_space_only(); - } - advance(';'); - if (semicolon_coda[next_token.id] !== true) { - spaces(); - } - } - } - - function use_strict() { - if (next_token.string === 'use strict') { - if (strict_mode) { - warn('unnecessary_use'); - } - edge(); - advance(); - semicolon(); - strict_mode = true; - option.undef = false; - return true; - } - return false; - } - - - function are_similar(a, b) { - if (a === b) { - return true; - } - if (Array.isArray(a)) { - if (Array.isArray(b) && a.length === b.length) { - var i; - for (i = 0; i < a.length; i += 1) { - if (!are_similar(a[i], b[i])) { - return false; - } - } - return true; - } - return false; - } - if (Array.isArray(b)) { - return false; - } - if (a.id === '(number)' && b.id === '(number)') { - return a.number === b.number; - } - if (a.arity === b.arity && a.string === b.string) { - switch (a.arity) { - case 'prefix': - case 'suffix': - case undefined: - return a.id === b.id && are_similar(a.first, b.first) && - a.id !== '{' && a.id !== '['; - case 'infix': - return are_similar(a.first, b.first) && - are_similar(a.second, b.second); - case 'ternary': - return are_similar(a.first, b.first) && - are_similar(a.second, b.second) && - are_similar(a.third, b.third); - case 'function': - case 'regexp': - return false; - default: - return true; - } - } else { - if (a.id === '.' && b.id === '[' && b.arity === 'infix') { - return a.second.string === b.second.string && b.second.id === '(string)'; - } - if (a.id === '[' && a.arity === 'infix' && b.id === '.') { - return a.second.string === b.second.string && a.second.id === '(string)'; - } - } - return false; - } - - -// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it -// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is -// like .nud except that it is only used on the first token of a statement. -// Having .fud makes it much easier to define statement-oriented languages like -// JavaScript. I retained Pratt's nomenclature. - -// .nud Null denotation -// .fud First null denotation -// .led Left denotation -// lbp Left binding power -// rbp Right binding power - -// They are elements of the parsing method called Top Down Operator Precedence. - - function expression(rbp, initial) { - -// rbp is the right binding power. -// initial indicates that this is the first expression of a statement. - - var left; - if (next_token.id === '(end)') { - stop('unexpected_a', token, next_token.id); - } - advance(); - if (option.safe && scope[token.string] && - scope[token.string] === global_scope[token.string] && - (next_token.id !== '(' && next_token.id !== '.')) { - warn('adsafe_a', token); - } - if (initial) { - anonname = 'anonymous'; - funct['(verb)'] = token.string; - } - if (initial === true && token.fud) { - left = token.fud(); - } else { - if (token.nud) { - left = token.nud(); - } else { - if (next_token.id === '(number)' && token.id === '.') { - warn('leading_decimal_a', token, artifact()); - advance(); - return token; - } - stop('expected_identifier_a', token, token.id); - } - while (rbp < next_token.lbp) { - advance(); - if (token.led) { - left = token.led(left); - } else { - stop('expected_operator_a', token, token.id); - } - } - } - return left; - } - - -// Functional constructors for making the symbols that will be inherited by -// tokens. - - function symbol(s, p) { - var x = syntax[s]; - if (!x || typeof x !== 'object') { - syntax[s] = x = { - id: s, - lbp: p || 0, - string: s - }; - } - return x; - } - - function postscript(x) { - x.postscript = true; - return x; - } - - function ultimate(s) { - var x = symbol(s, 0); - x.from = 1; - x.thru = 1; - x.line = 0; - x.edge = 'edge'; - x.string = s; - return postscript(x); - } - - - function stmt(s, f) { - var x = symbol(s); - x.identifier = x.reserved = true; - x.fud = f; - return x; - } - - function labeled_stmt(s, f) { - var x = stmt(s, f); - x.labeled = true; - } - - function disrupt_stmt(s, f) { - var x = stmt(s, f); - x.disrupt = true; - } - - - function reserve_name(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); - reserve_name(x); - x.nud = function () { - var that = this; - that.arity = 'prefix'; - if (typeof f === 'function') { - that = f(that); - if (that.arity !== 'prefix') { - return that; - } - } else { - if (s === 'typeof') { - one_space(); - } else { - no_space_only(); - } - that.first = expression(150); - } - switch (that.id) { - case '++': - case '--': - if (!option.plusplus) { - warn('unexpected_a', that); - } else if ((!that.first.identifier || that.first.reserved) && - that.first.id !== '.' && that.first.id !== '[') { - warn('bad_operand', that); - } - break; - default: - if (that.first.arity === 'prefix' || - that.first.arity === 'function') { - warn('unexpected_a', that); - } - } - return that; - }; - return x; - } - - - function type(s, t, nud) { - var x = symbol(s); - x.arity = t; - if (nud) { - x.nud = nud; - } - return x; - } - - - function reserve(s, f) { - var x = symbol(s); - x.identifier = x.reserved = true; - if (typeof f === 'function') { - x.nud = f; - } - return x; - } - - - function constant(name) { - var x = reserve(name); - x.string = name; - x.nud = return_this; - return x; - } - - - function reservevar(s, v) { - return reserve(s, function () { - if (typeof v === 'function') { - v(this); - } - return this; - }); - } - - - function infix(s, p, f, w) { - var x = symbol(s, p); - reserve_name(x); - x.led = function (left) { - this.arity = 'infix'; - if (!w) { - spaces(prev_token, token); - spaces(); - } - if (!option.bitwise && this.bitwise) { - warn('unexpected_a', this); - } - if (typeof f === 'function') { - return f(left, this); - } - this.first = left; - this.second = expression(p); - return this; - }; - return x; - } - - function expected_relation(node, message) { - if (node.assign) { - warn(message || bundle.conditional_assignment, node); - } - return node; - } - - function expected_condition(node, message) { - switch (node.id) { - case '[': - case '-': - if (node.arity !== 'infix') { - warn(message || bundle.weird_condition, node); - } - break; - case 'false': - case 'function': - case 'Infinity': - case 'NaN': - case 'null': - case 'true': - case 'undefined': - case 'void': - case '(number)': - case '(regexp)': - case '(string)': - case '{': - warn(message || bundle.weird_condition, node); - break; - case '(': - if (node.first.id === 'new' || - (node.first.string === 'Boolean') || - (node.first.id === '.' && - numbery[node.first.second.string] === true)) { - warn(message || bundle.weird_condition, node); - } - break; - } - return node; - } - - function check_relation(node) { - switch (node.arity) { - case 'prefix': - switch (node.id) { - case '{': - case '[': - warn('unexpected_a', node); - break; - case '!': - warn('confusing_a', node); - break; - } - break; - case 'function': - case 'regexp': - warn('unexpected_a', node); - break; - default: - if (node.id === 'NaN') { - warn('isNaN', node); - } - } - return node; - } - - - function relation(s, eqeq) { - return infix(s, 100, function (left, that) { - check_relation(left); - if (eqeq && !option.eqeq) { - warn('expected_a_b', that, eqeq, that.id); - } - var right = expression(100); - if (are_similar(left, right) || - ((left.id === '(string)' || left.id === '(number)') && - (right.id === '(string)' || right.id === '(number)'))) { - warn('weird_relation', that); - } else if (left.id === 'typeof') { - if (right.id !== '(string)') { - warn("expected_string_a", right, right.id === '(number)' - ? right.number - : right.string); - } else if (right.string === 'undefined' || - right.string === 'null') { - warn("unexpected_typeof_a", left, right.string); - } - } - that.first = left; - that.second = check_relation(right); - return that; - }); - } - - - function assignop(s, op) { - var x = infix(s, 20, function (left, that) { - var l; - that.first = left; - if (left.identifier) { - if (scope[left.string]) { - if (scope[left.string].writeable === false) { - warn('read_only', left); - } - } else { - stop('read_only'); - } - if (funct['(params)']) { - funct['(params)'].forEach(function (value) { - if (value.string === left.string) { - value.assign = true; - } - }); - } - } else if (option.safe) { - l = left; - do { - if (typeof predefined[l.string] === 'boolean') { - warn('adsafe_a', l); - } - l = l.first; - } while (l); - } - if (left === syntax['function']) { - warn('identifier_function', token); - } - if (left.id === '.' || left.id === '[') { - if (!left.first || left.first.string === 'arguments') { - warn('bad_assignment', that); - } - } else if (left.identifier) { - if (!left.reserved && funct[left.string] === 'exception') { - warn('assign_exception', left); - } - } else { - warn('bad_assignment', that); - } - that.second = expression(19); - if (that.id === '=' && are_similar(that.first, that.second)) { - warn('weird_assignment', that); - } - return that; - }); - x.assign = true; - if (op) { - if (syntax[op].bitwise) { - x.bitwise = true; - } - } - return x; - } - - - function bitwise(s, p) { - var x = infix(s, p, 'number'); - x.bitwise = true; - return x; - } - - - function suffix(s) { - var x = symbol(s, 150); - x.led = function (left) { - no_space_only(prev_token, token); - if (!option.plusplus) { - warn('unexpected_a', this); - } else if ((!left.identifier || left.reserved) && - left.id !== '.' && left.id !== '[') { - warn('bad_operand', this); - } - this.first = left; - this.arity = 'suffix'; - return this; - }; - return x; - } - - - function optional_identifier() { - if (next_token.identifier) { - advance(); - if (option.safe && banned[token.string]) { - warn('adsafe_a', token); - } else if (token.reserved && !option.es5) { - warn('expected_identifier_a_reserved', token); - } - return token.string; - } - } - - - function identifier() { - var i = optional_identifier(); - if (!i) { - stop(token.id === 'function' && next_token.id === '(' - ? 'name_function' - : 'expected_identifier_a'); - } - return i; - } - - - function statement() { - - var label, old_scope = scope, the_statement; - -// We don't like the empty statement. - - if (next_token.id === ';') { - warn('unexpected_a'); - semicolon(); - return; - } - -// Is this a labeled statement? - - if (next_token.identifier && !next_token.reserved && peek().id === ':') { - edge('label'); - label = next_token; - advance(); - advance(':'); - scope = Object.create(old_scope); - add_label(label, 'label'); - if (next_token.labeled !== true || funct === global_funct) { - stop('unexpected_label_a', label); - } else if (jx.test(label.string + ':')) { - warn('url', label); - } - next_token.label = label; - } - -// Parse the statement. - - if (token.id !== 'else') { - edge(); - } - step_in('statement'); - the_statement = expression(0, true); - if (the_statement) { - -// Look for the final semicolon. - - if (the_statement.arity === 'statement') { - if (the_statement.id === 'switch' || - (the_statement.block && the_statement.id !== 'do')) { - spaces(); - } else { - semicolon(); - } - } else { - -// If this is an expression statement, determine if it is acceptable. -// We do not like -// new Blah; -// statments. If it is to be used at all, new should only be used to make -// objects, not side effects. The expression statements we do like do -// assignment or invocation or delete. - - if (the_statement.id === '(') { - if (the_statement.first.id === 'new') { - warn('bad_new'); - } - } else if (!the_statement.assign && - the_statement.id !== 'delete' && - the_statement.id !== '++' && - the_statement.id !== '--') { - warn('assignment_function_expression', the_statement); - } - semicolon(); - } - } - step_out(); - scope = old_scope; - return the_statement; - } - - - function statements() { - var array = [], disruptor, the_statement; - -// A disrupt statement may not be followed by any other statement. -// If the last statement is disrupt, then the sequence is disrupt. - - while (next_token.postscript !== true) { - if (next_token.id === ';') { - warn('unexpected_a', next_token); - semicolon(); - } else { - if (next_token.string === 'use strict') { - if ((!node_js && xmode !== 'script') || funct !== global_funct || array.length > 0) { - warn('function_strict'); - } - use_strict(); - } - if (disruptor) { - warn('unreachable_a_b', next_token, next_token.string, - disruptor.string); - disruptor = null; - } - the_statement = statement(); - if (the_statement) { - array.push(the_statement); - if (the_statement.disrupt) { - disruptor = the_statement; - array.disrupt = true; - } - } - } - } - return array; - } - - - function block(ordinary) { - -// array block is array sequence of statements wrapped in braces. -// ordinary is false for function bodies and try blocks. -// ordinary is true for if statements, while, etc. - - var array, - curly = next_token, - old_in_block = in_block, - old_scope = scope, - old_strict_mode = strict_mode; - - in_block = ordinary; - scope = Object.create(scope); - spaces(); - if (next_token.id === '{') { - advance('{'); - step_in(); - if (!ordinary && !use_strict() && !old_strict_mode && - !option.sloppy && funct['(context)'] === global_funct) { - warn('missing_use_strict'); - } - array = statements(); - strict_mode = old_strict_mode; - step_out('}', curly); - } else if (!ordinary) { - stop('expected_a_b', next_token, '{', artifact()); - } else { - warn('expected_a_b', next_token, '{', artifact()); - array = [statement()]; - array.disrupt = array[0].disrupt; - } - funct['(verb)'] = null; - scope = old_scope; - in_block = old_in_block; - if (ordinary && array.length === 0) { - warn('empty_block'); - } - return array; - } - - - function tally_property(name) { - if (option.properties && typeof property[name] !== 'number') { - warn('unexpected_property_a', token, name); - } - if (typeof property[name] === 'number') { - property[name] += 1; - } else { - property[name] = 1; - } - } - - -// ECMAScript parser - - syntax['(identifier)'] = { - id: '(identifier)', - lbp: 0, - identifier: true, - nud: function () { - var name = this.string, - variable = scope[name], - site, - writeable; - -// If the variable is not in scope, then we may have an undeclared variable. -// Check the predefined list. If it was predefined, create the global -// variable. - - if (typeof variable !== 'object') { - writeable = predefined[name]; - if (typeof writeable === 'boolean') { - global_scope[name] = variable = { - string: name, - writeable: writeable, - funct: global_funct - }; - global_funct[name] = 'var'; - -// But if the variable is not in scope, and is not predefined, and if we are not -// in the global scope, then we have an undefined variable error. - - } else { - if (!option.undef) { - warn('used_before_a', token); - } - scope[name] = variable = { - string: name, - writeable: true, - funct: funct - }; - funct[name] = 'undef'; - } - - } - site = variable.funct; - -// The name is in scope and defined in the current function. - - if (funct === site) { - -// Change 'unused' to 'var', and reject labels. - - switch (funct[name]) { - case 'becoming': - warn('unexpected_a', token); - funct[name] = 'var'; - break; - case 'unused': - funct[name] = 'var'; - break; - case 'unparam': - funct[name] = 'parameter'; - break; - case 'unction': - funct[name] = 'function'; - break; - case 'label': - warn('a_label', token, name); - break; - } - -// If the name is already defined in the current -// function, but not as outer, then there is a scope error. - - } else { - switch (funct[name]) { - case 'closure': - case 'function': - case 'var': - case 'unused': - warn('a_scope', token, name); - break; - case 'label': - warn('a_label', token, name); - break; - case 'outer': - case 'global': - break; - default: - -// If the name is defined in an outer function, make an outer entry, and if -// it was unused, make it var. - - switch (site[name]) { - case 'becoming': - case 'closure': - case 'function': - case 'parameter': - case 'unction': - case 'unused': - case 'var': - site[name] = 'closure'; - funct[name] = site === global_funct - ? 'global' - : 'outer'; - break; - case 'unparam': - site[name] = 'parameter'; - funct[name] = 'outer'; - break; - case 'undef': - funct[name] = 'undef'; - break; - case 'label': - warn('a_label', token, name); - break; - } - } - } - return this; - }, - led: function () { - stop('expected_operator_a'); - } - }; - -// Build the syntax table by declaring the syntactic elements. - - type('(array)', 'array'); - type('(color)', 'color'); - type('(function)', 'function'); - type('(number)', 'number', return_this); - type('(object)', 'object'); - type('(string)', 'string', return_this); - type('(boolean)', 'boolean', return_this); - type('(range)', 'range'); - type('(regexp)', 'regexp', return_this); - - ultimate('(begin)'); - ultimate('(end)'); - ultimate('(error)'); - postscript(symbol(''); - postscript(symbol('}')); - symbol(')'); - symbol(']'); - postscript(symbol('"')); - postscript(symbol('\'')); - symbol(';'); - symbol(':'); - symbol(','); - symbol('#'); - symbol('@'); - symbol('*/'); - postscript(reserve('case')); - reserve('catch'); - postscript(reserve('default')); - reserve('else'); - reserve('finally'); - - reservevar('arguments', function (x) { - if (strict_mode && funct === global_funct) { - warn('strict', x); - } else if (option.safe) { - warn('adsafe_a', x); - } - funct['(arguments)'] = true; - }); - reservevar('eval', function (x) { - if (option.safe) { - warn('adsafe_a', x); - } - }); - constant('false', 'boolean'); - constant('Infinity', 'number'); - constant('NaN', 'number'); - constant('null', ''); - reservevar('this', function (x) { - if (option.safe) { - warn('adsafe_a', x); - } else if (strict_mode && funct['(token)'] && - (funct['(token)'].arity === 'statement' && - funct['(name)'].charAt(0) > 'Z')) { - warn('strict', x); - } - }); - constant('true', 'boolean'); - constant('undefined', ''); - - infix('?', 30, function (left, that) { - step_in('?'); - that.first = expected_condition(expected_relation(left)); - that.second = expression(0); - spaces(); - step_out(); - var colon = next_token; - advance(':'); - step_in(':'); - spaces(); - that.third = expression(10); - that.arity = 'ternary'; - if (are_similar(that.second, that.third)) { - warn('weird_ternary', colon); - } else if (are_similar(that.first, that.second)) { - warn('use_or', that); - } - step_out(); - return that; - }); - - infix('||', 40, function (left, that) { - function paren_check(that) { - if (that.id === '&&' && !that.paren) { - warn('and', that); - } - return that; - } - - that.first = paren_check(expected_condition(expected_relation(left))); - that.second = paren_check(expected_relation(expression(40))); - if (are_similar(that.first, that.second)) { - warn('weird_condition', that); - } - return that; - }); - - infix('&&', 50, function (left, that) { - that.first = expected_condition(expected_relation(left)); - that.second = expected_relation(expression(50)); - if (are_similar(that.first, that.second)) { - warn('weird_condition', that); - } - return that; - }); - - prefix('void', function (that) { - that.first = expression(0); - if (option.es5 || strict_mode) { - warn('expected_a_b', that, 'undefined', 'void'); - } else if (that.first.number !== 0) { - warn('expected_a_b', that.first, '0', artifact(that.first)); - } - return that; - }); - - bitwise('|', 70); - bitwise('^', 80); - bitwise('&', 90); - - relation('==', '==='); - relation('==='); - relation('!=', '!=='); - relation('!=='); - relation('<'); - relation('>'); - relation('<='); - relation('>='); - - bitwise('<<', 120); - bitwise('>>', 120); - bitwise('>>>', 120); - - infix('in', 120, function (left, that) { - warn('infix_in', that); - that.left = left; - that.right = expression(130); - return that; - }); - infix('instanceof', 120); - infix('+', 130, function (left, that) { - if (left.id === '(number)') { - if (left.number === 0) { - warn('unexpected_a', left, '0'); - } - } else if (left.id === '(string)') { - if (left.string === '') { - warn('expected_a_b', left, 'String', '\'\''); - } - } - var right = expression(130); - if (right.id === '(number)') { - if (right.number === 0) { - warn('unexpected_a', right, '0'); - } - } else if (right.id === '(string)') { - if (right.string === '') { - warn('expected_a_b', right, 'String', '\'\''); - } - } - if (left.id === right.id) { - if (left.id === '(string)' || left.id === '(number)') { - if (left.id === '(string)') { - left.string += right.string; - if (jx.test(left.string)) { - warn('url', left); - } - } else { - left.number += right.number; - } - left.thru = right.thru; - return left; - } - } - that.first = left; - that.second = right; - return that; - }); - prefix('+'); - prefix('+++', function () { - warn('confusing_a', token); - this.first = expression(150); - this.arity = 'prefix'; - return this; - }); - infix('+++', 130, function (left) { - warn('confusing_a', token); - this.first = left; - this.second = expression(130); - return this; - }); - infix('-', 130, function (left, that) { - if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { - warn('unexpected_a', left); - } - var right = expression(130); - if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { - warn('unexpected_a', right); - } - if (left.id === right.id && left.id === '(number)') { - left.number -= right.number; - left.thru = right.thru; - return left; - } - that.first = left; - that.second = right; - return that; - }); - prefix('-'); - prefix('---', function () { - warn('confusing_a', token); - this.first = expression(150); - this.arity = 'prefix'; - return this; - }); - infix('---', 130, function (left) { - warn('confusing_a', token); - this.first = left; - this.second = expression(130); - return this; - }); - infix('*', 140, function (left, that) { - if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { - warn('unexpected_a', left); - } - var right = expression(140); - if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { - warn('unexpected_a', right); - } - if (left.id === right.id && left.id === '(number)') { - left.number *= right.number; - left.thru = right.thru; - return left; - } - that.first = left; - that.second = right; - return that; - }); - infix('/', 140, function (left, that) { - if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { - warn('unexpected_a', left); - } - var right = expression(140); - if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { - warn('unexpected_a', right); - } - if (left.id === right.id && left.id === '(number)') { - left.number /= right.number; - left.thru = right.thru; - return left; - } - that.first = left; - that.second = right; - return that; - }); - infix('%', 140, function (left, that) { - if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { - warn('unexpected_a', left); - } - var right = expression(140); - if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { - warn('unexpected_a', right); - } - if (left.id === right.id && left.id === '(number)') { - left.number %= right.number; - left.thru = right.thru; - return left; - } - that.first = left; - that.second = right; - return that; - }); - - suffix('++'); - prefix('++'); - - suffix('--'); - prefix('--'); - prefix('delete', function (that) { - one_space(); - var p = expression(0); - if (!p || (p.id !== '.' && p.id !== '[')) { - warn('deleted'); - } - that.first = p; - return that; - }); - - - prefix('~', function (that) { - no_space_only(); - if (!option.bitwise) { - warn('unexpected_a', that); - } - that.first = expression(150); - return that; - }); - function banger(that) { - no_space_only(); - that.first = expected_condition(expression(150)); - if (bang[that.first.id] === that || that.first.assign) { - warn('confusing_a', that); - } - return that; - } - prefix('!', banger); - prefix('!!', banger); - prefix('typeof'); - prefix('new', function (that) { - one_space(); - var c = expression(160), n, p, v; - that.first = c; - if (c.id !== 'function') { - if (c.identifier) { - switch (c.string) { - case 'Object': - warn('use_object', token); - break; - case 'Array': - if (next_token.id === '(') { - p = next_token; - p.first = this; - advance('('); - if (next_token.id !== ')') { - n = expression(0); - p.second = [n]; - if (n.id !== '(number)' || next_token.id === ',') { - warn('use_array', p); - } - while (next_token.id === ',') { - advance(','); - p.second.push(expression(0)); - } - } else { - warn('use_array', token); - } - advance(')', p); - return p; - } - warn('use_array', token); - break; - case 'Number': - case 'String': - case 'Boolean': - case 'Math': - case 'JSON': - warn('not_a_constructor', c); - break; - case 'Function': - if (!option.evil) { - warn('function_eval'); - } - break; - case 'Date': - case 'RegExp': - case 'this': - break; - default: - if (c.id !== 'function') { - v = c.string.charAt(0); - if (!option.newcap && (v < 'A' || v > 'Z')) { - warn('constructor_name_a', token); - } - } - } - } else { - if (c.id !== '.' && c.id !== '[' && c.id !== '(') { - warn('bad_constructor', token); - } - } - } else { - warn('weird_new', that); - } - if (next_token.id !== '(') { - warn('missing_a', next_token, '()'); - } - return that; - }); - - infix('(', 160, function (left, that) { - var e, p; - if (indent && indent.mode === 'expression') { - no_space(prev_token, token); - } else { - no_space_only(prev_token, token); - } - if (!left.immed && left.id === 'function') { - warn('wrap_immediate'); - } - p = []; - if (left.identifier) { - if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { - if (left.string !== 'Number' && left.string !== 'String' && - left.string !== 'Boolean' && left.string !== 'Date') { - if (left.string === 'Math' || left.string === 'JSON') { - warn('not_a_function', left); - } else if (left.string === 'Object') { - warn('use_object', token); - } else if (left.string === 'Array' || !option.newcap) { - warn('missing_a', left, 'new'); - } - } - } - } else if (left.id === '.') { - if (option.safe && left.first.string === 'Math' && - left.second === 'random') { - warn('adsafe_a', left); - } else if (left.second.string === 'split' && - left.first.id === '(string)') { - warn('use_array', left.second); - } - } - step_in(); - if (next_token.id !== ')') { - no_space(); - for (;;) { - edge(); - e = expression(10); - if (left.string === 'Boolean' && (e.id === '!' || e.id === '~')) { - warn('weird_condition', e); - } - p.push(e); - if (next_token.id !== ',') { - break; - } - comma(); - } - } - no_space(); - step_out(')', that); - if (typeof left === 'object') { - if (left.string === 'parseInt' && p.length === 1) { - warn('radix', left); - } - if (!option.evil) { - if (left.string === 'eval' || left.string === 'Function' || - left.string === 'execScript') { - warn('evil', left); - } else if (p[0] && p[0].id === '(string)' && - (left.string === 'setTimeout' || - left.string === 'setInterval')) { - warn('implied_evil', left); - } - } - if (!left.identifier && left.id !== '.' && left.id !== '[' && - left.id !== '(' && left.id !== '&&' && left.id !== '||' && - left.id !== '?') { - warn('bad_invocation', left); - } - if (left.id === '.' && p.length > 0 && - left.first && left.first.first && - are_similar(p[0], left.first.first)) { - if (left.second.string === 'call' || - (left.second.string === 'apply' && (p.length === 1 || - (p[1].arity === 'prefix' && p[1].id === '[')))) { - warn('unexpected_a', left.second); - } - } - } - that.first = left; - that.second = p; - return that; - }, true); - - prefix('(', function (that) { - step_in('expression'); - no_space(); - edge(); - if (next_token.id === 'function') { - next_token.immed = true; - } - var value = expression(0); - value.paren = true; - no_space(); - step_out(')', that); - if (value.id === 'function') { - switch (next_token.id) { - case '(': - warn('move_invocation'); - break; - case '.': - case '[': - warn('unexpected_a'); - break; - default: - warn('bad_wrap', that); - } - } - return value; - }); - - infix('.', 170, function (left, that) { - no_space(prev_token, token); - no_space(); - var name = identifier(); - if (typeof name === 'string') { - tally_property(name); - } - that.first = left; - that.second = token; - if (left && left.string === 'arguments' && - (name === 'callee' || name === 'caller')) { - warn('avoid_a', left, 'arguments.' + name); - } else if (!option.evil && left && left.string === 'document' && - (name === 'write' || name === 'writeln')) { - warn('write_is_wrong', left); - } else if (!option.stupid && syx.test(name)) { - warn('sync_a', token); - } else if (option.adsafe) { - if (!adsafe_top && left.string === 'ADSAFE') { - if (name === 'id' || name === 'lib') { - warn('adsafe_a', that); - } else if (name === 'go') { - if (xmode !== 'script') { - warn('adsafe_a', that); - } else if (adsafe_went || next_token.id !== '(' || - peek(0).id !== '(string)' || - peek(0).string !== adsafe_id || - peek(1).id !== ',') { - stop('adsafe_a', that, 'go'); - } - adsafe_went = true; - adsafe_may = false; - } - } - adsafe_top = false; - } - if (!option.evil && (name === 'eval' || name === 'execScript')) { - warn('evil'); - } else if (option.safe) { - for (;;) { - if (banned[name] === true) { - warn('adsafe_a', token, name); - } - if (typeof predefined[left.string] !== 'boolean' || //// check for writeable - next_token.id === '(') { - break; - } - if (next_token.id !== '.') { - warn('adsafe_a', that); - break; - } - advance('.'); - token.first = that; - token.second = name; - that = token; - name = identifier(); - if (typeof name === 'string') { - tally_property(name); - } - } - } - return that; - }, true); - - infix('[', 170, function (left, that) { - var e, s; - no_space_only(prev_token, token); - no_space(); - step_in(); - edge(); - e = expression(0); - switch (e.id) { - case '(number)': - if (e.id === '(number)' && left.id === 'arguments') { - warn('use_param', left); - } - break; - case '(string)': - if (option.safe && (banned[e.string] || - e.string.charAt(0) === '_' || e.string.slice(-1) === '_')) { - warn('adsafe_subscript_a', e); - } else if (!option.evil && - (e.string === 'eval' || e.string === 'execScript')) { - warn('evil', e); - } else if (!option.sub && ix.test(e.string)) { - s = syntax[e.string]; - if (!s || !s.reserved) { - warn('subscript', e); - } - } - tally_property(e.string); - break; - default: - if (option.safe) { - if ((e.id !== '+' || e.arity !== 'prefix') && - e.id !== '-' && e.id !== '*') { - warn('adsafe_subscript_a', e); - } - } - } - step_out(']', that); - no_space(prev_token, token); - that.first = left; - that.second = e; - return that; - }, true); - - prefix('[', function (that) { - that.first = []; - step_in('array'); - while (next_token.id !== '(end)') { - while (next_token.id === ',') { - warn('unexpected_a', next_token); - advance(','); - } - if (next_token.id === ']') { - break; - } - indent.wrap = false; - edge(); - that.first.push(expression(10)); - if (next_token.id === ',') { - comma(); - if (next_token.id === ']' && !option.es5) { - warn('unexpected_a', token); - break; - } - } else { - break; - } - } - step_out(']', that); - return that; - }, 170); - - - function property_name() { - var id = optional_identifier(true); - if (!id) { - if (next_token.id === '(string)') { - id = next_token.string; - if (option.safe) { - if (banned[id]) { - warn('adsafe_a'); - } else if (id.charAt(0) === '_' || - id.charAt(id.length - 1) === '_') { - warn('dangling_a'); - } - } - advance(); - } else if (next_token.id === '(number)') { - id = next_token.number.toString(); - advance(); - } - } - return id; - } - - - function function_params() { - var id, paren = next_token, params = []; - advance('('); - step_in(); - no_space(); - if (next_token.id === ')') { - no_space(); - step_out(')', paren); - return params; - } - for (;;) { - edge(); - id = identifier(); - params.push(token); - add_label(token, option.unparam ? 'parameter' : 'unparam'); - if (next_token.id === ',') { - comma(); - } else { - no_space(); - step_out(')', paren); - return params; - } - } - } - - - - function do_function(func, name) { - var old_funct = funct, - old_option = option, - old_scope = scope; - funct = { - '(name)' : name || '\'' + (anonname || '').replace(nx, sanitize) + '\'', - '(line)' : next_token.line, - '(context)' : old_funct, - '(breakage)' : 0, - '(loopage)' : 0, - '(scope)' : scope, - '(token)' : func - }; - option = Object.create(old_option); - scope = Object.create(old_scope); - functions.push(funct); - func.name = name; - if (name) { - add_label(func, 'function', name); - } - func.writeable = false; - func.first = funct['(params)'] = function_params(); - one_space(); - func.block = block(false); - if (funct['(arguments)']) { - func.first.forEach(function (value) { - if (value.assign) { - warn('parameter_arguments_a', value, value.string); - } - }); - } - funct = old_funct; - option = old_option; - scope = old_scope; - } - - - assignop('='); - assignop('+=', '+'); - assignop('-=', '-'); - assignop('*=', '*'); - assignop('/=', '/').nud = function () { - stop('slash_equal'); - }; - assignop('%=', '%'); - assignop('&=', '&'); - assignop('|=', '|'); - assignop('^=', '^'); - assignop('<<=', '<<'); - assignop('>>=', '>>'); - assignop('>>>=', '>>>'); - - - prefix('{', function (that) { - var get, i, j, name, p, set, seen = {}; - that.first = []; - step_in(); - while (next_token.id !== '}') { - indent.wrap = false; - -// JSLint recognizes the ES5 extension for get/set in object literals, -// but requires that they be used in pairs. - - edge(); - if (next_token.string === 'get' && peek().id !== ':') { - if (!option.es5) { - warn('es5'); - } - get = next_token; - advance('get'); - one_space_only(); - name = next_token; - i = property_name(); - if (!i) { - stop('missing_property'); - } - get.string = ''; - do_function(get); - if (funct['(loopage)']) { - warn('function_loop', get); - } - p = get.first; - if (p && p.length) { - warn('parameter_a_get_b', p[0], p[0].string, i); - } - comma(); - set = next_token; - spaces(); - edge(); - advance('set'); - set.string = ''; - one_space_only(); - j = property_name(); - if (i !== j) { - stop('expected_a_b', token, i, j || next_token.string); - } - do_function(set); - if (set.block.length === 0) { - warn('missing_a', token, 'throw'); - } - p = set.first; - if (!p || p.length !== 1) { - stop('parameter_set_a', set, 'value'); - } else if (p[0].string !== 'value') { - stop('expected_a_b', p[0], 'value', p[0].string); - } - name.first = [get, set]; - } else { - name = next_token; - i = property_name(); - if (typeof i !== 'string') { - stop('missing_property'); - } - advance(':'); - spaces(); - name.first = expression(10); - } - that.first.push(name); - if (seen[i] === true) { - warn('duplicate_a', next_token, i); - } - seen[i] = true; - tally_property(i); - if (next_token.id !== ',') { - break; - } - for (;;) { - comma(); - if (next_token.id !== ',') { - break; - } - warn('unexpected_a', next_token); - } - if (next_token.id === '}' && !option.es5) { - warn('unexpected_a', token); - } - } - step_out('}', that); - return that; - }); - - stmt('{', function () { - warn('statement_block'); - this.arity = 'statement'; - this.block = statements(); - this.disrupt = this.block.disrupt; - advance('}', this); - return this; - }); - - stmt('/*global', directive); - stmt('/*globals', directive); - stmt('/*jslint', directive); - stmt('/*member', directive); - stmt('/*members', directive); - stmt('/*property', directive); - stmt('/*properties', directive); - - stmt('var', function () { - -// JavaScript does not have block scope. It only has function scope. So, -// declaring a variable in a block can have unexpected consequences. - -// var.first will contain an array, the array containing name tokens -// and assignment tokens. - - var assign, id, name; - - if (funct['(vars)'] && !option.vars) { - warn('combine_var'); - } else if (funct !== global_funct) { - funct['(vars)'] = true; - } - this.arity = 'statement'; - this.first = []; - step_in('var'); - for (;;) { - name = next_token; - id = identifier(); - add_label(name, 'becoming'); - - if (next_token.id === '=') { - assign = next_token; - assign.first = name; - spaces(); - advance('='); - spaces(); - if (next_token.id === 'undefined') { - warn('unnecessary_initialize', token, id); - } - if (peek(0).id === '=' && next_token.identifier) { - stop('var_a_not'); - } - assign.second = expression(0); - assign.arity = 'infix'; - this.first.push(assign); - } else { - this.first.push(name); - } - if (funct[id] === 'becoming') { - funct[id] = 'unused'; - } - if (next_token.id !== ',') { - break; - } - comma(); - indent.wrap = false; - if (var_mode && next_token.line === token.line && - this.first.length === 1) { - var_mode = null; - indent.open = false; - indent.at -= option.indent; - } - spaces(); - edge(); - } - var_mode = null; - step_out(); - return this; - }); - - stmt('function', function () { - one_space(); - if (in_block) { - warn('function_block', token); - } - var name = next_token, id = identifier(); - add_label(name, 'unction'); - no_space(); - this.arity = 'statement'; - do_function(this, id); - if (next_token.id === '(' && next_token.line === token.line) { - stop('function_statement'); - } - return this; - }); - - prefix('function', function (that) { - if (!option.anon) { - one_space(); - } - var id = optional_identifier(); - if (id) { - no_space(); - } else { - id = ''; - } - do_function(that, id); - if (funct['(loopage)']) { - warn('function_loop'); - } - switch (next_token.id) { - case ';': - case '(': - case ')': - case ',': - case ']': - case '}': - case ':': - break; - case '.': - if (peek().string !== 'bind' || peek(1).id !== '(') { - warn('unexpected_a'); - } - break; - default: - stop('unexpected_a'); - } - that.arity = 'function'; - return that; - }); - - stmt('if', function () { - var paren = next_token; - one_space(); - advance('('); - step_in('control'); - no_space(); - edge(); - this.arity = 'statement'; - this.first = expected_condition(expected_relation(expression(0))); - no_space(); - step_out(')', paren); - one_space(); - this.block = block(true); - if (next_token.id === 'else') { - one_space(); - advance('else'); - one_space(); - this['else'] = next_token.id === 'if' || next_token.id === 'switch' - ? statement(true) - : block(true); - if (this['else'].disrupt && this.block.disrupt) { - this.disrupt = true; - } - } - return this; - }); - - stmt('try', function () { - -// try.first The catch variable -// try.second The catch clause -// try.third The finally clause -// try.block The try block - - var exception_variable, old_scope, paren; - if (option.adsafe) { - warn('adsafe_a', this); - } - one_space(); - this.arity = 'statement'; - this.block = block(false); - if (next_token.id === 'catch') { - one_space(); - advance('catch'); - one_space(); - paren = next_token; - advance('('); - step_in('control'); - no_space(); - edge(); - old_scope = scope; - scope = Object.create(old_scope); - exception_variable = next_token.string; - this.first = exception_variable; - if (!next_token.identifier) { - warn('expected_identifier_a', next_token); - } else { - add_label(next_token, 'exception'); - } - advance(); - no_space(); - step_out(')', paren); - one_space(); - this.second = block(false); - scope = old_scope; - } - if (next_token.id === 'finally') { - one_space(); - advance('finally'); - one_space(); - this.third = block(false); - } else if (!this.second) { - stop('expected_a_b', next_token, 'catch', artifact()); - } - return this; - }); - - labeled_stmt('while', function () { - one_space(); - var paren = next_token; - funct['(breakage)'] += 1; - funct['(loopage)'] += 1; - advance('('); - step_in('control'); - no_space(); - edge(); - this.arity = 'statement'; - this.first = expected_relation(expression(0)); - if (this.first.id !== 'true') { - expected_condition(this.first, bundle.unexpected_a); - } - no_space(); - step_out(')', paren); - one_space(); - this.block = block(true); - if (this.block.disrupt) { - warn('strange_loop', prev_token); - } - funct['(breakage)'] -= 1; - funct['(loopage)'] -= 1; - return this; - }); - - reserve('with'); - - labeled_stmt('switch', function () { - -// switch.first the switch expression -// switch.second the array of cases. A case is 'case' or 'default' token: -// case.first the array of case expressions -// case.second the array of statements -// If all of the arrays of statements are disrupt, then the switch is disrupt. - - var cases = [], - old_in_block = in_block, - particular, - the_case = next_token, - unbroken = true; - - function find_duplicate_case(value) { - if (are_similar(particular, value)) { - warn('duplicate_a', value); - } - } - - funct['(breakage)'] += 1; - one_space(); - advance('('); - no_space(); - step_in(); - this.arity = 'statement'; - this.first = expected_condition(expected_relation(expression(0))); - no_space(); - step_out(')', the_case); - one_space(); - advance('{'); - step_in(); - in_block = true; - this.second = []; - while (next_token.id === 'case') { - the_case = next_token; - cases.forEach(find_duplicate_case); - the_case.first = []; - the_case.arity = 'case'; - spaces(); - edge('case'); - advance('case'); - for (;;) { - one_space(); - particular = expression(0); - cases.forEach(find_duplicate_case); - cases.push(particular); - the_case.first.push(particular); - if (particular.id === 'NaN') { - warn('unexpected_a', particular); - } - no_space_only(); - advance(':'); - if (next_token.id !== 'case') { - break; - } - spaces(); - edge('case'); - advance('case'); - } - spaces(); - the_case.second = statements(); - if (the_case.second && the_case.second.length > 0) { - particular = the_case.second[the_case.second.length - 1]; - if (particular.disrupt) { - if (particular.id === 'break') { - unbroken = false; - } - } else { - warn('missing_a_after_b', next_token, 'break', 'case'); - } - } else { - warn('empty_case'); - } - this.second.push(the_case); - } - if (this.second.length === 0) { - warn('missing_a', next_token, 'case'); - } - if (next_token.id === 'default') { - spaces(); - the_case = next_token; - the_case.arity = 'case'; - edge('case'); - advance('default'); - no_space_only(); - advance(':'); - spaces(); - the_case.second = statements(); - if (the_case.second && the_case.second.length > 0) { - particular = the_case.second[the_case.second.length - 1]; - if (unbroken && particular.disrupt && particular.id !== 'break') { - this.disrupt = true; - } - } - this.second.push(the_case); - } - funct['(breakage)'] -= 1; - spaces(); - step_out('}', this); - in_block = old_in_block; - return this; - }); - - stmt('debugger', function () { - if (!option.debug) { - warn('unexpected_a', this); - } - this.arity = 'statement'; - return this; - }); - - labeled_stmt('do', function () { - funct['(breakage)'] += 1; - funct['(loopage)'] += 1; - one_space(); - this.arity = 'statement'; - this.block = block(true); - if (this.block.disrupt) { - warn('strange_loop', prev_token); - } - one_space(); - advance('while'); - var paren = next_token; - one_space(); - advance('('); - step_in(); - no_space(); - edge(); - this.first = expected_condition(expected_relation(expression(0)), bundle.unexpected_a); - no_space(); - step_out(')', paren); - funct['(breakage)'] -= 1; - funct['(loopage)'] -= 1; - return this; - }); - - labeled_stmt('for', function () { - - var blok, filter, ok = false, paren = next_token, value; - this.arity = 'statement'; - funct['(breakage)'] += 1; - funct['(loopage)'] += 1; - advance('('); - if (next_token.id === ';') { - no_space(); - advance(';'); - no_space(); - advance(';'); - no_space(); - advance(')'); - blok = block(true); - } else { - step_in('control'); - spaces(this, paren); - no_space(); - if (next_token.id === 'var') { - stop('move_var'); - } - edge(); - if (peek(0).id === 'in') { - this.forin = true; - value = next_token; - switch (funct[value.string]) { - case 'unused': - funct[value.string] = 'var'; - break; - case 'closure': - case 'var': - break; - default: - warn('bad_in_a', value); - } - advance(); - advance('in'); - this.first = value; - this.second = expression(20); - step_out(')', paren); - blok = block(true); - if (!option.forin) { - if (blok.length === 1 && typeof blok[0] === 'object' && - blok[0].string === 'if' && !blok[0]['else']) { - filter = blok[0].first; - while (filter.id === '&&') { - filter = filter.first; - } - switch (filter.id) { - case '===': - case '!==': - ok = filter.first.id === '[' - ? filter.first.first.string === this.second.string && - filter.first.second.string === this.first.string - : filter.first.id === 'typeof' && - filter.first.first.id === '[' && - filter.first.first.first.string === this.second.string && - filter.first.first.second.string === this.first.string; - break; - case '(': - ok = filter.first.id === '.' && (( - filter.first.first.string === this.second.string && - filter.first.second.string === 'hasOwnProperty' && - filter.second[0].string === this.first.string - ) || ( - filter.first.first.string === 'ADSAFE' && - filter.first.second.string === 'has' && - filter.second[0].string === this.second.string && - filter.second[1].string === this.first.string - ) || ( - filter.first.first.id === '.' && - filter.first.first.first.id === '.' && - filter.first.first.first.first.string === 'Object' && - filter.first.first.first.second.string === 'prototype' && - filter.first.first.second.string === 'hasOwnProperty' && - filter.first.second.string === 'call' && - filter.second[0].string === this.second.string && - filter.second[1].string === this.first.string - )); - break; - } - } - if (!ok) { - warn('for_if', this); - } - } - } else { - edge(); - this.first = []; - for (;;) { - this.first.push(expression(0, 'for')); - if (next_token.id !== ',') { - break; - } - comma(); - } - semicolon(); - edge(); - this.second = expected_relation(expression(0)); - if (this.second.id !== 'true') { - expected_condition(this.second, bundle.unexpected_a); - } - semicolon(token); - if (next_token.id === ';') { - stop('expected_a_b', next_token, ')', ';'); - } - this.third = []; - edge(); - for (;;) { - this.third.push(expression(0, 'for')); - if (next_token.id !== ',') { - break; - } - comma(); - } - no_space(); - step_out(')', paren); - one_space(); - blok = block(true); - } - } - if (blok.disrupt) { - warn('strange_loop', prev_token); - } - this.block = blok; - funct['(breakage)'] -= 1; - funct['(loopage)'] -= 1; - return this; - }); - - disrupt_stmt('break', function () { - var label = next_token.string; - this.arity = 'statement'; - if (funct['(breakage)'] === 0) { - warn('unexpected_a', this); - } - if (next_token.identifier && token.line === next_token.line) { - one_space_only(); - if (funct[label] !== 'label') { - warn('not_a_label', next_token); - } else if (scope[label].funct !== funct) { - warn('not_a_scope', next_token); - } - this.first = next_token; - advance(); - } - return this; - }); - - disrupt_stmt('continue', function () { - if (!option['continue']) { - warn('unexpected_a', this); - } - var label = next_token.string; - this.arity = 'statement'; - if (funct['(breakage)'] === 0) { - warn('unexpected_a', this); - } - if (next_token.identifier && token.line === next_token.line) { - one_space_only(); - if (funct[label] !== 'label') { - warn('not_a_label', next_token); - } else if (scope[label].funct !== funct) { - warn('not_a_scope', next_token); - } - this.first = next_token; - advance(); - } - return this; - }); - - disrupt_stmt('return', function () { - if (funct === global_funct && xmode !== 'scriptstring') { - warn('unexpected_a', this); - } - this.arity = 'statement'; - if (next_token.id !== ';' && next_token.line === token.line) { - one_space_only(); - if (next_token.id === '/' || next_token.id === '(regexp)') { - warn('wrap_regexp'); - } - this.first = expression(20); - } - if (peek(0).id === '}' && peek(1).id === 'else') { - warn('unexpected_else', this); - } - return this; - }); - - disrupt_stmt('throw', function () { - this.arity = 'statement'; - one_space_only(); - this.first = expression(20); - return this; - }); - - -// Superfluous reserved words - - reserve('class'); - reserve('const'); - reserve('enum'); - reserve('export'); - reserve('extends'); - reserve('import'); - reserve('super'); - -// Harmony reserved words - - reserve('implements'); - reserve('interface'); - reserve('let'); - reserve('package'); - reserve('private'); - reserve('protected'); - reserve('public'); - reserve('static'); - reserve('yield'); - - -// Parse JSON - - function json_value() { - - function json_object() { - var brace = next_token, object = {}; - advance('{'); - if (next_token.id !== '}') { - while (next_token.id !== '(end)') { - while (next_token.id === ',') { - warn('unexpected_a', next_token); - advance(','); - } - if (next_token.id !== '(string)') { - warn('expected_string_a'); - } - if (object[next_token.string] === true) { - warn('duplicate_a'); - } else if (next_token.string === '__proto__') { - warn('dangling_a'); - } else { - object[next_token.string] = true; - } - advance(); - advance(':'); - json_value(); - if (next_token.id !== ',') { - break; - } - advance(','); - if (next_token.id === '}') { - warn('unexpected_a', token); - break; - } - } - } - advance('}', brace); - } - - function json_array() { - var bracket = next_token; - advance('['); - if (next_token.id !== ']') { - while (next_token.id !== '(end)') { - while (next_token.id === ',') { - warn('unexpected_a', next_token); - advance(','); - } - json_value(); - if (next_token.id !== ',') { - break; - } - advance(','); - if (next_token.id === ']') { - warn('unexpected_a', token); - break; - } - } - } - advance(']', bracket); - } - - switch (next_token.id) { - case '{': - json_object(); - break; - case '[': - json_array(); - break; - case 'true': - case 'false': - case 'null': - case '(number)': - case '(string)': - advance(); - break; - case '-': - advance('-'); - no_space_only(); - advance('(number)'); - break; - default: - stop('unexpected_a'); - } - } - - -// CSS parsing. - - function css_name() { - if (next_token.identifier) { - advance(); - return true; - } - } - - - function css_number() { - if (next_token.id === '-') { - advance('-'); - no_space_only(); - } - if (next_token.id === '(number)') { - advance('(number)'); - return true; - } - } - - - function css_string() { - if (next_token.id === '(string)') { - advance(); - return true; - } - } - - function css_color() { - var i, number, paren, value; - if (next_token.identifier) { - value = next_token.string; - if (value === 'rgb' || value === 'rgba') { - advance(); - paren = next_token; - advance('('); - for (i = 0; i < 3; i += 1) { - if (i) { - comma(); - } - number = next_token.number; - if (next_token.id !== '(number)' || number < 0) { - warn('expected_positive_a', next_token); - advance(); - } else { - advance(); - if (next_token.id === '%') { - advance('%'); - if (number > 100) { - warn('expected_percent_a', token, number); - } - } else { - if (number > 255) { - warn('expected_small_a', token, number); - } - } - } - } - if (value === 'rgba') { - comma(); - number = next_token.number; - if (next_token.id !== '(number)' || number < 0 || number > 1) { - warn('expected_fraction_a', next_token); - } - advance(); - if (next_token.id === '%') { - warn('unexpected_a'); - advance('%'); - } - } - advance(')', paren); - return true; - } - if (css_colorData[next_token.string] === true) { - advance(); - return true; - } - } else if (next_token.id === '(color)') { - advance(); - return true; - } - return false; - } - - - function css_length() { - if (next_token.id === '-') { - advance('-'); - no_space_only(); - } - if (next_token.id === '(number)') { - advance(); - if (next_token.id !== '(string)' && - css_lengthData[next_token.string] === true) { - no_space_only(); - advance(); - } else if (+token.number !== 0) { - warn('expected_linear_a'); - } - return true; - } - return false; - } - - - function css_line_height() { - if (next_token.id === '-') { - advance('-'); - no_space_only(); - } - if (next_token.id === '(number)') { - advance(); - if (next_token.id !== '(string)' && - css_lengthData[next_token.string] === true) { - no_space_only(); - advance(); - } - return true; - } - return false; - } - - - function css_width() { - if (next_token.identifier) { - switch (next_token.string) { - case 'thin': - case 'medium': - case 'thick': - advance(); - return true; - } - } else { - return css_length(); - } - } - - - function css_margin() { - if (next_token.identifier) { - if (next_token.string === 'auto') { - advance(); - return true; - } - } else { - return css_length(); - } - } - - function css_attr() { - if (next_token.identifier && next_token.string === 'attr') { - advance(); - advance('('); - if (!next_token.identifier) { - warn('expected_name_a'); - } - advance(); - advance(')'); - return true; - } - return false; - } - - - function css_comma_list() { - while (next_token.id !== ';') { - if (!css_name() && !css_string()) { - warn('expected_name_a'); - } - if (next_token.id !== ',') { - return true; - } - comma(); - } - } - - - function css_counter() { - if (next_token.identifier && next_token.string === 'counter') { - advance(); - advance('('); - advance(); - if (next_token.id === ',') { - comma(); - if (next_token.id !== '(string)') { - warn('expected_string_a'); - } - advance(); - } - advance(')'); - return true; - } - if (next_token.identifier && next_token.string === 'counters') { - advance(); - advance('('); - if (!next_token.identifier) { - warn('expected_name_a'); - } - advance(); - if (next_token.id === ',') { - comma(); - if (next_token.id !== '(string)') { - warn('expected_string_a'); - } - advance(); - } - if (next_token.id === ',') { - comma(); - if (next_token.id !== '(string)') { - warn('expected_string_a'); - } - advance(); - } - advance(')'); - return true; - } - return false; - } - - - function css_radius() { - return css_length() && (next_token.id !== '(number)' || css_length()); - } - - - function css_shadow() { - for (;;) { - if (next_token.string === 'inset') { - advance(); - } - for (;;) { - if (!css_length()) { - break; - } - } - css_color(); - if (next_token.id !== ',') { - break; - } - advance(','); - } - return true; - } - - - function css_shape() { - var i; - if (next_token.identifier && next_token.string === 'rect') { - advance(); - advance('('); - for (i = 0; i < 4; i += 1) { - if (!css_length()) { - warn('expected_number_a'); - break; - } - } - advance(')'); - return true; - } - return false; - } - - - function css_url() { - var c, url; - if (next_token.identifier && next_token.string === 'url') { - next_token = lex.range('(', ')'); - url = next_token.string; - c = url.charAt(0); - if (c === '"' || c === '\'') { - if (url.slice(-1) !== c) { - warn('bad_url_a'); - } else { - url = url.slice(1, -1); - if (url.indexOf(c) >= 0) { - warn('bad_url_a'); - } - } - } - if (!url) { - warn('missing_url'); - } - if (ux.test(url)) { - stop('bad_url_a'); - } - urls.push(url); - advance(); - return true; - } - return false; - } - - - css_any = [css_url, function () { - for (;;) { - if (next_token.identifier) { - switch (next_token.string.toLowerCase()) { - case 'url': - css_url(); - break; - case 'expression': - warn('unexpected_a'); - advance(); - break; - default: - advance(); - } - } else { - if (next_token.id === ';' || next_token.id === '!' || - next_token.id === '(end)' || next_token.id === '}') { - return true; - } - advance(); - } - } - }]; - - - function font_face() { - advance_identifier('font-family'); - advance(':'); - if (!css_name() && !css_string()) { - stop('expected_name_a'); - } - semicolon(); - advance_identifier('src'); - advance(':'); - while (true) { - if (next_token.string === 'local') { - advance_identifier('local'); - advance('('); - if (ux.test(next_token.string)) { - stop('bad_url_a'); - } - - if (!css_name() && !css_string()) { - stop('expected_name_a'); - } - advance(')'); - } else if (!css_url()) { - stop('expected_a_b', next_token, 'url', artifact()); - } - if (next_token.id !== ',') { - break; - } - comma(); - } - semicolon(); - } - - - css_border_style = [ - 'none', 'dashed', 'dotted', 'double', 'groove', - 'hidden', 'inset', 'outset', 'ridge', 'solid' - ]; - - css_break = [ - 'auto', 'always', 'avoid', 'left', 'right' - ]; - - css_media = { - 'all': true, - 'braille': true, - 'embossed': true, - 'handheld': true, - 'print': true, - 'projection': true, - 'screen': true, - 'speech': true, - 'tty': true, - 'tv': true - }; - - css_overflow = [ - 'auto', 'hidden', 'scroll', 'visible' - ]; - - css_attribute_data = { - background: [ - true, 'background-attachment', 'background-color', - 'background-image', 'background-position', 'background-repeat' - ], - 'background-attachment': ['scroll', 'fixed'], - 'background-color': ['transparent', css_color], - 'background-image': ['none', css_url], - 'background-position': [ - 2, [css_length, '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': css_color, - 'border-bottom-left-radius': css_radius, - 'border-bottom-right-radius': css_radius, - 'border-bottom-style': css_border_style, - 'border-bottom-width': css_width, - 'border-collapse': ['collapse', 'separate'], - 'border-color': ['transparent', 4, css_color], - 'border-left': [ - true, 'border-left-color', 'border-left-style', 'border-left-width' - ], - 'border-left-color': css_color, - 'border-left-style': css_border_style, - 'border-left-width': css_width, - 'border-radius': function () { - function count(separator) { - var n = 1; - if (separator) { - advance(separator); - } - if (!css_length()) { - return false; - } - while (next_token.id === '(number)') { - if (!css_length()) { - return false; - } - n += 1; - } - if (n > 4) { - warn('bad_style'); - } - return true; - } - - return count() && (next_token.id !== '/' || count('/')); - }, - 'border-right': [ - true, 'border-right-color', 'border-right-style', - 'border-right-width' - ], - 'border-right-color': css_color, - 'border-right-style': css_border_style, - 'border-right-width': css_width, - 'border-spacing': [2, css_length], - 'border-style': [4, css_border_style], - 'border-top': [ - true, 'border-top-color', 'border-top-style', 'border-top-width' - ], - 'border-top-color': css_color, - 'border-top-left-radius': css_radius, - 'border-top-right-radius': css_radius, - 'border-top-style': css_border_style, - 'border-top-width': css_width, - 'border-width': [4, css_width], - bottom: [css_length, 'auto'], - 'box-shadow': ['none', css_shadow], - 'caption-side' : ['bottom', 'left', 'right', 'top'], - clear: ['both', 'left', 'none', 'right'], - clip: [css_shape, 'auto'], - color: css_color, - content: [ - 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', - css_string, css_url, css_counter, css_attr - ], - 'counter-increment': [ - css_name, 'none' - ], - 'counter-reset': [ - css_name, 'none' - ], - cursor: [ - css_url, '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-cell', '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': css_comma_list, - 'font-size': [ - 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', - 'xx-large', 'larger', 'smaller', css_length - ], - 'font-size-adjust': ['none', css_number], - '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', css_number - ], - height: [css_length, 'auto'], - left: [css_length, 'auto'], - 'letter-spacing': ['normal', css_length], - 'line-height': ['normal', css_line_height], - 'list-style': [ - true, 'list-style-image', 'list-style-position', 'list-style-type' - ], - 'list-style-image': ['none', css_url], - '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, css_margin], - 'margin-bottom': css_margin, - 'margin-left': css_margin, - 'margin-right': css_margin, - 'margin-top': css_margin, - 'marker-offset': [css_length, 'auto'], - 'max-height': [css_length, 'none'], - 'max-width': [css_length, 'none'], - 'min-height': css_length, - 'min-width': css_length, - opacity: css_number, - outline: [true, 'outline-color', 'outline-style', 'outline-width'], - 'outline-color': ['invert', css_color], - 'outline-style': [ - 'dashed', 'dotted', 'double', 'groove', 'inset', 'none', - 'outset', 'ridge', 'solid' - ], - 'outline-width': css_width, - overflow: css_overflow, - 'overflow-x': css_overflow, - 'overflow-y': css_overflow, - padding: [4, css_length], - 'padding-bottom': css_length, - 'padding-left': css_length, - 'padding-right': css_length, - 'padding-top': css_length, - 'page-break-after': css_break, - 'page-break-before': css_break, - position: ['absolute', 'fixed', 'relative', 'static'], - quotes: [8, css_string], - right: [css_length, 'auto'], - 'table-layout': ['auto', 'fixed'], - 'text-align': ['center', 'justify', 'left', 'right'], - 'text-decoration': [ - 'none', 'underline', 'overline', 'line-through', 'blink' - ], - 'text-indent': css_length, - 'text-shadow': ['none', 4, [css_color, css_length]], - 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], - top: [css_length, 'auto'], - 'unicode-bidi': ['normal', 'embed', 'bidi-override'], - 'vertical-align': [ - 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', - 'text-bottom', css_length - ], - visibility: ['visible', 'hidden', 'collapse'], - 'white-space': [ - 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit' - ], - width: [css_length, 'auto'], - 'word-spacing': ['normal', css_length], - 'word-wrap': ['break-word', 'normal'], - 'z-index': ['auto', css_number] - }; - - function style_attribute() { - var v; - while (next_token.id === '*' || next_token.id === '#' || - next_token.string === '_') { - if (!option.css) { - warn('unexpected_a'); - } - advance(); - } - if (next_token.id === '-') { - if (!option.css) { - warn('unexpected_a'); - } - advance('-'); - if (!next_token.identifier) { - warn('expected_nonstandard_style_attribute'); - } - advance(); - return css_any; - } - if (!next_token.identifier) { - warn('expected_style_attribute'); - } else { - if (Object.prototype.hasOwnProperty.call(css_attribute_data, - next_token.string)) { - v = css_attribute_data[next_token.string]; - } else { - v = css_any; - if (!option.css) { - warn('unrecognized_style_attribute_a'); - } - } - } - advance(); - return v; - } - - - function style_value(v) { - var i = 0, - n, - once, - match, - round, - start = 0, - vi; - switch (typeof v) { - case 'function': - return v(); - case 'string': - if (next_token.identifier && next_token.string === v) { - advance(); - return true; - } - return false; - } - for (;;) { - if (i >= v.length) { - return false; - } - vi = v[i]; - i += 1; - if (typeof vi === 'boolean') { - break; - } else if (typeof vi === 'number') { - n = vi; - vi = v[i]; - i += 1; - } else { - n = 1; - } - match = false; - while (n > 0) { - if (style_value(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 (style_value(css_attribute_data[v[i]])) { - match = true; - round = true; - once[i] = true; - break; - } - } - } - if (!round) { - return match; - } - } - } - - function style_child() { - if (next_token.id === '(number)') { - advance(); - if (next_token.string === 'n' && next_token.identifier) { - no_space_only(); - advance(); - if (next_token.id === '+') { - no_space_only(); - advance('+'); - no_space_only(); - advance('(number)'); - } - } - return; - } - if (next_token.identifier && - (next_token.string === 'odd' || next_token.string === 'even')) { - advance(); - return; - } - warn('unexpected_a'); - } - - function substyle() { - var v; - for (;;) { - if (next_token.id === '}' || next_token.id === '(end)' || - (xquote && next_token.id === xquote)) { - return; - } - v = style_attribute(); - advance(':'); - if (next_token.identifier && next_token.string === 'inherit') { - advance(); - } else { - if (!style_value(v)) { - warn('unexpected_a'); - advance(); - } - } - if (next_token.id === '!') { - advance('!'); - no_space_only(); - if (next_token.identifier && next_token.string === 'important') { - advance(); - } else { - warn('expected_a_b', - next_token, 'important', artifact()); - } - } - if (next_token.id === '}' || next_token.id === xquote) { - warn('expected_a_b', next_token, ';', artifact()); - } else { - semicolon(); - } - } - } - - function style_selector() { - if (next_token.identifier) { - if (!Object.prototype.hasOwnProperty.call(html_tag, option.cap - ? next_token.string.toLowerCase() - : next_token.string)) { - warn('expected_tagname_a'); - } - advance(); - } else { - switch (next_token.id) { - case '>': - case '+': - advance(); - style_selector(); - break; - case ':': - advance(':'); - switch (next_token.string) { - case 'active': - case 'after': - case 'before': - case 'checked': - case 'disabled': - case 'empty': - case 'enabled': - case 'first-child': - case 'first-letter': - case 'first-line': - case 'first-of-type': - case 'focus': - case 'hover': - case 'last-child': - case 'last-of-type': - case 'link': - case 'only-of-type': - case 'root': - case 'target': - case 'visited': - advance_identifier(next_token.string); - break; - case 'lang': - advance_identifier('lang'); - advance('('); - if (!next_token.identifier) { - warn('expected_lang_a'); - } - advance(')'); - break; - case 'nth-child': - case 'nth-last-child': - case 'nth-last-of-type': - case 'nth-of-type': - advance_identifier(next_token.string); - advance('('); - style_child(); - advance(')'); - break; - case 'not': - advance_identifier('not'); - advance('('); - if (next_token.id === ':' && peek(0).string === 'not') { - warn('not'); - } - style_selector(); - advance(')'); - break; - default: - warn('expected_pseudo_a'); - } - break; - case '#': - advance('#'); - if (!next_token.identifier) { - warn('expected_id_a'); - } - advance(); - break; - case '*': - advance('*'); - break; - case '.': - advance('.'); - if (!next_token.identifier) { - warn('expected_class_a'); - } - advance(); - break; - case '[': - advance('['); - if (!next_token.identifier) { - warn('expected_attribute_a'); - } - advance(); - if (next_token.id === '=' || next_token.string === '~=' || - next_token.string === '$=' || - next_token.string === '|=' || - next_token.id === '*=' || - next_token.id === '^=') { - advance(); - if (next_token.id !== '(string)') { - warn('expected_string_a'); - } - advance(); - } - advance(']'); - break; - default: - stop('expected_selector_a'); - } - } - } - - function style_pattern() { - if (next_token.id === '{') { - warn('expected_style_pattern'); - } - for (;;) { - style_selector(); - if (next_token.id === '= 0) { - warn('unexpected_char_a_b', token, v.charAt(x), a); - } - ids[u] = true; - } else if (a === 'class' || a === 'type' || a === 'name') { - x = v.search(qx); - if (x >= 0) { - warn('unexpected_char_a_b', token, v.charAt(x), a); - } - ids[u] = true; - } else if (a === 'href' || a === 'background' || - a === 'content' || a === 'data' || - a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { - if (option.safe && ux.test(v)) { - stop('bad_url_a', next_token, v); - } - urls.push(v); - } else if (a === 'for') { - if (option.adsafe) { - if (adsafe_id) { - if (v.slice(0, adsafe_id.length) !== adsafe_id) { - warn('adsafe_prefix_a', next_token, adsafe_id); - } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { - warn('adsafe_bad_id'); - } - } else { - warn('adsafe_bad_id'); - } - } - } else if (a === 'name') { - if (option.adsafe && v.indexOf('_') >= 0) { - warn('adsafe_name_a', next_token, v); - } - } - } - - function do_tag(name, attribute) { - var i, tag = html_tag[name], script, x; - src = false; - if (!tag) { - stop( - bundle.unrecognized_tag_a, - next_token, - name === name.toLowerCase() - ? name - : name + ' (capitalization error)' - ); - } - if (stack.length > 0) { - if (name === 'html') { - stop('unexpected_a', token, name); - } - x = tag.parent; - if (x) { - if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { - stop('tag_a_in_b', token, name, x); - } - } else if (!option.adsafe && !option.fragment) { - i = stack.length; - do { - if (i <= 0) { - stop('tag_a_in_b', token, name, 'body'); - } - i -= 1; - } while (stack[i].name !== 'body'); - } - } - switch (name) { - case 'div': - if (option.adsafe && stack.length === 1 && !adsafe_id) { - warn('adsafe_missing_id'); - } - break; - case 'script': - xmode = 'script'; - advance('>'); - if (attribute.lang) { - warn('lang', token); - } - if (option.adsafe && stack.length !== 1) { - warn('adsafe_placement', token); - } - if (attribute.src) { - if (option.adsafe && (!adsafe_may || !approved[attribute.src])) { - warn('adsafe_source', token); - } - } else { - step_in(next_token.from); - edge(); - use_strict(); - adsafe_top = true; - script = statements(); - -// JSLint is also the static analyzer for ADsafe. See www.ADsafe.org. - - if (option.adsafe) { - if (adsafe_went) { - stop('adsafe_script', token); - } - if (script.length !== 1 || - aint(script[0], 'id', '(') || - aint(script[0].first, 'id', '.') || - aint(script[0].first.first, 'string', 'ADSAFE') || - aint(script[0].second[0], 'string', adsafe_id)) { - stop('adsafe_id_go'); - } - switch (script[0].first.second.string) { - case 'id': - if (adsafe_may || adsafe_went || - script[0].second.length !== 1) { - stop('adsafe_id', next_token); - } - adsafe_may = true; - break; - case 'go': - if (adsafe_went) { - stop('adsafe_go'); - } - if (script[0].second.length !== 2 || - aint(script[0].second[1], 'id', 'function') || - !script[0].second[1].first || - aint(script[0].second[1].first[0], 'string', 'dom') || - script[0].second[1].first.length > 2 || - (script[0].second[1].first.length === 2 && - aint(script[0].second[1].first[1], 'string', 'lib'))) { - stop('adsafe_go', next_token); - } - adsafe_went = true; - break; - default: - stop('adsafe_id_go'); - } - } - indent = null; - } - xmode = 'html'; - advance(''); - styles(); - xmode = 'html'; - advance(''; - } - - function html() { - var attribute, attributes, is_empty, name, old_white = option.white, - quote, tag_name, tag, wmode; - xmode = 'html'; - xquote = ''; - stack = null; - for (;;) { - switch (next_token.string) { - case '<': - xmode = 'html'; - advance('<'); - attributes = {}; - tag_name = next_token; - name = tag_name.string; - advance_identifier(name); - if (option.cap) { - name = name.toLowerCase(); - } - tag_name.name = name; - if (!stack) { - stack = []; - do_begin(name); - } - tag = html_tag[name]; - if (typeof tag !== 'object') { - stop('unrecognized_tag_a', tag_name, name); - } - is_empty = tag.empty; - tag_name.type = name; - for (;;) { - if (next_token.id === '/') { - advance('/'); - if (next_token.id !== '>') { - warn('expected_a_b', next_token, '>', artifact()); - } - break; - } - if (next_token.id && next_token.id.charAt(0) === '>') { - break; - } - if (!next_token.identifier) { - if (next_token.id === '(end)' || next_token.id === '(error)') { - warn('expected_a_b', next_token, '>', artifact()); - } - warn('bad_name_a'); - } - option.white = false; - spaces(); - attribute = next_token.string; - option.white = old_white; - advance(); - if (!option.cap && attribute !== attribute.toLowerCase()) { - warn('attribute_case_a', token); - } - attribute = attribute.toLowerCase(); - xquote = ''; - if (Object.prototype.hasOwnProperty.call(attributes, attribute)) { - warn('duplicate_a', token, attribute); - } - if (attribute.slice(0, 2) === 'on') { - if (!option.on) { - warn('html_handlers'); - } - xmode = 'scriptstring'; - advance('='); - quote = next_token.id; - if (quote !== '"' && quote !== '\'') { - stop('expected_a_b', next_token, '"', artifact()); - } - xquote = quote; - wmode = option.white; - option.white = true; - advance(quote); - use_strict(); - statements(); - option.white = wmode; - if (next_token.id !== quote) { - stop('expected_a_b', next_token, quote, artifact()); - } - xmode = 'html'; - xquote = ''; - advance(quote); - tag = false; - } else if (attribute === 'style') { - xmode = 'scriptstring'; - advance('='); - quote = next_token.id; - if (quote !== '"' && quote !== '\'') { - stop('expected_a_b', next_token, '"', artifact()); - } - xmode = 'styleproperty'; - xquote = quote; - advance(quote); - substyle(); - xmode = 'html'; - xquote = ''; - advance(quote); - tag = false; - } else { - if (next_token.id === '=') { - advance('='); - tag = next_token.string; - if (!next_token.identifier && - next_token.id !== '"' && - next_token.id !== '\'' && - next_token.id !== '(string)' && - next_token.id !== '(number)' && - next_token.id !== '(color)') { - warn('expected_attribute_value_a', token, attribute); - } - advance(); - } else { - tag = true; - } - } - attributes[attribute] = tag; - do_attribute(attribute, tag); - } - do_tag(name, attributes); - if (!is_empty) { - stack.push(tag_name); - } - xmode = 'outer'; - advance('>'); - break; - case '') { - stop('expected_a_b', next_token, '>', artifact()); - } - xmode = 'outer'; - advance('>'); - break; - case '' || next_token.id === '(end)') { - break; - } - if (next_token.string.indexOf('--') >= 0) { - stop('unexpected_a', next_token, '--'); - } - if (next_token.string.indexOf('<') >= 0) { - stop('unexpected_a', next_token, '<'); - } - if (next_token.string.indexOf('>') >= 0) { - stop('unexpected_a', next_token, '>'); - } - } - xmode = 'outer'; - advance('>'); - break; - case '(end)': - if (stack.length !== 0) { - warn('missing_a', next_token, ''); - } - return; - default: - if (next_token.id === '(end)') { - stop('missing_a', next_token, - ''); - } else { - advance(); - } - } - if (stack && stack.length === 0 && (option.adsafe || - !option.fragment || next_token.id === '(end)')) { - break; - } - } - if (next_token.id !== '(end)') { - stop('unexpected_a'); - } - } - - -// The actual JSLINT function itself. - - itself = function JSLint(the_source, the_option) { - - var i, predef, tree; - JSLINT.errors = []; - JSLINT.tree = ''; - JSLINT.properties = ''; - begin = prev_token = token = next_token = - Object.create(syntax['(begin)']); - predefined = {}; - add_to_predefined(standard); - property = {}; - if (the_option) { - option = Object.create(the_option); - predef = option.predef; - if (predef) { - if (Array.isArray(predef)) { - for (i = 0; i < predef.length; i += 1) { - predefined[predef[i]] = true; - } - } else if (typeof predef === 'object') { - add_to_predefined(predef); - } - } - do_safe(); - } else { - option = {}; - } - option.indent = +option.indent || 4; - option.maxerr = +option.maxerr || 50; - adsafe_id = ''; - adsafe_may = adsafe_top = adsafe_went = false; - approved = {}; - if (option.approved) { - for (i = 0; i < option.approved.length; i += 1) { - approved[option.approved[i]] = option.approved[i]; - } - } else { - approved.test = 'test'; - } - tab = ''; - for (i = 0; i < option.indent; i += 1) { - tab += ' '; - } - global_scope = scope = {}; - global_funct = funct = { - '(scope)': scope, - '(breakage)': 0, - '(loopage)': 0 - }; - functions = [funct]; - - comments_off = false; - ids = {}; - in_block = false; - indent = null; - json_mode = false; - lookahead = []; - node_js = false; - prereg = true; - src = false; - stack = null; - strict_mode = false; - urls = []; - var_mode = null; - warnings = 0; - xmode = ''; - lex.init(the_source); - - assume(); - - try { - advance(); - if (next_token.id === '(number)') { - stop('unexpected_a'); - } else if (next_token.string.charAt(0) === '<') { - html(); - if (option.adsafe && !adsafe_went) { - warn('adsafe_go', this); - } - } else { - switch (next_token.id) { - case '{': - case '[': - json_mode = true; - json_value(); - break; - case '@': - case '*': - case '#': - case '.': - case ':': - xmode = 'style'; - advance(); - if (token.id !== '@' || !next_token.identifier || - next_token.string !== 'charset' || token.line !== 1 || - token.from !== 1) { - stop('css'); - } - advance(); - if (next_token.id !== '(string)' && - next_token.string !== 'UTF-8') { - stop('css'); - } - advance(); - semicolon(); - styles(); - break; - - default: - if (option.adsafe && option.fragment) { - stop('expected_a_b', - next_token, '
', artifact()); - } - -// If the first token is a semicolon, ignore it. This is sometimes used when -// files are intended to be appended to files that may be sloppy. A sloppy -// file may be depending on semicolon insertion on its last line. - - step_in(1); - if (next_token.id === ';' && !node_js) { - semicolon(); - } - adsafe_top = true; - tree = statements(); - begin.first = tree; - itself.tree = begin; - if (option.adsafe && (tree.length !== 1 || - aint(tree[0], 'id', '(') || - aint(tree[0].first, 'id', '.') || - aint(tree[0].first.first, 'string', 'ADSAFE') || - aint(tree[0].first.second, 'string', 'lib') || - tree[0].second.length !== 2 || - tree[0].second[0].id !== '(string)' || - aint(tree[0].second[1], 'id', 'function'))) { - stop('adsafe_lib'); - } - if (tree.disrupt) { - warn('weird_program', prev_token); - } - } - } - indent = null; - advance('(end)'); - itself.property = property; - } catch (e) { - if (e) { // ~~ - JSLINT.errors.push({ - reason : e.message, - line : e.line || next_token.line, - character : e.character || next_token.from - }, null); - } - } - return JSLINT.errors.length === 0; - }; - - -// Data summary. - - itself.data = function () { - var data = {functions: []}, - function_data, - globals, - i, - j, - kind, - name, - the_function, - undef = [], - unused = []; - if (itself.errors.length) { - data.errors = itself.errors; - } - - if (json_mode) { - data.json = true; - } - - if (urls.length > 0) { - data.urls = urls; - } - - globals = Object.keys(global_scope).filter(function (value) { - return value.charAt(0) !== '(' && typeof standard[value] !== 'boolean'; - }); - if (globals.length > 0) { - data.globals = globals; - } - - for (i = 1; i < functions.length; i += 1) { - the_function = functions[i]; - function_data = {}; - for (j = 0; j < functionicity.length; j += 1) { - function_data[functionicity[j]] = []; - } - for (name in the_function) { - if (Object.prototype.hasOwnProperty.call(the_function, name)) { - if (name.charAt(0) !== '(') { - kind = the_function[name]; - if (kind === 'unction' || kind === 'unparam') { - kind = 'unused'; - } - if (Array.isArray(function_data[kind])) { - function_data[kind].push(name); - if (kind === 'unused') { - unused.push({ - name: name, - line: the_function['(line)'], - 'function': the_function['(name)'] - }); - } else if (kind === 'undef') { - undef.push({ - name: name, - line: the_function['(line)'], - 'function': the_function['(name)'] - }); - } - } - } - } - } - for (j = 0; j < functionicity.length; j += 1) { - if (function_data[functionicity[j]].length === 0) { - delete function_data[functionicity[j]]; - } - } - function_data.name = the_function['(name)']; - function_data.params = the_function['(params)']; - function_data.line = the_function['(line)']; - data.functions.push(function_data); - } - - if (unused.length > 0) { - data.unused = unused; - } - if (undef.length > 0) { - data['undefined'] = undef; - } - return data; - }; - - itself.error_report = function (data) { - var evidence, i, output = [], snippets, warning; - if (data.errors) { - if (data.json) { - output.push('JSON: bad.
'); - } - for (i = 0; i < data.errors.length; i += 1) { - warning = data.errors[i]; - if (warning) { - evidence = warning.evidence || ''; - output.push(''); - if (isFinite(warning.line)) { - output.push('
line ' + - String(warning.line) + - ' character ' + String(warning.character) + - '
'); - } - output.push(warning.reason.entityify() + '
'); - if (evidence) { - output.push('
' + evidence.entityify() + '
'); - } - } - } - } - if (data.unused || data['undefined']) { - output.push('
'); - if (data['undefined']) { - output.push('
undefined
'); - snippets = []; - for (i = 0; i < data['undefined'].length; i += 1) { - snippets[i] = '' + data['undefined'][i].name + - ' 
' + - data['undefined'][i]['function'] + ' ' + - String(data['undefined'][i].line) + '
'; - } - output.push(snippets.join(', ')); - output.push('
'); - } - if (data.unused) { - output.push('
unused
'); - snippets = []; - for (i = 0; i < data.unused.length; i += 1) { - snippets[i] = '' + data.unused[i].name + ' 
' + - data.unused[i]['function'] + ' ' + - String(data.unused[i].line) + '
'; - } - output.push(snippets.join(', ')); - output.push('
'); - } - output.push('
'); - } - return output.join(''); - }; - - - itself.report = function (data) { - var dl, i, j, names, output = [], the_function; - - function detail(h, value) { - var comma_needed, singularity; - if (Array.isArray(value)) { - output.push("
" + h + "
"); - value.sort().forEach(function (item) { - if (item !== singularity) { - singularity = item; - output.push((comma_needed ? ', ' : '') + singularity); - comma_needed = true; - } - }); - output.push("
"); - } else if (value) { - output.push("
" + h + "
", value, "
"); - } - } - - output.push('
'); - if (data.urls) { - detail('url', data.urls); - dl = true; - } - if (data.globals) { - detail('global', data.globals); - dl = true; - } else if (xmode === 'style') { - output.push("
CSS.
"); - } else if (data.json) { - if (!data.errors) { - output.push("
JSON: good.
"); - } - } else { - output.push("
No new global variables introduced.
"); - } - if (dl) { - output.push("
"); - } else { - output[0] = ''; - } - - if (data.functions) { - for (i = 0; i < data.functions.length; i += 1) { - the_function = data.functions[i]; - names = []; - if (the_function.params) { - for (j = 0; j < the_function.params.length; j += 1) { - names[j] = the_function.params[j].string; - } - } - output.push('
line ' + - String(the_function.line) + '
' + - the_function.name.entityify() + - '(' + names.join(', ') + ')'); - detail('undefined', the_function['undefined']); - detail('unused', the_function.unused); - detail('closure', the_function.closure); - detail('variable', the_function['var']); - detail('exception', the_function.exception); - detail('outer', the_function.outer); - detail('global', the_function.global); - detail('label', the_function.label); - output.push('
'); - } - } - return output.join(''); - }; - - itself.properties_report = function (property) { - if (!property) { - return ''; - } - var i, - key, - keys = Object.keys(property).sort(), - length, - output = ['/*properties'], - mem = ' ', - name, - not_first = false; - for (i = 0; i < keys.length; i += 1) { - key = keys[i]; - if (property[key] > 0) { - if (not_first) { - mem += ', '; - } - name = ix.test(key) - ? key - : '\'' + key.replace(nx, sanitize) + '\''; - length += name.length + 2; - if (mem.length + name.length > 80) { - output.push(mem); - mem = ' '; - } - mem += name; - not_first = true; - } - } - output.push(mem, '*/\n'); - return output.join('\n'); - }; - - itself.jslint = itself; - - itself.edition = '2012-10-01'; - - return itself; -}()); - - -exports.JSLINT = JSLINT; diff --git a/tools/lint-all b/tools/lint-all index 824dad9de6..cc4a80ebbd 100755 --- a/tools/lint-all +++ b/tools/lint-all @@ -30,7 +30,6 @@ puppet/apt/.forge-release puppet/apt/README.md static/locale static/third -tools/jslint/jslint.js zerver/migrations zproject/dev_settings.py zproject/settings.py @@ -502,13 +501,6 @@ def run(): result = subprocess.call(['tools/check-css']) return result - @lint - def jslint(): - # type: () -> int - result = subprocess.call(['node', 'tools/jslint/check-all.js'] - + by_lang['js']) - return result - @lint def eslint(): # type: () -> int diff --git a/tools/print-all/print-all b/tools/print-all/print-all index d6d9ac9587..469f665c0c 100755 --- a/tools/print-all/print-all +++ b/tools/print-all/print-all @@ -35,7 +35,6 @@ certs exclude_files = """ zilencer/management/commands/test_messages.txt tools/usability_testing/humbug_usability_test_data -tools/jslint/jslint.js """.split() extensions = dict(