"no use strict"; var console = { log: function(msg) { postMessage({type: "log", data: msg}); } }; var window = { console: console }; var normalizeModule = function(parentId, moduleName) { // normalize plugin requires if (moduleName.indexOf("!") !== -1) { var chunks = moduleName.split("!"); return normalizeModule(parentId, chunks[0]) + "!" + normalizeModule(parentId, chunks[1]); } // normalize relative requires if (moduleName.charAt(0) == ".") { var base = parentId.split("/").slice(0, -1).join("/"); var moduleName = base + "/" + moduleName; while(moduleName.indexOf(".") !== -1 && previous != moduleName) { var previous = moduleName; var moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, ""); } } return moduleName; }; var require = function(parentId, id) { var id = normalizeModule(parentId, id); var module = require.modules[id]; if (module) { if (!module.initialized) { module.exports = module.factory().exports; module.initialized = true; } return module.exports; } var chunks = id.split("/"); chunks[0] = require.tlns[chunks[0]] || chunks[0]; var path = chunks.join("/") + ".js"; require.id = id; importScripts(path); return require(parentId, id); }; require.modules = {}; require.tlns = {}; var define = function(id, deps, factory) { if (arguments.length == 2) { factory = deps; } else if (arguments.length == 1) { factory = id; id = require.id; } if (id.indexOf("text!") === 0) return; var req = function(deps, factory) { return require(id, deps, factory); }; require.modules[id] = { factory: function() { var module = { exports: {} }; var returnExports = factory(req, module.exports, module); if (returnExports) module.exports = returnExports; return module; } }; }; function initBaseUrls(topLevelNamespaces) { require.tlns = topLevelNamespaces; } function initSender() { var EventEmitter = require(null, "ace/lib/event_emitter").EventEmitter; var oop = require(null, "ace/lib/oop"); var Sender = function() {}; (function() { oop.implement(this, EventEmitter); this.callback = function(data, callbackId) { postMessage({ type: "call", id: callbackId, data: data }); }; this.emit = function(name, data) { postMessage({ type: "event", name: name, data: data }); }; }).call(Sender.prototype); return new Sender(); } var main; var sender; onmessage = function(e) { var msg = e.data; if (msg.command) { main[msg.command].apply(main, msg.args); } else if (msg.init) { initBaseUrls(msg.tlns); require(null, "ace/lib/fixoldbrowsers"); sender = initSender(); var clazz = require(null, msg.module)[msg.classname]; main = new clazz(sender); } else if (msg.event && sender) { sender._emit(msg.event, msg.data); } }; // vim:set ts=4 sts=4 sw=4 st: // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License // -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project) // -- dantman Daniel Friesen Copyright(C) 2010 XXX No License Specified // -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License // -- Irakli Gozalishvili Copyright (C) 2010 MIT License /*! Copyright (c) 2009, 280 North Inc. http://280north.com/ MIT License. http://github.com/280north/narwhal/blob/master/README.md */ define('ace/lib/fixoldbrowsers', ['require', 'exports', 'module' , 'ace/lib/regexp', 'ace/lib/es5-shim'], function(require, exports, module) { "use strict"; require("./regexp"); require("./es5-shim"); });/* * Based on code from: * * XRegExp 1.5.0 * (c) 2007-2010 Steven Levithan * MIT License * * Provides an augmented, extensible, cross-browser implementation of regular expressions, * including support for additional syntax, flags, and methods */ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, exports, module) { "use strict"; //--------------------------------- // Private variables //--------------------------------- var real = { exec: RegExp.prototype.exec, test: RegExp.prototype.test, match: String.prototype.match, replace: String.prototype.replace, split: String.prototype.split }, compliantExecNpcg = real.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups compliantLastIndexIncrement = function () { var x = /^/g; real.test.call(x, ""); return !x.lastIndex; }(); //--------------------------------- // Overriden native methods //--------------------------------- // Adds named capture support (with backreferences returned as `result.name`), and fixes two // cross-browser issues per ES3: // - Captured values for nonparticipating capturing groups should be returned as `undefined`, // rather than the empty string. // - `lastIndex` should not be incremented after zero-length matches. RegExp.prototype.exec = function (str) { var match = real.exec.apply(this, arguments), name, r2; if ( typeof(str) == 'string' && match) { // Fix browsers whose `exec` methods don't consistently return `undefined` for // nonparticipating capturing groups if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { r2 = RegExp(this.source, real.replace.call(getNativeFlags(this), "g", "")); // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed // matching due to characters outside the match real.replace.call(str.slice(match.index), r2, function () { for (var i = 1; i < arguments.length - 2; i++) { if (arguments[i] === undefined) match[i] = undefined; } }); } // Attach named capture properties if (this._xregexp && this._xregexp.captureNames) { for (var i = 1; i < match.length; i++) { name = this._xregexp.captureNames[i - 1]; if (name) match[name] = match[i]; } } // Fix browsers that increment `lastIndex` after zero-length matches if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) this.lastIndex--; } return match; }; // Don't override `test` if it won't change anything if (!compliantLastIndexIncrement) { // Fix browser bug in native method RegExp.prototype.test = function (str) { // Use the native `exec` to skip some processing overhead, even though the overriden // `exec` would take care of the `lastIndex` fix var match = real.exec.call(this, str); // Fix browsers that increment `lastIndex` after zero-length matches if (match && this.global && !match[0].length && (this.lastIndex > match.index)) this.lastIndex--; return !!match; }; } //--------------------------------- // Private helper functions //--------------------------------- function getNativeFlags (regex) { return (regex.global ? "g" : "") + (regex.ignoreCase ? "i" : "") + (regex.multiline ? "m" : "") + (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3 (regex.sticky ? "y" : ""); }; function indexOf (array, item, from) { if (Array.prototype.indexOf) // Use the native array method if available return array.indexOf(item, from); for (var i = from || 0; i < array.length; i++) { if (array[i] === item) return i; } return -1; }; }); // vim: ts=4 sts=4 sw=4 expandtab // -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License // -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project) // -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA // -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License // -- Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License // -- kitcambridge Kit Cambridge Copyright (C) 2011 MIT License // -- kossnocorp Sasha Koss XXX TODO License or CLA // -- bryanforbes Bryan Forbes XXX TODO License or CLA // -- killdream Quildreen Motta Copyright (C) 2011 MIT Licence // -- michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD License // -- sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License // -- bbqsrc Brendan Molloy (C) 2011 Creative Commons Zero (public domain) // -- iwyg XXX TODO License or CLA // -- DomenicDenicola Domenic Denicola Copyright (C) 2011 MIT License // -- xavierm02 Montillet Xavier XXX TODO License or CLA // -- Raynos Raynos XXX TODO License or CLA // -- samsonjs Sami Samhuri Copyright (C) 2010 MIT License // -- rwldrn Rick Waldron Copyright (C) 2011 MIT License // -- lexer Alexey Zakharov XXX TODO License or CLA /*! Copyright (c) 2009, 280 North Inc. http://280north.com/ MIT License. http://github.com/280north/narwhal/blob/master/README.md */ define('ace/lib/es5-shim', ['require', 'exports', 'module' ], function(require, exports, module) { /* * Brings an environment as close to ECMAScript 5 compliance * as is possible with the facilities of erstwhile engines. * * Annotated ES5: http://es5.github.com/ (specific links below) * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf * * @module */ /*whatsupdoc*/ // // Function // ======== // // ES-5 15.3.4.5 // http://es5.github.com/#x15.3.4.5 if (!Function.prototype.bind) { Function.prototype.bind = function bind(that) { // .length is 1 // 1. Let Target be the this value. var target = this; // 2. If IsCallable(Target) is false, throw a TypeError exception. if (typeof target != "function") throw new TypeError(); // TODO message // 3. Let A be a new (possibly empty) internal list of all of the // argument values provided after thisArg (arg1, arg2 etc), in order. // XXX slicedArgs will stand in for "A" if used var args = slice.call(arguments, 1); // for normal call // 4. Let F be a new native ECMAScript object. // 11. Set the [[Prototype]] internal property of F to the standard // built-in Function prototype object as specified in 15.3.3.1. // 12. Set the [[Call]] internal property of F as described in // 15.3.4.5.1. // 13. Set the [[Construct]] internal property of F as described in // 15.3.4.5.2. // 14. Set the [[HasInstance]] internal property of F as described in // 15.3.4.5.3. var bound = function () { if (this instanceof bound) { // 15.3.4.5.2 [[Construct]] // When the [[Construct]] internal method of a function object, // F that was created using the bind function is called with a // list of arguments ExtraArgs, the following steps are taken: // 1. Let target be the value of F's [[TargetFunction]] // internal property. // 2. If target has no [[Construct]] internal method, a // TypeError exception is thrown. // 3. Let boundArgs be the value of F's [[BoundArgs]] internal // property. // 4. Let args be a new list containing the same values as the // list boundArgs in the same order followed by the same // values as the list ExtraArgs in the same order. // 5. Return the result of calling the [[Construct]] internal // method of target providing args as the arguments. var F = function(){}; F.prototype = target.prototype; var self = new F; var result = target.apply( self, args.concat(slice.call(arguments)) ); if (result !== null && Object(result) === result) return result; return self; } else { // 15.3.4.5.1 [[Call]] // When the [[Call]] internal method of a function object, F, // which was created using the bind function is called with a // this value and a list of arguments ExtraArgs, the following // steps are taken: // 1. Let boundArgs be the value of F's [[BoundArgs]] internal // property. // 2. Let boundThis be the value of F's [[BoundThis]] internal // property. // 3. Let target be the value of F's [[TargetFunction]] internal // property. // 4. Let args be a new list containing the same values as the // list boundArgs in the same order followed by the same // values as the list ExtraArgs in the same order. // 5. Return the result of calling the [[Call]] internal method // of target providing boundThis as the this value and // providing args as the arguments. // equiv: target.call(this, ...boundArgs, ...args) return target.apply( that, args.concat(slice.call(arguments)) ); } }; // XXX bound.length is never writable, so don't even try // // 15. If the [[Class]] internal property of Target is "Function", then // a. Let L be the length property of Target minus the length of A. // b. Set the length own property of F to either 0 or L, whichever is // larger. // 16. Else set the length own property of F to 0. // 17. Set the attributes of the length own property of F to the values // specified in 15.3.5.1. // TODO // 18. Set the [[Extensible]] internal property of F to true. // TODO // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3). // 20. Call the [[DefineOwnProperty]] internal method of F with // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and // false. // 21. Call the [[DefineOwnProperty]] internal method of F with // arguments "arguments", PropertyDescriptor {[[Get]]: thrower, // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, // and false. // TODO // NOTE Function objects created using Function.prototype.bind do not // have a prototype property or the [[Code]], [[FormalParameters]], and // [[Scope]] internal properties. // XXX can't delete prototype in pure-js. // 22. Return F. return bound; }; } // Shortcut to an often accessed properties, in order to avoid multiple // dereference that costs universally. // _Please note: Shortcuts are defined after `Function.prototype.bind` as we // us it in defining shortcuts. var call = Function.prototype.call; var prototypeOfArray = Array.prototype; var prototypeOfObject = Object.prototype; var slice = prototypeOfArray.slice; var toString = call.bind(prototypeOfObject.toString); var owns = call.bind(prototypeOfObject.hasOwnProperty); // If JS engine supports accessors creating shortcuts. var defineGetter; var defineSetter; var lookupGetter; var lookupSetter; var supportsAccessors; if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) { defineGetter = call.bind(prototypeOfObject.__defineGetter__); defineSetter = call.bind(prototypeOfObject.__defineSetter__); lookupGetter = call.bind(prototypeOfObject.__lookupGetter__); lookupSetter = call.bind(prototypeOfObject.__lookupSetter__); } // // Array // ===== // // ES5 15.4.3.2 // http://es5.github.com/#x15.4.3.2 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray if (!Array.isArray) { Array.isArray = function isArray(obj) { return toString(obj) == "[object Array]"; }; } // The IsCallable() check in the Array functions // has been replaced with a strict check on the // internal class of the object to trap cases where // the provided function was actually a regular // expression literal, which in V8 and // JavaScriptCore is a typeof "function". Only in // V8 are regular expression literals permitted as // reduce parameters, so it is desirable in the // general case for the shim to match the more // strict and common behavior of rejecting regular // expressions. // ES5 15.4.4.18 // http://es5.github.com/#x15.4.4.18 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach if (!Array.prototype.forEach) { Array.prototype.forEach = function forEach(fun /*, thisp*/) { var self = toObject(this), thisp = arguments[1], i = 0, length = self.length >>> 0; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } while (i < length) { if (i in self) { // Invoke the callback function with call, passing arguments: // context, property value, property key, thisArg object context fun.call(thisp, self[i], i, self); } i++; } }; } // ES5 15.4.4.19 // http://es5.github.com/#x15.4.4.19 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map if (!Array.prototype.map) { Array.prototype.map = function map(fun /*, thisp*/) { var self = toObject(this), length = self.length >>> 0, result = Array(length), thisp = arguments[1]; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } for (var i = 0; i < length; i++) { if (i in self) result[i] = fun.call(thisp, self[i], i, self); } return result; }; } // ES5 15.4.4.20 // http://es5.github.com/#x15.4.4.20 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter if (!Array.prototype.filter) { Array.prototype.filter = function filter(fun /*, thisp */) { var self = toObject(this), length = self.length >>> 0, result = [], thisp = arguments[1]; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } for (var i = 0; i < length; i++) { if (i in self && fun.call(thisp, self[i], i, self)) result.push(self[i]); } return result; }; } // ES5 15.4.4.16 // http://es5.github.com/#x15.4.4.16 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every if (!Array.prototype.every) { Array.prototype.every = function every(fun /*, thisp */) { var self = toObject(this), length = self.length >>> 0, thisp = arguments[1]; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } for (var i = 0; i < length; i++) { if (i in self && !fun.call(thisp, self[i], i, self)) return false; } return true; }; } // ES5 15.4.4.17 // http://es5.github.com/#x15.4.4.17 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some if (!Array.prototype.some) { Array.prototype.some = function some(fun /*, thisp */) { var self = toObject(this), length = self.length >>> 0, thisp = arguments[1]; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } for (var i = 0; i < length; i++) { if (i in self && fun.call(thisp, self[i], i, self)) return true; } return false; }; } // ES5 15.4.4.21 // http://es5.github.com/#x15.4.4.21 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce if (!Array.prototype.reduce) { Array.prototype.reduce = function reduce(fun /*, initial*/) { var self = toObject(this), length = self.length >>> 0; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } // no value to return if no initial value and an empty array if (!length && arguments.length == 1) throw new TypeError(); // TODO message var i = 0; var result; if (arguments.length >= 2) { result = arguments[1]; } else { do { if (i in self) { result = self[i++]; break; } // if array contains no values, no initial value to return if (++i >= length) throw new TypeError(); // TODO message } while (true); } for (; i < length; i++) { if (i in self) result = fun.call(void 0, result, self[i], i, self); } return result; }; } // ES5 15.4.4.22 // http://es5.github.com/#x15.4.4.22 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight if (!Array.prototype.reduceRight) { Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) { var self = toObject(this), length = self.length >>> 0; // If no callback function or if callback is not a callable function if (toString(fun) != "[object Function]") { throw new TypeError(); // TODO message } // no value to return if no initial value, empty array if (!length && arguments.length == 1) throw new TypeError(); // TODO message var result, i = length - 1; if (arguments.length >= 2) { result = arguments[1]; } else { do { if (i in self) { result = self[i--]; break; } // if array contains no values, no initial value to return if (--i < 0) throw new TypeError(); // TODO message } while (true); } do { if (i in this) result = fun.call(void 0, result, self[i], i, self); } while (i--); return result; }; } // ES5 15.4.4.14 // http://es5.github.com/#x15.4.4.14 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf if (!Array.prototype.indexOf) { Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) { var self = toObject(this), length = self.length >>> 0; if (!length) return -1; var i = 0; if (arguments.length > 1) i = toInteger(arguments[1]); // handle negative indices i = i >= 0 ? i : Math.max(0, length + i); for (; i < length; i++) { if (i in self && self[i] === sought) { return i; } } return -1; }; } // ES5 15.4.4.15 // http://es5.github.com/#x15.4.4.15 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf if (!Array.prototype.lastIndexOf) { Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) { var self = toObject(this), length = self.length >>> 0; if (!length) return -1; var i = length - 1; if (arguments.length > 1) i = Math.min(i, toInteger(arguments[1])); // handle negative indices i = i >= 0 ? i : length - Math.abs(i); for (; i >= 0; i--) { if (i in self && sought === self[i]) return i; } return -1; }; } // // Object // ====== // // ES5 15.2.3.2 // http://es5.github.com/#x15.2.3.2 if (!Object.getPrototypeOf) { // https://github.com/kriskowal/es5-shim/issues#issue/2 // http://ejohn.org/blog/objectgetprototypeof/ // recommended by fschaefer on github Object.getPrototypeOf = function getPrototypeOf(object) { return object.__proto__ || ( object.constructor ? object.constructor.prototype : prototypeOfObject ); }; } // ES5 15.2.3.3 // http://es5.github.com/#x15.2.3.3 if (!Object.getOwnPropertyDescriptor) { var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " + "non-object: "; Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) { if ((typeof object != "object" && typeof object != "function") || object === null) throw new TypeError(ERR_NON_OBJECT + object); // If object does not owns property return undefined immediately. if (!owns(object, property)) return; var descriptor, getter, setter; // If object has a property then it's for sure both `enumerable` and // `configurable`. descriptor = { enumerable: true, configurable: true }; // If JS engine supports accessor properties then property may be a // getter or setter. if (supportsAccessors) { // Unfortunately `__lookupGetter__` will return a getter even // if object has own non getter property along with a same named // inherited getter. To avoid misbehavior we temporary remove // `__proto__` so that `__lookupGetter__` will return getter only // if it's owned by an object. var prototype = object.__proto__; object.__proto__ = prototypeOfObject; var getter = lookupGetter(object, property); var setter = lookupSetter(object, property); // Once we have getter and setter we can put values back. object.__proto__ = prototype; if (getter || setter) { if (getter) descriptor.get = getter; if (setter) descriptor.set = setter; // If it was accessor property we're done and return here // in order to avoid adding `value` to the descriptor. return descriptor; } } // If we got this far we know that object has an own property that is // not an accessor so we set it as a value and return descriptor. descriptor.value = object[property]; return descriptor; }; } // ES5 15.2.3.4 // http://es5.github.com/#x15.2.3.4 if (!Object.getOwnPropertyNames) { Object.getOwnPropertyNames = function getOwnPropertyNames(object) { return Object.keys(object); }; } // ES5 15.2.3.5 // http://es5.github.com/#x15.2.3.5 if (!Object.create) { Object.create = function create(prototype, properties) { var object; if (prototype === null) { object = { "__proto__": null }; } else { if (typeof prototype != "object") throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'"); var Type = function () {}; Type.prototype = prototype; object = new Type(); // IE has no built-in implementation of `Object.getPrototypeOf` // neither `__proto__`, but this manually setting `__proto__` will // guarantee that `Object.getPrototypeOf` will work as expected with // objects created using `Object.create` object.__proto__ = prototype; } if (properties !== void 0) Object.defineProperties(object, properties); return object; }; } // ES5 15.2.3.6 // http://es5.github.com/#x15.2.3.6 // Patch for WebKit and IE8 standard mode // Designed by hax // related issue: https://github.com/kriskowal/es5-shim/issues#issue/5 // IE8 Reference: // http://msdn.microsoft.com/en-us/library/dd282900.aspx // http://msdn.microsoft.com/en-us/library/dd229916.aspx // WebKit Bugs: // https://bugs.webkit.org/show_bug.cgi?id=36423 function doesDefinePropertyWork(object) { try { Object.defineProperty(object, "sentinel", {}); return "sentinel" in object; } catch (exception) { // returns falsy } } // check whether defineProperty works if it's given. Otherwise, // shim partially. if (Object.defineProperty) { var definePropertyWorksOnObject = doesDefinePropertyWork({}); var definePropertyWorksOnDom = typeof document == "undefined" || doesDefinePropertyWork(document.createElement("div")); if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) { var definePropertyFallback = Object.defineProperty; } } if (!Object.defineProperty || definePropertyFallback) { var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: "; var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: " var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " + "on this javascript engine"; Object.defineProperty = function defineProperty(object, property, descriptor) { if ((typeof object != "object" && typeof object != "function") || object === null) throw new TypeError(ERR_NON_OBJECT_TARGET + object); if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null) throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor); // make a valiant attempt to use the real defineProperty // for I8's DOM elements. if (definePropertyFallback) { try { return definePropertyFallback.call(Object, object, property, descriptor); } catch (exception) { // try the shim if the real one doesn't work } } // If it's a data property. if (owns(descriptor, "value")) { // fail silently if "writable", "enumerable", or "configurable" // are requested but not supported /* // alternate approach: if ( // can't implement these features; allow false but not true !(owns(descriptor, "writable") ? descriptor.writable : true) || !(owns(descriptor, "enumerable") ? descriptor.enumerable : true) || !(owns(descriptor, "configurable") ? descriptor.configurable : true) ) throw new RangeError( "This implementation of Object.defineProperty does not " + "support configurable, enumerable, or writable." ); */ if (supportsAccessors && (lookupGetter(object, property) || lookupSetter(object, property))) { // As accessors are supported only on engines implementing // `__proto__` we can safely override `__proto__` while defining // a property to make sure that we don't hit an inherited // accessor. var prototype = object.__proto__; object.__proto__ = prototypeOfObject; // Deleting a property anyway since getter / setter may be // defined on object itself. delete object[property]; object[property] = descriptor.value; // Setting original `__proto__` back now. object.__proto__ = prototype; } else { object[property] = descriptor.value; } } else { if (!supportsAccessors) throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED); // If we got that far then getters and setters can be defined !! if (owns(descriptor, "get")) defineGetter(object, property, descriptor.get); if (owns(descriptor, "set")) defineSetter(object, property, descriptor.set); } return object; }; } // ES5 15.2.3.7 // http://es5.github.com/#x15.2.3.7 if (!Object.defineProperties) { Object.defineProperties = function defineProperties(object, properties) { for (var property in properties) { if (owns(properties, property)) Object.defineProperty(object, property, properties[property]); } return object; }; } // ES5 15.2.3.8 // http://es5.github.com/#x15.2.3.8 if (!Object.seal) { Object.seal = function seal(object) { // this is misleading and breaks feature-detection, but // allows "securable" code to "gracefully" degrade to working // but insecure code. return object; }; } // ES5 15.2.3.9 // http://es5.github.com/#x15.2.3.9 if (!Object.freeze) { Object.freeze = function freeze(object) { // this is misleading and breaks feature-detection, but // allows "securable" code to "gracefully" degrade to working // but insecure code. return object; }; } // detect a Rhino bug and patch it try { Object.freeze(function () {}); } catch (exception) { Object.freeze = (function freeze(freezeObject) { return function freeze(object) { if (typeof object == "function") { return object; } else { return freezeObject(object); } }; })(Object.freeze); } // ES5 15.2.3.10 // http://es5.github.com/#x15.2.3.10 if (!Object.preventExtensions) { Object.preventExtensions = function preventExtensions(object) { // this is misleading and breaks feature-detection, but // allows "securable" code to "gracefully" degrade to working // but insecure code. return object; }; } // ES5 15.2.3.11 // http://es5.github.com/#x15.2.3.11 if (!Object.isSealed) { Object.isSealed = function isSealed(object) { return false; }; } // ES5 15.2.3.12 // http://es5.github.com/#x15.2.3.12 if (!Object.isFrozen) { Object.isFrozen = function isFrozen(object) { return false; }; } // ES5 15.2.3.13 // http://es5.github.com/#x15.2.3.13 if (!Object.isExtensible) { Object.isExtensible = function isExtensible(object) { // 1. If Type(O) is not Object throw a TypeError exception. if (Object(object) === object) { throw new TypeError(); // TODO message } // 2. Return the Boolean value of the [[Extensible]] internal property of O. var name = ''; while (owns(object, name)) { name += '?'; } object[name] = true; var returnValue = owns(object, name); delete object[name]; return returnValue; }; } // ES5 15.2.3.14 // http://es5.github.com/#x15.2.3.14 if (!Object.keys) { // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation var hasDontEnumBug = true, dontEnums = [ "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "constructor" ], dontEnumsLength = dontEnums.length; for (var key in {"toString": null}) hasDontEnumBug = false; Object.keys = function keys(object) { if ((typeof object != "object" && typeof object != "function") || object === null) throw new TypeError("Object.keys called on a non-object"); var keys = []; for (var name in object) { if (owns(object, name)) { keys.push(name); } } if (hasDontEnumBug) { for (var i = 0, ii = dontEnumsLength; i < ii; i++) { var dontEnum = dontEnums[i]; if (owns(object, dontEnum)) { keys.push(dontEnum); } } } return keys; }; } // // Date // ==== // // ES5 15.9.5.43 // http://es5.github.com/#x15.9.5.43 // This function returns a String value represent the instance in time // represented by this Date object. The format of the String is the Date Time // string format defined in 15.9.1.15. All fields are present in the String. // The time zone is always UTC, denoted by the suffix Z. If the time value of // this object is not a finite Number a RangeError exception is thrown. if (!Date.prototype.toISOString || (new Date(-62198755200000).toISOString().indexOf('-000001') === -1)) { Date.prototype.toISOString = function toISOString() { var result, length, value, year; if (!isFinite(this)) throw new RangeError; // the date time string format is specified in 15.9.1.15. result = [this.getUTCMonth() + 1, this.getUTCDate(), this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()]; year = this.getUTCFullYear(); year = (year < 0 ? '-' : (year > 9999 ? '+' : '')) + ('00000' + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6); length = result.length; while (length--) { value = result[length]; // pad months, days, hours, minutes, and seconds to have two digits. if (value < 10) result[length] = "0" + value; } // pad milliseconds to have three digits. return year + "-" + result.slice(0, 2).join("-") + "T" + result.slice(2).join(":") + "." + ("000" + this.getUTCMilliseconds()).slice(-3) + "Z"; } } // ES5 15.9.4.4 // http://es5.github.com/#x15.9.4.4 if (!Date.now) { Date.now = function now() { return new Date().getTime(); }; } // ES5 15.9.5.44 // http://es5.github.com/#x15.9.5.44 // This function provides a String representation of a Date object for use by // JSON.stringify (15.12.3). if (!Date.prototype.toJSON) { Date.prototype.toJSON = function toJSON(key) { // When the toJSON method is called with argument key, the following // steps are taken: // 1. Let O be the result of calling ToObject, giving it the this // value as its argument. // 2. Let tv be ToPrimitive(O, hint Number). // 3. If tv is a Number and is not finite, return null. // XXX // 4. Let toISO be the result of calling the [[Get]] internal method of // O with argument "toISOString". // 5. If IsCallable(toISO) is false, throw a TypeError exception. if (typeof this.toISOString != "function") throw new TypeError(); // TODO message // 6. Return the result of calling the [[Call]] internal method of // toISO with O as the this value and an empty argument list. return this.toISOString(); // NOTE 1 The argument is ignored. // NOTE 2 The toJSON function is intentionally generic; it does not // require that its this value be a Date object. Therefore, it can be // transferred to other kinds of objects for use as a method. However, // it does require that any such object have a toISOString method. An // object is free to use the argument key to filter its // stringification. }; } // ES5 15.9.4.2 // http://es5.github.com/#x15.9.4.2 // based on work shared by Daniel Friesen (dantman) // http://gist.github.com/303249 if (Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) { // XXX global assignment won't work in embeddings that use // an alternate object for the context. Date = (function(NativeDate) { // Date.length === 7 var Date = function Date(Y, M, D, h, m, s, ms) { var length = arguments.length; if (this instanceof NativeDate) { var date = length == 1 && String(Y) === Y ? // isString(Y) // We explicitly pass it through parse: new NativeDate(Date.parse(Y)) : // We have to manually make calls depending on argument // length here length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) : length >= 6 ? new NativeDate(Y, M, D, h, m, s) : length >= 5 ? new NativeDate(Y, M, D, h, m) : length >= 4 ? new NativeDate(Y, M, D, h) : length >= 3 ? new NativeDate(Y, M, D) : length >= 2 ? new NativeDate(Y, M) : length >= 1 ? new NativeDate(Y) : new NativeDate(); // Prevent mixups with unfixed Date object date.constructor = Date; return date; } return NativeDate.apply(this, arguments); }; // 15.9.1.15 Date Time String Format. var isoDateExpression = new RegExp("^" + "(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign + 6-digit extended year "(?:-(\\d{2})" + // optional month capture "(?:-(\\d{2})" + // optional day capture "(?:" + // capture hours:minutes:seconds.milliseconds "T(\\d{2})" + // hours capture ":(\\d{2})" + // minutes capture "(?:" + // optional :seconds.milliseconds ":(\\d{2})" + // seconds capture "(?:\\.(\\d{3}))?" + // milliseconds capture ")?" + "(?:" + // capture UTC offset component "Z|" + // UTC capture "(?:" + // offset specifier +/-hours:minutes "([-+])" + // sign capture "(\\d{2})" + // hours offset capture ":(\\d{2})" + // minutes offset capture ")" + ")?)?)?)?" + "$"); // Copy any custom methods a 3rd party library may have added for (var key in NativeDate) Date[key] = NativeDate[key]; // Copy "native" methods explicitly; they may be non-enumerable Date.now = NativeDate.now; Date.UTC = NativeDate.UTC; Date.prototype = NativeDate.prototype; Date.prototype.constructor = Date; // Upgrade Date.parse to handle simplified ISO 8601 strings Date.parse = function parse(string) { var match = isoDateExpression.exec(string); if (match) { match.shift(); // kill match[0], the full match // parse months, days, hours, minutes, seconds, and milliseconds for (var i = 1; i < 7; i++) { // provide default values if necessary match[i] = +(match[i] || (i < 3 ? 1 : 0)); // match[1] is the month. Months are 0-11 in JavaScript // `Date` objects, but 1-12 in ISO notation, so we // decrement. if (i == 1) match[i]--; } // parse the UTC offset component var minuteOffset = +match.pop(), hourOffset = +match.pop(), sign = match.pop(); // compute the explicit time zone offset if specified var offset = 0; if (sign) { // detect invalid offsets and return early if (hourOffset > 23 || minuteOffset > 59) return NaN; // express the provided time zone offset in minutes. The offset is // negative for time zones west of UTC; positive otherwise. offset = (hourOffset * 60 + minuteOffset) * 6e4 * (sign == "+" ? -1 : 1); } // Date.UTC for years between 0 and 99 converts year to 1900 + year // The Gregorian calendar has a 400-year cycle, so // to Date.UTC(year + 400, .... ) - 12622780800000 == Date.UTC(year, ...), // where 12622780800000 - number of milliseconds in Gregorian calendar 400 years var year = +match[0]; if (0 <= year && year <= 99) { match[0] = year + 400; return NativeDate.UTC.apply(this, match) + offset - 12622780800000; } // compute a new UTC date value, accounting for the optional offset return NativeDate.UTC.apply(this, match) + offset; } return NativeDate.parse.apply(this, arguments); }; return Date; })(Date); } // // String // ====== // // ES5 15.5.4.20 // http://es5.github.com/#x15.5.4.20 var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" + "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" + "\u2029\uFEFF"; if (!String.prototype.trim || ws.trim()) { // http://blog.stevenlevithan.com/archives/faster-trim-javascript // http://perfectionkills.com/whitespace-deviations/ ws = "[" + ws + "]"; var trimBeginRegexp = new RegExp("^" + ws + ws + "*"), trimEndRegexp = new RegExp(ws + ws + "*$"); String.prototype.trim = function trim() { return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, ""); }; } // // Util // ====== // // ES5 9.4 // http://es5.github.com/#x9.4 // http://jsperf.com/to-integer var toInteger = function (n) { n = +n; if (n !== n) // isNaN n = 0; else if (n !== 0 && n !== (1/0) && n !== -(1/0)) n = (n > 0 || -1) * Math.floor(Math.abs(n)); return n; }; var prepareString = "a"[0] != "a", // ES5 9.9 // http://es5.github.com/#x9.9 toObject = function (o) { if (o == null) { // this matches both null and undefined throw new TypeError(); // TODO message } // If the implementation doesn't support by-index access of // string characters (ex. IE < 7), split the string if (prepareString && typeof o == "string" && o) { return o.split(""); } return Object(o); }; });/* vim:ts=4:sts=4:sw=4: * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Ajax.org Code Editor (ACE). * * The Initial Developer of the Original Code is * Ajax.org B.V. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabian Jakobs * Irakli Gozalishvili (http://jeditoolkit.com) * Mike de Boer * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) { "use strict"; var EventEmitter = {}; EventEmitter._emit = EventEmitter._dispatchEvent = function(eventName, e) { this._eventRegistry = this._eventRegistry || {}; this._defaultHandlers = this._defaultHandlers || {}; var listeners = this._eventRegistry[eventName] || []; var defaultHandler = this._defaultHandlers[eventName]; if (!listeners.length && !defaultHandler) return; e = e || {}; e.type = eventName; if (!e.stopPropagation) { e.stopPropagation = function() { this.propagationStopped = true; }; } if (!e.preventDefault) { e.preventDefault = function() { this.defaultPrevented = true; }; } for (var i=0; i * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) { "use strict"; exports.inherits = (function() { var tempCtor = function() {}; return function(ctor, superCtor) { tempCtor.prototype = superCtor.prototype; ctor.super_ = superCtor.prototype; ctor.prototype = new tempCtor(); ctor.prototype.constructor = ctor; }; }()); exports.mixin = function(obj, mixin) { for (var key in mixin) { obj[key] = mixin[key]; } }; exports.implement = function(proto, mixin) { exports.mixin(proto, mixin); }; }); /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Ajax.org Code Editor (ACE). * * The Initial Developer of the Original Code is * Ajax.org B.V. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabian Jakobs * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/mode/javascript_worker', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/worker/mirror', 'ace/worker/jshint', 'ace/narcissus/parser'], function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); var Mirror = require("../worker/mirror").Mirror; var lint = require("../worker/jshint").JSHINT; var parser = require("../narcissus/parser"); var JavaScriptWorker = exports.JavaScriptWorker = function(sender) { Mirror.call(this, sender); this.setTimeout(500); }; oop.inherits(JavaScriptWorker, Mirror); (function() { this.onUpdate = function() { var value = this.doc.getValue(); value = value.replace(/^#!.*\n/, "\n"); // var start = new Date(); try { parser.parse(value); } catch(e) { // console.log("narcissus") // console.log(e); var chunks = e.message.split(":") var message = chunks.pop().trim(); var lineNumber = parseInt(chunks.pop().trim()) - 1; this.sender.emit("narcissus", { row: lineNumber, column: null, // TODO convert e.cursor text: message, type: "error" }); return; } finally { // console.log("parse time: " + (new Date() - start)); } // var start = new Date(); // console.log("jslint") lint(value, {undef: false, onevar: false, passfail: false}); this.sender.emit("jslint", lint.errors); // console.log("lint time: " + (new Date() - start)); } }).call(JavaScriptWorker.prototype); });define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) { "use strict"; var Document = require("../document").Document; var lang = require("../lib/lang"); var Mirror = exports.Mirror = function(sender) { this.sender = sender; var doc = this.doc = new Document(""); var deferredUpdate = this.deferredUpdate = lang.deferredCall(this.onUpdate.bind(this)); var _self = this; sender.on("change", function(e) { doc.applyDeltas([e.data]); deferredUpdate.schedule(_self.$timeout); }); }; (function() { this.$timeout = 500; this.setTimeout = function(timeout) { this.$timeout = timeout; }; this.setValue = function(value) { this.doc.setValue(value); this.deferredUpdate.schedule(this.$timeout); }; this.getValue = function(callbackId) { this.sender.callback(this.doc.getValue(), callbackId); }; this.onUpdate = function() { // abstract method }; }).call(Mirror.prototype); });/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Ajax.org Code Editor (ACE). * * The Initial Developer of the Original Code is * Ajax.org B.V. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabian Jakobs * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); var EventEmitter = require("./lib/event_emitter").EventEmitter; var Range = require("./range").Range; var Anchor = require("./anchor").Anchor; /** * class Document * * Contains the text of the document. Documents are controlled by a single [[EditSession `EditSession`]]. At its core, `Document`s are just an array of strings, with each row in the document matching up to the array index. * * **/ /** * new Document([text]) * - text (String | Array): The starting text * * Creates a new `Document`. If `text` is included, the `Document` contains those strings; otherwise, it's empty. * **/ var Document = function(text) { this.$lines = []; if (Array.isArray(text)) { this.insertLines(0, text); } // There has to be one line at least in the document. If you pass an empty // string to the insert function, nothing will happen. Workaround. else if (text.length == 0) { this.$lines = [""]; } else { this.insert({row: 0, column:0}, text); } }; (function() { oop.implement(this, EventEmitter); /** * Document.setValue(text) -> Void * - text (String): The text to use * * Replaces all the lines in the current `Document` with the value of `text`. **/ this.setValue = function(text) { var len = this.getLength(); this.remove(new Range(0, 0, len, this.getLine(len-1).length)); this.insert({row: 0, column:0}, text); }; /** * Document.getValue() -> String * * Returns all the lines in the document as a single string, split by the new line character. **/ this.getValue = function() { return this.getAllLines().join(this.getNewLineCharacter()); }; /** * Document.createAnchor(row, column) -> Anchor * - row (Number): The row number to use * - column (Number): The column number to use * * Creates a new `Anchor` to define a floating point in the document. **/ this.createAnchor = function(row, column) { return new Anchor(this, row, column); }; /** internal, hide * Document.$split(text) -> [String] * - text (String): The text to work with * + ([String]): A String array, with each index containing a piece of the original `text` string. * * Splits a string of text on any newline (`\n`) or carriage-return ('\r') characters. * * **/ // check for IE split bug if ("aaa".split(/a/).length == 0) this.$split = function(text) { return text.replace(/\r\n|\r/g, "\n").split("\n"); } else this.$split = function(text) { return text.split(/\r\n|\r|\n/); }; /** internal, hide * Document.$detectNewLine(text) -> Void * * **/ this.$detectNewLine = function(text) { var match = text.match(/^.*?(\r\n|\r|\n)/m); if (match) { this.$autoNewLine = match[1]; } else { this.$autoNewLine = "\n"; } }; /** * Document.getNewLineCharacter() -> String * + (String): If `newLineMode == windows`, `\r\n` is returned.
* If `newLineMode == unix`, `\n` is returned.
* If `newLineMode == auto`, the value of `autoNewLine` is returned. * * Returns the newline character that's being used, depending on the value of `newLineMode`. * * * **/ this.getNewLineCharacter = function() { switch (this.$newLineMode) { case "windows": return "\r\n"; case "unix": return "\n"; case "auto": return this.$autoNewLine; } }; this.$autoNewLine = "\n"; this.$newLineMode = "auto"; /** * Document.setNewLineMode(newLineMode) -> Void * - newLineMode(String): [The newline mode to use; can be either `windows`, `unix`, or `auto`]{: #Document.setNewLineMode.param} * * [Sets the new line mode.]{: #Document.setNewLineMode.desc} **/ this.setNewLineMode = function(newLineMode) { if (this.$newLineMode === newLineMode) return; this.$newLineMode = newLineMode; }; /** * Document.getNewLineMode() -> String * * [Returns the type of newlines being used; either `windows`, `unix`, or `auto`]{: #Document.getNewLineMode} * **/ this.getNewLineMode = function() { return this.$newLineMode; }; /** * Document.isNewLine(text) -> Boolean * - text (String): The text to check * * Returns `true` if `text` is a newline character (either `\r\n`, `\r`, or `\n`). * **/ this.isNewLine = function(text) { return (text == "\r\n" || text == "\r" || text == "\n"); }; /** * Document.getLine(row) -> String * - row (Number): The row index to retrieve * * Returns a verbatim copy of the given line as it is in the document * **/ this.getLine = function(row) { return this.$lines[row] || ""; }; /** * Document.getLines(firstRow, lastRow) -> [String] * - firstRow (Number): The first row index to retrieve * - lastRow (Number): The final row index to retrieve * * Returns an array of strings of the rows between `firstRow` and `lastRow`. This function is inclusive of `lastRow`. * **/ this.getLines = function(firstRow, lastRow) { return this.$lines.slice(firstRow, lastRow + 1); }; /** * Document.getAllLines() -> [String] * * Returns all lines in the document as string array. Warning: The caller should not modify this array! **/ this.getAllLines = function() { return this.getLines(0, this.getLength()); }; /** * Document.getLength() -> Number * * Returns the number of rows in the document. **/ this.getLength = function() { return this.$lines.length; }; /** * Document.getTextRange(range) -> String * - range (Range): The range to work with * * [Given a range within the document, this function returns all the text within that range as a single string.]{: #Document.getTextRange.desc} **/ this.getTextRange = function(range) { if (range.start.row == range.end.row) { return this.$lines[range.start.row].substring(range.start.column, range.end.column); } else { var lines = []; lines.push(this.$lines[range.start.row].substring(range.start.column)); lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1)); lines.push(this.$lines[range.end.row].substring(0, range.end.column)); return lines.join(this.getNewLineCharacter()); } }; /** internal, hide * Document.$clipPosition(position) -> Number * * **/ this.$clipPosition = function(position) { var length = this.getLength(); if (position.row >= length) { position.row = Math.max(0, length - 1); position.column = this.getLine(length-1).length; } return position; }; /** * Document.insert(position, text) -> Number * - position (Number): The position to start inserting at * - text (String): A chunk of text to insert * + (Number): The position of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. * Inserts a block of `text` and the indicated `position`. * * **/ this.insert = function(position, text) { if (!text || text.length === 0) return position; position = this.$clipPosition(position); // only detect new lines if the document has no line break yet if (this.getLength() <= 1) this.$detectNewLine(text); var lines = this.$split(text); var firstLine = lines.splice(0, 1)[0]; var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0]; position = this.insertInLine(position, firstLine); if (lastLine !== null) { position = this.insertNewLine(position); // terminate first line position = this.insertLines(position.row, lines); position = this.insertInLine(position, lastLine || ""); } return position; }; /** * Document.insertLines(row, lines) -> Object * - row (Number): The index of the row to insert at * - lines (Array): An array of strings * + (Object): Returns an object containing the final row and column, like this:
* ```{row: endRow, column: 0}```
* If `lines` is empty, this function returns an object containing the current row, and column, like this:
* ```{row: row, column: 0}``` * * Inserts the elements in `lines` into the document, starting at the row index given by `row`. This method also triggers the `'change'` event. * * **/ this.insertLines = function(row, lines) { if (lines.length == 0) return {row: row, column: 0}; var args = [row, 0]; args.push.apply(args, lines); this.$lines.splice.apply(this.$lines, args); var range = new Range(row, 0, row + lines.length, 0); var delta = { action: "insertLines", range: range, lines: lines }; this._emit("change", { data: delta }); return range.end; }; /** * Document.insertNewLine(position) -> Object * - position (String): The position to insert at * + (Object): Returns an object containing the final row and column, like this:
* ```{row: endRow, column: 0}``` * * Inserts a new line into the document at the current row's `position`. This method also triggers the `'change'` event. * * * **/ this.insertNewLine = function(position) { position = this.$clipPosition(position); var line = this.$lines[position.row] || ""; this.$lines[position.row] = line.substring(0, position.column); this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length)); var end = { row : position.row + 1, column : 0 }; var delta = { action: "insertText", range: Range.fromPoints(position, end), text: this.getNewLineCharacter() }; this._emit("change", { data: delta }); return end; }; /** * Document.insertInLine(position, text) -> Object | Number * - position (Number): The position to insert at * - text (String): A chunk of text * + (Object): Returns an object containing the final row and column, like this:
* ```{row: endRow, column: 0}``` * + (Number): If `text` is empty, this function returns the value of `position` * * Inserts `text` into the `position` at the current row. This method also triggers the `'change'` event. * * * **/ this.insertInLine = function(position, text) { if (text.length == 0) return position; var line = this.$lines[position.row] || ""; this.$lines[position.row] = line.substring(0, position.column) + text + line.substring(position.column); var end = { row : position.row, column : position.column + text.length }; var delta = { action: "insertText", range: Range.fromPoints(position, end), text: text }; this._emit("change", { data: delta }); return end; }; /** * Document.remove(range) -> Object * - range (Range): A specified Range to remove * + (Object): Returns the new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`. * * Removes the `range` from the document. * * **/ this.remove = function(range) { // clip to document range.start = this.$clipPosition(range.start); range.end = this.$clipPosition(range.end); if (range.isEmpty()) return range.start; var firstRow = range.start.row; var lastRow = range.end.row; if (range.isMultiLine()) { var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1; var lastFullRow = lastRow - 1; if (range.end.column > 0) this.removeInLine(lastRow, 0, range.end.column); if (lastFullRow >= firstFullRow) this.removeLines(firstFullRow, lastFullRow); if (firstFullRow != firstRow) { this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length); this.removeNewLine(range.start.row); } } else { this.removeInLine(firstRow, range.start.column, range.end.column); } return range.start; }; /** * Document.removeInLine(row, startColumn, endColumn) -> Object * - row (Number): The row to remove from * - startColumn (Number): The column to start removing at * - endColumn (Number): The column to stop removing at * + (Object): Returns an object containing `startRow` and `startColumn`, indicating the new row and column values.
If `startColumn` is equal to `endColumn`, this function returns nothing. * * Removes the specified columns from the `row`. This method also triggers the `'change'` event. * * **/ this.removeInLine = function(row, startColumn, endColumn) { if (startColumn == endColumn) return; var range = new Range(row, startColumn, row, endColumn); var line = this.getLine(row); var removed = line.substring(startColumn, endColumn); var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length); this.$lines.splice(row, 1, newLine); var delta = { action: "removeText", range: range, text: removed }; this._emit("change", { data: delta }); return range.start; }; /** * Document.removeLines(firstRow, lastRow) -> [String] * - firstRow (Number): The first row to be removed * - lastRow (Number): The last row to be removed * + ([String]): Returns all the removed lines. * * Removes a range of full lines. This method also triggers the `'change'` event. * * **/ this.removeLines = function(firstRow, lastRow) { var range = new Range(firstRow, 0, lastRow + 1, 0); var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1); var delta = { action: "removeLines", range: range, nl: this.getNewLineCharacter(), lines: removed }; this._emit("change", { data: delta }); return removed; }; /** * Document.removeNewLine(row) -> Void * - row (Number): The row to check * * Removes the new line between `row` and the row immediately following it. This method also triggers the `'change'` event. * **/ this.removeNewLine = function(row) { var firstLine = this.getLine(row); var secondLine = this.getLine(row+1); var range = new Range(row, firstLine.length, row+1, 0); var line = firstLine + secondLine; this.$lines.splice(row, 2, line); var delta = { action: "removeText", range: range, text: this.getNewLineCharacter() }; this._emit("change", { data: delta }); }; /** * Document.replace(range, text) -> Object * - range (Range): A specified Range to replace * - text (String): The new text to use as a replacement * + (Object): Returns an object containing the final row and column, like this: * {row: endRow, column: 0} * If the text and range are empty, this function returns an object containing the current `range.start` value. * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value. * * Replaces a range in the document with the new `text`. * **/ this.replace = function(range, text) { if (text.length == 0 && range.isEmpty()) return range.start; // Shortcut: If the text we want to insert is the same as it is already // in the document, we don't have to replace anything. if (text == this.getTextRange(range)) return range.end; this.remove(range); if (text) { var end = this.insert(range.start, text); } else { end = range.start; } return end; }; /** * Document.applyDeltas(deltas) -> Void * * Applies all the changes previously accumulated. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`. **/ this.applyDeltas = function(deltas) { for (var i=0; i Void * * Reverts any changes previously applied. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`. **/ this.revertDeltas = function(deltas) { for (var i=deltas.length-1; i>=0; i--) { var delta = deltas[i]; var range = Range.fromPoints(delta.range.start, delta.range.end); if (delta.action == "insertLines") this.removeLines(range.start.row, range.end.row - 1); else if (delta.action == "insertText") this.remove(range); else if (delta.action == "removeLines") this.insertLines(range.start.row, delta.lines); else if (delta.action == "removeText") this.insert(range.start, delta.text); } }; }).call(Document.prototype); exports.Document = Document; }); /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Ajax.org Code Editor (ACE). * * The Initial Developer of the Original Code is * Ajax.org B.V. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabian Jakobs * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) { "use strict"; /** * class Range * * This object is used in various places to indicate a region within the editor. To better visualize how this works, imagine a rectangle. Each quadrant of the rectangle is analogus to a range, as ranges contain a starting row and starting column, and an ending row, and ending column. * **/ /** * new Range(startRow, startColumn, endRow, endColumn) * - startRow (Number): The starting row * - startColumn (Number): The starting column * - endRow (Number): The ending row * - endColumn (Number): The ending column * * Creates a new `Range` object with the given starting and ending row and column points. * **/ var Range = function(startRow, startColumn, endRow, endColumn) { this.start = { row: startRow, column: startColumn }; this.end = { row: endRow, column: endColumn }; }; (function() { /** * Range.isEqual(range) -> Boolean * - range (Range): A range to check against * * Returns `true` if and only if the starting row and column, and ending tow and column, are equivalent to those given by `range`. * **/ this.isEqual = function(range) { return this.start.row == range.start.row && this.end.row == range.end.row && this.start.column == range.start.column && this.end.column == range.end.column }; /** * Range.toString() -> String * * Returns a string containing the range's row and column information, given like this: * * [start.row/start.column] -> [end.row/end.column] * **/ this.toString = function() { return ("Range: [" + this.start.row + "/" + this.start.column + "] -> [" + this.end.row + "/" + this.end.column + "]"); }; /** related to: Range.compare * Range.contains(row, column) -> Boolean * - row (Number): A row to check for * - column (Number): A column to check for * * Returns `true` if the `row` and `column` provided are within the given range. This can better be expressed as returning `true` if: * * this.start.row <= row <= this.end.row && * this.start.column <= column <= this.end.column * **/ this.contains = function(row, column) { return this.compare(row, column) == 0; }; /** related to: Range.compare * Range.compareRange(range) -> Number * - range (Range): A range to compare with * + (Number): This method returns one of the following numbers:
*
* * `-2`: (B) is in front of (A), and doesn't intersect with (A)
* * `-1`: (B) begins before (A) but ends inside of (A)
* * `0`: (B) is completely inside of (A) OR (A) is completely inside of (B)
* * `+1`: (B) begins inside of (A) but ends outside of (A)
* * `+2`: (B) is after (A) and doesn't intersect with (A)
* * `42`: FTW state: (B) ends in (A) but starts outside of (A) * * Compares `this` range (A) with another range (B). * **/ this.compareRange = function(range) { var cmp, end = range.end, start = range.start; cmp = this.compare(end.row, end.column); if (cmp == 1) { cmp = this.compare(start.row, start.column); if (cmp == 1) { return 2; } else if (cmp == 0) { return 1; } else { return 0; } } else if (cmp == -1) { return -2; } else { cmp = this.compare(start.row, start.column); if (cmp == -1) { return -1; } else if (cmp == 1) { return 42; } else { return 0; } } } /** related to: Range.compare * Range.comparePoint(p) -> Number * - p (Range): A point to compare with * + (Number): This method returns one of the following numbers:
* * `0` if the two points are exactly equal
* * `-1` if `p.row` is less then the calling range
* * `1` if `p.row` is greater than the calling range
*
* If the starting row of the calling range is equal to `p.row`, and:
* * `p.column` is greater than or equal to the calling range's starting column, this returns `0`
* * Otherwise, it returns -1
*
* If the ending row of the calling range is equal to `p.row`, and:
* * `p.column` is less than or equal to the calling range's ending column, this returns `0`
* * Otherwise, it returns 1
* * Checks the row and column points of `p` with the row and column points of the calling range. * * * **/ this.comparePoint = function(p) { return this.compare(p.row, p.column); } /** related to: Range.comparePoint * Range.containsRange(range) -> Boolean * - range (Range): A range to compare with * * Checks the start and end points of `range` and compares them to the calling range. Returns `true` if the `range` is contained within the caller's range. * **/ this.containsRange = function(range) { return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0; } /** * Range.intersects(range) -> Boolean * - range (Range): A range to compare with * * Returns `true` if passed in `range` intersects with the one calling this method. * **/ this.intersects = function(range) { var cmp = this.compareRange(range); return (cmp == -1 || cmp == 0 || cmp == 1); } /** * Range.isEnd(row, column) -> Boolean * - row (Number): A row point to compare with * - column (Number): A column point to compare with * * Returns `true` if the caller's ending row point is the same as `row`, and if the caller's ending column is the same as `column`. * **/ this.isEnd = function(row, column) { return this.end.row == row && this.end.column == column; } /** * Range.isStart(row, column) -> Boolean * - row (Number): A row point to compare with * - column (Number): A column point to compare with * * Returns `true` if the caller's starting row point is the same as `row`, and if the caller's starting column is the same as `column`. * **/ this.isStart = function(row, column) { return this.start.row == row && this.start.column == column; } /** * Range.setStart(row, column) * - row (Number): A row point to set * - column (Number): A column point to set * * Sets the starting row and column for the range. * **/ this.setStart = function(row, column) { if (typeof row == "object") { this.start.column = row.column; this.start.row = row.row; } else { this.start.row = row; this.start.column = column; } } /** * Range.setEnd(row, column) * - row (Number): A row point to set * - column (Number): A column point to set * * Sets the starting row and column for the range. * **/ this.setEnd = function(row, column) { if (typeof row == "object") { this.end.column = row.column; this.end.row = row.row; } else { this.end.row = row; this.end.column = column; } } /** related to: Range.compare * Range.inside(row, column) -> Boolean * - row (Number): A row point to compare with * - column (Number): A column point to compare with * * Returns `true` if the `row` and `column` are within the given range. * **/ this.inside = function(row, column) { if (this.compare(row, column) == 0) { if (this.isEnd(row, column) || this.isStart(row, column)) { return false; } else { return true; } } return false; } /** related to: Range.compare * Range.insideStart(row, column) -> Boolean * - row (Number): A row point to compare with * - column (Number): A column point to compare with * * Returns `true` if the `row` and `column` are within the given range's starting points. * **/ this.insideStart = function(row, column) { if (this.compare(row, column) == 0) { if (this.isEnd(row, column)) { return false; } else { return true; } } return false; } /** related to: Range.compare * Range.insideEnd(row, column) -> Boolean * - row (Number): A row point to compare with * - column (Number): A column point to compare with * * Returns `true` if the `row` and `column` are within the given range's ending points. * **/ this.insideEnd = function(row, column) { if (this.compare(row, column) == 0) { if (this.isStart(row, column)) { return false; } else { return true; } } return false; } /** * Range.compare(row, column) -> Number * - row (Number): A row point to compare with * - column (Number): A column point to compare with * + (Number): This method returns one of the following numbers:
* * `0` if the two points are exactly equal
* * `-1` if `p.row` is less then the calling range
* * `1` if `p.row` is greater than the calling range
*
* If the starting row of the calling range is equal to `p.row`, and:
* * `p.column` is greater than or equal to the calling range's starting column, this returns `0`
* * Otherwise, it returns -1
*
* If the ending row of the calling range is equal to `p.row`, and:
* * `p.column` is less than or equal to the calling range's ending column, this returns `0`
* * Otherwise, it returns 1 * * Checks the row and column points with the row and column points of the calling range. * * **/ this.compare = function(row, column) { if (!this.isMultiLine()) { if (row === this.start.row) { return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); }; } if (row < this.start.row) return -1; if (row > this.end.row) return 1; if (this.start.row === row) return column >= this.start.column ? 0 : -1; if (this.end.row === row) return column <= this.end.column ? 0 : 1; return 0; }; /** * Range.compareStart(row, column) -> Number * - row (Number): A row point to compare with * - column (Number): A column point to compare with * + (Number): This method returns one of the following numbers:
*
* * `0` if the two points are exactly equal
* * `-1` if `p.row` is less then the calling range
* * `1` if `p.row` is greater than the calling range, or if `isStart` is `true`.
*
* If the starting row of the calling range is equal to `p.row`, and:
* * `p.column` is greater than or equal to the calling range's starting column, this returns `0`
* * Otherwise, it returns -1
*
* If the ending row of the calling range is equal to `p.row`, and:
* * `p.column` is less than or equal to the calling range's ending column, this returns `0`
* * Otherwise, it returns 1 * * Checks the row and column points with the row and column points of the calling range. * * * **/ this.compareStart = function(row, column) { if (this.start.row == row && this.start.column == column) { return -1; } else { return this.compare(row, column); } } /** * Range.compareEnd(row, column) -> Number * - row (Number): A row point to compare with * - column (Number): A column point to compare with * + (Number): This method returns one of the following numbers:
* * `0` if the two points are exactly equal
* * `-1` if `p.row` is less then the calling range
* * `1` if `p.row` is greater than the calling range, or if `isEnd` is `true.
*
* If the starting row of the calling range is equal to `p.row`, and:
* * `p.column` is greater than or equal to the calling range's starting column, this returns `0`
* * Otherwise, it returns -1
*
* If the ending row of the calling range is equal to `p.row`, and:
* * `p.column` is less than or equal to the calling range's ending column, this returns `0`
* * Otherwise, it returns 1 * * Checks the row and column points with the row and column points of the calling range. * * **/ this.compareEnd = function(row, column) { if (this.end.row == row && this.end.column == column) { return 1; } else { return this.compare(row, column); } } /** * Range.compareInside(row, column) -> Number * - row (Number): A row point to compare with * - column (Number): A column point to compare with * + (Number): This method returns one of the following numbers:
* * `1` if the ending row of the calling range is equal to `row`, and the ending column of the calling range is equal to `column`
* * `-1` if the starting row of the calling range is equal to `row`, and the starting column of the calling range is equal to `column`
*
* Otherwise, it returns the value after calling [[Range.compare `compare()`]]. * * Checks the row and column points with the row and column points of the calling range. * * * **/ this.compareInside = function(row, column) { if (this.end.row == row && this.end.column == column) { return 1; } else if (this.start.row == row && this.start.column == column) { return -1; } else { return this.compare(row, column); } } /** * Range.clipRows(firstRow, lastRow) -> Range * - firstRow (Number): The starting row * - lastRow (Number): The ending row * * Returns the part of the current `Range` that occurs within the boundaries of `firstRow` and `lastRow` as a new `Range` object. * **/ this.clipRows = function(firstRow, lastRow) { if (this.end.row > lastRow) { var end = { row: lastRow+1, column: 0 }; } if (this.start.row > lastRow) { var start = { row: lastRow+1, column: 0 }; } if (this.start.row < firstRow) { var start = { row: firstRow, column: 0 }; } if (this.end.row < firstRow) { var end = { row: firstRow, column: 0 }; } return Range.fromPoints(start || this.start, end || this.end); }; /** * Range.extend(row, column) -> Range * - row (Number): A new row to extend to * - column (Number): A new column to extend to * * Changes the row and column points for the calling range for both the starting and ending points. This method returns that range with a new row. * **/ this.extend = function(row, column) { var cmp = this.compare(row, column); if (cmp == 0) return this; else if (cmp == -1) var start = {row: row, column: column}; else var end = {row: row, column: column}; return Range.fromPoints(start || this.start, end || this.end); }; this.isEmpty = function() { return (this.start.row == this.end.row && this.start.column == this.end.column); }; /** * Range.isMultiLine() -> Boolean * * Returns true if the range spans across multiple lines. * **/ this.isMultiLine = function() { return (this.start.row !== this.end.row); }; /** * Range.clone() -> Range * * Returns a duplicate of the calling range. * **/ this.clone = function() { return Range.fromPoints(this.start, this.end); }; /** * Range.collapseRows() -> Range * * Returns a range containing the starting and ending rows of the original range, but with a column value of `0`. * **/ this.collapseRows = function() { if (this.end.column == 0) return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0) else return new Range(this.start.row, 0, this.end.row, 0) }; /** * Range.toScreenRange(session) -> Range * - session (EditSession): The `EditSession` to retrieve coordinates from * * Given the current `Range`, this function converts those starting and ending points into screen positions, and then returns a new `Range` object. **/ this.toScreenRange = function(session) { var screenPosStart = session.documentToScreenPosition(this.start); var screenPosEnd = session.documentToScreenPosition(this.end); return new Range( screenPosStart.row, screenPosStart.column, screenPosEnd.row, screenPosEnd.column ); }; }).call(Range.prototype); /** * Range.fromPoints(start, end) -> Range * - start (Range): A starting point to use * - end (Range): An ending point to use * * Creates and returns a new `Range` based on the row and column of the given parameters. * **/ Range.fromPoints = function(start, end) { return new Range(start.row, start.column, end.row, end.column); }; exports.Range = Range; }); /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Ajax.org Code Editor (ACE). * * The Initial Developer of the Original Code is * Ajax.org B.V. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabian Jakobs * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) { "use strict"; var oop = require("./lib/oop"); var EventEmitter = require("./lib/event_emitter").EventEmitter; /** * class Anchor * * Defines the floating pointer in the document. Whenever text is inserted or deleted before the cursor, the position of the cursor is updated * **/ /** * new Anchor(doc, row, column) * - doc (Document): The document to associate with the anchor * - row (Number): The starting row position * - column (Number): The starting column position * * Creates a new `Anchor` and associates it with a document. * **/ var Anchor = exports.Anchor = function(doc, row, column) { this.document = doc; if (typeof column == "undefined") this.setPosition(row.row, row.column); else this.setPosition(row, column); this.$onChange = this.onChange.bind(this); doc.on("change", this.$onChange); }; (function() { oop.implement(this, EventEmitter); /** * Anchor.getPosition() -> Object * * Returns an object identifying the `row` and `column` position of the current anchor. * **/ this.getPosition = function() { return this.$clipPositionToDocument(this.row, this.column); }; /** * Anchor.getDocument() -> Document * * Returns the current document. * **/ this.getDocument = function() { return this.document; }; /** * Anchor@onChange(e) * - e (Event): Contains data about the event * * Fires whenever the anchor position changes. Events that can trigger this function include `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`. * **/ this.onChange = function(e) { var delta = e.data; var range = delta.range; if (range.start.row == range.end.row && range.start.row != this.row) return; if (range.start.row > this.row) return; if (range.start.row == this.row && range.start.column > this.column) return; var row = this.row; var column = this.column; if (delta.action === "insertText") { if (range.start.row === row && range.start.column <= column) { if (range.start.row === range.end.row) { column += range.end.column - range.start.column; } else { column -= range.start.column; row += range.end.row - range.start.row; } } else if (range.start.row !== range.end.row && range.start.row < row) { row += range.end.row - range.start.row; } } else if (delta.action === "insertLines") { if (range.start.row <= row) { row += range.end.row - range.start.row; } } else if (delta.action == "removeText") { if (range.start.row == row && range.start.column < column) { if (range.end.column >= column) column = range.start.column; else column = Math.max(0, column - (range.end.column - range.start.column)); } else if (range.start.row !== range.end.row && range.start.row < row) { if (range.end.row == row) { column = Math.max(0, column - range.end.column) + range.start.column; } row -= (range.end.row - range.start.row); } else if (range.end.row == row) { row -= range.end.row - range.start.row; column = Math.max(0, column - range.end.column) + range.start.column; } } else if (delta.action == "removeLines") { if (range.start.row <= row) { if (range.end.row <= row) row -= range.end.row - range.start.row; else { row = range.start.row; column = 0; } } } this.setPosition(row, column, true); }; /** * Anchor.setPosition(row, column, noClip) * - row (Number): The row index to move the anchor to * - column (Number): The column index to move the anchor to * - noClip (Boolean): Identifies if you want the position to be clipped * * Sets the anchor position to the specified row and column. If `noClip` is `true`, the position is not clipped. * **/ this.setPosition = function(row, column, noClip) { var pos; if (noClip) { pos = { row: row, column: column }; } else { pos = this.$clipPositionToDocument(row, column); } if (this.row == pos.row && this.column == pos.column) return; var old = { row: this.row, column: this.column }; this.row = pos.row; this.column = pos.column; this._emit("change", { old: old, value: pos }); }; /** * Anchor.detach() * * When called, the `'change'` event listener is removed. * **/ this.detach = function() { this.document.removeEventListener("change", this.$onChange); }; /** internal, hide * Anchor.clipPositionToDocument(row, column) * - row (Number): The row index to clip the anchor to * - column (Number): The column index to clip the anchor to * * Clips the anchor position to the specified row and column. * **/ this.$clipPositionToDocument = function(row, column) { var pos = {}; if (row >= this.document.getLength()) { pos.row = Math.max(0, this.document.getLength() - 1); pos.column = this.document.getLine(pos.row).length; } else if (row < 0) { pos.row = 0; pos.column = 0; } else { pos.row = row; pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column)); } if (column < 0) pos.column = 0; return pos; }; }).call(Anchor.prototype); }); /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Ajax.org Code Editor (ACE). * * The Initial Developer of the Original Code is * Ajax.org B.V. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabian Jakobs * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) { "use strict"; exports.stringReverse = function(string) { return string.split("").reverse().join(""); }; exports.stringRepeat = function (string, count) { return new Array(count + 1).join(string); }; var trimBeginRegexp = /^\s\s*/; var trimEndRegexp = /\s\s*$/; exports.stringTrimLeft = function (string) { return string.replace(trimBeginRegexp, ''); }; exports.stringTrimRight = function (string) { return string.replace(trimEndRegexp, ''); }; exports.copyObject = function(obj) { var copy = {}; for (var key in obj) { copy[key] = obj[key]; } return copy; }; exports.copyArray = function(array){ var copy = []; for (var i=0, l=array.length; i. var myReport = JSHINT.report(limited); If limited is true, then the report will be limited to only errors. You can request a data structure which contains JSHint's results. var myData = JSHINT.data(); It returns a structure with this form: { errors: [ { line: NUMBER, character: NUMBER, reason: STRING, evidence: STRING } ], functions: [ name: STRING, line: NUMBER, last: NUMBER, param: [ STRING ], closure: [ STRING ], var: [ STRING ], exception: [ STRING ], outer: [ STRING ], unused: [ STRING ], global: [ STRING ], label: [ STRING ] ], globals: [ STRING ], member: { STRING: NUMBER }, unused: [ { name: STRING, line: NUMBER } ], implieds: [ { name: STRING, line: NUMBER } ], urls: [ STRING ], json: BOOLEAN } Empty arrays will not be included. */ /*jshint evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true, undef: true, maxlen: 100, indent:4 */ /*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)", "(breakage)", "(context)", "(error)", "(global)", "(identifier)", "(last)", "(line)", "(loopage)", "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "*", "+", "++", "-", "--", "\/", "<", "<=", "==", "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax, __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio, Autocompleter, Assets, Boolean, Builder, Buffer, Browser, COM, CScript, Canvas, CustomAnimation, Class, Control, Chain, Color, Cookie, Core, DataView, Date, Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMReady, DOMParser, Drag, E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event, Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form, FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey, HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement, HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement, HTMLDivElement, HTMLDListElement, HTMLFieldSetElement, HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement, HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement, HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement, HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement, HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement, HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement, HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement, HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement, HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement, HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement, HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement, Iframe, IframeShim, Image, Int16Array, Int32Array, Int8Array, Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E, MAX_VALUE, MIN_VALUE, Mask, Math, MenuItem, MessageChannel, MessageEvent, MessagePort, MoveAnimation, MooTools, Native, NEGATIVE_INFINITY, Number, Object, ObjectRange, Option, Options, OverText, PI, POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype, RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation, SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion, ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller, Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables, SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template, Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL, VBArray, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XMLSerializer, XPathEvaluator, XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult, "\\", a, addEventListener, address, alert, apply, applicationCache, arguments, arity, asi, atob, b, basic, basicToken, bitwise, block, blur, boolOptions, boss, browser, btoa, c, call, callee, caller, cases, charAt, charCodeAt, character, clearInterval, clearTimeout, close, closed, closure, comment, condition, confirm, console, constructor, content, couch, create, css, curly, d, data, datalist, dd, debug, decodeURI, decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document, dojo, dijit, dojox, define, else, emit, encodeURI, encodeURIComponent, entityify, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil, ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus, forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions, g, gc, getComputedStyle, getRow, getter, getterToken, GLOBAL, global, globals, globalstrict, hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include, indent, indexOf, init, ins, instanceOf, isAlpha, isApplicationRunning, isArray, isDigit, isFinite, isNaN, iterator, java, join, jshint, JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastsemic, laxbreak, laxcomma, latedef, lbp, led, left, length, line, load, loadClass, localStorage, location, log, loopfunc, m, match, maxerr, maxlen, member,message, meta, module, moveBy, moveTo, mootools, multistr, name, navigator, new, newcap, noarg, node, noempty, nomen, nonew, nonstandard, nud, onbeforeunload, onblur, onerror, onevar, onecase, onfocus, onload, onresize, onunload, open, openDatabase, openURL, opener, opera, options, outer, param, parent, parseFloat, parseInt, passfail, plusplus, predef, print, process, prompt, proto, prototype, prototypejs, provides, push, quit, range, raw, reach, reason, regexp, readFile, readUrl, regexdash, removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, respond, rhino, right, runCommand, scroll, screen, scripturl, scrollBy, scrollTo, scrollbar, search, seal, send, serialize, sessionStorage, setInterval, setTimeout, setter, setterToken, shift, slice, smarttabs, sort, spawn, split, stack, status, start, strict, sub, substr, supernew, shadow, supplant, sum, sync, test, toLowerCase, toString, toUpperCase, toint32, token, top, trailing, type, typeOf, Uint16Array, Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis, value, valueOf, var, version, WebSocket, withstmt, white, window, Worker, wsh*/ /*global exports: false */ // 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 JSHINT function itself. var JSHINT = (function () { "use strict"; var anonname, // The guessed name for anonymous functions. // 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 the JSHint boolean options. boolOptions = { asi : true, // if automatic semicolon insertion should be tolerated bitwise : true, // if bitwise operators should not be allowed boss : true, // if advanced usage of assignments should be allowed browser : true, // if the standard browser globals should be predefined couch : true, // if CouchDB globals should be predefined curly : true, // if curly braces around all blocks should be required debug : true, // if debugger statements should be allowed devel : true, // if logging globals should be predefined (console, // alert, etc.) dojo : true, // if Dojo Toolkit globals should be predefined eqeqeq : true, // if === should be required eqnull : true, // if == null comparisons should be tolerated es5 : true, // if ES5 syntax should be allowed esnext : true, // if es.next specific syntax should be allowed evil : true, // if eval should be allowed expr : true, // if ExpressionStatement should be allowed as Programs forin : true, // if for in statements must filter funcscope : true, // if only function scope should be used for scope tests globalstrict: true, // if global "use strict"; should be allowed (also // enables 'strict') immed : true, // if immediate invocations must be wrapped in parens iterator : true, // if the `__iterator__` property should be allowed jquery : true, // if jQuery globals should be predefined lastsemic : true, // if semicolons may be ommitted for the trailing // statements inside of a one-line blocks. latedef : true, // if the use before definition should not be tolerated laxbreak : true, // if line breaks should not be checked laxcomma : true, // if line breaks should not be checked around commas loopfunc : true, // if functions should be allowed to be defined within // loops mootools : true, // if MooTools globals should be predefined multistr : true, // allow multiline strings newcap : true, // if constructor names must be capitalized noarg : true, // if arguments.caller and arguments.callee should be // disallowed node : true, // if the Node.js environment globals should be // predefined noempty : true, // if empty blocks should be disallowed nonew : true, // if using `new` for side-effects should be disallowed nonstandard : true, // if non-standard (but widely adopted) globals should // be predefined nomen : true, // if names should be checked onevar : true, // if only one var statement per function should be // allowed onecase : true, // if one case switch statements should be allowed passfail : true, // if the scan should stop on first error plusplus : true, // if increment/decrement should not be allowed proto : true, // if the `__proto__` property should be allowed prototypejs : true, // if Prototype and Scriptaculous globals should be // predefined regexdash : true, // if unescaped first/last dash (-) inside brackets // should be tolerated regexp : true, // if the . should not be allowed in regexp literals rhino : true, // if the Rhino environment globals should be predefined undef : true, // if variables should be declared before used scripturl : true, // if script-targeted URLs should be tolerated shadow : true, // if variable shadowing should be tolerated smarttabs : true, // if smarttabs should be tolerated // (http://www.emacswiki.org/emacs/SmartTabs) strict : true, // require the "use strict"; pragma sub : true, // if all forms of subscript notation are tolerated supernew : true, // if `new function () { ... };` and `new Object;` // should be tolerated trailing : true, // if trailing whitespace rules apply validthis : true, // if 'this' inside a non-constructor function is valid. // This is a function scoped option only. withstmt : true, // if with statements should be allowed white : true, // if strict whitespace rules apply wsh : true // if the Windows Scripting Host environment globals // should be predefined }, // These are the JSHint options that can take any value // (we use this object to detect invalid options) valOptions = { maxlen: false, indent: false, maxerr: false, predef: false }, // browser contains a set of global names which are commonly provided by a // web browser environment. browser = { ArrayBuffer : false, ArrayBufferView : false, Audio : false, addEventListener : false, applicationCache : false, atob : false, blur : false, btoa : false, clearInterval : false, clearTimeout : false, close : false, closed : false, DataView : false, DOMParser : false, defaultStatus : false, document : false, event : false, FileReader : false, Float32Array : false, Float64Array : false, FormData : false, focus : false, frames : false, getComputedStyle : false, HTMLElement : false, HTMLAnchorElement : false, HTMLBaseElement : false, HTMLBlockquoteElement : false, HTMLBodyElement : false, HTMLBRElement : false, HTMLButtonElement : false, HTMLCanvasElement : false, HTMLDirectoryElement : false, HTMLDivElement : false, HTMLDListElement : false, HTMLFieldSetElement : false, HTMLFontElement : false, HTMLFormElement : false, HTMLFrameElement : false, HTMLFrameSetElement : false, HTMLHeadElement : false, HTMLHeadingElement : false, HTMLHRElement : false, HTMLHtmlElement : false, HTMLIFrameElement : false, HTMLImageElement : false, HTMLInputElement : false, HTMLIsIndexElement : false, HTMLLabelElement : false, HTMLLayerElement : false, HTMLLegendElement : false, HTMLLIElement : false, HTMLLinkElement : false, HTMLMapElement : false, HTMLMenuElement : false, HTMLMetaElement : false, HTMLModElement : false, HTMLObjectElement : false, HTMLOListElement : false, HTMLOptGroupElement : false, HTMLOptionElement : false, HTMLParagraphElement : false, HTMLParamElement : false, HTMLPreElement : false, HTMLQuoteElement : false, HTMLScriptElement : false, HTMLSelectElement : false, HTMLStyleElement : false, HTMLTableCaptionElement : false, HTMLTableCellElement : false, HTMLTableColElement : false, HTMLTableElement : false, HTMLTableRowElement : false, HTMLTableSectionElement : false, HTMLTextAreaElement : false, HTMLTitleElement : false, HTMLUListElement : false, HTMLVideoElement : false, history : false, Int16Array : false, Int32Array : false, Int8Array : false, Image : false, length : false, localStorage : false, location : false, MessageChannel : false, MessageEvent : false, MessagePort : false, moveBy : false, moveTo : false, name : false, navigator : false, onbeforeunload : true, onblur : true, onerror : true, onfocus : true, onload : true, onresize : true, onunload : true, open : false, openDatabase : false, opener : false, Option : false, parent : false, print : false, removeEventListener : false, resizeBy : false, resizeTo : false, screen : false, scroll : false, scrollBy : false, scrollTo : false, sessionStorage : false, setInterval : false, setTimeout : false, SharedWorker : false, status : false, top : false, Uint16Array : false, Uint32Array : false, Uint8Array : false, WebSocket : false, window : false, Worker : false, XMLHttpRequest : false, XMLSerializer : false, XPathEvaluator : false, XPathException : false, XPathExpression : false, XPathNamespace : false, XPathNSResolver : false, XPathResult : false }, couch = { "require" : false, respond : false, getRow : false, emit : false, send : false, start : false, sum : false, log : false, exports : false, module : false, provides : false }, devel = { alert : false, confirm : false, console : false, Debug : false, opera : false, prompt : false }, dojo = { dojo : false, dijit : false, dojox : false, define : false, "require" : false }, escapes = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '/' : '\\/', '\\': '\\\\' }, funct, // The current function functionicity = [ 'closure', 'exception', 'global', 'label', 'outer', 'unused', 'var' ], functions, // All of the functions global, // The global scope implied, // Implied globals inblock, indent, jsonmode, jquery = { '$' : false, jQuery : false }, lines, lookahead, member, membersOnly, mootools = { '$' : false, '$$' : false, Assets : false, Browser : false, Chain : false, Class : false, Color : false, Cookie : false, Core : false, Document : false, DomReady : false, DOMReady : false, Drag : false, Element : false, Elements : false, Event : false, Events : false, Fx : false, Group : false, Hash : false, HtmlTable : false, Iframe : false, IframeShim : false, InputValidator : false, instanceOf : false, Keyboard : false, Locale : false, Mask : false, MooTools : false, Native : false, Options : false, OverText : false, Request : false, Scroller : false, Slick : false, Slider : false, Sortables : false, Spinner : false, Swiff : false, Tips : false, Type : false, typeOf : false, URI : false, Window : false }, nexttoken, node = { __filename : false, __dirname : false, Buffer : false, console : false, exports : false, GLOBAL : false, global : false, module : false, process : false, require : false, setTimeout : false, clearTimeout : false, setInterval : false, clearInterval : false }, noreach, option, predefined, // Global variables defined by option prereg, prevtoken, prototypejs = { '$' : false, '$$' : false, '$A' : false, '$F' : false, '$H' : false, '$R' : false, '$break' : false, '$continue' : false, '$w' : false, Abstract : false, Ajax : false, Class : false, Enumerable : false, Element : false, Event : false, Field : false, Form : false, Hash : false, Insertion : false, ObjectRange : false, PeriodicalExecuter: false, Position : false, Prototype : false, Selector : false, Template : false, Toggle : false, Try : false, Autocompleter : false, Builder : false, Control : false, Draggable : false, Draggables : false, Droppables : false, Effect : false, Sortable : false, SortableObserver : false, Sound : false, Scriptaculous : false }, rhino = { defineClass : false, deserialize : false, gc : false, help : false, importPackage: false, "java" : false, load : false, loadClass : false, print : false, quit : false, readFile : false, readUrl : false, runCommand : false, seal : false, serialize : false, spawn : false, sync : false, toint32 : false, version : false }, scope, // The current scope stack, // standard contains the global names that are provided by the // ECMAScript standard. standard = { Array : false, Boolean : false, Date : false, decodeURI : false, decodeURIComponent : false, encodeURI : false, encodeURIComponent : false, Error : false, 'eval' : false, EvalError : false, Function : false, hasOwnProperty : false, isFinite : false, isNaN : false, JSON : false, Math : false, Number : false, Object : false, parseInt : false, parseFloat : false, RangeError : false, ReferenceError : false, RegExp : false, String : false, SyntaxError : false, TypeError : false, URIError : false }, // widely adopted global names that are not part of ECMAScript standard nonstandard = { escape : false, unescape : false }, standard_member = { E : true, LN2 : true, LN10 : true, LOG2E : true, LOG10E : true, MAX_VALUE : true, MIN_VALUE : true, NEGATIVE_INFINITY : true, PI : true, POSITIVE_INFINITY : true, SQRT1_2 : true, SQRT2 : true }, directive, syntax = {}, tab, token, urls, useESNextSyntax, warnings, wsh = { ActiveXObject : true, Enumerator : true, GetObject : true, ScriptEngine : true, ScriptEngineBuildVersion : true, ScriptEngineMajorVersion : true, ScriptEngineMinorVersion : true, VBArray : true, WSH : true, WScript : true, XDomainRequest : true }; // Regular expressions. Some of these are stupidly long. var ax, cx, tx, nx, nxg, lx, ix, jx, ft; (function () { /*jshint maxlen:300 */ // unsafe comment or string ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i; // 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]/; // token tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint|jslint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/; // 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]/; nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; // star slash lx = /\*\/|\/\*/; // identifier ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; // javascript url jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; // catches /* falls through */ comments ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; }()); function F() {} // Used by Object.create function is_own(object, name) { // The object.hasOwnProperty method fails when the property under consideration // is named 'hasOwnProperty'. So we have to use this more convoluted form. return Object.prototype.hasOwnProperty.call(object, name); } function checkOption(name, t) { if (valOptions[name] === undefined && boolOptions[name] === undefined) { warning("Bad option: '" + name + "'.", t); } } // Provide critical ES5 functions to ES3. if (typeof Array.isArray !== 'function') { Array.isArray = function (o) { return Object.prototype.toString.apply(o) === '[object Array]'; }; } if (typeof Object.create !== 'function') { Object.create = function (o) { F.prototype = o; return new F(); }; } if (typeof Object.keys !== 'function') { Object.keys = function (o) { var a = [], k; for (k in o) { if (is_own(o, k)) { a.push(k); } } return a; }; } // Non standard methods 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 r = o[b]; return typeof r === 'string' || typeof r === 'number' ? r : a; }); }; } if (typeof String.prototype.name !== 'function') { String.prototype.name = function () { // If the string looks like an identifier, then we can return it as is. // If the string contains no control characters, no quote characters, and no // backslash characters, then we can simply slap some quotes around it. // Otherwise we must also replace the offending characters with safe // sequences. if (ix.test(this)) { return this; } if (nx.test(this)) { return '"' + this.replace(nxg, function (a) { var c = escapes[a]; if (c) { return c; } return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); }) + '"'; } return '"' + this + '"'; }; } function combine(t, o) { var n; for (n in o) { if (is_own(o, n)) { t[n] = o[n]; } } } function assume() { if (option.couch) { combine(predefined, couch); } if (option.rhino) { combine(predefined, rhino); } if (option.prototypejs) { combine(predefined, prototypejs); } if (option.node) { combine(predefined, node); option.globalstrict = true; } if (option.devel) { combine(predefined, devel); } if (option.dojo) { combine(predefined, dojo); } if (option.browser) { combine(predefined, browser); } if (option.nonstandard) { combine(predefined, nonstandard); } if (option.jquery) { combine(predefined, jquery); } if (option.mootools) { combine(predefined, mootools); } if (option.wsh) { combine(predefined, wsh); } if (option.esnext) { useESNextSyntax(); } if (option.globalstrict && option.strict !== false) { option.strict = true; } } // Produce an error warning. function quit(message, line, chr) { var percentage = Math.floor((line / lines.length) * 100); throw { name: 'JSHintError', line: line, character: chr, message: message + " (" + percentage + "% scanned).", raw: message }; } function isundef(scope, m, t, a) { return JSHINT.undefs.push([scope, m, t, a]); } function warning(m, t, a, b, c, d) { var ch, l, w; t = t || nexttoken; if (t.id === '(end)') { // `~ t = token; } l = t.line || 0; ch = t.from || 0; w = { id: '(error)', raw: m, evidence: lines[l - 1] || '', line: l, character: ch, a: a, b: b, c: c, d: d }; w.reason = m.supplant(w); JSHINT.errors.push(w); if (option.passfail) { quit('Stopping. ', l, ch); } warnings += 1; if (warnings >= option.maxerr) { quit("Too many errors.", l, ch); } return w; } function warningAt(m, l, ch, a, b, c, d) { return warning(m, { line: l, from: ch }, a, b, c, d); } function error(m, t, a, b, c, d) { var w = warning(m, t, a, b, c, d); } function errorAt(m, l, ch, a, b, c, d) { return error(m, { line: l, from: ch }, a, b, c, d); } // lexical analysis and token construction var lex = (function lex() { var character, from, line, s; // Private lex methods function nextLine() { var at, tw; // trailing whitespace check if (line >= lines.length) return false; character = 1; s = lines[line]; line += 1; // If smarttabs option is used check for spaces followed by tabs only. // Otherwise check for any occurence of mixed tabs and spaces. if (option.smarttabs) at = s.search(/ \t/); else at = s.search(/ \t|\t /); if (at >= 0) warningAt("Mixed spaces and tabs.", line, at + 1); s = s.replace(/\t/g, tab); at = s.search(cx); if (at >= 0) warningAt("Unsafe character.", line, at); if (option.maxlen && option.maxlen < s.length) warningAt("Line too long.", line, s.length); // Check for trailing whitespaces tw = option.trailing && s.match(/^(.*?)\s+$/); if (tw && !/^\s+$/.test(s)) { warningAt("Trailing whitespace.", line, tw[1].length + 1); } return true; } // Produce a token object. The token inherits from a syntax symbol. function it(type, value) { var i, t; if (type === '(color)' || type === '(range)') { t = {type: type}; } else if (type === '(punctuator)' || (type === '(identifier)' && is_own(syntax, value))) { t = syntax[value] || syntax['(error)']; } else { t = syntax[type]; } t = Object.create(t); if (type === '(string)' || type === '(range)') { if (!option.scripturl && jx.test(value)) { warningAt("Script URL.", line, from); } } if (type === '(identifier)') { t.identifier = true; if (value === '__proto__' && !option.proto) { warningAt("The '{a}' property is deprecated.", line, from, value); } else if (value === '__iterator__' && !option.iterator) { warningAt("'{a}' is only available in JavaScript 1.7.", line, from, value); } else if (option.nomen && (value.charAt(0) === '_' || value.charAt(value.length - 1) === '_')) { if (!option.node || token.id === '.' || (value !== '__dirname' && value !== '__filename')) { warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", value); } } } t.value = value; t.line = line; t.character = character; t.from = from; i = t.id; if (i !== '(endline)') { prereg = i && (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) || i === 'return' || i === 'case'); } return t; } // Public lex methods return { init: function (source) { if (typeof source === 'string') { lines = source .replace(/\r\n/g, '\n') .replace(/\r/g, '\n') .split('\n'); } else { lines = source; } // If the first line is a shebang (#!), make it a blank and move on. // Shebangs are used by Node scripts. if (lines[0] && lines[0].substr(0, 2) === '#!') lines[0] = ''; line = 0; nextLine(); from = 1; }, range: function (begin, end) { var c, value = ''; from = character; if (s.charAt(0) !== begin) { errorAt("Expected '{a}' and instead saw '{b}'.", line, character, begin, s.charAt(0)); } for (;;) { s = s.slice(1); character += 1; c = s.charAt(0); switch (c) { case '': errorAt("Missing '{a}'.", line, character, c); break; case end: s = s.slice(1); character += 1; return it('(range)', value); case '\\': warningAt("Unexpected '{a}'.", line, character, c); } value += c; } }, // token -- this is called by advance to get the next token token: function () { var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange, n; function match(x) { var r = x.exec(s), r1; if (r) { l = r[0].length; r1 = r[1]; c = r1.charAt(0); s = s.substr(l); from = character + l - r1.length; character += l; return r1; } } function string(x) { var c, j, r = '', allowNewLine = false; if (jsonmode && x !== '"') { warningAt("Strings must use doublequote.", line, character); } function esc(n) { var i = parseInt(s.substr(j + 1, n), 16); j += n; if (i >= 32 && i <= 126 && i !== 34 && i !== 92 && i !== 39) { warningAt("Unnecessary escapement.", line, character); } character += n; c = String.fromCharCode(i); } j = 0; unclosedString: for (;;) { while (j >= s.length) { j = 0; var cl = line, cf = from; if (!nextLine()) { errorAt("Unclosed string.", cl, cf); break unclosedString; } if (allowNewLine) { allowNewLine = false; } else { warningAt("Unclosed string.", cl, cf); } } c = s.charAt(j); if (c === x) { character += 1; s = s.substr(j + 1); return it('(string)', r, x); } if (c < ' ') { if (c === '\n' || c === '\r') { break; } warningAt("Control character in string: {a}.", line, character + j, s.slice(0, j)); } else if (c === '\\') { j += 1; character += 1; c = s.charAt(j); n = s.charAt(j + 1); switch (c) { case '\\': case '"': case '/': break; case '\'': if (jsonmode) { warningAt("Avoid \\'.", line, character); } break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '0': c = '\0'; // Octal literals fail in strict mode // check if the number is between 00 and 07 // where 'n' is the token next to 'c' if (n >= 0 && n <= 7 && directive["use strict"]) { warningAt( "Octal literals are not allowed in strict mode.", line, character); } break; case 'u': esc(4); break; case 'v': if (jsonmode) { warningAt("Avoid \\v.", line, character); } c = '\v'; break; case 'x': if (jsonmode) { warningAt("Avoid \\x-.", line, character); } esc(2); break; case '': // last character is escape character // always allow new line if escaped, but show // warning if option is not set allowNewLine = true; if (option.multistr) { if (jsonmode) { warningAt("Avoid EOL escapement.", line, character); } c = ''; character -= 1; break; } warningAt("Bad escapement of EOL. Use option multistr if needed.", line, character); break; default: warningAt("Bad escapement.", line, character); } } r += c; character += 1; j += 1; } } for (;;) { if (!s) { return it(nextLine() ? '(endline)' : '(end)', ''); } t = match(tx); if (!t) { t = ''; c = ''; while (s && s < '!') { s = s.substr(1); } if (s) { errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); s = ''; } } else { // identifier if (c.isAlpha() || c === '_' || c === '$') { return it('(identifier)', t); } // number if (c.isDigit()) { if (!isFinite(Number(t))) { warningAt("Bad number '{a}'.", line, character, t); } if (s.substr(0, 1).isAlpha()) { warningAt("Missing space after '{a}'.", line, character, t); } if (c === '0') { d = t.substr(1, 1); if (d.isDigit()) { if (token.id !== '.') { warningAt("Don't use extra leading zeros '{a}'.", line, character, t); } } else if (jsonmode && (d === 'x' || d === 'X')) { warningAt("Avoid 0x-. '{a}'.", line, character, t); } } if (t.substr(t.length - 1) === '.') { warningAt( "A trailing decimal point can be confused with a dot '{a}'.", line, character, t); } return it('(number)', t); } switch (t) { // string case '"': case "'": return string(t); // // comment case '//': s = ''; token.comment = true; break; // /* comment case '/*': for (;;) { i = s.search(lx); if (i >= 0) { break; } if (!nextLine()) { errorAt("Unclosed comment.", line, character); } } character += i + 2; if (s.substr(i, 1) === '/') { errorAt("Nested comment.", line, character); } s = s.substr(i + 2); token.comment = true; break; // /*members /*jshint /*global case '/*members': case '/*member': case '/*jshint': case '/*jslint': case '/*global': case '*/': return { value: t, type: 'special', line: line, character: character, from: from }; case '': break; // / case '/': if (token.id === '/=') { errorAt("A regular expression literal can be confused with '/='.", line, from); } if (prereg) { depth = 0; captures = 0; l = 0; for (;;) { b = true; c = s.charAt(l); l += 1; switch (c) { case '': errorAt("Unclosed regular expression.", line, from); return quit('Stopping.', line, from); case '/': if (depth > 0) { warningAt("{a} unterminated regular expression " + "group(s).", line, from + l, depth); } c = s.substr(0, l - 1); q = { g: true, i: true, m: true }; while (q[s.charAt(l)] === true) { q[s.charAt(l)] = false; l += 1; } character += l; s = s.substr(l); q = s.charAt(0); if (q === '/' || q === '*') { errorAt("Confusing regular expression.", line, from); } return it('(regexp)', c); case '\\': c = s.charAt(l); if (c < ' ') { warningAt( "Unexpected control character in regular expression.", line, from + l); } else if (c === '<') { warningAt( "Unexpected escaped character '{a}' in regular expression.", line, from + l, c); } l += 1; break; case '(': depth += 1; b = false; if (s.charAt(l) === '?') { l += 1; switch (s.charAt(l)) { case ':': case '=': case '!': l += 1; break; default: warningAt( "Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l)); } } else { captures += 1; } break; case '|': b = false; break; case ')': if (depth === 0) { warningAt("Unescaped '{a}'.", line, from + l, ')'); } else { depth -= 1; } break; case ' ': q = 1; while (s.charAt(l) === ' ') { l += 1; q += 1; } if (q > 1) { warningAt( "Spaces are hard to count. Use {{a}}.", line, from + l, q); } break; case '[': c = s.charAt(l); if (c === '^') { l += 1; if (option.regexp) { warningAt("Insecure '{a}'.", line, from + l, c); } else if (s.charAt(l) === ']') { errorAt("Unescaped '{a}'.", line, from + l, '^'); } } if (c === ']') { warningAt("Empty class.", line, from + l - 1); } isLiteral = false; isInRange = false; klass: do { c = s.charAt(l); l += 1; switch (c) { case '[': case '^': warningAt("Unescaped '{a}'.", line, from + l, c); if (isInRange) { isInRange = false; } else { isLiteral = true; } break; case '-': if (isLiteral && !isInRange) { isLiteral = false; isInRange = true; } else if (isInRange) { isInRange = false; } else if (s.charAt(l) === ']') { isInRange = true; } else { if (option.regexdash !== (l === 2 || (l === 3 && s.charAt(1) === '^'))) { warningAt("Unescaped '{a}'.", line, from + l - 1, '-'); } isLiteral = true; } break; case ']': if (isInRange && !option.regexdash) { warningAt("Unescaped '{a}'.", line, from + l - 1, '-'); } break klass; case '\\': c = s.charAt(l); if (c < ' ') { warningAt( "Unexpected control character in regular expression.", line, from + l); } else if (c === '<') { warningAt( "Unexpected escaped character '{a}' in regular expression.", line, from + l, c); } l += 1; // \w, \s and \d are never part of a character range if (/[wsd]/i.test(c)) { if (isInRange) { warningAt("Unescaped '{a}'.", line, from + l, '-'); isInRange = false; } isLiteral = false; } else if (isInRange) { isInRange = false; } else { isLiteral = true; } break; case '/': warningAt("Unescaped '{a}'.", line, from + l - 1, '/'); if (isInRange) { isInRange = false; } else { isLiteral = true; } break; case '<': if (isInRange) { isInRange = false; } else { isLiteral = true; } break; default: if (isInRange) { isInRange = false; } else { isLiteral = true; } } } while (c); break; case '.': if (option.regexp) { warningAt("Insecure '{a}'.", line, from + l, c); } break; case ']': case '?': case '{': case '}': case '+': case '*': warningAt("Unescaped '{a}'.", line, from + l, c); } if (b) { switch (s.charAt(l)) { case '?': case '+': case '*': l += 1; if (s.charAt(l) === '?') { l += 1; } break; case '{': l += 1; c = s.charAt(l); if (c < '0' || c > '9') { warningAt( "Expected a number and instead saw '{a}'.", line, from + l, c); } l += 1; low = +c; for (;;) { c = s.charAt(l); if (c < '0' || c > '9') { break; } l += 1; low = +c + (low * 10); } high = low; if (c === ',') { l += 1; high = Infinity; c = s.charAt(l); if (c >= '0' && c <= '9') { l += 1; high = +c; for (;;) { c = s.charAt(l); if (c < '0' || c > '9') { break; } l += 1; high = +c + (high * 10); } } } if (s.charAt(l) !== '}') { warningAt( "Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c); } else { l += 1; } if (s.charAt(l) === '?') { l += 1; } if (low > high) { warningAt( "'{a}' should not be greater than '{b}'.", line, from + l, low, high); } } } } c = s.substr(0, l - 1); character += l; s = s.substr(l); return it('(regexp)', c); } return it('(punctuator)', t); // punctuator case '#': return it('(punctuator)', t); default: return it('(punctuator)', t); } } } } }; }()); function addlabel(t, type) { if (t === 'hasOwnProperty') { warning("'hasOwnProperty' is a really bad name."); } // Define t in the current function in the current scope. if (is_own(funct, t) && !funct['(global)']) { if (funct[t] === true) { if (option.latedef) warning("'{a}' was used before it was defined.", nexttoken, t); } else { if (!option.shadow && type !== "exception") warning("'{a}' is already defined.", nexttoken, t); } } funct[t] = type; if (funct['(global)']) { global[t] = funct; if (is_own(implied, t)) { if (option.latedef) warning("'{a}' was used before it was defined.", nexttoken, t); delete implied[t]; } } else { scope[t] = funct; } } function doOption() { var b, obj, filter, o = nexttoken.value, t, v; switch (o) { case '*/': error("Unbegun comment."); break; case '/*members': case '/*member': o = '/*members'; if (!membersOnly) { membersOnly = {}; } obj = membersOnly; break; case '/*jshint': case '/*jslint': obj = option; filter = boolOptions; break; case '/*global': obj = predefined; break; default: error("What?"); } t = lex.token(); loop: for (;;) { for (;;) { if (t.type === 'special' && t.value === '*/') { break loop; } if (t.id !== '(endline)' && t.id !== ',') { break; } t = lex.token(); } if (t.type !== '(string)' && t.type !== '(identifier)' && o !== '/*members') { error("Bad option.", t); } v = lex.token(); if (v.id === ':') { v = lex.token(); if (obj === membersOnly) { error("Expected '{a}' and instead saw '{b}'.", t, '*/', ':'); } if (o === '/*jshint') { checkOption(t.value, t); } if (t.value === 'indent' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { error("Expected a small integer and instead saw '{a}'.", v, v.value); } obj.white = true; obj.indent = b; } else if (t.value === 'maxerr' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { error("Expected a small integer and instead saw '{a}'.", v, v.value); } obj.maxerr = b; } else if (t.value === 'maxlen' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { error("Expected a small integer and instead saw '{a}'.", v, v.value); } obj.maxlen = b; } else if (t.value === 'validthis') { if (funct['(global)']) { error("Option 'validthis' can't be used in a global scope."); } else { if (v.value === 'true' || v.value === 'false') obj[t.value] = v.value === 'true'; else error("Bad option value.", v); } } else if (v.value === 'true') { obj[t.value] = true; } else if (v.value === 'false') { obj[t.value] = false; } else { error("Bad option value.", v); } t = lex.token(); } else { if (o === '/*jshint' || o === '/*jslint') { error("Missing option value.", t); } obj[t.value] = false; t = v; } } if (filter) { assume(); } } // We need a peek function. If it has an argument, it peeks that much farther // ahead. It is used to distinguish // for ( var i in ... // from // for ( var i = ... function peek(p) { var i = p || 0, j = 0, t; while (j <= i) { t = lookahead[j]; if (!t) { t = lookahead[j] = lex.token(); } j += 1; } return t; } // Produce the next token. It looks for programming errors. function advance(id, t) { switch (token.id) { case '(number)': if (nexttoken.id === '.') { warning("A dot following a number can be confused with a decimal point.", token); } break; case '-': if (nexttoken.id === '-' || nexttoken.id === '--') { warning("Confusing minusses."); } break; case '+': if (nexttoken.id === '+' || nexttoken.id === '++') { warning("Confusing plusses."); } break; } if (token.type === '(string)' || token.identifier) { anonname = token.value; } if (id && nexttoken.id !== id) { if (t) { if (nexttoken.id === '(end)') { warning("Unmatched '{a}'.", t, t.id); } else { warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", nexttoken, id, t.id, t.line, nexttoken.value); } } else if (nexttoken.type !== '(identifier)' || nexttoken.value !== id) { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, id, nexttoken.value); } } prevtoken = token; token = nexttoken; for (;;) { nexttoken = lookahead.shift() || lex.token(); if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { return; } if (nexttoken.type === 'special') { doOption(); } else { if (nexttoken.id !== '(endline)') { break; } } } } // This is the heart of JSHINT, 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) { var left, isArray = false, isObject = false; if (nexttoken.id === '(end)') error("Unexpected early end of program.", token); advance(); if (initial) { anonname = 'anonymous'; funct['(verb)'] = token.value; } if (initial === true && token.fud) { left = token.fud(); } else { if (token.nud) { left = token.nud(); } else { if (nexttoken.type === '(number)' && token.id === '.') { warning("A leading decimal point can be confused with a dot: '.{a}'.", token, nexttoken.value); advance(); return token; } else { error("Expected an identifier and instead saw '{a}'.", token, token.id); } } while (rbp < nexttoken.lbp) { isArray = token.value === 'Array'; isObject = token.value === 'Object'; advance(); if (isArray && token.id === '(' && nexttoken.id === ')') warning("Use the array literal notation [].", token); if (isObject && token.id === '(' && nexttoken.id === ')') warning("Use the object literal notation {}.", token); if (token.led) { left = token.led(left); } else { error("Expected an operator and instead saw '{a}'.", token, token.id); } } } return left; } // Functions for conformance of style. function adjacent(left, right) { left = left || token; right = right || nexttoken; if (option.white) { if (left.character !== right.from && left.line === right.line) { left.from += (left.character - left.from); warning("Unexpected space after '{a}'.", left, left.value); } } } function nobreak(left, right) { left = left || token; right = right || nexttoken; if (option.white && (left.character !== right.from || left.line !== right.line)) { warning("Unexpected space before '{a}'.", right, right.value); } } function nospace(left, right) { left = left || token; right = right || nexttoken; if (option.white && !left.comment) { if (left.line === right.line) { adjacent(left, right); } } } function nonadjacent(left, right) { if (option.white) { left = left || token; right = right || nexttoken; if (left.line === right.line && left.character === right.from) { left.from += (left.character - left.from); warning("Missing space after '{a}'.", left, left.value); } } } function nobreaknonadjacent(left, right) { left = left || token; right = right || nexttoken; if (!option.laxbreak && left.line !== right.line) { warning("Bad line breaking before '{a}'.", right, right.id); } else if (option.white) { left = left || token; right = right || nexttoken; if (left.character === right.from) { left.from += (left.character - left.from); warning("Missing space after '{a}'.", left, left.value); } } } function indentation(bias) { var i; if (option.white && nexttoken.id !== '(end)') { i = indent + (bias || 0); if (nexttoken.from !== i) { warning( "Expected '{a}' to have an indentation at {b} instead at {c}.", nexttoken, nexttoken.value, i, nexttoken.from); } } } function nolinebreak(t) { t = t || token; if (t.line !== nexttoken.line) { warning("Line breaking error '{a}'.", t, t.value); } } function comma() { if (token.line !== nexttoken.line) { if (!option.laxcomma) { if (comma.first) { warning("Comma warnings can be turned off with 'laxcomma'"); comma.first = false; } warning("Bad line breaking before '{a}'.", token, nexttoken.id); } } else if (!token.comment && token.character !== nexttoken.from && option.white) { token.from += (token.character - token.from); warning("Unexpected space after '{a}'.", token, token.value); } advance(','); nonadjacent(token, nexttoken); } // 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, value: s }; } return x; } function delim(s) { return symbol(s, 0); } function stmt(s, f) { var x = delim(s); x.identifier = x.reserved = true; x.fud = f; return x; } function blockstmt(s, f) { var x = stmt(s, f); x.block = true; return x; } function reserveName(x) { var c = x.id.charAt(0); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { x.identifier = x.reserved = true; } return x; } function prefix(s, f) { var x = symbol(s, 150); reserveName(x); x.nud = (typeof f === 'function') ? f : function () { this.right = expression(150); this.arity = 'unary'; if (this.id === '++' || this.id === '--') { if (option.plusplus) { warning("Unexpected use of '{a}'.", this, this.id); } else if ((!this.right.identifier || this.right.reserved) && this.right.id !== '.' && this.right.id !== '[') { warning("Bad operand.", this); } } return this; }; return x; } function type(s, f) { var x = delim(s); x.type = s; x.nud = f; return x; } function reserve(s, f) { var x = type(s, f); x.identifier = x.reserved = true; return x; } function reservevar(s, v) { return reserve(s, function () { if (typeof v === 'function') { v(this); } return this; }); } function infix(s, f, p, w) { var x = symbol(s, p); reserveName(x); x.led = function (left) { if (!w) { nobreaknonadjacent(prevtoken, token); nonadjacent(token, nexttoken); } if (s === "in" && left.id === "!") { warning("Confusing use of '{a}'.", left, '!'); } if (typeof f === 'function') { return f(left, this); } else { this.left = left; this.right = expression(p); return this; } }; return x; } function relation(s, f) { var x = symbol(s, 100); x.led = function (left) { nobreaknonadjacent(prevtoken, token); nonadjacent(token, nexttoken); var right = expression(100); if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) { warning("Use the isNaN function to compare with NaN.", this); } else if (f) { f.apply(this, [left, right]); } if (left.id === '!') { warning("Confusing use of '{a}'.", left, '!'); } if (right.id === '!') { warning("Confusing use of '{a}'.", right, '!'); } this.left = left; this.right = right; return this; }; return x; } function isPoorRelation(node) { return node && ((node.type === '(number)' && +node.value === 0) || (node.type === '(string)' && node.value === '') || (node.type === 'null' && !option.eqnull) || node.type === 'true' || node.type === 'false' || node.type === 'undefined'); } function assignop(s, f) { symbol(s, 20).exps = true; return infix(s, function (left, that) { var l; that.left = left; if (predefined[left.value] === false && scope[left.value]['(global)'] === true) { warning("Read only.", left); } else if (left['function']) { warning("'{a}' is a function.", left, left.value); } if (left) { if (option.esnext && funct[left.value] === 'const') { warning("Attempting to override '{a}' which is a constant", left, left.value); } if (left.id === '.' || left.id === '[') { if (!left.left || left.left.value === 'arguments') { warning('Bad assignment.', that); } that.right = expression(19); return that; } else if (left.identifier && !left.reserved) { if (funct[left.value] === 'exception') { warning("Do not assign to the exception parameter.", left); } that.right = expression(19); return that; } if (left === syntax['function']) { warning( "Expected an identifier in an assignment and instead saw a function invocation.", token); } } error("Bad assignment.", that); }, 20); } function bitwise(s, f, p) { var x = symbol(s, p); reserveName(x); x.led = (typeof f === 'function') ? f : function (left) { if (option.bitwise) { warning("Unexpected use of '{a}'.", this, this.id); } this.left = left; this.right = expression(p); return this; }; return x; } function bitwiseassignop(s) { symbol(s, 20).exps = true; return infix(s, function (left, that) { if (option.bitwise) { warning("Unexpected use of '{a}'.", that, that.id); } nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); if (left) { if (left.id === '.' || left.id === '[' || (left.identifier && !left.reserved)) { expression(19); return that; } if (left === syntax['function']) { warning( "Expected an identifier in an assignment, and instead saw a function invocation.", token); } return that; } error("Bad assignment.", that); }, 20); } function suffix(s, f) { var x = symbol(s, 150); x.led = function (left) { if (option.plusplus) { warning("Unexpected use of '{a}'.", this, this.id); } else if ((!left.identifier || left.reserved) && left.id !== '.' && left.id !== '[') { warning("Bad operand.", this); } this.left = left; return this; }; return x; } // fnparam means that this identifier is being defined as a function // argument (see identifier()) function optionalidentifier(fnparam) { if (nexttoken.identifier) { advance(); if (token.reserved && !option.es5) { // `undefined` as a function param is a common pattern to protect // against the case when somebody does `undefined = true` and // help with minification. More info: https://gist.github.com/315916 if (!fnparam || token.value !== 'undefined') { warning("Expected an identifier and instead saw '{a}' (a reserved word).", token, token.id); } } return token.value; } } // fnparam means that this identifier is being defined as a function // argument function identifier(fnparam) { var i = optionalidentifier(fnparam); if (i) { return i; } if (token.id === 'function' && nexttoken.id === '(') { warning("Missing name in function declaration."); } else { error("Expected an identifier and instead saw '{a}'.", nexttoken, nexttoken.value); } } function reachable(s) { var i = 0, t; if (nexttoken.id !== ';' || noreach) { return; } for (;;) { t = peek(i); if (t.reach) { return; } if (t.id !== '(endline)') { if (t.id === 'function') { if (!option.latedef) { break; } warning( "Inner functions should be listed at the top of the outer function.", t); break; } warning("Unreachable '{a}' after '{b}'.", t, t.value, s); break; } i += 1; } } function statement(noindent) { var i = indent, r, s = scope, t = nexttoken; if (t.id === ";") { advance(";"); return; } // Is this a labelled statement? if (t.identifier && !t.reserved && peek().id === ':') { advance(); advance(':'); scope = Object.create(s); addlabel(t.value, 'label'); if (!nexttoken.labelled) { warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); } if (jx.test(t.value + ':')) { warning("Label '{a}' looks like a javascript url.", t, t.value); } nexttoken.label = t.value; t = nexttoken; } // Parse the statement. if (!noindent) { indentation(); } r = expression(0, true); // Look for the final semicolon. if (!t.block) { if (!option.expr && (!r || !r.exps)) { warning("Expected an assignment or function call and instead saw an expression.", token); } else if (option.nonew && r.id === '(' && r.left.id === 'new') { warning("Do not use 'new' for side effects."); } if (nexttoken.id === ',') { return comma(); } if (nexttoken.id !== ';') { if (!option.asi) { // If this is the last statement in a block that ends on // the same line *and* option lastsemic is on, ignore the warning. // Otherwise, complain about missing semicolon. if (!option.lastsemic || nexttoken.id !== '}' || nexttoken.line !== token.line) { warningAt("Missing semicolon.", token.line, token.character); } } } else { adjacent(token, nexttoken); advance(';'); nonadjacent(token, nexttoken); } } // Restore the indentation. indent = i; scope = s; return r; } function statements(startLine) { var a = [], f, p; while (!nexttoken.reach && nexttoken.id !== '(end)') { if (nexttoken.id === ';') { p = peek(); if (!p || p.id !== "(") { warning("Unnecessary semicolon."); } advance(';'); } else { a.push(statement(startLine === nexttoken.line)); } } return a; } /* * read all directives * recognizes a simple form of asi, but always * warns, if it is used */ function directives() { var i, p, pn; for (;;) { if (nexttoken.id === "(string)") { p = peek(0); if (p.id === "(endline)") { i = 1; do { pn = peek(i); i = i + 1; } while (pn.id === "(endline)"); if (pn.id !== ";") { if (pn.id !== "(string)" && pn.id !== "(number)" && pn.id !== "(regexp)" && pn.identifier !== true && pn.id !== "}") { break; } warning("Missing semicolon.", nexttoken); } else { p = pn; } } else if (p.id === "}") { // directive with no other statements, warn about missing semicolon warning("Missing semicolon.", p); } else if (p.id !== ";") { break; } indentation(); advance(); if (directive[token.value]) { warning("Unnecessary directive \"{a}\".", token, token.value); } if (token.value === "use strict") { option.newcap = true; option.undef = true; } // there's no directive negation, so always set to true directive[token.value] = true; if (p.id === ";") { advance(";"); } continue; } break; } } /* * Parses a single block. A block is a sequence of statements wrapped in * braces. * * ordinary - true for everything but function bodies and try blocks. * stmt - true if block can be a single statement (e.g. in if/for/while). * isfunc - true if block is a function body */ function block(ordinary, stmt, isfunc) { var a, b = inblock, old_indent = indent, m, s = scope, t, line, d; inblock = ordinary; if (!ordinary || !option.funcscope) scope = Object.create(scope); nonadjacent(token, nexttoken); t = nexttoken; if (nexttoken.id === '{') { advance('{'); line = token.line; if (nexttoken.id !== '}') { indent += option.indent; while (!ordinary && nexttoken.from > indent) { indent += option.indent; } if (isfunc) { m = {}; for (d in directive) { if (is_own(directive, d)) { m[d] = directive[d]; } } directives(); if (option.strict && funct['(context)']['(global)']) { if (!m["use strict"] && !directive["use strict"]) { warning("Missing \"use strict\" statement."); } } } a = statements(line); if (isfunc) { directive = m; } indent -= option.indent; if (line !== nexttoken.line) { indentation(); } } else if (line !== nexttoken.line) { indentation(); } advance('}', t); indent = old_indent; } else if (!ordinary) { error("Expected '{a}' and instead saw '{b}'.", nexttoken, '{', nexttoken.value); } else { if (!stmt || option.curly) warning("Expected '{a}' and instead saw '{b}'.", nexttoken, '{', nexttoken.value); noreach = true; indent += option.indent; // test indentation only if statement is in new line a = [statement(nexttoken.line === token.line)]; indent -= option.indent; noreach = false; } funct['(verb)'] = null; if (!ordinary || !option.funcscope) scope = s; inblock = b; if (ordinary && option.noempty && (!a || a.length === 0)) { warning("Empty block."); } return a; } function countMember(m) { if (membersOnly && typeof membersOnly[m] !== 'boolean') { warning("Unexpected /*member '{a}'.", token, m); } if (typeof member[m] === 'number') { member[m] += 1; } else { member[m] = 1; } } function note_implied(token) { var name = token.value, line = token.line, a = implied[name]; if (typeof a === 'function') { a = false; } if (!a) { a = [line]; implied[name] = a; } else if (a[a.length - 1] !== line) { a.push(line); } } // Build the syntax table by declaring the syntactic elements of the language. type('(number)', function () { return this; }); type('(string)', function () { return this; }); syntax['(identifier)'] = { type: '(identifier)', lbp: 0, identifier: true, nud: function () { var v = this.value, s = scope[v], f; if (typeof s === 'function') { // Protection against accidental inheritance. s = undefined; } else if (typeof s === 'boolean') { f = funct; funct = functions[0]; addlabel(v, 'var'); s = funct; funct = f; } // The name is in scope and defined in the current function. if (funct === s) { // Change 'unused' to 'var', and reject labels. switch (funct[v]) { case 'unused': funct[v] = 'var'; break; case 'unction': funct[v] = 'function'; this['function'] = true; break; case 'function': this['function'] = true; break; case 'label': warning("'{a}' is a statement label.", token, v); break; } } else if (funct['(global)']) { // The name is not defined in the function. If we are in the global // scope, then we have an undefined variable. // // Operators typeof and delete do not raise runtime errors even if // the base object of a reference is null so no need to display warning // if we're inside of typeof or delete. if (option.undef && typeof predefined[v] !== 'boolean') { // Attempting to subscript a null reference will throw an // error, even within the typeof and delete operators if (!(anonname === 'typeof' || anonname === 'delete') || (nexttoken && (nexttoken.value === '.' || nexttoken.value === '['))) { isundef(funct, "'{a}' is not defined.", token, v); } } note_implied(token); } else { // If the name is already defined in the current // function, but not as outer, then there is a scope error. switch (funct[v]) { case 'closure': case 'function': case 'var': case 'unused': warning("'{a}' used out of scope.", token, v); break; case 'label': warning("'{a}' is a statement label.", token, v); break; case 'outer': case 'global': break; default: // If the name is defined in an outer function, make an outer entry, // and if it was unused, make it var. if (s === true) { funct[v] = true; } else if (s === null) { warning("'{a}' is not allowed.", token, v); note_implied(token); } else if (typeof s !== 'object') { // Operators typeof and delete do not raise runtime errors even // if the base object of a reference is null so no need to // display warning if we're inside of typeof or delete. if (option.undef) { // Attempting to subscript a null reference will throw an // error, even within the typeof and delete operators if (!(anonname === 'typeof' || anonname === 'delete') || (nexttoken && (nexttoken.value === '.' || nexttoken.value === '['))) { isundef(funct, "'{a}' is not defined.", token, v); } } funct[v] = true; note_implied(token); } else { switch (s[v]) { case 'function': case 'unction': this['function'] = true; s[v] = 'closure'; funct[v] = s['(global)'] ? 'global' : 'outer'; break; case 'var': case 'unused': s[v] = 'closure'; funct[v] = s['(global)'] ? 'global' : 'outer'; break; case 'closure': case 'parameter': funct[v] = s['(global)'] ? 'global' : 'outer'; break; case 'label': warning("'{a}' is a statement label.", token, v); } } } } return this; }, led: function () { error("Expected an operator and instead saw '{a}'.", nexttoken, nexttoken.value); } }; type('(regexp)', function () { return this; }); // ECMAScript parser delim('(endline)'); delim('(begin)'); delim('(end)').reach = true; delim(''); delim('(error)').reach = true; delim('}').reach = true; delim(')'); delim(']'); delim('"').reach = true; delim("'").reach = true; delim(';'); delim(':').reach = true; delim(','); delim('#'); delim('@'); reserve('else'); reserve('case').reach = true; reserve('catch'); reserve('default').reach = true; reserve('finally'); reservevar('arguments', function (x) { if (directive['use strict'] && funct['(global)']) { warning("Strict violation.", x); } }); reservevar('eval'); reservevar('false'); reservevar('Infinity'); reservevar('NaN'); reservevar('null'); reservevar('this', function (x) { if (directive['use strict'] && !option.validthis && ((funct['(statement)'] && funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) { warning("Possible strict violation.", x); } }); reservevar('true'); reservevar('undefined'); assignop('=', 'assign', 20); assignop('+=', 'assignadd', 20); assignop('-=', 'assignsub', 20); assignop('*=', 'assignmult', 20); assignop('/=', 'assigndiv', 20).nud = function () { error("A regular expression literal can be confused with '/='."); }; assignop('%=', 'assignmod', 20); bitwiseassignop('&=', 'assignbitand', 20); bitwiseassignop('|=', 'assignbitor', 20); bitwiseassignop('^=', 'assignbitxor', 20); bitwiseassignop('<<=', 'assignshiftleft', 20); bitwiseassignop('>>=', 'assignshiftright', 20); bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20); infix('?', function (left, that) { that.left = left; that.right = expression(10); advance(':'); that['else'] = expression(10); return that; }, 30); infix('||', 'or', 40); infix('&&', 'and', 50); bitwise('|', 'bitor', 70); bitwise('^', 'bitxor', 80); bitwise('&', 'bitand', 90); relation('==', function (left, right) { var eqnull = option.eqnull && (left.value === 'null' || right.value === 'null'); if (!eqnull && option.eqeqeq) warning("Expected '{a}' and instead saw '{b}'.", this, '===', '=='); else if (isPoorRelation(left)) warning("Use '{a}' to compare with '{b}'.", this, '===', left.value); else if (isPoorRelation(right)) warning("Use '{a}' to compare with '{b}'.", this, '===', right.value); return this; }); relation('==='); relation('!=', function (left, right) { var eqnull = option.eqnull && (left.value === 'null' || right.value === 'null'); if (!eqnull && option.eqeqeq) { warning("Expected '{a}' and instead saw '{b}'.", this, '!==', '!='); } else if (isPoorRelation(left)) { warning("Use '{a}' to compare with '{b}'.", this, '!==', left.value); } else if (isPoorRelation(right)) { warning("Use '{a}' to compare with '{b}'.", this, '!==', right.value); } return this; }); relation('!=='); relation('<'); relation('>'); relation('<='); relation('>='); bitwise('<<', 'shiftleft', 120); bitwise('>>', 'shiftright', 120); bitwise('>>>', 'shiftrightunsigned', 120); infix('in', 'in', 120); infix('instanceof', 'instanceof', 120); infix('+', function (left, that) { var right = expression(130); if (left && right && left.id === '(string)' && right.id === '(string)') { left.value += right.value; left.character = right.character; if (!option.scripturl && jx.test(left.value)) { warning("JavaScript URL.", left); } return left; } that.left = left; that.right = right; return that; }, 130); prefix('+', 'num'); prefix('+++', function () { warning("Confusing pluses."); this.right = expression(150); this.arity = 'unary'; return this; }); infix('+++', function (left) { warning("Confusing pluses."); this.left = left; this.right = expression(130); return this; }, 130); infix('-', 'sub', 130); prefix('-', 'neg'); prefix('---', function () { warning("Confusing minuses."); this.right = expression(150); this.arity = 'unary'; return this; }); infix('---', function (left) { warning("Confusing minuses."); this.left = left; this.right = expression(130); return this; }, 130); infix('*', 'mult', 140); infix('/', 'div', 140); infix('%', 'mod', 140); suffix('++', 'postinc'); prefix('++', 'preinc'); syntax['++'].exps = true; suffix('--', 'postdec'); prefix('--', 'predec'); syntax['--'].exps = true; prefix('delete', function () { var p = expression(0); if (!p || (p.id !== '.' && p.id !== '[')) { warning("Variables should not be deleted."); } this.first = p; return this; }).exps = true; prefix('~', function () { if (option.bitwise) { warning("Unexpected '{a}'.", this, '~'); } expression(150); return this; }); prefix('!', function () { this.right = expression(150); this.arity = 'unary'; if (bang[this.right.id] === true) { warning("Confusing use of '{a}'.", this, '!'); } return this; }); prefix('typeof', 'typeof'); prefix('new', function () { var c = expression(155), i; if (c && c.id !== 'function') { if (c.identifier) { c['new'] = true; switch (c.value) { case 'Number': case 'String': case 'Boolean': case 'Math': case 'JSON': warning("Do not use {a} as a constructor.", token, c.value); break; case 'Function': if (!option.evil) { warning("The Function constructor is eval."); } break; case 'Date': case 'RegExp': break; default: if (c.id !== 'function') { i = c.value.substr(0, 1); if (option.newcap && (i < 'A' || i > 'Z')) { warning("A constructor name should start with an uppercase letter.", token); } } } } else { if (c.id !== '.' && c.id !== '[' && c.id !== '(') { warning("Bad constructor.", token); } } } else { if (!option.supernew) warning("Weird construction. Delete 'new'.", this); } adjacent(token, nexttoken); if (nexttoken.id !== '(' && !option.supernew) { warning("Missing '()' invoking a constructor."); } this.first = c; return this; }); syntax['new'].exps = true; prefix('void').exps = true; infix('.', function (left, that) { adjacent(prevtoken, token); nobreak(); var m = identifier(); if (typeof m === 'string') { countMember(m); } that.left = left; that.right = m; if (left && left.value === 'arguments' && (m === 'callee' || m === 'caller')) { if (option.noarg) warning("Avoid arguments.{a}.", left, m); else if (directive['use strict']) error('Strict violation.'); } else if (!option.evil && left && left.value === 'document' && (m === 'write' || m === 'writeln')) { warning("document.write can be a form of eval.", left); } if (!option.evil && (m === 'eval' || m === 'execScript')) { warning('eval is evil.'); } return that; }, 160, true); infix('(', function (left, that) { if (prevtoken.id !== '}' && prevtoken.id !== ')') { nobreak(prevtoken, token); } nospace(); if (option.immed && !left.immed && left.id === 'function') { warning("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."); } var n = 0, p = []; if (left) { if (left.type === '(identifier)') { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { if (left.value !== 'Number' && left.value !== 'String' && left.value !== 'Boolean' && left.value !== 'Date') { if (left.value === 'Math') { warning("Math is not a function.", left); } else if (option.newcap) { warning( "Missing 'new' prefix when invoking a constructor.", left); } } } } } if (nexttoken.id !== ')') { for (;;) { p[p.length] = expression(10); n += 1; if (nexttoken.id !== ',') { break; } comma(); } } advance(')'); nospace(prevtoken, token); if (typeof left === 'object') { if (left.value === 'parseInt' && n === 1) { warning("Missing radix parameter.", left); } if (!option.evil) { if (left.value === 'eval' || left.value === 'Function' || left.value === 'execScript') { warning("eval is evil.", left); } else if (p[0] && p[0].id === '(string)' && (left.value === 'setTimeout' || left.value === 'setInterval')) { warning( "Implied eval is evil. Pass a function instead of a string.", left); } } if (!left.identifier && left.id !== '.' && left.id !== '[' && left.id !== '(' && left.id !== '&&' && left.id !== '||' && left.id !== '?') { warning("Bad invocation.", left); } } that.left = left; return that; }, 155, true).exps = true; prefix('(', function () { nospace(); if (nexttoken.id === 'function') { nexttoken.immed = true; } var v = expression(0); advance(')', this); nospace(prevtoken, token); if (option.immed && v.id === 'function') { if (nexttoken.id === '(' || (nexttoken.id === '.' && (peek().value === 'call' || peek().value === 'apply'))) { warning( "Move the invocation into the parens that contain the function.", nexttoken); } else { warning( "Do not wrap function literals in parens unless they are to be immediately invoked.", this); } } return v; }); infix('[', function (left, that) { nobreak(prevtoken, token); nospace(); var e = expression(0), s; if (e && e.type === '(string)') { if (!option.evil && (e.value === 'eval' || e.value === 'execScript')) { warning("eval is evil.", that); } countMember(e.value); if (!option.sub && ix.test(e.value)) { s = syntax[e.value]; if (!s || !s.reserved) { warning("['{a}'] is better written in dot notation.", e, e.value); } } } advance(']', that); nospace(prevtoken, token); that.left = left; that.right = e; return that; }, 160, true); prefix('[', function () { var b = token.line !== nexttoken.line; this.first = []; if (b) { indent += option.indent; if (nexttoken.from === indent + option.indent) { indent += option.indent; } } while (nexttoken.id !== '(end)') { while (nexttoken.id === ',') { warning("Extra comma."); advance(','); } if (nexttoken.id === ']') { break; } if (b && token.line !== nexttoken.line) { indentation(); } this.first.push(expression(10)); if (nexttoken.id === ',') { comma(); if (nexttoken.id === ']' && !option.es5) { warning("Extra comma.", token); break; } } else { break; } } if (b) { indent -= option.indent; indentation(); } advance(']', this); return this; }, 160); function property_name() { var id = optionalidentifier(true); if (!id) { if (nexttoken.id === '(string)') { id = nexttoken.value; advance(); } else if (nexttoken.id === '(number)') { id = nexttoken.value.toString(); advance(); } } return id; } function functionparams() { var i, t = nexttoken, p = []; advance('('); nospace(); if (nexttoken.id === ')') { advance(')'); return; } for (;;) { i = identifier(true); p.push(i); addlabel(i, 'parameter'); if (nexttoken.id === ',') { comma(); } else { advance(')', t); nospace(prevtoken, token); return p; } } } function doFunction(i, statement) { var f, oldOption = option, oldScope = scope; option = Object.create(option); scope = Object.create(scope); funct = { '(name)' : i || '"' + anonname + '"', '(line)' : nexttoken.line, '(context)' : funct, '(breakage)' : 0, '(loopage)' : 0, '(scope)' : scope, '(statement)': statement }; f = funct; token.funct = funct; functions.push(funct); if (i) { addlabel(i, 'function'); } funct['(params)'] = functionparams(); block(false, false, true); scope = oldScope; option = oldOption; funct['(last)'] = token.line; funct = funct['(context)']; return f; } (function (x) { x.nud = function () { var b, f, i, j, p, t; var props = {}; // All properties, including accessors function saveProperty(name, token) { if (props[name] && is_own(props, name)) warning("Duplicate member '{a}'.", nexttoken, i); else props[name] = {}; props[name].basic = true; props[name].basicToken = token; } function saveSetter(name, token) { if (props[name] && is_own(props, name)) { if (props[name].basic || props[name].setter) warning("Duplicate member '{a}'.", nexttoken, i); } else { props[name] = {}; } props[name].setter = true; props[name].setterToken = token; } function saveGetter(name) { if (props[name] && is_own(props, name)) { if (props[name].basic || props[name].getter) warning("Duplicate member '{a}'.", nexttoken, i); } else { props[name] = {}; } props[name].getter = true; props[name].getterToken = token; } b = token.line !== nexttoken.line; if (b) { indent += option.indent; if (nexttoken.from === indent + option.indent) { indent += option.indent; } } for (;;) { if (nexttoken.id === '}') { break; } if (b) { indentation(); } if (nexttoken.value === 'get' && peek().id !== ':') { advance('get'); if (!option.es5) { error("get/set are ES5 features."); } i = property_name(); if (!i) { error("Missing property name."); } saveGetter(i); t = nexttoken; adjacent(token, nexttoken); f = doFunction(); p = f['(params)']; if (p) { warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); } adjacent(token, nexttoken); } else if (nexttoken.value === 'set' && peek().id !== ':') { advance('set'); if (!option.es5) { error("get/set are ES5 features."); } i = property_name(); if (!i) { error("Missing property name."); } saveSetter(i, nexttoken); t = nexttoken; adjacent(token, nexttoken); f = doFunction(); p = f['(params)']; if (!p || p.length !== 1) { warning("Expected a single parameter in set {a} function.", t, i); } } else { i = property_name(); saveProperty(i, nexttoken); if (typeof i !== 'string') { break; } advance(':'); nonadjacent(token, nexttoken); expression(10); } countMember(i); if (nexttoken.id === ',') { comma(); if (nexttoken.id === ',') { warning("Extra comma.", token); } else if (nexttoken.id === '}' && !option.es5) { warning("Extra comma.", token); } } else { break; } } if (b) { indent -= option.indent; indentation(); } advance('}', this); // Check for lonely setters if in the ES5 mode. if (option.es5) { for (var name in props) { if (is_own(props, name) && props[name].setter && !props[name].getter) { warning("Setter is defined without getter.", props[name].setterToken); } } } return this; }; x.fud = function () { error("Expected to see a statement and instead saw a block.", token); }; }(delim('{'))); // This Function is called when esnext option is set to true // it adds the `const` statement to JSHINT useESNextSyntax = function () { var conststatement = stmt('const', function (prefix) { var id, name, value; this.first = []; for (;;) { nonadjacent(token, nexttoken); id = identifier(); if (funct[id] === "const") { warning("const '" + id + "' has already been declared"); } if (funct['(global)'] && predefined[id] === false) { warning("Redefinition of '{a}'.", token, id); } addlabel(id, 'const'); if (prefix) { break; } name = token; this.first.push(token); if (nexttoken.id !== "=") { warning("const " + "'{a}' is initialized to 'undefined'.", token, id); } if (nexttoken.id === '=') { nonadjacent(token, nexttoken); advance('='); nonadjacent(token, nexttoken); if (nexttoken.id === 'undefined') { warning("It is not necessary to initialize " + "'{a}' to 'undefined'.", token, id); } if (peek(0).id === '=' && nexttoken.identifier) { error("Constant {a} was not declared correctly.", nexttoken, nexttoken.value); } value = expression(0); name.first = value; } if (nexttoken.id !== ',') { break; } comma(); } return this; }); conststatement.exps = true; }; var varstatement = stmt('var', function (prefix) { // JavaScript does not have block scope. It only has function scope. So, // declaring a variable in a block can have unexpected consequences. var id, name, value; if (funct['(onevar)'] && option.onevar) { warning("Too many var statements."); } else if (!funct['(global)']) { funct['(onevar)'] = true; } this.first = []; for (;;) { nonadjacent(token, nexttoken); id = identifier(); if (option.esnext && funct[id] === "const") { warning("const '" + id + "' has already been declared"); } if (funct['(global)'] && predefined[id] === false) { warning("Redefinition of '{a}'.", token, id); } addlabel(id, 'unused'); if (prefix) { break; } name = token; this.first.push(token); if (nexttoken.id === '=') { nonadjacent(token, nexttoken); advance('='); nonadjacent(token, nexttoken); if (nexttoken.id === 'undefined') { warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); } if (peek(0).id === '=' && nexttoken.identifier) { error("Variable {a} was not declared correctly.", nexttoken, nexttoken.value); } value = expression(0); name.first = value; } if (nexttoken.id !== ',') { break; } comma(); } return this; }); varstatement.exps = true; blockstmt('function', function () { if (inblock) { warning("Function declarations should not be placed in blocks. " + "Use a function expression or move the statement to the top of " + "the outer function.", token); } var i = identifier(); if (option.esnext && funct[i] === "const") { warning("const '" + i + "' has already been declared"); } adjacent(token, nexttoken); addlabel(i, 'unction'); doFunction(i, true); if (nexttoken.id === '(' && nexttoken.line === token.line) { error( "Function declarations are not invocable. Wrap the whole function invocation in parens."); } return this; }); prefix('function', function () { var i = optionalidentifier(); if (i) { adjacent(token, nexttoken); } else { nonadjacent(token, nexttoken); } doFunction(i); if (!option.loopfunc && funct['(loopage)']) { warning("Don't make functions within a loop."); } return this; }); blockstmt('if', function () { var t = nexttoken; advance('('); nonadjacent(this, t); nospace(); expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } advance(')', t); nospace(prevtoken, token); block(true, true); if (nexttoken.id === 'else') { nonadjacent(token, nexttoken); advance('else'); if (nexttoken.id === 'if' || nexttoken.id === 'switch') { statement(true); } else { block(true, true); } } return this; }); blockstmt('try', function () { var b, e, s; block(false); if (nexttoken.id === 'catch') { advance('catch'); nonadjacent(token, nexttoken); advance('('); s = scope; scope = Object.create(s); e = nexttoken.value; if (nexttoken.type !== '(identifier)') { warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); } else { addlabel(e, 'exception'); } advance(); advance(')'); block(false); b = true; scope = s; } if (nexttoken.id === 'finally') { advance('finally'); block(false); return; } else if (!b) { error("Expected '{a}' and instead saw '{b}'.", nexttoken, 'catch', nexttoken.value); } return this; }); blockstmt('while', function () { var t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); nonadjacent(this, t); nospace(); expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } advance(')', t); nospace(prevtoken, token); block(true, true); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }).labelled = true; blockstmt('with', function () { var t = nexttoken; if (directive['use strict']) { error("'with' is not allowed in strict mode.", token); } else if (!option.withstmt) { warning("Don't use 'with'.", token); } advance('('); nonadjacent(this, t); nospace(); expression(0); advance(')', t); nospace(prevtoken, token); block(true, true); return this; }); blockstmt('switch', function () { var t = nexttoken, g = false; funct['(breakage)'] += 1; advance('('); nonadjacent(this, t); nospace(); this.condition = expression(20); advance(')', t); nospace(prevtoken, token); nonadjacent(token, nexttoken); t = nexttoken; advance('{'); nonadjacent(token, nexttoken); indent += option.indent; this.cases = []; for (;;) { switch (nexttoken.id) { case 'case': switch (funct['(verb)']) { case 'break': case 'case': case 'continue': case 'return': case 'switch': case 'throw': break; default: // You can tell JSHint that you don't use break intentionally by // adding a comment /* falls through */ on a line just before // the next `case`. if (!ft.test(lines[nexttoken.line - 2])) { warning( "Expected a 'break' statement before 'case'.", token); } } indentation(-option.indent); advance('case'); this.cases.push(expression(20)); g = true; advance(':'); funct['(verb)'] = 'case'; break; case 'default': switch (funct['(verb)']) { case 'break': case 'continue': case 'return': case 'throw': break; default: if (!ft.test(lines[nexttoken.line - 2])) { warning( "Expected a 'break' statement before 'default'.", token); } } indentation(-option.indent); advance('default'); g = true; advance(':'); break; case '}': indent -= option.indent; indentation(); advance('}', t); if (this.cases.length === 1 || this.condition.id === 'true' || this.condition.id === 'false') { if (!option.onecase) warning("This 'switch' should be an 'if'.", this); } funct['(breakage)'] -= 1; funct['(verb)'] = undefined; return; case '(end)': error("Missing '{a}'.", nexttoken, '}'); return; default: if (g) { switch (token.id) { case ',': error("Each value should have its own case label."); return; case ':': g = false; statements(); break; default: error("Missing ':' on a case clause.", token); return; } } else { if (token.id === ':') { advance(':'); error("Unexpected '{a}'.", token, ':'); statements(); } else { error("Expected '{a}' and instead saw '{b}'.", nexttoken, 'case', nexttoken.value); return; } } } } }).labelled = true; stmt('debugger', function () { if (!option.debug) { warning("All 'debugger' statements should be removed."); } return this; }).exps = true; (function () { var x = stmt('do', function () { funct['(breakage)'] += 1; funct['(loopage)'] += 1; this.first = block(true); advance('while'); var t = nexttoken; nonadjacent(token, t); advance('('); nospace(); expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } advance(')', t); nospace(prevtoken, token); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }); x.labelled = true; x.exps = true; }()); blockstmt('for', function () { var s, t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); nonadjacent(this, t); nospace(); if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') { if (nexttoken.id === 'var') { advance('var'); varstatement.fud.call(varstatement, true); } else { switch (funct[nexttoken.value]) { case 'unused': funct[nexttoken.value] = 'var'; break; case 'var': break; default: warning("Bad for in variable '{a}'.", nexttoken, nexttoken.value); } advance(); } advance('in'); expression(20); advance(')', t); s = block(true, true); if (option.forin && s && (s.length > 1 || typeof s[0] !== 'object' || s[0].value !== 'if')) { warning("The body of a for in should be wrapped in an if statement to filter " + "unwanted properties from the prototype.", this); } funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; } else { if (nexttoken.id !== ';') { if (nexttoken.id === 'var') { advance('var'); varstatement.fud.call(varstatement); } else { for (;;) { expression(0, 'for'); if (nexttoken.id !== ',') { break; } comma(); } } } nolinebreak(token); advance(';'); if (nexttoken.id !== ';') { expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } } nolinebreak(token); advance(';'); if (nexttoken.id === ';') { error("Expected '{a}' and instead saw '{b}'.", nexttoken, ')', ';'); } if (nexttoken.id !== ')') { for (;;) { expression(0, 'for'); if (nexttoken.id !== ',') { break; } comma(); } } advance(')', t); nospace(prevtoken, token); block(true, true); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; } }).labelled = true; stmt('break', function () { var v = nexttoken.value; if (funct['(breakage)'] === 0) warning("Unexpected '{a}'.", nexttoken, this.value); if (!option.asi) nolinebreak(this); if (nexttoken.id !== ';') { if (token.line === nexttoken.line) { if (funct[v] !== 'label') { warning("'{a}' is not a statement label.", nexttoken, v); } else if (scope[v] !== funct) { warning("'{a}' is out of scope.", nexttoken, v); } this.first = nexttoken; advance(); } } reachable('break'); return this; }).exps = true; stmt('continue', function () { var v = nexttoken.value; if (funct['(breakage)'] === 0) warning("Unexpected '{a}'.", nexttoken, this.value); if (!option.asi) nolinebreak(this); if (nexttoken.id !== ';') { if (token.line === nexttoken.line) { if (funct[v] !== 'label') { warning("'{a}' is not a statement label.", nexttoken, v); } else if (scope[v] !== funct) { warning("'{a}' is out of scope.", nexttoken, v); } this.first = nexttoken; advance(); } } else if (!funct['(loopage)']) { warning("Unexpected '{a}'.", nexttoken, this.value); } reachable('continue'); return this; }).exps = true; stmt('return', function () { if (this.line === nexttoken.line) { if (nexttoken.id === '(regexp)') warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); if (nexttoken.id !== ';' && !nexttoken.reach) { nonadjacent(token, nexttoken); if (peek().value === "=" && !option.boss) { warningAt("Did you mean to return a conditional instead of an assignment?", token.line, token.character + 1); } this.first = expression(0); } } else if (!option.asi) { nolinebreak(this); // always warn (Line breaking error) } reachable('return'); return this; }).exps = true; stmt('throw', function () { nolinebreak(this); nonadjacent(token, nexttoken); this.first = expression(20); reachable('throw'); return this; }).exps = true; // Superfluous reserved words reserve('class'); reserve('const'); reserve('enum'); reserve('export'); reserve('extends'); reserve('import'); reserve('super'); reserve('let'); reserve('yield'); reserve('implements'); reserve('interface'); reserve('package'); reserve('private'); reserve('protected'); reserve('public'); reserve('static'); // Parse JSON function jsonValue() { function jsonObject() { var o = {}, t = nexttoken; advance('{'); if (nexttoken.id !== '}') { for (;;) { if (nexttoken.id === '(end)') { error("Missing '}' to match '{' from line {a}.", nexttoken, t.line); } else if (nexttoken.id === '}') { warning("Unexpected comma.", token); break; } else if (nexttoken.id === ',') { error("Unexpected comma.", nexttoken); } else if (nexttoken.id !== '(string)') { warning("Expected a string and instead saw {a}.", nexttoken, nexttoken.value); } if (o[nexttoken.value] === true) { warning("Duplicate key '{a}'.", nexttoken, nexttoken.value); } else if ((nexttoken.value === '__proto__' && !option.proto) || (nexttoken.value === '__iterator__' && !option.iterator)) { warning("The '{a}' key may produce unexpected results.", nexttoken, nexttoken.value); } else { o[nexttoken.value] = true; } advance(); advance(':'); jsonValue(); if (nexttoken.id !== ',') { break; } advance(','); } } advance('}'); } function jsonArray() { var t = nexttoken; advance('['); if (nexttoken.id !== ']') { for (;;) { if (nexttoken.id === '(end)') { error("Missing ']' to match '[' from line {a}.", nexttoken, t.line); } else if (nexttoken.id === ']') { warning("Unexpected comma.", token); break; } else if (nexttoken.id === ',') { error("Unexpected comma.", nexttoken); } jsonValue(); if (nexttoken.id !== ',') { break; } advance(','); } } advance(']'); } switch (nexttoken.id) { case '{': jsonObject(); break; case '[': jsonArray(); break; case 'true': case 'false': case 'null': case '(number)': case '(string)': advance(); break; case '-': advance('-'); if (token.character !== nexttoken.from) { warning("Unexpected space after '-'.", token); } adjacent(token, nexttoken); advance('(number)'); break; default: error("Expected a JSON value.", nexttoken); } } // The actual JSHINT function itself. var itself = function (s, o, g) { var a, i, k; JSHINT.errors = []; JSHINT.undefs = []; predefined = Object.create(standard); combine(predefined, g || {}); if (o) { a = o.predef; if (a) { if (Array.isArray(a)) { for (i = 0; i < a.length; i += 1) { predefined[a[i]] = true; } } else if (typeof a === 'object') { k = Object.keys(a); for (i = 0; i < k.length; i += 1) { predefined[k[i]] = !!a[k[i]]; } } } option = o; } else { option = {}; } option.indent = option.indent || 4; option.maxerr = option.maxerr || 50; tab = ''; for (i = 0; i < option.indent; i += 1) { tab += ' '; } indent = 1; global = Object.create(predefined); scope = global; funct = { '(global)': true, '(name)': '(global)', '(scope)': scope, '(breakage)': 0, '(loopage)': 0 }; functions = [funct]; urls = []; stack = null; member = {}; membersOnly = null; implied = {}; inblock = false; lookahead = []; jsonmode = false; warnings = 0; lex.init(s); prereg = true; directive = {}; prevtoken = token = nexttoken = syntax['(begin)']; // Check options for (var name in o) { if (is_own(o, name)) { checkOption(name, token); } } assume(); // combine the passed globals after we've assumed all our options combine(predefined, g || {}); //reset values comma.first = true; try { advance(); switch (nexttoken.id) { case '{': case '[': option.laxbreak = true; jsonmode = true; jsonValue(); break; default: directives(); if (directive["use strict"] && !option.globalstrict) { warning("Use the function form of \"use strict\".", prevtoken); } statements(); } advance('(end)'); var markDefined = function (name, context) { do { if (typeof context[name] === 'string') { // JSHINT marks unused variables as 'unused' and // unused function declaration as 'unction'. This // code changes such instances back 'var' and // 'closure' so that the code in JSHINT.data() // doesn't think they're unused. if (context[name] === 'unused') context[name] = 'var'; else if (context[name] === 'unction') context[name] = 'closure'; return true; } context = context['(context)']; } while (context); return false; }; var clearImplied = function (name, line) { if (!implied[name]) return; var newImplied = []; for (var i = 0; i < implied[name].length; i += 1) { if (implied[name][i] !== line) newImplied.push(implied[name][i]); } if (newImplied.length === 0) delete implied[name]; else implied[name] = newImplied; }; // Check queued 'x is not defined' instances to see if they're still undefined. for (i = 0; i < JSHINT.undefs.length; i += 1) { k = JSHINT.undefs[i].slice(0); if (markDefined(k[2].value, k[0])) { clearImplied(k[2].value, k[2].line); } else { warning.apply(warning, k.slice(1)); } } } catch (e) { if (e) { var nt = nexttoken || {}; JSHINT.errors.push({ raw : e.raw, reason : e.message, line : e.line || nt.line, character : e.character || nt.from }, null); } } return JSHINT.errors.length === 0; }; // Data summary. itself.data = function () { var data = { functions: [], options: option }, fu, globals, implieds = [], f, i, j, members = [], n, unused = [], v; if (itself.errors.length) { data.errors = itself.errors; } if (jsonmode) { data.json = true; } for (n in implied) { if (is_own(implied, n)) { implieds.push({ name: n, line: implied[n] }); } } if (implieds.length > 0) { data.implieds = implieds; } if (urls.length > 0) { data.urls = urls; } globals = Object.keys(scope); if (globals.length > 0) { data.globals = globals; } for (i = 1; i < functions.length; i += 1) { f = functions[i]; fu = {}; for (j = 0; j < functionicity.length; j += 1) { fu[functionicity[j]] = []; } for (n in f) { if (is_own(f, n) && n.charAt(0) !== '(') { v = f[n]; if (v === 'unction') { v = 'unused'; } if (Array.isArray(fu[v])) { fu[v].push(n); if (v === 'unused') { unused.push({ name: n, line: f['(line)'], 'function': f['(name)'] }); } } } } for (j = 0; j < functionicity.length; j += 1) { if (fu[functionicity[j]].length === 0) { delete fu[functionicity[j]]; } } fu.name = f['(name)']; fu.param = f['(params)']; fu.line = f['(line)']; fu.last = f['(last)']; data.functions.push(fu); } if (unused.length > 0) { data.unused = unused; } members = []; for (n in member) { if (typeof member[n] === 'number') { data.member = member; break; } } return data; }; itself.report = function (option) { var data = itself.data(); var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s; function detail(h, array) { var b, i, singularity; if (array) { o.push('
' + h + ' '); array = array.sort(); for (i = 0; i < array.length; i += 1) { if (array[i] !== singularity) { singularity = array[i]; o.push((b ? ', ' : '') + singularity); b = true; } } o.push('
'); } } if (data.errors || data.implieds || data.unused) { err = true; o.push('
Error:'); if (data.errors) { for (i = 0; i < data.errors.length; i += 1) { c = data.errors[i]; if (c) { e = c.evidence || ''; o.push('

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

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

'); } } } if (data.implieds) { s = []; for (i = 0; i < data.implieds.length; i += 1) { s[i] = '' + data.implieds[i].name + ' ' + data.implieds[i].line + ''; } o.push('

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

'); } if (data.unused) { s = []; for (i = 0; i < data.unused.length; i += 1) { s[i] = '' + data.unused[i].name + ' ' + data.unused[i].line + ' ' + data.unused[i]['function'] + ''; } o.push('

Unused variable: ' + s.join(', ') + '

'); } if (data.json) { o.push('

JSON: bad.

'); } o.push('
'); } if (!option) { o.push('
'); if (data.urls) { detail("URLs
", data.urls, '
'); } if (data.json && !err) { o.push('

JSON: good.

'); } else if (data.globals) { o.push('
Global ' + data.globals.sort().join(', ') + '
'); } else { o.push('
No new global variables introduced.
'); } for (i = 0; i < data.functions.length; i += 1) { f = data.functions[i]; o.push('
' + f.line + '-' + f.last + ' ' + (f.name || '') + '(' + (f.param ? f.param.join(', ') : '') + ')
'); detail('Unused', f.unused); detail('Closure', f.closure); detail('Variable', f['var']); detail('Exception', f.exception); detail('Outer', f.outer); detail('Global', f.global); detail('Label', f.label); } if (data.member) { a = Object.keys(data.member); if (a.length) { a = a.sort(); m = '
/*members ';
                    l = 10;
                    for (i = 0; i < a.length; i += 1) {
                        k = a[i];
                        n = k.name();
                        if (l + n.length > 72) {
                            o.push(m + '
'); m = ' '; l = 1; } l += n.length + 2; if (data.member[k] === 1) { n = '' + n + ''; } if (i < a.length - 1) { n += ', '; } m += n; } o.push(m + '
*/
'); } o.push('
'); } } return o.join(''); }; itself.jshint = itself; return itself; }()); // Make JSHINT a Node module, if possible. if (typeof exports === 'object' && exports) exports.JSHINT = JSHINT; });/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*- * vim: set sw=4 ts=4 et tw=78: * ***** BEGIN LICENSE BLOCK ***** * * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Narcissus JavaScript engine. * * The Initial Developer of the Original Code is * Brendan Eich . * Portions created by the Initial Developer are Copyright (C) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Tom Austin * Brendan Eich * Shu-Yu Guo * Dave Herman * Dimitris Vardoulakis * Patrick Walton * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * Narcissus - JS implemented in JS. * * Parser. */ define('ace/narcissus/parser', ['require', 'exports', 'module' , 'ace/narcissus/lexer', 'ace/narcissus/definitions', 'ace/narcissus/options'], function(require, exports, module) { var lexer = require('./lexer'); var definitions = require('./definitions'); var options = require('./options'); var Tokenizer = lexer.Tokenizer; var Dict = definitions.Dict; var Stack = definitions.Stack; // Set constants in the local scope. eval(definitions.consts); /* * pushDestructuringVarDecls :: (node, hoisting node) -> void * * Recursively add all destructured declarations to varDecls. */ function pushDestructuringVarDecls(n, s) { for (var i in n) { var sub = n[i]; if (sub.type === IDENTIFIER) { s.varDecls.push(sub); } else { pushDestructuringVarDecls(sub, s); } } } function Parser(tokenizer) { tokenizer.parser = this; this.t = tokenizer; this.x = null; this.unexpectedEOF = false; options.mozillaMode && (this.mozillaMode = true); options.parenFreeMode && (this.parenFreeMode = true); } function StaticContext(parentScript, parentBlock, inModule, inFunction, strictMode) { this.parentScript = parentScript; this.parentBlock = parentBlock || parentScript; this.inModule = inModule || false; this.inFunction = inFunction || false; this.inForLoopInit = false; this.topLevel = true; this.allLabels = new Stack(); this.currentLabels = new Stack(); this.labeledTargets = new Stack(); this.defaultLoopTarget = null; this.defaultTarget = null; this.strictMode = strictMode; } StaticContext.prototype = { // non-destructive update via prototype extension update: function(ext) { var desc = {}; for (var key in ext) { desc[key] = { value: ext[key], writable: true, enumerable: true, configurable: true } } return Object.create(this, desc); }, pushLabel: function(label) { return this.update({ currentLabels: this.currentLabels.push(label), allLabels: this.allLabels.push(label) }); }, pushTarget: function(target) { var isDefaultLoopTarget = target.isLoop; var isDefaultTarget = isDefaultLoopTarget || target.type === SWITCH; if (this.currentLabels.isEmpty()) { if (isDefaultLoopTarget) this.update({ defaultLoopTarget: target }); if (isDefaultTarget) this.update({ defaultTarget: target }); return this; } target.labels = new Dict(); this.currentLabels.forEach(function(label) { target.labels.set(label, true); }); return this.update({ currentLabels: new Stack(), labeledTargets: this.labeledTargets.push(target), defaultLoopTarget: isDefaultLoopTarget ? target : this.defaultLoopTarget, defaultTarget: isDefaultTarget ? target : this.defaultTarget }); }, nest: function() { return this.topLevel ? this.update({ topLevel: false }) : this; }, canImport: function() { return this.topLevel && !this.inFunction; }, canExport: function() { return this.inModule && this.topLevel && !this.inFunction; }, banWith: function() { return this.strictMode || this.inModule; }, modulesAllowed: function() { return this.topLevel && !this.inFunction; } }; var Pp = Parser.prototype; Pp.mozillaMode = false; Pp.parenFreeMode = false; Pp.withContext = function(x, f) { var x0 = this.x; this.x = x; var result = f.call(this); // NB: we don't bother with finally, since exceptions trash the parser this.x = x0; return result; }; Pp.newNode = function newNode(opts) { return new Node(this.t, opts); }; Pp.fail = function fail(msg) { throw this.t.newSyntaxError(msg); }; Pp.match = function match(tt, scanOperand, keywordIsName) { return this.t.match(tt, scanOperand, keywordIsName); }; Pp.mustMatch = function mustMatch(tt, keywordIsName) { return this.t.mustMatch(tt, keywordIsName); }; Pp.peek = function peek(scanOperand) { return this.t.peek(scanOperand); }; Pp.peekOnSameLine = function peekOnSameLine(scanOperand) { return this.t.peekOnSameLine(scanOperand); }; Pp.done = function done() { return this.t.done; }; /* * Script :: (boolean, boolean, boolean) -> node * * Parses the toplevel and module/function bodies. */ Pp.Script = function Script(inModule, inFunction, expectEnd) { var node = this.newNode(scriptInit()); var x2 = new StaticContext(node, node, inModule, inFunction); this.withContext(x2, function() { this.Statements(node, true); }); if (expectEnd && !this.done()) this.fail("expected end of input"); return node; }; /* * Pragma :: (expression statement node) -> boolean * * Checks whether a node is a pragma and annotates it. */ function Pragma(n) { if (n.type === SEMICOLON) { var e = n.expression; if (e.type === STRING && e.value === "use strict") { n.pragma = "strict"; return true; } } return false; } /* * Node :: (tokenizer, optional init object) -> node */ function Node(t, init) { var token = t.token; if (token) { // If init.type exists it will override token.type. this.type = token.type; this.value = token.value; this.lineno = token.lineno; // Start and end are file positions for error handling. this.start = token.start; this.end = token.end; } else { this.lineno = t.lineno; } this.filename = t.filename; this.children = []; for (var prop in init) this[prop] = init[prop]; } /* * SyntheticNode :: (optional init object) -> node */ function SyntheticNode(init) { this.children = []; for (var prop in init) this[prop] = init[prop]; this.synthetic = true; } var Np = Node.prototype = SyntheticNode.prototype = {}; Np.constructor = Node; var TO_SOURCE_SKIP = { type: true, value: true, lineno: true, start: true, end: true, tokenizer: true, assignOp: true }; function unevalableConst(code) { var token = definitions.tokens[code]; var constName = definitions.opTypeNames.hasOwnProperty(token) ? definitions.opTypeNames[token] : token in definitions.keywords ? token.toUpperCase() : token; return { toSource: function() { return constName } }; } Np.toSource = function toSource() { var mock = {}; var self = this; mock.type = unevalableConst(this.type); // avoid infinite recursion in case of back-links if (this.generatingSource) return mock.toSource(); this.generatingSource = true; if ("value" in this) mock.value = this.value; if ("lineno" in this) mock.lineno = this.lineno; if ("start" in this) mock.start = this.start; if ("end" in this) mock.end = this.end; if (this.assignOp) mock.assignOp = unevalableConst(this.assignOp); for (var key in this) { if (this.hasOwnProperty(key) && !(key in TO_SOURCE_SKIP)) mock[key] = this[key]; } try { return mock.toSource(); } finally { delete this.generatingSource; } }; // Always use push to add operands to an expression, to update start and end. Np.push = function (kid) { // kid can be null e.g. [1, , 2]. if (kid !== null) { if (kid.start < this.start) this.start = kid.start; if (this.end < kid.end) this.end = kid.end; } return this.children.push(kid); } Node.indentLevel = 0; function tokenString(tt) { var t = definitions.tokens[tt]; return /^\W/.test(t) ? definitions.opTypeNames[t] : t.toUpperCase(); } Np.toString = function () { var a = []; for (var i in this) { if (this.hasOwnProperty(i) && i !== 'type' && i !== 'target') a.push({id: i, value: this[i]}); } a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; }); var INDENTATION = " "; var n = ++Node.indentLevel; var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenString(this.type); for (i = 0; i < a.length; i++) s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value; n = --Node.indentLevel; s += "\n" + INDENTATION.repeat(n) + "}"; return s; } Np.synth = function(init) { var node = new SyntheticNode(init); node.filename = this.filename; node.lineno = this.lineno; node.start = this.start; node.end = this.end; return node; }; /* * Helper init objects for common nodes. */ var LOOP_INIT = { isLoop: true }; function blockInit() { return { type: BLOCK, varDecls: [] }; } function scriptInit() { return { type: SCRIPT, funDecls: [], varDecls: [], modDefns: new Dict(), modAssns: new Dict(), modDecls: new Dict(), modLoads: new Dict(), impDecls: [], expDecls: [], exports: new Dict(), hasEmptyReturn: false, hasReturnWithValue: false, hasYield: false }; } definitions.defineGetter(Np, "length", function() { throw new Error("Node.prototype.length is gone; " + "use n.children.length instead"); }); definitions.defineProperty(String.prototype, "repeat", function(n) { var s = "", t = this + s; while (--n >= 0) s += t; return s; }, false, false, true); Pp.MaybeLeftParen = function MaybeLeftParen() { if (this.parenFreeMode) return this.match(LEFT_PAREN) ? LEFT_PAREN : END; return this.mustMatch(LEFT_PAREN).type; }; Pp.MaybeRightParen = function MaybeRightParen(p) { if (p === LEFT_PAREN) this.mustMatch(RIGHT_PAREN); } /* * Statements :: (node[, boolean]) -> void * * Parses a sequence of Statements. */ Pp.Statements = function Statements(n, topLevel) { var prologue = !!topLevel; try { while (!this.done() && this.peek(true) !== RIGHT_CURLY) { var n2 = this.Statement(); n.push(n2); if (prologue && Pragma(n2)) { this.x.strictMode = true; n.strict = true; } else { prologue = false; } } } catch (e) { try { if (this.done()) this.unexpectedEOF = true; } catch(e) {} throw e; } } Pp.Block = function Block() { this.mustMatch(LEFT_CURLY); var n = this.newNode(blockInit()); var x2 = this.x.update({ parentBlock: n }).pushTarget(n); this.withContext(x2, function() { this.Statements(n); }); this.mustMatch(RIGHT_CURLY); return n; } var DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2; /* * Export :: (binding node, boolean) -> Export * * Static semantic representation of a module export. */ function Export(node, isDefinition) { this.node = node; // the AST node declaring this individual export this.isDefinition = isDefinition; // is the node an 'export'-annotated definition? this.resolved = null; // resolved pointer to the target of this export } /* * registerExport :: (Dict, EXPORT node) -> void */ function registerExport(exports, decl) { function register(name, exp) { if (exports.has(name)) throw new SyntaxError("multiple exports of " + name); exports.set(name, exp); } switch (decl.type) { case MODULE: case FUNCTION: register(decl.name, new Export(decl, true)); break; case VAR: for (var i = 0; i < decl.children.length; i++) register(decl.children[i].name, new Export(decl.children[i], true)); break; case LET: case CONST: throw new Error("NYI: " + definitions.tokens[decl.type]); case EXPORT: for (var i = 0; i < decl.pathList.length; i++) { var path = decl.pathList[i]; switch (path.type) { case OBJECT_INIT: for (var j = 0; j < path.children.length; j++) { // init :: IDENTIFIER | PROPERTY_INIT var init = path.children[j]; if (init.type === IDENTIFIER) register(init.value, new Export(init, false)); else register(init.children[0].value, new Export(init.children[1], false)); } break; case DOT: register(path.children[1].value, new Export(path, false)); break; case IDENTIFIER: register(path.value, new Export(path, false)); break; default: throw new Error("unexpected export path: " + definitions.tokens[path.type]); } } break; default: throw new Error("unexpected export decl: " + definitions.tokens[exp.type]); } } /* * Module :: (node) -> Module * * Static semantic representation of a module. */ function Module(node) { var exports = node.body.exports; var modDefns = node.body.modDefns; var exportedModules = new Dict(); exports.forEach(function(name, exp) { var node = exp.node; if (node.type === MODULE) { exportedModules.set(name, node); } else if (!exp.isDefinition && node.type === IDENTIFIER && modDefns.has(node.value)) { var mod = modDefns.get(node.value); exportedModules.set(name, mod); } }); this.node = node; this.exports = exports; this.exportedModules = exportedModules; } /* * Statement :: () -> node * * Parses a Statement. */ Pp.Statement = function Statement() { var i, label, n, n2, p, c, ss, tt = this.t.get(true), tt2, x0, x2, x3; var comments = this.t.blockComments; // Cases for statements ending in a right curly return early, avoiding the // common semicolon insertion magic after this switch. switch (tt) { case IMPORT: if (!this.x.canImport()) this.fail("illegal context for import statement"); n = this.newNode(); n.pathList = this.ImportPathList(); this.x.parentScript.impDecls.push(n); break; case EXPORT: if (!this.x.canExport()) this.fail("export statement not in module top level"); switch (this.peek()) { case MODULE: case FUNCTION: case LET: case VAR: case CONST: n = this.Statement(); n.blockComments = comments; n.exported = true; this.x.parentScript.expDecls.push(n); registerExport(this.x.parentScript.exports, n); return n; } n = this.newNode(); n.pathList = this.ExportPathList(); this.x.parentScript.expDecls.push(n); registerExport(this.x.parentScript.exports, n); break; case FUNCTION: // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't. return this.FunctionDefinition(true, this.x.topLevel ? DECLARED_FORM : STATEMENT_FORM, comments); case LEFT_CURLY: n = this.newNode(blockInit()); x2 = this.x.update({ parentBlock: n }).pushTarget(n).nest(); this.withContext(x2, function() { this.Statements(n); }); this.mustMatch(RIGHT_CURLY); return n; case IF: n = this.newNode(); n.condition = this.HeadExpression(); x2 = this.x.pushTarget(n).nest(); this.withContext(x2, function() { n.thenPart = this.Statement(); n.elsePart = this.match(ELSE, true) ? this.Statement() : null; }); return n; case SWITCH: // This allows CASEs after a DEFAULT, which is in the standard. n = this.newNode({ cases: [], defaultIndex: -1 }); n.discriminant = this.HeadExpression(); x2 = this.x.pushTarget(n).nest(); this.withContext(x2, function() { this.mustMatch(LEFT_CURLY); while ((tt = this.t.get()) !== RIGHT_CURLY) { switch (tt) { case DEFAULT: if (n.defaultIndex >= 0) this.fail("More than one switch default"); // FALL THROUGH case CASE: n2 = this.newNode(); if (tt === DEFAULT) n.defaultIndex = n.cases.length; else n2.caseLabel = this.Expression(COLON); break; default: this.fail("Invalid switch case"); } this.mustMatch(COLON); n2.statements = this.newNode(blockInit()); while ((tt=this.peek(true)) !== CASE && tt !== DEFAULT && tt !== RIGHT_CURLY) n2.statements.push(this.Statement()); n.cases.push(n2); } }); return n; case FOR: n = this.newNode(LOOP_INIT); n.blockComments = comments; if (this.match(IDENTIFIER)) { if (this.t.token.value === "each") n.isEach = true; else this.t.unget(); } if (!this.parenFreeMode) this.mustMatch(LEFT_PAREN); x2 = this.x.pushTarget(n).nest(); x3 = this.x.update({ inForLoopInit: true }); n2 = null; if ((tt = this.peek(true)) !== SEMICOLON) { this.withContext(x3, function() { if (tt === VAR || tt === CONST) { this.t.get(); n2 = this.Variables(); } else if (tt === LET) { this.t.get(); if (this.peek() === LEFT_PAREN) { n2 = this.LetBlock(false); } else { // Let in for head, we need to add an implicit block // around the rest of the for. this.x.parentBlock = n; n.varDecls = []; n2 = this.Variables(); } } else { n2 = this.Expression(); } }); } if (n2 && this.match(IN)) { n.type = FOR_IN; this.withContext(x3, function() { n.object = this.Expression(); if (n2.type === VAR || n2.type === LET) { c = n2.children; // Destructuring turns one decl into multiples, so either // there must be only one destructuring or only one // decl. if (c.length !== 1 && n2.destructurings.length !== 1) { // FIXME: this.fail ? throw new SyntaxError("Invalid for..in left-hand side", this.filename, n2.lineno); } if (n2.destructurings.length > 0) { n.iterator = n2.destructurings[0]; } else { n.iterator = c[0]; } n.varDecl = n2; } else { if (n2.type === ARRAY_INIT || n2.type === OBJECT_INIT) { n2.destructuredNames = this.checkDestructuring(n2); } n.iterator = n2; } }); } else { x3.inForLoopInit = false; n.setup = n2; this.mustMatch(SEMICOLON); if (n.isEach) this.fail("Invalid for each..in loop"); this.withContext(x3, function() { n.condition = (this.peek(true) === SEMICOLON) ? null : this.Expression(); this.mustMatch(SEMICOLON); tt2 = this.peek(true); n.update = (this.parenFreeMode ? tt2 === LEFT_CURLY || definitions.isStatementStartCode[tt2] : tt2 === RIGHT_PAREN) ? null : this.Expression(); }); } if (!this.parenFreeMode) this.mustMatch(RIGHT_PAREN); this.withContext(x2, function() { n.body = this.Statement(); }); return n; case WHILE: n = this.newNode({ isLoop: true }); n.blockComments = comments; n.condition = this.HeadExpression(); x2 = this.x.pushTarget(n).nest(); this.withContext(x2, function() { n.body = this.Statement(); }); return n; case DO: n = this.newNode({ isLoop: true }); n.blockComments = comments; x2 = this.x.pushTarget(n).next(); this.withContext(x2, function() { n.body = this.Statement(); }); this.mustMatch(WHILE); n.condition = this.HeadExpression(); //