michael@0: //2.1.3 michael@0: var JSHINT; michael@0: (function () { michael@0: var require; michael@0: require=(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 0) { michael@0: var fn = queue.shift(); michael@0: fn(); michael@0: } michael@0: } michael@0: }, true); michael@0: michael@0: return function nextTick(fn) { michael@0: queue.push(fn); michael@0: window.postMessage('process-tick', '*'); michael@0: }; michael@0: } michael@0: michael@0: return function nextTick(fn) { michael@0: setTimeout(fn, 0); michael@0: }; michael@0: })(); michael@0: michael@0: process.title = 'browser'; michael@0: process.browser = true; michael@0: process.env = {}; michael@0: process.argv = []; michael@0: michael@0: process.binding = function (name) { michael@0: throw new Error('process.binding is not supported'); michael@0: } michael@0: michael@0: // TODO(shtylman) michael@0: process.cwd = function () { return '/' }; michael@0: process.chdir = function (dir) { michael@0: throw new Error('process.chdir is not supported'); michael@0: }; michael@0: michael@0: },{}],2:[function(require,module,exports){ michael@0: (function(process){if (!process.EventEmitter) process.EventEmitter = function () {}; michael@0: michael@0: var EventEmitter = exports.EventEmitter = process.EventEmitter; michael@0: var isArray = typeof Array.isArray === 'function' michael@0: ? Array.isArray michael@0: : function (xs) { michael@0: return Object.prototype.toString.call(xs) === '[object Array]' michael@0: } michael@0: ; michael@0: function indexOf (xs, x) { michael@0: if (xs.indexOf) return xs.indexOf(x); michael@0: for (var i = 0; i < xs.length; i++) { michael@0: if (x === xs[i]) return i; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: // By default EventEmitters will print a warning if more than michael@0: // 10 listeners are added to it. This is a useful default which michael@0: // helps finding memory leaks. michael@0: // michael@0: // Obviously not all Emitters should be limited to 10. This function allows michael@0: // that to be increased. Set to zero for unlimited. michael@0: var defaultMaxListeners = 10; michael@0: EventEmitter.prototype.setMaxListeners = function(n) { michael@0: if (!this._events) this._events = {}; michael@0: this._events.maxListeners = n; michael@0: }; michael@0: michael@0: michael@0: EventEmitter.prototype.emit = function(type) { michael@0: // If there is no 'error' event listener then throw. michael@0: if (type === 'error') { michael@0: if (!this._events || !this._events.error || michael@0: (isArray(this._events.error) && !this._events.error.length)) michael@0: { michael@0: if (arguments[1] instanceof Error) { michael@0: throw arguments[1]; // Unhandled 'error' event michael@0: } else { michael@0: throw new Error("Uncaught, unspecified 'error' event."); michael@0: } michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!this._events) return false; michael@0: var handler = this._events[type]; michael@0: if (!handler) return false; michael@0: michael@0: if (typeof handler == 'function') { michael@0: switch (arguments.length) { michael@0: // fast cases michael@0: case 1: michael@0: handler.call(this); michael@0: break; michael@0: case 2: michael@0: handler.call(this, arguments[1]); michael@0: break; michael@0: case 3: michael@0: handler.call(this, arguments[1], arguments[2]); michael@0: break; michael@0: // slower michael@0: default: michael@0: var args = Array.prototype.slice.call(arguments, 1); michael@0: handler.apply(this, args); michael@0: } michael@0: return true; michael@0: michael@0: } else if (isArray(handler)) { michael@0: var args = Array.prototype.slice.call(arguments, 1); michael@0: michael@0: var listeners = handler.slice(); michael@0: for (var i = 0, l = listeners.length; i < l; i++) { michael@0: listeners[i].apply(this, args); michael@0: } michael@0: return true; michael@0: michael@0: } else { michael@0: return false; michael@0: } michael@0: }; michael@0: michael@0: // EventEmitter is defined in src/node_events.cc michael@0: // EventEmitter.prototype.emit() is also defined there. michael@0: EventEmitter.prototype.addListener = function(type, listener) { michael@0: if ('function' !== typeof listener) { michael@0: throw new Error('addListener only takes instances of Function'); michael@0: } michael@0: michael@0: if (!this._events) this._events = {}; michael@0: michael@0: // To avoid recursion in the case that type == "newListeners"! Before michael@0: // adding it to the listeners, first emit "newListeners". michael@0: this.emit('newListener', type, listener); michael@0: michael@0: if (!this._events[type]) { michael@0: // Optimize the case of one listener. Don't need the extra array object. michael@0: this._events[type] = listener; michael@0: } else if (isArray(this._events[type])) { michael@0: michael@0: // Check for listener leak michael@0: if (!this._events[type].warned) { michael@0: var m; michael@0: if (this._events.maxListeners !== undefined) { michael@0: m = this._events.maxListeners; michael@0: } else { michael@0: m = defaultMaxListeners; michael@0: } michael@0: michael@0: if (m && m > 0 && this._events[type].length > m) { michael@0: this._events[type].warned = true; michael@0: console.error('(node) warning: possible EventEmitter memory ' + michael@0: 'leak detected. %d listeners added. ' + michael@0: 'Use emitter.setMaxListeners() to increase limit.', michael@0: this._events[type].length); michael@0: console.trace(); michael@0: } michael@0: } michael@0: michael@0: // If we've already got an array, just append. michael@0: this._events[type].push(listener); michael@0: } else { michael@0: // Adding the second element, need to change to array. michael@0: this._events[type] = [this._events[type], listener]; michael@0: } michael@0: michael@0: return this; michael@0: }; michael@0: michael@0: EventEmitter.prototype.on = EventEmitter.prototype.addListener; michael@0: michael@0: EventEmitter.prototype.once = function(type, listener) { michael@0: var self = this; michael@0: self.on(type, function g() { michael@0: self.removeListener(type, g); michael@0: listener.apply(this, arguments); michael@0: }); michael@0: michael@0: return this; michael@0: }; michael@0: michael@0: EventEmitter.prototype.removeListener = function(type, listener) { michael@0: if ('function' !== typeof listener) { michael@0: throw new Error('removeListener only takes instances of Function'); michael@0: } michael@0: michael@0: // does not use listeners(), so no side effect of creating _events[type] michael@0: if (!this._events || !this._events[type]) return this; michael@0: michael@0: var list = this._events[type]; michael@0: michael@0: if (isArray(list)) { michael@0: var i = indexOf(list, listener); michael@0: if (i < 0) return this; michael@0: list.splice(i, 1); michael@0: if (list.length == 0) michael@0: delete this._events[type]; michael@0: } else if (this._events[type] === listener) { michael@0: delete this._events[type]; michael@0: } michael@0: michael@0: return this; michael@0: }; michael@0: michael@0: EventEmitter.prototype.removeAllListeners = function(type) { michael@0: if (arguments.length === 0) { michael@0: this._events = {}; michael@0: return this; michael@0: } michael@0: michael@0: // does not use listeners(), so no side effect of creating _events[type] michael@0: if (type && this._events && this._events[type]) this._events[type] = null; michael@0: return this; michael@0: }; michael@0: michael@0: EventEmitter.prototype.listeners = function(type) { michael@0: if (!this._events) this._events = {}; michael@0: if (!this._events[type]) this._events[type] = []; michael@0: if (!isArray(this._events[type])) { michael@0: this._events[type] = [this._events[type]]; michael@0: } michael@0: return this._events[type]; michael@0: }; michael@0: michael@0: })(require("__browserify_process")) michael@0: },{"__browserify_process":1}],3:[function(require,module,exports){ michael@0: (function(){// jshint -W001 michael@0: michael@0: "use strict"; michael@0: michael@0: // Identifiers provided by the ECMAScript standard. michael@0: michael@0: exports.reservedVars = { michael@0: arguments : false, michael@0: NaN : false michael@0: }; michael@0: michael@0: exports.ecmaIdentifiers = { michael@0: Array : false, michael@0: Boolean : false, michael@0: Date : false, michael@0: decodeURI : false, michael@0: decodeURIComponent : false, michael@0: encodeURI : false, michael@0: encodeURIComponent : false, michael@0: Error : false, michael@0: "eval" : false, michael@0: EvalError : false, michael@0: Function : false, michael@0: hasOwnProperty : false, michael@0: isFinite : false, michael@0: isNaN : false, michael@0: JSON : false, michael@0: Math : false, michael@0: Map : false, michael@0: Number : false, michael@0: Object : false, michael@0: parseInt : false, michael@0: parseFloat : false, michael@0: RangeError : false, michael@0: ReferenceError : false, michael@0: RegExp : false, michael@0: Set : false, michael@0: String : false, michael@0: SyntaxError : false, michael@0: TypeError : false, michael@0: URIError : false, michael@0: WeakMap : false michael@0: }; michael@0: michael@0: // Global variables commonly provided by a web browser environment. michael@0: michael@0: exports.browser = { michael@0: ArrayBuffer : false, michael@0: ArrayBufferView : false, michael@0: Audio : false, michael@0: Blob : false, michael@0: addEventListener : false, michael@0: applicationCache : false, michael@0: atob : false, michael@0: blur : false, michael@0: btoa : false, michael@0: clearInterval : false, michael@0: clearTimeout : false, michael@0: close : false, michael@0: closed : false, michael@0: DataView : false, michael@0: DOMParser : false, michael@0: defaultStatus : false, michael@0: document : false, michael@0: Element : false, michael@0: ElementTimeControl : false, michael@0: event : false, michael@0: FileReader : false, michael@0: Float32Array : false, michael@0: Float64Array : false, michael@0: FormData : false, michael@0: focus : false, michael@0: frames : false, michael@0: getComputedStyle : false, michael@0: HTMLElement : false, michael@0: HTMLAnchorElement : false, michael@0: HTMLBaseElement : false, michael@0: HTMLBlockquoteElement: false, michael@0: HTMLBodyElement : false, michael@0: HTMLBRElement : false, michael@0: HTMLButtonElement : false, michael@0: HTMLCanvasElement : false, michael@0: HTMLDirectoryElement : false, michael@0: HTMLDivElement : false, michael@0: HTMLDListElement : false, michael@0: HTMLFieldSetElement : false, michael@0: HTMLFontElement : false, michael@0: HTMLFormElement : false, michael@0: HTMLFrameElement : false, michael@0: HTMLFrameSetElement : false, michael@0: HTMLHeadElement : false, michael@0: HTMLHeadingElement : false, michael@0: HTMLHRElement : false, michael@0: HTMLHtmlElement : false, michael@0: HTMLIFrameElement : false, michael@0: HTMLImageElement : false, michael@0: HTMLInputElement : false, michael@0: HTMLIsIndexElement : false, michael@0: HTMLLabelElement : false, michael@0: HTMLLayerElement : false, michael@0: HTMLLegendElement : false, michael@0: HTMLLIElement : false, michael@0: HTMLLinkElement : false, michael@0: HTMLMapElement : false, michael@0: HTMLMenuElement : false, michael@0: HTMLMetaElement : false, michael@0: HTMLModElement : false, michael@0: HTMLObjectElement : false, michael@0: HTMLOListElement : false, michael@0: HTMLOptGroupElement : false, michael@0: HTMLOptionElement : false, michael@0: HTMLParagraphElement : false, michael@0: HTMLParamElement : false, michael@0: HTMLPreElement : false, michael@0: HTMLQuoteElement : false, michael@0: HTMLScriptElement : false, michael@0: HTMLSelectElement : false, michael@0: HTMLStyleElement : false, michael@0: HTMLTableCaptionElement: false, michael@0: HTMLTableCellElement : false, michael@0: HTMLTableColElement : false, michael@0: HTMLTableElement : false, michael@0: HTMLTableRowElement : false, michael@0: HTMLTableSectionElement: false, michael@0: HTMLTextAreaElement : false, michael@0: HTMLTitleElement : false, michael@0: HTMLUListElement : false, michael@0: HTMLVideoElement : false, michael@0: history : false, michael@0: Int16Array : false, michael@0: Int32Array : false, michael@0: Int8Array : false, michael@0: Image : false, michael@0: length : false, michael@0: localStorage : false, michael@0: location : false, michael@0: MessageChannel : false, michael@0: MessageEvent : false, michael@0: MessagePort : false, michael@0: moveBy : false, michael@0: moveTo : false, michael@0: MutationObserver : false, michael@0: name : false, michael@0: Node : false, michael@0: NodeFilter : false, michael@0: navigator : false, michael@0: onbeforeunload : true, michael@0: onblur : true, michael@0: onerror : true, michael@0: onfocus : true, michael@0: onload : true, michael@0: onresize : true, michael@0: onunload : true, michael@0: open : false, michael@0: openDatabase : false, michael@0: opener : false, michael@0: Option : false, michael@0: parent : false, michael@0: print : false, michael@0: removeEventListener : false, michael@0: resizeBy : false, michael@0: resizeTo : false, michael@0: screen : false, michael@0: scroll : false, michael@0: scrollBy : false, michael@0: scrollTo : false, michael@0: sessionStorage : false, michael@0: setInterval : false, michael@0: setTimeout : false, michael@0: SharedWorker : false, michael@0: status : false, michael@0: SVGAElement : false, michael@0: SVGAltGlyphDefElement: false, michael@0: SVGAltGlyphElement : false, michael@0: SVGAltGlyphItemElement: false, michael@0: SVGAngle : false, michael@0: SVGAnimateColorElement: false, michael@0: SVGAnimateElement : false, michael@0: SVGAnimateMotionElement: false, michael@0: SVGAnimateTransformElement: false, michael@0: SVGAnimatedAngle : false, michael@0: SVGAnimatedBoolean : false, michael@0: SVGAnimatedEnumeration: false, michael@0: SVGAnimatedInteger : false, michael@0: SVGAnimatedLength : false, michael@0: SVGAnimatedLengthList: false, michael@0: SVGAnimatedNumber : false, michael@0: SVGAnimatedNumberList: false, michael@0: SVGAnimatedPathData : false, michael@0: SVGAnimatedPoints : false, michael@0: SVGAnimatedPreserveAspectRatio: false, michael@0: SVGAnimatedRect : false, michael@0: SVGAnimatedString : false, michael@0: SVGAnimatedTransformList: false, michael@0: SVGAnimationElement : false, michael@0: SVGCSSRule : false, michael@0: SVGCircleElement : false, michael@0: SVGClipPathElement : false, michael@0: SVGColor : false, michael@0: SVGColorProfileElement: false, michael@0: SVGColorProfileRule : false, michael@0: SVGComponentTransferFunctionElement: false, michael@0: SVGCursorElement : false, michael@0: SVGDefsElement : false, michael@0: SVGDescElement : false, michael@0: SVGDocument : false, michael@0: SVGElement : false, michael@0: SVGElementInstance : false, michael@0: SVGElementInstanceList: false, michael@0: SVGEllipseElement : false, michael@0: SVGExternalResourcesRequired: false, michael@0: SVGFEBlendElement : false, michael@0: SVGFEColorMatrixElement: false, michael@0: SVGFEComponentTransferElement: false, michael@0: SVGFECompositeElement: false, michael@0: SVGFEConvolveMatrixElement: false, michael@0: SVGFEDiffuseLightingElement: false, michael@0: SVGFEDisplacementMapElement: false, michael@0: SVGFEDistantLightElement: false, michael@0: SVGFEDropShadowElement: false, michael@0: SVGFEFloodElement : false, michael@0: SVGFEFuncAElement : false, michael@0: SVGFEFuncBElement : false, michael@0: SVGFEFuncGElement : false, michael@0: SVGFEFuncRElement : false, michael@0: SVGFEGaussianBlurElement: false, michael@0: SVGFEImageElement : false, michael@0: SVGFEMergeElement : false, michael@0: SVGFEMergeNodeElement: false, michael@0: SVGFEMorphologyElement: false, michael@0: SVGFEOffsetElement : false, michael@0: SVGFEPointLightElement: false, michael@0: SVGFESpecularLightingElement: false, michael@0: SVGFESpotLightElement: false, michael@0: SVGFETileElement : false, michael@0: SVGFETurbulenceElement: false, michael@0: SVGFilterElement : false, michael@0: SVGFilterPrimitiveStandardAttributes: false, michael@0: SVGFitToViewBox : false, michael@0: SVGFontElement : false, michael@0: SVGFontFaceElement : false, michael@0: SVGFontFaceFormatElement: false, michael@0: SVGFontFaceNameElement: false, michael@0: SVGFontFaceSrcElement: false, michael@0: SVGFontFaceUriElement: false, michael@0: SVGForeignObjectElement: false, michael@0: SVGGElement : false, michael@0: SVGGlyphElement : false, michael@0: SVGGlyphRefElement : false, michael@0: SVGGradientElement : false, michael@0: SVGHKernElement : false, michael@0: SVGICCColor : false, michael@0: SVGImageElement : false, michael@0: SVGLangSpace : false, michael@0: SVGLength : false, michael@0: SVGLengthList : false, michael@0: SVGLineElement : false, michael@0: SVGLinearGradientElement: false, michael@0: SVGLocatable : false, michael@0: SVGMPathElement : false, michael@0: SVGMarkerElement : false, michael@0: SVGMaskElement : false, michael@0: SVGMatrix : false, michael@0: SVGMetadataElement : false, michael@0: SVGMissingGlyphElement: false, michael@0: SVGNumber : false, michael@0: SVGNumberList : false, michael@0: SVGPaint : false, michael@0: SVGPathElement : false, michael@0: SVGPathSeg : false, michael@0: SVGPathSegArcAbs : false, michael@0: SVGPathSegArcRel : false, michael@0: SVGPathSegClosePath : false, michael@0: SVGPathSegCurvetoCubicAbs: false, michael@0: SVGPathSegCurvetoCubicRel: false, michael@0: SVGPathSegCurvetoCubicSmoothAbs: false, michael@0: SVGPathSegCurvetoCubicSmoothRel: false, michael@0: SVGPathSegCurvetoQuadraticAbs: false, michael@0: SVGPathSegCurvetoQuadraticRel: false, michael@0: SVGPathSegCurvetoQuadraticSmoothAbs: false, michael@0: SVGPathSegCurvetoQuadraticSmoothRel: false, michael@0: SVGPathSegLinetoAbs : false, michael@0: SVGPathSegLinetoHorizontalAbs: false, michael@0: SVGPathSegLinetoHorizontalRel: false, michael@0: SVGPathSegLinetoRel : false, michael@0: SVGPathSegLinetoVerticalAbs: false, michael@0: SVGPathSegLinetoVerticalRel: false, michael@0: SVGPathSegList : false, michael@0: SVGPathSegMovetoAbs : false, michael@0: SVGPathSegMovetoRel : false, michael@0: SVGPatternElement : false, michael@0: SVGPoint : false, michael@0: SVGPointList : false, michael@0: SVGPolygonElement : false, michael@0: SVGPolylineElement : false, michael@0: SVGPreserveAspectRatio: false, michael@0: SVGRadialGradientElement: false, michael@0: SVGRect : false, michael@0: SVGRectElement : false, michael@0: SVGRenderingIntent : false, michael@0: SVGSVGElement : false, michael@0: SVGScriptElement : false, michael@0: SVGSetElement : false, michael@0: SVGStopElement : false, michael@0: SVGStringList : false, michael@0: SVGStylable : false, michael@0: SVGStyleElement : false, michael@0: SVGSwitchElement : false, michael@0: SVGSymbolElement : false, michael@0: SVGTRefElement : false, michael@0: SVGTSpanElement : false, michael@0: SVGTests : false, michael@0: SVGTextContentElement: false, michael@0: SVGTextElement : false, michael@0: SVGTextPathElement : false, michael@0: SVGTextPositioningElement: false, michael@0: SVGTitleElement : false, michael@0: SVGTransform : false, michael@0: SVGTransformList : false, michael@0: SVGTransformable : false, michael@0: SVGURIReference : false, michael@0: SVGUnitTypes : false, michael@0: SVGUseElement : false, michael@0: SVGVKernElement : false, michael@0: SVGViewElement : false, michael@0: SVGViewSpec : false, michael@0: SVGZoomAndPan : false, michael@0: TimeEvent : false, michael@0: top : false, michael@0: Uint16Array : false, michael@0: Uint32Array : false, michael@0: Uint8Array : false, michael@0: Uint8ClampedArray : false, michael@0: WebSocket : false, michael@0: window : false, michael@0: Worker : false, michael@0: XMLHttpRequest : false, michael@0: XMLSerializer : false, michael@0: XPathEvaluator : false, michael@0: XPathException : false, michael@0: XPathExpression : false, michael@0: XPathNSResolver : false, michael@0: XPathResult : false michael@0: }; michael@0: michael@0: exports.devel = { michael@0: alert : false, michael@0: confirm: false, michael@0: console: false, michael@0: Debug : false, michael@0: opera : false, michael@0: prompt : false michael@0: }; michael@0: michael@0: exports.worker = { michael@0: importScripts: true, michael@0: postMessage : true, michael@0: self : true michael@0: }; michael@0: michael@0: // Widely adopted global names that are not part of ECMAScript standard michael@0: exports.nonstandard = { michael@0: escape : false, michael@0: unescape: false michael@0: }; michael@0: michael@0: // Globals provided by popular JavaScript environments. michael@0: michael@0: exports.couch = { michael@0: "require" : false, michael@0: respond : false, michael@0: getRow : false, michael@0: emit : false, michael@0: send : false, michael@0: start : false, michael@0: sum : false, michael@0: log : false, michael@0: exports : false, michael@0: module : false, michael@0: provides : false michael@0: }; michael@0: michael@0: exports.node = { michael@0: __filename : false, michael@0: __dirname : false, michael@0: Buffer : false, michael@0: DataView : false, michael@0: console : false, michael@0: exports : true, // In Node it is ok to exports = module.exports = foo(); michael@0: GLOBAL : false, michael@0: global : false, michael@0: module : false, michael@0: process : false, michael@0: require : false, michael@0: setTimeout : false, michael@0: clearTimeout : false, michael@0: setInterval : false, michael@0: clearInterval : false, michael@0: setImmediate : false, // v0.9.1+ michael@0: clearImmediate: false // v0.9.1+ michael@0: }; michael@0: michael@0: exports.phantom = { michael@0: phantom : true, michael@0: require : true, michael@0: WebPage : true michael@0: }; michael@0: michael@0: exports.rhino = { michael@0: defineClass : false, michael@0: deserialize : false, michael@0: gc : false, michael@0: help : false, michael@0: importPackage: false, michael@0: "java" : false, michael@0: load : false, michael@0: loadClass : false, michael@0: print : false, michael@0: quit : false, michael@0: readFile : false, michael@0: readUrl : false, michael@0: runCommand : false, michael@0: seal : false, michael@0: serialize : false, michael@0: spawn : false, michael@0: sync : false, michael@0: toint32 : false, michael@0: version : false michael@0: }; michael@0: michael@0: exports.wsh = { michael@0: ActiveXObject : true, michael@0: Enumerator : true, michael@0: GetObject : true, michael@0: ScriptEngine : true, michael@0: ScriptEngineBuildVersion : true, michael@0: ScriptEngineMajorVersion : true, michael@0: ScriptEngineMinorVersion : true, michael@0: VBArray : true, michael@0: WSH : true, michael@0: WScript : true, michael@0: XDomainRequest : true michael@0: }; michael@0: michael@0: // Globals provided by popular JavaScript libraries. michael@0: michael@0: exports.dojo = { michael@0: dojo : false, michael@0: dijit : false, michael@0: dojox : false, michael@0: define : false, michael@0: "require": false michael@0: }; michael@0: michael@0: exports.jquery = { michael@0: "$" : false, michael@0: jQuery : false michael@0: }; michael@0: michael@0: exports.mootools = { michael@0: "$" : false, michael@0: "$$" : false, michael@0: Asset : false, michael@0: Browser : false, michael@0: Chain : false, michael@0: Class : false, michael@0: Color : false, michael@0: Cookie : false, michael@0: Core : false, michael@0: Document : false, michael@0: DomReady : false, michael@0: DOMEvent : false, michael@0: DOMReady : false, michael@0: Drag : false, michael@0: Element : false, michael@0: Elements : false, michael@0: Event : false, michael@0: Events : false, michael@0: Fx : false, michael@0: Group : false, michael@0: Hash : false, michael@0: HtmlTable : false, michael@0: Iframe : false, michael@0: IframeShim : false, michael@0: InputValidator: false, michael@0: instanceOf : false, michael@0: Keyboard : false, michael@0: Locale : false, michael@0: Mask : false, michael@0: MooTools : false, michael@0: Native : false, michael@0: Options : false, michael@0: OverText : false, michael@0: Request : false, michael@0: Scroller : false, michael@0: Slick : false, michael@0: Slider : false, michael@0: Sortables : false, michael@0: Spinner : false, michael@0: Swiff : false, michael@0: Tips : false, michael@0: Type : false, michael@0: typeOf : false, michael@0: URI : false, michael@0: Window : false michael@0: }; michael@0: michael@0: exports.prototypejs = { michael@0: "$" : false, michael@0: "$$" : false, michael@0: "$A" : false, michael@0: "$F" : false, michael@0: "$H" : false, michael@0: "$R" : false, michael@0: "$break" : false, michael@0: "$continue" : false, michael@0: "$w" : false, michael@0: Abstract : false, michael@0: Ajax : false, michael@0: Class : false, michael@0: Enumerable : false, michael@0: Element : false, michael@0: Event : false, michael@0: Field : false, michael@0: Form : false, michael@0: Hash : false, michael@0: Insertion : false, michael@0: ObjectRange : false, michael@0: PeriodicalExecuter: false, michael@0: Position : false, michael@0: Prototype : false, michael@0: Selector : false, michael@0: Template : false, michael@0: Toggle : false, michael@0: Try : false, michael@0: Autocompleter : false, michael@0: Builder : false, michael@0: Control : false, michael@0: Draggable : false, michael@0: Draggables : false, michael@0: Droppables : false, michael@0: Effect : false, michael@0: Sortable : false, michael@0: SortableObserver : false, michael@0: Sound : false, michael@0: Scriptaculous : false michael@0: }; michael@0: michael@0: exports.yui = { michael@0: YUI : false, michael@0: Y : false, michael@0: YUI_config: false michael@0: }; michael@0: michael@0: michael@0: })() michael@0: },{}],4:[function(require,module,exports){ michael@0: "use strict"; michael@0: michael@0: var state = { michael@0: syntax: {}, michael@0: michael@0: reset: function () { michael@0: this.tokens = { michael@0: prev: null, michael@0: next: null, michael@0: curr: null michael@0: }; michael@0: michael@0: this.option = {}; michael@0: this.ignored = {}; michael@0: this.directive = {}; michael@0: this.jsonMode = false; michael@0: this.jsonWarnings = []; michael@0: this.lines = []; michael@0: this.tab = ""; michael@0: this.cache = {}; // Node.JS doesn't have Map. Sniff. michael@0: } michael@0: }; michael@0: michael@0: exports.state = state; michael@0: michael@0: },{}],5:[function(require,module,exports){ michael@0: (function(){"use strict"; michael@0: michael@0: exports.register = function (linter) { michael@0: // Check for properties named __proto__. This special property was michael@0: // deprecated and then re-introduced for ES6. michael@0: michael@0: linter.on("Identifier", function style_scanProto(data) { michael@0: if (linter.getOption("proto")) { michael@0: return; michael@0: } michael@0: michael@0: if (data.name === "__proto__") { michael@0: linter.warn("W103", { michael@0: line: data.line, michael@0: char: data.char, michael@0: data: [ data.name ] michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: // Check for properties named __iterator__. This is a special property michael@0: // available only in browsers with JavaScript 1.7 implementation. michael@0: michael@0: linter.on("Identifier", function style_scanIterator(data) { michael@0: if (linter.getOption("iterator")) { michael@0: return; michael@0: } michael@0: michael@0: if (data.name === "__iterator__") { michael@0: linter.warn("W104", { michael@0: line: data.line, michael@0: char: data.char, michael@0: data: [ data.name ] michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: // Check for dangling underscores. michael@0: michael@0: linter.on("Identifier", function style_scanDangling(data) { michael@0: if (!linter.getOption("nomen")) { michael@0: return; michael@0: } michael@0: michael@0: // Underscore.js michael@0: if (data.name === "_") { michael@0: return; michael@0: } michael@0: michael@0: // In Node, __dirname and __filename should be ignored. michael@0: if (linter.getOption("node")) { michael@0: if (/^(__dirname|__filename)$/.test(data.name) && !data.isProperty) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (/^(_+.*|.*_+)$/.test(data.name)) { michael@0: linter.warn("W105", { michael@0: line: data.line, michael@0: char: data.from, michael@0: data: [ "dangling '_'", data.name ] michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: // Check that all identifiers are using camelCase notation. michael@0: // Exceptions: names like MY_VAR and _myVar. michael@0: michael@0: linter.on("Identifier", function style_scanCamelCase(data) { michael@0: if (!linter.getOption("camelcase")) { michael@0: return; michael@0: } michael@0: michael@0: if (data.name.replace(/^_+/, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { michael@0: linter.warn("W106", { michael@0: line: data.line, michael@0: char: data.from, michael@0: data: [ data.name ] michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: // Enforce consistency in style of quoting. michael@0: michael@0: linter.on("String", function style_scanQuotes(data) { michael@0: var quotmark = linter.getOption("quotmark"); michael@0: var code; michael@0: michael@0: if (!quotmark) { michael@0: return; michael@0: } michael@0: michael@0: // If quotmark is set to 'single' warn about all double-quotes. michael@0: michael@0: if (quotmark === "single" && data.quote !== "'") { michael@0: code = "W109"; michael@0: } michael@0: michael@0: // If quotmark is set to 'double' warn about all single-quotes. michael@0: michael@0: if (quotmark === "double" && data.quote !== "\"") { michael@0: code = "W108"; michael@0: } michael@0: michael@0: // If quotmark is set to true, remember the first quotation style michael@0: // and then warn about all others. michael@0: michael@0: if (quotmark === true) { michael@0: if (!linter.getCache("quotmark")) { michael@0: linter.setCache("quotmark", data.quote); michael@0: } michael@0: michael@0: if (linter.getCache("quotmark") !== data.quote) { michael@0: code = "W110"; michael@0: } michael@0: } michael@0: michael@0: if (code) { michael@0: linter.warn(code, { michael@0: line: data.line, michael@0: char: data.char, michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: linter.on("Number", function style_scanNumbers(data) { michael@0: if (data.value.charAt(0) === ".") { michael@0: // Warn about a leading decimal point. michael@0: linter.warn("W008", { michael@0: line: data.line, michael@0: char: data.char, michael@0: data: [ data.value ] michael@0: }); michael@0: } michael@0: michael@0: if (data.value.substr(data.value.length - 1) === ".") { michael@0: // Warn about a trailing decimal point. michael@0: linter.warn("W047", { michael@0: line: data.line, michael@0: char: data.char, michael@0: data: [ data.value ] michael@0: }); michael@0: } michael@0: michael@0: if (/^00+/.test(data.value)) { michael@0: // Multiple leading zeroes. michael@0: linter.warn("W046", { michael@0: line: data.line, michael@0: char: data.char, michael@0: data: [ data.value ] michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: // Warn about script URLs. michael@0: michael@0: linter.on("String", function style_scanJavaScriptURLs(data) { michael@0: var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; michael@0: michael@0: if (linter.getOption("scripturl")) { michael@0: return; michael@0: } michael@0: michael@0: if (re.test(data.value)) { michael@0: linter.warn("W107", { michael@0: line: data.line, michael@0: char: data.char michael@0: }); michael@0: } michael@0: }); michael@0: }; michael@0: })() michael@0: },{}],6:[function(require,module,exports){ michael@0: /* michael@0: * Regular expressions. Some of these are stupidly long. michael@0: */ michael@0: michael@0: /*jshint maxlen:1000 */ michael@0: michael@0: "use string"; michael@0: michael@0: // Unsafe comment or string (ax) michael@0: exports.unsafeString = michael@0: /@cc|<\/?|script|\]\s*\]|<\s*!|</i; michael@0: michael@0: // Unsafe characters that are silently deleted by one or more browsers (cx) michael@0: exports.unsafeChars = michael@0: /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; michael@0: michael@0: // Characters in strings that need escaping (nx and nxg) michael@0: exports.needEsc = michael@0: /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; michael@0: michael@0: exports.needEscGlobal = michael@0: /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; michael@0: michael@0: // Star slash (lx) michael@0: exports.starSlash = /\*\//; michael@0: michael@0: // Identifier (ix) michael@0: exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; michael@0: michael@0: // JavaScript URL (jx) michael@0: exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; michael@0: michael@0: // Catches /* falls through */ comments (ft) michael@0: //exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; michael@0: exports.fallsThrough = /^\s*\/\/\s*Falls?\sthrough.*\s*$/; michael@0: michael@0: },{}],7:[function(require,module,exports){ michael@0: (function(global){/*global window, global*/ michael@0: var util = require("util") michael@0: var assert = require("assert") michael@0: michael@0: var slice = Array.prototype.slice michael@0: var console michael@0: var times = {} michael@0: michael@0: if (typeof global !== "undefined" && global.console) { michael@0: console = global.console michael@0: } else if (typeof window !== "undefined" && window.console) { michael@0: console = window.console michael@0: } else { michael@0: console = window.console = {} michael@0: } michael@0: michael@0: var functions = [ michael@0: [log, "log"] michael@0: , [info, "info"] michael@0: , [warn, "warn"] michael@0: , [error, "error"] michael@0: , [time, "time"] michael@0: , [timeEnd, "timeEnd"] michael@0: , [trace, "trace"] michael@0: , [dir, "dir"] michael@0: , [assert, "assert"] michael@0: ] michael@0: michael@0: for (var i = 0; i < functions.length; i++) { michael@0: var tuple = functions[i] michael@0: var f = tuple[0] michael@0: var name = tuple[1] michael@0: michael@0: if (!console[name]) { michael@0: console[name] = f michael@0: } michael@0: } michael@0: michael@0: module.exports = console michael@0: michael@0: function log() {} michael@0: michael@0: function info() { michael@0: console.log.apply(console, arguments) michael@0: } michael@0: michael@0: function warn() { michael@0: console.log.apply(console, arguments) michael@0: } michael@0: michael@0: function error() { michael@0: console.warn.apply(console, arguments) michael@0: } michael@0: michael@0: function time(label) { michael@0: times[label] = Date.now() michael@0: } michael@0: michael@0: function timeEnd(label) { michael@0: var time = times[label] michael@0: if (!time) { michael@0: throw new Error("No such label: " + label) michael@0: } michael@0: michael@0: var duration = Date.now() - time michael@0: console.log(label + ": " + duration + "ms") michael@0: } michael@0: michael@0: function trace() { michael@0: var err = new Error() michael@0: err.name = "Trace" michael@0: err.message = util.format.apply(null, arguments) michael@0: console.error(err.stack) michael@0: } michael@0: michael@0: function dir(object) { michael@0: console.log(util.inspect(object) + "\n") michael@0: } michael@0: michael@0: function assert(expression) { michael@0: if (!expression) { michael@0: var arr = slice.call(arguments, 1) michael@0: assert.ok(false, util.format.apply(null, arr)) michael@0: } michael@0: } michael@0: michael@0: })(window) michael@0: },{"util":8,"assert":9}],10:[function(require,module,exports){ michael@0: (function(){/* michael@0: * Lexical analysis and token construction. michael@0: */ michael@0: michael@0: "use strict"; michael@0: michael@0: var _ = require("underscore"); michael@0: var events = require("events"); michael@0: var reg = require("./reg.js"); michael@0: var state = require("./state.js").state; michael@0: michael@0: // Some of these token types are from JavaScript Parser API michael@0: // while others are specific to JSHint parser. michael@0: // JS Parser API: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API michael@0: michael@0: var Token = { michael@0: Identifier: 1, michael@0: Punctuator: 2, michael@0: NumericLiteral: 3, michael@0: StringLiteral: 4, michael@0: Comment: 5, michael@0: Keyword: 6, michael@0: NullLiteral: 7, michael@0: BooleanLiteral: 8, michael@0: RegExp: 9 michael@0: }; michael@0: michael@0: // This is auto generated from the unicode tables. michael@0: // The tables are at: michael@0: // http://www.fileformat.info/info/unicode/category/Lu/list.htm michael@0: // http://www.fileformat.info/info/unicode/category/Ll/list.htm michael@0: // http://www.fileformat.info/info/unicode/category/Lt/list.htm michael@0: // http://www.fileformat.info/info/unicode/category/Lm/list.htm michael@0: // http://www.fileformat.info/info/unicode/category/Lo/list.htm michael@0: // http://www.fileformat.info/info/unicode/category/Nl/list.htm michael@0: michael@0: var unicodeLetterTable = [ michael@0: 170, 170, 181, 181, 186, 186, 192, 214, michael@0: 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, michael@0: 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, michael@0: 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, michael@0: 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, michael@0: 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, michael@0: 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, michael@0: 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, michael@0: 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361, michael@0: 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, michael@0: 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, michael@0: 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, michael@0: 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, michael@0: 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, michael@0: 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, michael@0: 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, michael@0: 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, michael@0: 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, michael@0: 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, michael@0: 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, michael@0: 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, michael@0: 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, michael@0: 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, michael@0: 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, michael@0: 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, michael@0: 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, michael@0: 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, michael@0: 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, michael@0: 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, michael@0: 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, michael@0: 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, michael@0: 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, michael@0: 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346, michael@0: 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696, michael@0: 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, michael@0: 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, michael@0: 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, michael@0: 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, michael@0: 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, michael@0: 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, michael@0: 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, michael@0: 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, michael@0: 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141, michael@0: 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, michael@0: 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, michael@0: 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, michael@0: 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, michael@0: 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, michael@0: 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, michael@0: 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, michael@0: 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, michael@0: 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, michael@0: 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621, michael@0: 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, michael@0: 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, michael@0: 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, michael@0: 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, michael@0: 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, michael@0: 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312, michael@0: 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124, michael@0: 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, michael@0: 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, michael@0: 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921, michael@0: 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042, michael@0: 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, michael@0: 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, michael@0: 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, michael@0: 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, michael@0: 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, michael@0: 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798, michael@0: 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032, michael@0: 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045, michael@0: 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279, michael@0: 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, michael@0: 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, michael@0: 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, michael@0: 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, michael@0: 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, michael@0: 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, michael@0: 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, michael@0: 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334, michael@0: 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511, michael@0: 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592, michael@0: 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, michael@0: 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115, michael@0: 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405, michael@0: 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687, michael@0: 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894, michael@0: 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964, michael@0: 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, michael@0: 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, michael@0: 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, michael@0: 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, michael@0: 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, michael@0: 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, michael@0: 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, michael@0: 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972, michael@0: 177984, 177984, 178205, 178205, 194560, 195101 michael@0: ]; michael@0: michael@0: var identifierStartTable = []; michael@0: michael@0: for (var i = 0; i < 128; i++) { michael@0: identifierStartTable[i] = michael@0: i === 36 || // $ michael@0: i >= 65 && i <= 90 || // A-Z michael@0: i === 95 || // _ michael@0: i >= 97 && i <= 122; // a-z michael@0: } michael@0: michael@0: var identifierPartTable = []; michael@0: michael@0: for (var i = 0; i < 128; i++) { michael@0: identifierPartTable[i] = michael@0: identifierStartTable[i] || // $, _, A-Z, a-z michael@0: i >= 48 && i <= 57; // 0-9 michael@0: } michael@0: michael@0: // Object that handles postponed lexing verifications that checks the parsed michael@0: // environment state. michael@0: michael@0: function asyncTrigger() { michael@0: var _checks = []; michael@0: michael@0: return { michael@0: push: function (fn) { michael@0: _checks.push(fn); michael@0: }, michael@0: michael@0: check: function () { michael@0: for (var check in _checks) { michael@0: _checks[check](); michael@0: } michael@0: michael@0: _checks.splice(0, _checks.length); michael@0: } michael@0: }; michael@0: } michael@0: michael@0: /* michael@0: * Lexer for JSHint. michael@0: * michael@0: * This object does a char-by-char scan of the provided source code michael@0: * and produces a sequence of tokens. michael@0: * michael@0: * var lex = new Lexer("var i = 0;"); michael@0: * lex.start(); michael@0: * lex.token(); // returns the next token michael@0: * michael@0: * You have to use the token() method to move the lexer forward michael@0: * but you don't have to use its return value to get tokens. In addition michael@0: * to token() method returning the next token, the Lexer object also michael@0: * emits events. michael@0: * michael@0: * lex.on("Identifier", function (data) { michael@0: * if (data.name.indexOf("_") >= 0) { michael@0: * // Produce a warning. michael@0: * } michael@0: * }); michael@0: * michael@0: * Note that the token() method returns tokens in a JSLint-compatible michael@0: * format while the event emitter uses a slightly modified version of michael@0: * Mozilla's JavaScript Parser API. Eventually, we will move away from michael@0: * JSLint format. michael@0: */ michael@0: function Lexer(source) { michael@0: var lines = source; michael@0: michael@0: if (typeof lines === "string") { michael@0: lines = lines michael@0: .replace(/\r\n/g, "\n") michael@0: .replace(/\r/g, "\n") michael@0: .split("\n"); michael@0: } michael@0: michael@0: // If the first line is a shebang (#!), make it a blank and move on. michael@0: // Shebangs are used by Node scripts. michael@0: michael@0: if (lines[0] && lines[0].substr(0, 2) === "#!") { michael@0: lines[0] = ""; michael@0: } michael@0: michael@0: this.emitter = new events.EventEmitter(); michael@0: this.source = source; michael@0: this.lines = lines; michael@0: this.prereg = true; michael@0: michael@0: this.line = 0; michael@0: this.char = 1; michael@0: this.from = 1; michael@0: this.input = ""; michael@0: michael@0: for (var i = 0; i < state.option.indent; i += 1) { michael@0: state.tab += " "; michael@0: } michael@0: } michael@0: michael@0: Lexer.prototype = { michael@0: _lines: [], michael@0: michael@0: get lines() { michael@0: this._lines = state.lines; michael@0: return this._lines; michael@0: }, michael@0: michael@0: set lines(val) { michael@0: this._lines = val; michael@0: state.lines = this._lines; michael@0: }, michael@0: michael@0: /* michael@0: * Return the next i character without actually moving the michael@0: * char pointer. michael@0: */ michael@0: peek: function (i) { michael@0: return this.input.charAt(i || 0); michael@0: }, michael@0: michael@0: /* michael@0: * Move the char pointer forward i times. michael@0: */ michael@0: skip: function (i) { michael@0: i = i || 1; michael@0: this.char += i; michael@0: this.input = this.input.slice(i); michael@0: }, michael@0: michael@0: /* michael@0: * Subscribe to a token event. The API for this method is similar michael@0: * Underscore.js i.e. you can subscribe to multiple events with michael@0: * one call: michael@0: * michael@0: * lex.on("Identifier Number", function (data) { michael@0: * // ... michael@0: * }); michael@0: */ michael@0: on: function (names, listener) { michael@0: names.split(" ").forEach(function (name) { michael@0: this.emitter.on(name, listener); michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: /* michael@0: * Trigger a token event. All arguments will be passed to each michael@0: * listener. michael@0: */ michael@0: trigger: function () { michael@0: this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments)); michael@0: }, michael@0: michael@0: /* michael@0: * Postpone a token event. the checking condition is set as michael@0: * last parameter, and the trigger function is called in a michael@0: * stored callback. To be later called using the check() function michael@0: * by the parser. This avoids parser's peek() to give the lexer michael@0: * a false context. michael@0: */ michael@0: triggerAsync: function (type, args, checks, fn) { michael@0: checks.push(function () { michael@0: if (fn()) { michael@0: this.trigger(type, args); michael@0: } michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: /* michael@0: * Extract a punctuator out of the next sequence of characters michael@0: * or return 'null' if its not possible. michael@0: * michael@0: * This method's implementation was heavily influenced by the michael@0: * scanPunctuator function in the Esprima parser's source code. michael@0: */ michael@0: scanPunctuator: function () { michael@0: var ch1 = this.peek(); michael@0: var ch2, ch3, ch4; michael@0: michael@0: switch (ch1) { michael@0: // Most common single-character punctuators michael@0: case ".": michael@0: if ((/^[0-9]$/).test(this.peek(1))) { michael@0: return null; michael@0: } michael@0: if (this.peek(1) === "." && this.peek(2) === ".") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: "..." michael@0: }; michael@0: } michael@0: /* falls through */ michael@0: case "(": michael@0: case ")": michael@0: case ";": michael@0: case ",": michael@0: case "{": michael@0: case "}": michael@0: case "[": michael@0: case "]": michael@0: case ":": michael@0: case "~": michael@0: case "?": michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ch1 michael@0: }; michael@0: michael@0: // A pound sign (for Node shebangs) michael@0: case "#": michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ch1 michael@0: }; michael@0: michael@0: // We're at the end of input michael@0: case "": michael@0: return null; michael@0: } michael@0: michael@0: // Peek more characters michael@0: michael@0: ch2 = this.peek(1); michael@0: ch3 = this.peek(2); michael@0: ch4 = this.peek(3); michael@0: michael@0: // 4-character punctuator: >>>= michael@0: michael@0: if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ">>>=" michael@0: }; michael@0: } michael@0: michael@0: // 3-character punctuators: === !== >>> <<= >>= michael@0: michael@0: if (ch1 === "=" && ch2 === "=" && ch3 === "=") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: "===" michael@0: }; michael@0: } michael@0: michael@0: if (ch1 === "!" && ch2 === "=" && ch3 === "=") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: "!==" michael@0: }; michael@0: } michael@0: michael@0: if (ch1 === ">" && ch2 === ">" && ch3 === ">") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ">>>" michael@0: }; michael@0: } michael@0: michael@0: if (ch1 === "<" && ch2 === "<" && ch3 === "=") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: "<<=" michael@0: }; michael@0: } michael@0: michael@0: if (ch1 === ">" && ch2 === ">" && ch3 === "=") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ">>=" michael@0: }; michael@0: } michael@0: michael@0: // Fat arrow punctuator michael@0: if (ch1 === "=" && ch2 === ">") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ch1 + ch2 michael@0: }; michael@0: } michael@0: michael@0: // 2-character punctuators: <= >= == != ++ -- << >> && || michael@0: // += -= *= %= &= |= ^= (but not /=, see below) michael@0: if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ch1 + ch2 michael@0: }; michael@0: } michael@0: michael@0: if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { michael@0: if (ch2 === "=") { michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ch1 + ch2 michael@0: }; michael@0: } michael@0: michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: ch1 michael@0: }; michael@0: } michael@0: michael@0: // Special case: /=. We need to make sure that this is an michael@0: // operator and not a regular expression. michael@0: michael@0: if (ch1 === "/") { michael@0: if (ch2 === "=" && /\/=(?!(\S*\/[gim]?))/.test(this.input)) { michael@0: // /= is not a part of a regular expression, return it as a michael@0: // punctuator. michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: "/=" michael@0: }; michael@0: } michael@0: michael@0: return { michael@0: type: Token.Punctuator, michael@0: value: "/" michael@0: }; michael@0: } michael@0: michael@0: return null; michael@0: }, michael@0: michael@0: /* michael@0: * Extract a comment out of the next sequence of characters and/or michael@0: * lines or return 'null' if its not possible. Since comments can michael@0: * span across multiple lines this method has to move the char michael@0: * pointer. michael@0: * michael@0: * In addition to normal JavaScript comments (// and /*) this method michael@0: * also recognizes JSHint- and JSLint-specific comments such as michael@0: * /*jshint, /*jslint, /*globals and so on. michael@0: */ michael@0: scanComments: function () { michael@0: var ch1 = this.peek(); michael@0: var ch2 = this.peek(1); michael@0: var rest = this.input.substr(2); michael@0: var startLine = this.line; michael@0: var startChar = this.char; michael@0: michael@0: // Create a comment token object and make sure it michael@0: // has all the data JSHint needs to work with special michael@0: // comments. michael@0: michael@0: function commentToken(label, body, opt) { michael@0: var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"]; michael@0: var isSpecial = false; michael@0: var value = label + body; michael@0: var commentType = "plain"; michael@0: opt = opt || {}; michael@0: michael@0: if (opt.isMultiline) { michael@0: value += "*/"; michael@0: } michael@0: michael@0: special.forEach(function (str) { michael@0: if (isSpecial) { michael@0: return; michael@0: } michael@0: michael@0: // Don't recognize any special comments other than jshint for single-line michael@0: // comments. This introduced many problems with legit comments. michael@0: if (label === "//" && str !== "jshint") { michael@0: return; michael@0: } michael@0: michael@0: if (body.substr(0, str.length) === str) { michael@0: isSpecial = true; michael@0: label = label + str; michael@0: body = body.substr(str.length); michael@0: } michael@0: michael@0: if (!isSpecial && body.charAt(0) === " " && body.substr(1, str.length) === str) { michael@0: isSpecial = true; michael@0: label = label + " " + str; michael@0: body = body.substr(str.length + 1); michael@0: } michael@0: michael@0: if (!isSpecial) { michael@0: return; michael@0: } michael@0: michael@0: switch (str) { michael@0: case "member": michael@0: commentType = "members"; michael@0: break; michael@0: case "global": michael@0: commentType = "globals"; michael@0: break; michael@0: default: michael@0: commentType = str; michael@0: } michael@0: }); michael@0: michael@0: return { michael@0: type: Token.Comment, michael@0: commentType: commentType, michael@0: value: value, michael@0: body: body, michael@0: isSpecial: isSpecial, michael@0: isMultiline: opt.isMultiline || false, michael@0: isMalformed: opt.isMalformed || false michael@0: }; michael@0: } michael@0: michael@0: // End of unbegun comment. Raise an error and skip that input. michael@0: if (ch1 === "*" && ch2 === "/") { michael@0: this.trigger("error", { michael@0: code: "E018", michael@0: line: startLine, michael@0: character: startChar michael@0: }); michael@0: michael@0: this.skip(2); michael@0: return null; michael@0: } michael@0: michael@0: // Comments must start either with // or /* michael@0: if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) { michael@0: return null; michael@0: } michael@0: michael@0: // One-line comment michael@0: if (ch2 === "/") { michael@0: this.skip(this.input.length); // Skip to the EOL. michael@0: return commentToken("//", rest); michael@0: } michael@0: michael@0: var body = ""; michael@0: michael@0: /* Multi-line comment */ michael@0: if (ch2 === "*") { michael@0: this.skip(2); michael@0: michael@0: while (this.peek() !== "*" || this.peek(1) !== "/") { michael@0: if (this.peek() === "") { // End of Line michael@0: body += "\n"; michael@0: michael@0: // If we hit EOF and our comment is still unclosed, michael@0: // trigger an error and end the comment implicitly. michael@0: if (!this.nextLine()) { michael@0: this.trigger("error", { michael@0: code: "E017", michael@0: line: startLine, michael@0: character: startChar michael@0: }); michael@0: michael@0: return commentToken("/*", body, { michael@0: isMultiline: true, michael@0: isMalformed: true michael@0: }); michael@0: } michael@0: } else { michael@0: body += this.peek(); michael@0: this.skip(); michael@0: } michael@0: } michael@0: michael@0: this.skip(2); michael@0: return commentToken("/*", body, { isMultiline: true }); michael@0: } michael@0: }, michael@0: michael@0: /* michael@0: * Extract a keyword out of the next sequence of characters or michael@0: * return 'null' if its not possible. michael@0: */ michael@0: scanKeyword: function () { michael@0: var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input); michael@0: var keywords = [ michael@0: "if", "in", "do", "var", "for", "new", michael@0: "try", "let", "this", "else", "case", michael@0: "void", "with", "enum", "while", "break", michael@0: "catch", "throw", "const", "yield", "class", michael@0: "super", "return", "typeof", "delete", michael@0: "switch", "export", "import", "default", michael@0: "finally", "extends", "function", "continue", michael@0: "debugger", "instanceof" michael@0: ]; michael@0: michael@0: if (result && keywords.indexOf(result[0]) >= 0) { michael@0: return { michael@0: type: Token.Keyword, michael@0: value: result[0] michael@0: }; michael@0: } michael@0: michael@0: return null; michael@0: }, michael@0: michael@0: /* michael@0: * Extract a JavaScript identifier out of the next sequence of michael@0: * characters or return 'null' if its not possible. In addition, michael@0: * to Identifier this method can also produce BooleanLiteral michael@0: * (true/false) and NullLiteral (null). michael@0: */ michael@0: scanIdentifier: function () { michael@0: var id = ""; michael@0: var index = 0; michael@0: var type, char; michael@0: michael@0: // Detects any character in the Unicode categories "Uppercase michael@0: // letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter michael@0: // (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or michael@0: // "Letter number (Nl)". michael@0: // michael@0: // Both approach and unicodeLetterTable were borrowed from michael@0: // Google's Traceur. michael@0: michael@0: function isUnicodeLetter(code) { michael@0: for (var i = 0; i < unicodeLetterTable.length;) { michael@0: if (code < unicodeLetterTable[i++]) { michael@0: return false; michael@0: } michael@0: michael@0: if (code <= unicodeLetterTable[i++]) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: function isHexDigit(str) { michael@0: return (/^[0-9a-fA-F]$/).test(str); michael@0: } michael@0: michael@0: var readUnicodeEscapeSequence = function () { michael@0: /*jshint validthis:true */ michael@0: index += 1; michael@0: michael@0: if (this.peek(index) !== "u") { michael@0: return null; michael@0: } michael@0: michael@0: var ch1 = this.peek(index + 1); michael@0: var ch2 = this.peek(index + 2); michael@0: var ch3 = this.peek(index + 3); michael@0: var ch4 = this.peek(index + 4); michael@0: var code; michael@0: michael@0: if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) { michael@0: code = parseInt(ch1 + ch2 + ch3 + ch4, 16); michael@0: michael@0: if (isUnicodeLetter(code)) { michael@0: index += 5; michael@0: return "\\u" + ch1 + ch2 + ch3 + ch4; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: michael@0: return null; michael@0: }.bind(this); michael@0: michael@0: var getIdentifierStart = function () { michael@0: /*jshint validthis:true */ michael@0: var chr = this.peek(index); michael@0: var code = chr.charCodeAt(0); michael@0: michael@0: if (code === 92) { michael@0: return readUnicodeEscapeSequence(); michael@0: } michael@0: michael@0: if (code < 128) { michael@0: if (identifierStartTable[code]) { michael@0: index += 1; michael@0: return chr; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: michael@0: if (isUnicodeLetter(code)) { michael@0: index += 1; michael@0: return chr; michael@0: } michael@0: michael@0: return null; michael@0: }.bind(this); michael@0: michael@0: var getIdentifierPart = function () { michael@0: /*jshint validthis:true */ michael@0: var chr = this.peek(index); michael@0: var code = chr.charCodeAt(0); michael@0: michael@0: if (code === 92) { michael@0: return readUnicodeEscapeSequence(); michael@0: } michael@0: michael@0: if (code < 128) { michael@0: if (identifierPartTable[code]) { michael@0: index += 1; michael@0: return chr; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: michael@0: if (isUnicodeLetter(code)) { michael@0: index += 1; michael@0: return chr; michael@0: } michael@0: michael@0: return null; michael@0: }.bind(this); michael@0: michael@0: char = getIdentifierStart(); michael@0: if (char === null) { michael@0: return null; michael@0: } michael@0: michael@0: id = char; michael@0: for (;;) { michael@0: char = getIdentifierPart(); michael@0: michael@0: if (char === null) { michael@0: break; michael@0: } michael@0: michael@0: id += char; michael@0: } michael@0: michael@0: switch (id) { michael@0: case "true": michael@0: case "false": michael@0: type = Token.BooleanLiteral; michael@0: break; michael@0: case "null": michael@0: type = Token.NullLiteral; michael@0: break; michael@0: default: michael@0: type = Token.Identifier; michael@0: } michael@0: michael@0: return { michael@0: type: type, michael@0: value: id michael@0: }; michael@0: }, michael@0: michael@0: /* michael@0: * Extract a numeric literal out of the next sequence of michael@0: * characters or return 'null' if its not possible. This method michael@0: * supports all numeric literals described in section 7.8.3 michael@0: * of the EcmaScript 5 specification. michael@0: * michael@0: * This method's implementation was heavily influenced by the michael@0: * scanNumericLiteral function in the Esprima parser's source code. michael@0: */ michael@0: scanNumericLiteral: function () { michael@0: var index = 0; michael@0: var value = ""; michael@0: var length = this.input.length; michael@0: var char = this.peek(index); michael@0: var bad; michael@0: michael@0: function isDecimalDigit(str) { michael@0: return (/^[0-9]$/).test(str); michael@0: } michael@0: michael@0: function isOctalDigit(str) { michael@0: return (/^[0-7]$/).test(str); michael@0: } michael@0: michael@0: function isHexDigit(str) { michael@0: return (/^[0-9a-fA-F]$/).test(str); michael@0: } michael@0: michael@0: function isIdentifierStart(ch) { michael@0: return (ch === "$") || (ch === "_") || (ch === "\\") || michael@0: (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z"); michael@0: } michael@0: michael@0: // Numbers must start either with a decimal digit or a point. michael@0: michael@0: if (char !== "." && !isDecimalDigit(char)) { michael@0: return null; michael@0: } michael@0: michael@0: if (char !== ".") { michael@0: value = this.peek(index); michael@0: index += 1; michael@0: char = this.peek(index); michael@0: michael@0: if (value === "0") { michael@0: // Base-16 numbers. michael@0: if (char === "x" || char === "X") { michael@0: index += 1; michael@0: value += char; michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: if (!isHexDigit(char)) { michael@0: break; michael@0: } michael@0: value += char; michael@0: index += 1; michael@0: } michael@0: michael@0: if (value.length <= 2) { // 0x michael@0: return { michael@0: type: Token.NumericLiteral, michael@0: value: value, michael@0: isMalformed: true michael@0: }; michael@0: } michael@0: michael@0: if (index < length) { michael@0: char = this.peek(index); michael@0: if (isIdentifierStart(char)) { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: return { michael@0: type: Token.NumericLiteral, michael@0: value: value, michael@0: base: 16, michael@0: isMalformed: false michael@0: }; michael@0: } michael@0: michael@0: // Base-8 numbers. michael@0: if (isOctalDigit(char)) { michael@0: index += 1; michael@0: value += char; michael@0: bad = false; michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: michael@0: // Numbers like '019' (note the 9) are not valid octals michael@0: // but we still parse them and mark as malformed. michael@0: michael@0: if (isDecimalDigit(char)) { michael@0: bad = true; michael@0: } else if (!isOctalDigit(char)) { michael@0: break; michael@0: } michael@0: value += char; michael@0: index += 1; michael@0: } michael@0: michael@0: if (index < length) { michael@0: char = this.peek(index); michael@0: if (isIdentifierStart(char)) { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: return { michael@0: type: Token.NumericLiteral, michael@0: value: value, michael@0: base: 8, michael@0: isMalformed: false michael@0: }; michael@0: } michael@0: michael@0: // Decimal numbers that start with '0' such as '09' are illegal michael@0: // but we still parse them and return as malformed. michael@0: michael@0: if (isDecimalDigit(char)) { michael@0: index += 1; michael@0: value += char; michael@0: } michael@0: } michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: if (!isDecimalDigit(char)) { michael@0: break; michael@0: } michael@0: value += char; michael@0: index += 1; michael@0: } michael@0: } michael@0: michael@0: // Decimal digits. michael@0: michael@0: if (char === ".") { michael@0: value += char; michael@0: index += 1; michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: if (!isDecimalDigit(char)) { michael@0: break; michael@0: } michael@0: value += char; michael@0: index += 1; michael@0: } michael@0: } michael@0: michael@0: // Exponent part. michael@0: michael@0: if (char === "e" || char === "E") { michael@0: value += char; michael@0: index += 1; michael@0: char = this.peek(index); michael@0: michael@0: if (char === "+" || char === "-") { michael@0: value += this.peek(index); michael@0: index += 1; michael@0: } michael@0: michael@0: char = this.peek(index); michael@0: if (isDecimalDigit(char)) { michael@0: value += char; michael@0: index += 1; michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: if (!isDecimalDigit(char)) { michael@0: break; michael@0: } michael@0: value += char; michael@0: index += 1; michael@0: } michael@0: } else { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: if (index < length) { michael@0: char = this.peek(index); michael@0: if (isIdentifierStart(char)) { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: return { michael@0: type: Token.NumericLiteral, michael@0: value: value, michael@0: base: 10, michael@0: isMalformed: !isFinite(value) michael@0: }; michael@0: }, michael@0: michael@0: /* michael@0: * Extract a string out of the next sequence of characters and/or michael@0: * lines or return 'null' if its not possible. Since strings can michael@0: * span across multiple lines this method has to move the char michael@0: * pointer. michael@0: * michael@0: * This method recognizes pseudo-multiline JavaScript strings: michael@0: * michael@0: * var str = "hello\ michael@0: * world"; michael@0: */ michael@0: scanStringLiteral: function (checks) { michael@0: /*jshint loopfunc:true */ michael@0: var quote = this.peek(); michael@0: michael@0: // String must start with a quote. michael@0: if (quote !== "\"" && quote !== "'") { michael@0: return null; michael@0: } michael@0: michael@0: // In JSON strings must always use double quotes. michael@0: this.triggerAsync("warning", { michael@0: code: "W108", michael@0: line: this.line, michael@0: character: this.char // +1? michael@0: }, checks, function () { return state.jsonMode && quote !== "\""; }); michael@0: michael@0: var value = ""; michael@0: var startLine = this.line; michael@0: var startChar = this.char; michael@0: var allowNewLine = false; michael@0: michael@0: this.skip(); michael@0: michael@0: while (this.peek() !== quote) { michael@0: while (this.peek() === "") { // End Of Line michael@0: michael@0: // If an EOL is not preceded by a backslash, show a warning michael@0: // and proceed like it was a legit multi-line string where michael@0: // author simply forgot to escape the newline symbol. michael@0: // michael@0: // Another approach is to implicitly close a string on EOL michael@0: // but it generates too many false positives. michael@0: michael@0: if (!allowNewLine) { michael@0: this.trigger("warning", { michael@0: code: "W112", michael@0: line: this.line, michael@0: character: this.char michael@0: }); michael@0: } else { michael@0: allowNewLine = false; michael@0: michael@0: // Otherwise show a warning if multistr option was not set. michael@0: // For JSON, show warning no matter what. michael@0: michael@0: this.triggerAsync("warning", { michael@0: code: "W043", michael@0: line: this.line, michael@0: character: this.char michael@0: }, checks, function () { return !state.option.multistr; }); michael@0: michael@0: this.triggerAsync("warning", { michael@0: code: "W042", michael@0: line: this.line, michael@0: character: this.char michael@0: }, checks, function () { return state.jsonMode && state.option.multistr; }); michael@0: } michael@0: michael@0: // If we get an EOF inside of an unclosed string, show an michael@0: // error and implicitly close it at the EOF point. michael@0: michael@0: if (!this.nextLine()) { michael@0: this.trigger("error", { michael@0: code: "E029", michael@0: line: startLine, michael@0: character: startChar michael@0: }); michael@0: michael@0: return { michael@0: type: Token.StringLiteral, michael@0: value: value, michael@0: isUnclosed: true, michael@0: quote: quote michael@0: }; michael@0: } michael@0: } michael@0: michael@0: allowNewLine = false; michael@0: var char = this.peek(); michael@0: var jump = 1; // A length of a jump, after we're done michael@0: // parsing this character. michael@0: michael@0: if (char < " ") { michael@0: // Warn about a control character in a string. michael@0: this.trigger("warning", { michael@0: code: "W113", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ "" ] michael@0: }); michael@0: } michael@0: michael@0: // Special treatment for some escaped characters. michael@0: michael@0: if (char === "\\") { michael@0: this.skip(); michael@0: char = this.peek(); michael@0: michael@0: switch (char) { michael@0: case "'": michael@0: this.triggerAsync("warning", { michael@0: code: "W114", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ "\\'" ] michael@0: }, checks, function () {return state.jsonMode; }); michael@0: break; michael@0: case "b": michael@0: char = "\b"; michael@0: break; michael@0: case "f": michael@0: char = "\f"; michael@0: break; michael@0: case "n": michael@0: char = "\n"; michael@0: break; michael@0: case "r": michael@0: char = "\r"; michael@0: break; michael@0: case "t": michael@0: char = "\t"; michael@0: break; michael@0: case "0": michael@0: char = "\0"; michael@0: michael@0: // Octal literals fail in strict mode. michael@0: // Check if the number is between 00 and 07. michael@0: var n = parseInt(this.peek(1), 10); michael@0: this.triggerAsync("warning", { michael@0: code: "W115", michael@0: line: this.line, michael@0: character: this.char michael@0: }, checks, michael@0: function () { return n >= 0 && n <= 7 && state.directive["use strict"]; }); michael@0: break; michael@0: case "u": michael@0: char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16)); michael@0: jump = 5; michael@0: break; michael@0: case "v": michael@0: this.triggerAsync("warning", { michael@0: code: "W114", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ "\\v" ] michael@0: }, checks, function () { return state.jsonMode; }); michael@0: michael@0: char = "\v"; michael@0: break; michael@0: case "x": michael@0: var x = parseInt(this.input.substr(1, 2), 16); michael@0: michael@0: this.triggerAsync("warning", { michael@0: code: "W114", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ "\\x-" ] michael@0: }, checks, function () { return state.jsonMode; }); michael@0: michael@0: char = String.fromCharCode(x); michael@0: jump = 3; michael@0: break; michael@0: case "\\": michael@0: case "\"": michael@0: case "/": michael@0: break; michael@0: case "": michael@0: allowNewLine = true; michael@0: char = ""; michael@0: break; michael@0: case "!": michael@0: if (value.slice(value.length - 2) === "<") { michael@0: break; michael@0: } michael@0: michael@0: /*falls through */ michael@0: default: michael@0: // Weird escaping. michael@0: this.trigger("warning", { michael@0: code: "W044", michael@0: line: this.line, michael@0: character: this.char michael@0: }); michael@0: } michael@0: } michael@0: michael@0: value += char; michael@0: this.skip(jump); michael@0: } michael@0: michael@0: this.skip(); michael@0: return { michael@0: type: Token.StringLiteral, michael@0: value: value, michael@0: isUnclosed: false, michael@0: quote: quote michael@0: }; michael@0: }, michael@0: michael@0: /* michael@0: * Extract a regular expression out of the next sequence of michael@0: * characters and/or lines or return 'null' if its not possible. michael@0: * michael@0: * This method is platform dependent: it accepts almost any michael@0: * regular expression values but then tries to compile and run michael@0: * them using system's RegExp object. This means that there are michael@0: * rare edge cases where one JavaScript engine complains about michael@0: * your regular expression while others don't. michael@0: */ michael@0: scanRegExp: function () { michael@0: var index = 0; michael@0: var length = this.input.length; michael@0: var char = this.peek(); michael@0: var value = char; michael@0: var body = ""; michael@0: var flags = []; michael@0: var malformed = false; michael@0: var isCharSet = false; michael@0: var terminated; michael@0: michael@0: var scanUnexpectedChars = function () { michael@0: // Unexpected control character michael@0: if (char < " ") { michael@0: malformed = true; michael@0: this.trigger("warning", { michael@0: code: "W048", michael@0: line: this.line, michael@0: character: this.char michael@0: }); michael@0: } michael@0: michael@0: // Unexpected escaped character michael@0: if (char === "<") { michael@0: malformed = true; michael@0: this.trigger("warning", { michael@0: code: "W049", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ char ] michael@0: }); michael@0: } michael@0: }.bind(this); michael@0: michael@0: // Regular expressions must start with '/' michael@0: if (!this.prereg || char !== "/") { michael@0: return null; michael@0: } michael@0: michael@0: index += 1; michael@0: terminated = false; michael@0: michael@0: // Try to get everything in between slashes. A couple of michael@0: // cases aside (see scanUnexpectedChars) we don't really michael@0: // care whether the resulting expression is valid or not. michael@0: // We will check that later using the RegExp object. michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: value += char; michael@0: body += char; michael@0: michael@0: if (isCharSet) { michael@0: if (char === "]") { michael@0: if (this.peek(index - 1) !== "\\" || this.peek(index - 2) === "\\") { michael@0: isCharSet = false; michael@0: } michael@0: } michael@0: michael@0: if (char === "\\") { michael@0: index += 1; michael@0: char = this.peek(index); michael@0: body += char; michael@0: value += char; michael@0: michael@0: scanUnexpectedChars(); michael@0: } michael@0: michael@0: index += 1; michael@0: continue; michael@0: } michael@0: michael@0: if (char === "\\") { michael@0: index += 1; michael@0: char = this.peek(index); michael@0: body += char; michael@0: value += char; michael@0: michael@0: scanUnexpectedChars(); michael@0: michael@0: if (char === "/") { michael@0: index += 1; michael@0: continue; michael@0: } michael@0: michael@0: if (char === "[") { michael@0: index += 1; michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: if (char === "[") { michael@0: isCharSet = true; michael@0: index += 1; michael@0: continue; michael@0: } michael@0: michael@0: if (char === "/") { michael@0: body = body.substr(0, body.length - 1); michael@0: terminated = true; michael@0: index += 1; michael@0: break; michael@0: } michael@0: michael@0: index += 1; michael@0: } michael@0: michael@0: // A regular expression that was never closed is an michael@0: // error from which we cannot recover. michael@0: michael@0: if (!terminated) { michael@0: this.trigger("error", { michael@0: code: "E015", michael@0: line: this.line, michael@0: character: this.from michael@0: }); michael@0: michael@0: return void this.trigger("fatal", { michael@0: line: this.line, michael@0: from: this.from michael@0: }); michael@0: } michael@0: michael@0: // Parse flags (if any). michael@0: michael@0: while (index < length) { michael@0: char = this.peek(index); michael@0: if (!/[gim]/.test(char)) { michael@0: break; michael@0: } michael@0: flags.push(char); michael@0: value += char; michael@0: index += 1; michael@0: } michael@0: michael@0: // Check regular expression for correctness. michael@0: michael@0: try { michael@0: new RegExp(body, flags.join("")); michael@0: } catch (err) { michael@0: malformed = true; michael@0: this.trigger("error", { michael@0: code: "E016", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ err.message ] // Platform dependent! michael@0: }); michael@0: } michael@0: michael@0: return { michael@0: type: Token.RegExp, michael@0: value: value, michael@0: flags: flags, michael@0: isMalformed: malformed michael@0: }; michael@0: }, michael@0: michael@0: /* michael@0: * Scan for any occurence of mixed tabs and spaces. If smarttabs option michael@0: * is on, ignore tabs followed by spaces. michael@0: * michael@0: * Tabs followed by one space followed by a block comment are allowed. michael@0: */ michael@0: scanMixedSpacesAndTabs: function () { michael@0: var at, match; michael@0: michael@0: if (state.option.smarttabs) { michael@0: // Negative look-behind for "//" michael@0: match = this.input.match(/(\/\/|^\s?\*)? \t/); michael@0: at = match && !match[1] ? 0 : -1; michael@0: } else { michael@0: at = this.input.search(/ \t|\t [^\*]/); michael@0: } michael@0: michael@0: return at; michael@0: }, michael@0: michael@0: /* michael@0: * Scan for characters that get silently deleted by one or more browsers. michael@0: */ michael@0: scanUnsafeChars: function () { michael@0: return this.input.search(reg.unsafeChars); michael@0: }, michael@0: michael@0: /* michael@0: * Produce the next raw token or return 'null' if no tokens can be matched. michael@0: * This method skips over all space characters. michael@0: */ michael@0: next: function (checks) { michael@0: this.from = this.char; michael@0: michael@0: // Move to the next non-space character. michael@0: var start; michael@0: if (/\s/.test(this.peek())) { michael@0: start = this.char; michael@0: michael@0: while (/\s/.test(this.peek())) { michael@0: this.from += 1; michael@0: this.skip(); michael@0: } michael@0: michael@0: if (this.peek() === "") { // EOL michael@0: if (!/^\s*$/.test(this.lines[this.line - 1]) && state.option.trailing) { michael@0: this.trigger("warning", { code: "W102", line: this.line, character: start }); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Methods that work with multi-line structures and move the michael@0: // character pointer. michael@0: michael@0: var match = this.scanComments() || michael@0: this.scanStringLiteral(checks); michael@0: michael@0: if (match) { michael@0: return match; michael@0: } michael@0: michael@0: // Methods that don't move the character pointer. michael@0: michael@0: match = michael@0: this.scanRegExp() || michael@0: this.scanPunctuator() || michael@0: this.scanKeyword() || michael@0: this.scanIdentifier() || michael@0: this.scanNumericLiteral(); michael@0: michael@0: if (match) { michael@0: this.skip(match.value.length); michael@0: return match; michael@0: } michael@0: michael@0: // No token could be matched, give up. michael@0: michael@0: return null; michael@0: }, michael@0: michael@0: /* michael@0: * Switch to the next line and reset all char pointers. Once michael@0: * switched, this method also checks for mixed spaces and tabs michael@0: * and other minor warnings. michael@0: */ michael@0: nextLine: function () { michael@0: var char; michael@0: michael@0: if (this.line >= this.lines.length) { michael@0: return false; michael@0: } michael@0: michael@0: this.input = this.lines[this.line]; michael@0: this.line += 1; michael@0: this.char = 1; michael@0: this.from = 1; michael@0: michael@0: char = this.scanMixedSpacesAndTabs(); michael@0: if (char >= 0) { michael@0: this.trigger("warning", { code: "W099", line: this.line, character: char + 1 }); michael@0: } michael@0: michael@0: this.input = this.input.replace(/\t/g, state.tab); michael@0: char = this.scanUnsafeChars(); michael@0: michael@0: if (char >= 0) { michael@0: this.trigger("warning", { code: "W100", line: this.line, character: char }); michael@0: } michael@0: michael@0: // If there is a limit on line length, warn when lines get too michael@0: // long. michael@0: michael@0: if (state.option.maxlen && state.option.maxlen < this.input.length) { michael@0: this.trigger("warning", { code: "W101", line: this.line, character: this.input.length }); michael@0: } michael@0: michael@0: return true; michael@0: }, michael@0: michael@0: /* michael@0: * This is simply a synonym for nextLine() method with a friendlier michael@0: * public name. michael@0: */ michael@0: start: function () { michael@0: this.nextLine(); michael@0: }, michael@0: michael@0: /* michael@0: * Produce the next token. This function is called by advance() to get michael@0: * the next token. It retuns a token in a JSLint-compatible format. michael@0: */ michael@0: token: function () { michael@0: /*jshint loopfunc:true */ michael@0: var checks = asyncTrigger(); michael@0: var token; michael@0: michael@0: michael@0: function isReserved(token, isProperty) { michael@0: if (!token.reserved) { michael@0: return false; michael@0: } michael@0: michael@0: if (token.meta && token.meta.isFutureReservedWord) { michael@0: // ES3 FutureReservedWord in an ES5 environment. michael@0: if (state.option.inES5(true) && !token.meta.es5) { michael@0: return false; michael@0: } michael@0: michael@0: // Some ES5 FutureReservedWord identifiers are active only michael@0: // within a strict mode environment. michael@0: if (token.meta.strictOnly) { michael@0: if (!state.option.strict && !state.directive["use strict"]) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (isProperty) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Produce a token object. michael@0: var create = function (type, value, isProperty) { michael@0: /*jshint validthis:true */ michael@0: var obj; michael@0: michael@0: if (type !== "(endline)" && type !== "(end)") { michael@0: this.prereg = false; michael@0: } michael@0: michael@0: if (type === "(punctuator)") { michael@0: switch (value) { michael@0: case ".": michael@0: case ")": michael@0: case "~": michael@0: case "#": michael@0: case "]": michael@0: this.prereg = false; michael@0: break; michael@0: default: michael@0: this.prereg = true; michael@0: } michael@0: michael@0: obj = Object.create(state.syntax[value] || state.syntax["(error)"]); michael@0: } michael@0: michael@0: if (type === "(identifier)") { michael@0: if (value === "return" || value === "case" || value === "typeof") { michael@0: this.prereg = true; michael@0: } michael@0: michael@0: if (_.has(state.syntax, value)) { michael@0: obj = Object.create(state.syntax[value] || state.syntax["(error)"]); michael@0: michael@0: // If this can't be a reserved keyword, reset the object. michael@0: if (!isReserved(obj, isProperty && type === "(identifier)")) { michael@0: obj = null; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!obj) { michael@0: obj = Object.create(state.syntax[type]); michael@0: } michael@0: michael@0: obj.identifier = (type === "(identifier)"); michael@0: obj.type = obj.type || type; michael@0: obj.value = value; michael@0: obj.line = this.line; michael@0: obj.character = this.char; michael@0: obj.from = this.from; michael@0: michael@0: if (isProperty && obj.identifier) { michael@0: obj.isProperty = isProperty; michael@0: } michael@0: michael@0: obj.check = checks.check; michael@0: michael@0: return obj; michael@0: }.bind(this); michael@0: michael@0: for (;;) { michael@0: if (!this.input.length) { michael@0: return create(this.nextLine() ? "(endline)" : "(end)", ""); michael@0: } michael@0: michael@0: token = this.next(checks); michael@0: michael@0: if (!token) { michael@0: if (this.input.length) { michael@0: // Unexpected character. michael@0: this.trigger("error", { michael@0: code: "E024", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ this.peek() ] michael@0: }); michael@0: michael@0: this.input = ""; michael@0: } michael@0: michael@0: continue; michael@0: } michael@0: michael@0: switch (token.type) { michael@0: case Token.StringLiteral: michael@0: this.triggerAsync("String", { michael@0: line: this.line, michael@0: char: this.char, michael@0: from: this.from, michael@0: value: token.value, michael@0: quote: token.quote michael@0: }, checks, function () { return true; }); michael@0: michael@0: return create("(string)", token.value); michael@0: case Token.Identifier: michael@0: this.trigger("Identifier", { michael@0: line: this.line, michael@0: char: this.char, michael@0: from: this.form, michael@0: name: token.value, michael@0: isProperty: state.tokens.curr.id === "." michael@0: }); michael@0: michael@0: /* falls through */ michael@0: case Token.Keyword: michael@0: case Token.NullLiteral: michael@0: case Token.BooleanLiteral: michael@0: return create("(identifier)", token.value, state.tokens.curr.id === "."); michael@0: michael@0: case Token.NumericLiteral: michael@0: if (token.isMalformed) { michael@0: this.trigger("warning", { michael@0: code: "W045", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ token.value ] michael@0: }); michael@0: } michael@0: michael@0: this.triggerAsync("warning", { michael@0: code: "W114", michael@0: line: this.line, michael@0: character: this.char, michael@0: data: [ "0x-" ] michael@0: }, checks, function () { return token.base === 16 && state.jsonMode; }); michael@0: michael@0: this.triggerAsync("warning", { michael@0: code: "W115", michael@0: line: this.line, michael@0: character: this.char michael@0: }, checks, function () { michael@0: return state.directive["use strict"] && token.base === 8; michael@0: }); michael@0: michael@0: this.trigger("Number", { michael@0: line: this.line, michael@0: char: this.char, michael@0: from: this.from, michael@0: value: token.value, michael@0: base: token.base, michael@0: isMalformed: token.malformed michael@0: }); michael@0: michael@0: return create("(number)", token.value); michael@0: michael@0: case Token.RegExp: michael@0: return create("(regexp)", token.value); michael@0: michael@0: case Token.Comment: michael@0: state.tokens.curr.comment = true; michael@0: michael@0: if (token.isSpecial) { michael@0: return { michael@0: value: token.value, michael@0: body: token.body, michael@0: type: token.commentType, michael@0: isSpecial: token.isSpecial, michael@0: line: this.line, michael@0: character: this.char, michael@0: from: this.from michael@0: }; michael@0: } michael@0: michael@0: break; michael@0: michael@0: case "": michael@0: break; michael@0: michael@0: default: michael@0: return create("(punctuator)", token.value); michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: exports.Lexer = Lexer; michael@0: michael@0: })() michael@0: },{"events":2,"./reg.js":6,"./state.js":4,"underscore":11}],"jshint":[function(require,module,exports){ michael@0: module.exports=require('E/GbHF'); michael@0: },{}],"E/GbHF":[function(require,module,exports){ michael@0: (function(){/*! michael@0: * JSHint, by JSHint Community. michael@0: * michael@0: * This file (and this file only) is licensed under the same slightly modified michael@0: * MIT license that JSLint is. It stops evil-doers everywhere: michael@0: * michael@0: * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) michael@0: * michael@0: * Permission is hereby granted, free of charge, to any person obtaining michael@0: * a copy of this software and associated documentation files (the "Software"), michael@0: * to deal in the Software without restriction, including without limitation michael@0: * the rights to use, copy, modify, merge, publish, distribute, sublicense, michael@0: * and/or sell copies of the Software, and to permit persons to whom michael@0: * the Software is furnished to do so, subject to the following conditions: michael@0: * michael@0: * The above copyright notice and this permission notice shall be included michael@0: * in all copies or substantial portions of the Software. michael@0: * michael@0: * The Software shall be used for Good, not Evil. michael@0: * michael@0: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR michael@0: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, michael@0: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE michael@0: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER michael@0: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING michael@0: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER michael@0: * DEALINGS IN THE SOFTWARE. michael@0: * michael@0: */ michael@0: michael@0: /*jshint quotmark:double */ michael@0: /*global console:true */ michael@0: /*exported console */ michael@0: michael@0: var _ = require("underscore"); michael@0: var events = require("events"); michael@0: var vars = require("../shared/vars.js"); michael@0: var messages = require("../shared/messages.js"); michael@0: var Lexer = require("./lex.js").Lexer; michael@0: var reg = require("./reg.js"); michael@0: var state = require("./state.js").state; michael@0: var style = require("./style.js"); michael@0: michael@0: // We need this module here because environments such as IE and Rhino michael@0: // don't necessarilly expose the 'console' API and browserify uses michael@0: // it to log things. It's a sad state of affair, really. michael@0: var console = require("console-browserify"); michael@0: michael@0: // We build the application inside a function so that we produce only a singleton michael@0: // variable. That function will be invoked immediately, and its return value is michael@0: // the JSHINT function itself. michael@0: michael@0: var JSHINT = (function () { michael@0: "use strict"; michael@0: michael@0: var anonname, // The guessed name for anonymous functions. michael@0: api, // Extension API michael@0: michael@0: // These are operators that should not be used with the ! operator. michael@0: bang = { michael@0: "<" : true, michael@0: "<=" : true, michael@0: "==" : true, michael@0: "===": true, michael@0: "!==": true, michael@0: "!=" : true, michael@0: ">" : true, michael@0: ">=" : true, michael@0: "+" : true, michael@0: "-" : true, michael@0: "*" : true, michael@0: "/" : true, michael@0: "%" : true michael@0: }, michael@0: michael@0: // These are the JSHint boolean options. michael@0: boolOptions = { michael@0: asi : true, // if automatic semicolon insertion should be tolerated michael@0: bitwise : true, // if bitwise operators should not be allowed michael@0: boss : true, // if advanced usage of assignments should be allowed michael@0: browser : true, // if the standard browser globals should be predefined michael@0: camelcase : true, // if identifiers should be required in camel case michael@0: couch : true, // if CouchDB globals should be predefined michael@0: curly : true, // if curly braces around all blocks should be required michael@0: debug : true, // if debugger statements should be allowed michael@0: devel : true, // if logging globals should be predefined (console, alert, etc.) michael@0: dojo : true, // if Dojo Toolkit globals should be predefined michael@0: eqeqeq : true, // if === should be required michael@0: eqnull : true, // if == null comparisons should be tolerated michael@0: es3 : true, // if ES3 syntax should be allowed michael@0: es5 : true, // if ES5 syntax should be allowed (is now set per default) michael@0: esnext : true, // if es.next specific syntax should be allowed michael@0: moz : true, // if mozilla specific syntax should be allowed michael@0: evil : true, // if eval should be allowed michael@0: expr : true, // if ExpressionStatement should be allowed as Programs michael@0: forin : true, // if for in statements must filter michael@0: funcscope : true, // if only function scope should be used for scope tests michael@0: gcl : true, // if JSHint should be compatible with Google Closure Linter michael@0: globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict') michael@0: immed : true, // if immediate invocations must be wrapped in parens michael@0: iterator : true, // if the `__iterator__` property should be allowed michael@0: jquery : true, // if jQuery globals should be predefined michael@0: lastsemic : true, // if semicolons may be ommitted for the trailing michael@0: // statements inside of a one-line blocks. michael@0: laxbreak : true, // if line breaks should not be checked michael@0: laxcomma : true, // if line breaks should not be checked around commas michael@0: loopfunc : true, // if functions should be allowed to be defined within michael@0: // loops michael@0: mootools : true, // if MooTools globals should be predefined michael@0: multistr : true, // allow multiline strings michael@0: newcap : true, // if constructor names must be capitalized michael@0: noarg : true, // if arguments.caller and arguments.callee should be michael@0: // disallowed michael@0: node : true, // if the Node.js environment globals should be michael@0: // predefined michael@0: noempty : true, // if empty blocks should be disallowed michael@0: nonew : true, // if using `new` for side-effects should be disallowed michael@0: nonstandard : true, // if non-standard (but widely adopted) globals should michael@0: // be predefined michael@0: nomen : true, // if names should be checked michael@0: onevar : true, // if only one var statement per function should be michael@0: // allowed michael@0: passfail : true, // if the scan should stop on first error michael@0: phantom : true, // if PhantomJS symbols should be allowed michael@0: plusplus : true, // if increment/decrement should not be allowed michael@0: proto : true, // if the `__proto__` property should be allowed michael@0: prototypejs : true, // if Prototype and Scriptaculous globals should be michael@0: // predefined michael@0: rhino : true, // if the Rhino environment globals should be predefined michael@0: undef : true, // if variables should be declared before used michael@0: scripturl : true, // if script-targeted URLs should be tolerated michael@0: shadow : true, // if variable shadowing should be tolerated michael@0: smarttabs : true, // if smarttabs should be tolerated michael@0: // (http://www.emacswiki.org/emacs/SmartTabs) michael@0: strict : true, // require the "use strict"; pragma michael@0: sub : true, // if all forms of subscript notation are tolerated michael@0: supernew : true, // if `new function () { ... };` and `new Object;` michael@0: // should be tolerated michael@0: trailing : true, // if trailing whitespace rules apply michael@0: validthis : true, // if 'this' inside a non-constructor function is valid. michael@0: // This is a function scoped option only. michael@0: withstmt : true, // if with statements should be allowed michael@0: white : true, // if strict whitespace rules apply michael@0: worker : true, // if Web Worker script symbols should be allowed michael@0: wsh : true, // if the Windows Scripting Host environment globals michael@0: // should be predefined michael@0: yui : true, // YUI variables should be predefined michael@0: michael@0: // Obsolete options michael@0: onecase : true, // if one case switch statements should be allowed michael@0: regexp : true, // if the . should not be allowed in regexp literals michael@0: regexdash : true // if unescaped first/last dash (-) inside brackets michael@0: // should be tolerated michael@0: }, michael@0: michael@0: // These are the JSHint options that can take any value michael@0: // (we use this object to detect invalid options) michael@0: valOptions = { michael@0: maxlen : false, michael@0: indent : false, michael@0: maxerr : false, michael@0: predef : false, michael@0: quotmark : false, //'single'|'double'|true michael@0: scope : false, michael@0: maxstatements: false, // {int} max statements per function michael@0: maxdepth : false, // {int} max nested block depth per function michael@0: maxparams : false, // {int} max params per function michael@0: maxcomplexity: false, // {int} max cyclomatic complexity per function michael@0: unused : true, // warn if variables are unused. Available options: michael@0: // false - don't check for unused variables michael@0: // true - "vars" + check last function param michael@0: // "vars" - skip checking unused function params michael@0: // "strict" - "vars" + check all function params michael@0: latedef : false // warn if the variable is used before its definition michael@0: // false - don't emit any warnings michael@0: // true - warn if any variable is used before its definition michael@0: // "nofunc" - warn for any variable but function declarations michael@0: }, michael@0: michael@0: // These are JSHint boolean options which are shared with JSLint michael@0: // where the definition in JSHint is opposite JSLint michael@0: invertedOptions = { michael@0: bitwise : true, michael@0: forin : true, michael@0: newcap : true, michael@0: nomen : true, michael@0: plusplus: true, michael@0: regexp : true, michael@0: undef : true, michael@0: white : true, michael@0: michael@0: // Inverted and renamed, use JSHint name here michael@0: eqeqeq : true, michael@0: onevar : true, michael@0: strict : true michael@0: }, michael@0: michael@0: // These are JSHint boolean options which are shared with JSLint michael@0: // where the name has been changed but the effect is unchanged michael@0: renamedOptions = { michael@0: eqeq : "eqeqeq", michael@0: vars : "onevar", michael@0: windows: "wsh", michael@0: sloppy : "strict" michael@0: }, michael@0: michael@0: declared, // Globals that were declared using /*global ... */ syntax. michael@0: exported, // Variables that are used outside of the current file. michael@0: michael@0: functionicity = [ michael@0: "closure", "exception", "global", "label", michael@0: "outer", "unused", "var" michael@0: ], michael@0: michael@0: funct, // The current function michael@0: functions, // All of the functions michael@0: michael@0: global, // The global scope michael@0: implied, // Implied globals michael@0: inblock, michael@0: indent, michael@0: lookahead, michael@0: lex, michael@0: member, michael@0: membersOnly, michael@0: noreach, michael@0: predefined, // Global variables defined by option michael@0: michael@0: scope, // The current scope michael@0: stack, michael@0: unuseds, michael@0: urls, michael@0: warnings, michael@0: michael@0: extraModules = [], michael@0: emitter = new events.EventEmitter(); michael@0: michael@0: function checkOption(name, t) { michael@0: name = name.trim(); michael@0: michael@0: if (/^[+-]W\d{3}$/g.test(name)) { michael@0: return true; michael@0: } michael@0: michael@0: if (valOptions[name] === undefined && boolOptions[name] === undefined) { michael@0: if (t.type !== "jslint") { michael@0: error("E001", t, name); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: function isString(obj) { michael@0: return Object.prototype.toString.call(obj) === "[object String]"; michael@0: } michael@0: michael@0: function isIdentifier(tkn, value) { michael@0: if (!tkn) michael@0: return false; michael@0: michael@0: if (!tkn.identifier || tkn.value !== value) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: function isReserved(token) { michael@0: if (!token.reserved) { michael@0: return false; michael@0: } michael@0: michael@0: if (token.meta && token.meta.isFutureReservedWord) { michael@0: // ES3 FutureReservedWord in an ES5 environment. michael@0: if (state.option.inES5(true) && !token.meta.es5) { michael@0: return false; michael@0: } michael@0: michael@0: // Some ES5 FutureReservedWord identifiers are active only michael@0: // within a strict mode environment. michael@0: if (token.meta.strictOnly) { michael@0: if (!state.option.strict && !state.directive["use strict"]) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (token.isProperty) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: function supplant(str, data) { michael@0: return str.replace(/\{([^{}]*)\}/g, function (a, b) { michael@0: var r = data[b]; michael@0: return typeof r === "string" || typeof r === "number" ? r : a; michael@0: }); michael@0: } michael@0: michael@0: function combine(t, o) { michael@0: var n; michael@0: for (n in o) { michael@0: if (_.has(o, n) && !_.has(JSHINT.blacklist, n)) { michael@0: t[n] = o[n]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: function updatePredefined() { michael@0: Object.keys(JSHINT.blacklist).forEach(function (key) { michael@0: delete predefined[key]; michael@0: }); michael@0: } michael@0: michael@0: function assume() { michael@0: if (state.option.es5) { michael@0: warning("I003"); michael@0: } michael@0: if (state.option.couch) { michael@0: combine(predefined, vars.couch); michael@0: } michael@0: michael@0: if (state.option.rhino) { michael@0: combine(predefined, vars.rhino); michael@0: } michael@0: michael@0: if (state.option.phantom) { michael@0: combine(predefined, vars.phantom); michael@0: } michael@0: michael@0: if (state.option.prototypejs) { michael@0: combine(predefined, vars.prototypejs); michael@0: } michael@0: michael@0: if (state.option.node) { michael@0: combine(predefined, vars.node); michael@0: } michael@0: michael@0: if (state.option.devel) { michael@0: combine(predefined, vars.devel); michael@0: } michael@0: michael@0: if (state.option.dojo) { michael@0: combine(predefined, vars.dojo); michael@0: } michael@0: michael@0: if (state.option.browser) { michael@0: combine(predefined, vars.browser); michael@0: } michael@0: michael@0: if (state.option.nonstandard) { michael@0: combine(predefined, vars.nonstandard); michael@0: } michael@0: michael@0: if (state.option.jquery) { michael@0: combine(predefined, vars.jquery); michael@0: } michael@0: michael@0: if (state.option.mootools) { michael@0: combine(predefined, vars.mootools); michael@0: } michael@0: michael@0: if (state.option.worker) { michael@0: combine(predefined, vars.worker); michael@0: } michael@0: michael@0: if (state.option.wsh) { michael@0: combine(predefined, vars.wsh); michael@0: } michael@0: michael@0: if (state.option.globalstrict && state.option.strict !== false) { michael@0: state.option.strict = true; michael@0: } michael@0: michael@0: if (state.option.yui) { michael@0: combine(predefined, vars.yui); michael@0: } michael@0: michael@0: // Let's assume that chronologically ES3 < ES5 < ES6/ESNext < Moz michael@0: michael@0: state.option.inMoz = function (strict) { michael@0: if (strict) { michael@0: return state.option.moz && !state.option.esnext; michael@0: } michael@0: return state.option.moz; michael@0: }; michael@0: michael@0: state.option.inESNext = function (strict) { michael@0: if (strict) { michael@0: return !state.option.moz && state.option.esnext; michael@0: } michael@0: return state.option.moz || state.option.esnext; michael@0: }; michael@0: michael@0: state.option.inES5 = function (/* strict */) { michael@0: return !state.option.es3; michael@0: }; michael@0: michael@0: state.option.inES3 = function (strict) { michael@0: if (strict) { michael@0: return !state.option.moz && !state.option.esnext && state.option.es3; michael@0: } michael@0: return state.option.es3; michael@0: }; michael@0: } michael@0: michael@0: // Produce an error warning. michael@0: function quit(code, line, chr) { michael@0: var percentage = Math.floor((line / state.lines.length) * 100); michael@0: var message = messages.errors[code].desc; michael@0: michael@0: throw { michael@0: name: "JSHintError", michael@0: line: line, michael@0: character: chr, michael@0: message: message + " (" + percentage + "% scanned).", michael@0: raw: message michael@0: }; michael@0: } michael@0: michael@0: function isundef(scope, code, token, a) { michael@0: return JSHINT.undefs.push([scope, code, token, a]); michael@0: } michael@0: michael@0: function warning(code, t, a, b, c, d) { michael@0: var ch, l, w, msg; michael@0: michael@0: if (/^W\d{3}$/.test(code)) { michael@0: if (state.ignored[code]) michael@0: return; michael@0: michael@0: msg = messages.warnings[code]; michael@0: } else if (/E\d{3}/.test(code)) { michael@0: msg = messages.errors[code]; michael@0: } else if (/I\d{3}/.test(code)) { michael@0: msg = messages.info[code]; michael@0: } michael@0: michael@0: t = t || state.tokens.next; michael@0: if (t.id === "(end)") { // `~ michael@0: t = state.tokens.curr; michael@0: } michael@0: michael@0: l = t.line || 0; michael@0: ch = t.from || 0; michael@0: michael@0: w = { michael@0: id: "(error)", michael@0: raw: msg.desc, michael@0: code: msg.code, michael@0: evidence: state.lines[l - 1] || "", michael@0: line: l, michael@0: character: ch, michael@0: scope: JSHINT.scope, michael@0: a: a, michael@0: b: b, michael@0: c: c, michael@0: d: d michael@0: }; michael@0: michael@0: w.reason = supplant(msg.desc, w); michael@0: JSHINT.errors.push(w); michael@0: michael@0: if (state.option.passfail) { michael@0: quit("E042", l, ch); michael@0: } michael@0: michael@0: warnings += 1; michael@0: if (warnings >= state.option.maxerr) { michael@0: quit("E043", l, ch); michael@0: } michael@0: michael@0: return w; michael@0: } michael@0: michael@0: function warningAt(m, l, ch, a, b, c, d) { michael@0: return warning(m, { michael@0: line: l, michael@0: from: ch michael@0: }, a, b, c, d); michael@0: } michael@0: michael@0: function error(m, t, a, b, c, d) { michael@0: warning(m, t, a, b, c, d); michael@0: } michael@0: michael@0: function errorAt(m, l, ch, a, b, c, d) { michael@0: return error(m, { michael@0: line: l, michael@0: from: ch michael@0: }, a, b, c, d); michael@0: } michael@0: michael@0: // Tracking of "internal" scripts, like eval containing a static string michael@0: function addInternalSrc(elem, src) { michael@0: var i; michael@0: i = { michael@0: id: "(internal)", michael@0: elem: elem, michael@0: value: src michael@0: }; michael@0: JSHINT.internals.push(i); michael@0: return i; michael@0: } michael@0: michael@0: function addlabel(t, type, tkn, islet) { michael@0: // Define t in the current function in the current scope. michael@0: if (type === "exception") { michael@0: if (_.has(funct["(context)"], t)) { michael@0: if (funct[t] !== true && !state.option.node) { michael@0: warning("W002", state.tokens.next, t); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (_.has(funct, t) && !funct["(global)"]) { michael@0: if (funct[t] === true) { michael@0: if (state.option.latedef) { michael@0: if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) || michael@0: !_.contains([funct[t], type], "unction")) { michael@0: warning("W003", state.tokens.next, t); michael@0: } michael@0: } michael@0: } else { michael@0: if (!state.option.shadow && type !== "exception" || michael@0: (funct["(blockscope)"].getlabel(t))) { michael@0: warning("W004", state.tokens.next, t); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // a double definition of a let variable in same block throws a TypeError michael@0: //if (funct["(blockscope)"] && funct["(blockscope)"].current.has(t)) { michael@0: // error("E044", state.tokens.next, t); michael@0: //} michael@0: michael@0: // if the identifier is from a let, adds it only to the current blockscope michael@0: if (islet) { michael@0: funct["(blockscope)"].current.add(t, type, state.tokens.curr); michael@0: } else { michael@0: michael@0: funct[t] = type; michael@0: michael@0: if (tkn) { michael@0: funct["(tokens)"][t] = tkn; michael@0: } michael@0: michael@0: if (funct["(global)"]) { michael@0: global[t] = funct; michael@0: if (_.has(implied, t)) { michael@0: if (state.option.latedef) { michael@0: if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) || michael@0: !_.contains([funct[t], type], "unction")) { michael@0: warning("W003", state.tokens.next, t); michael@0: } michael@0: } michael@0: michael@0: delete implied[t]; michael@0: } michael@0: } else { michael@0: scope[t] = funct; michael@0: } michael@0: } michael@0: } michael@0: michael@0: function doOption() { michael@0: var nt = state.tokens.next; michael@0: var body = nt.body.split(",").map(function (s) { return s.trim(); }); michael@0: var predef = {}; michael@0: michael@0: if (nt.type === "globals") { michael@0: body.forEach(function (g) { michael@0: g = g.split(":"); michael@0: var key = g[0]; michael@0: var val = g[1]; michael@0: michael@0: if (key.charAt(0) === "-") { michael@0: key = key.slice(1); michael@0: val = false; michael@0: michael@0: JSHINT.blacklist[key] = key; michael@0: updatePredefined(); michael@0: } else { michael@0: predef[key] = (val === "true"); michael@0: } michael@0: }); michael@0: michael@0: combine(predefined, predef); michael@0: michael@0: for (var key in predef) { michael@0: if (_.has(predef, key)) { michael@0: declared[key] = nt; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (nt.type === "exported") { michael@0: body.forEach(function (e) { michael@0: exported[e] = true; michael@0: }); michael@0: } michael@0: michael@0: if (nt.type === "members") { michael@0: membersOnly = membersOnly || {}; michael@0: michael@0: body.forEach(function (m) { michael@0: var ch1 = m.charAt(0); michael@0: var ch2 = m.charAt(m.length - 1); michael@0: michael@0: if (ch1 === ch2 && (ch1 === "\"" || ch1 === "'")) { michael@0: m = m michael@0: .substr(1, m.length - 2) michael@0: .replace("\\b", "\b") michael@0: .replace("\\t", "\t") michael@0: .replace("\\n", "\n") michael@0: .replace("\\v", "\v") michael@0: .replace("\\f", "\f") michael@0: .replace("\\r", "\r") michael@0: .replace("\\\\", "\\") michael@0: .replace("\\\"", "\""); michael@0: } michael@0: michael@0: membersOnly[m] = false; michael@0: }); michael@0: } michael@0: michael@0: var numvals = [ michael@0: "maxstatements", michael@0: "maxparams", michael@0: "maxdepth", michael@0: "maxcomplexity", michael@0: "maxerr", michael@0: "maxlen", michael@0: "indent" michael@0: ]; michael@0: michael@0: if (nt.type === "jshint" || nt.type === "jslint") { michael@0: body.forEach(function (g) { michael@0: g = g.split(":"); michael@0: var key = (g[0] || "").trim(); michael@0: var val = (g[1] || "").trim(); michael@0: michael@0: if (!checkOption(key, nt)) { michael@0: return; michael@0: } michael@0: michael@0: if (numvals.indexOf(key) >= 0) { michael@0: michael@0: // GH988 - numeric options can be disabled by setting them to `false` michael@0: if (val !== "false") { michael@0: val = +val; michael@0: michael@0: if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) { michael@0: error("E032", nt, g[1].trim()); michael@0: return; michael@0: } michael@0: michael@0: if (key === "indent") { michael@0: state.option["(explicitIndent)"] = true; michael@0: } michael@0: state.option[key] = val; michael@0: } else { michael@0: if (key === "indent") { michael@0: state.option["(explicitIndent)"] = false; michael@0: } else { michael@0: state.option[key] = false; michael@0: } michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (key === "validthis") { michael@0: // `validthis` is valid only within a function scope. michael@0: if (funct["(global)"]) { michael@0: error("E009"); michael@0: } else { michael@0: if (val === "true" || val === "false") { michael@0: state.option.validthis = (val === "true"); michael@0: } else { michael@0: error("E002", nt); michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (key === "quotmark") { michael@0: switch (val) { michael@0: case "true": michael@0: case "false": michael@0: state.option.quotmark = (val === "true"); michael@0: break; michael@0: case "double": michael@0: case "single": michael@0: state.option.quotmark = val; michael@0: break; michael@0: default: michael@0: error("E002", nt); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (key === "unused") { michael@0: switch (val) { michael@0: case "true": michael@0: state.option.unused = true; michael@0: break; michael@0: case "false": michael@0: state.option.unused = false; michael@0: break; michael@0: case "vars": michael@0: case "strict": michael@0: state.option.unused = val; michael@0: break; michael@0: default: michael@0: error("E002", nt); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (key === "latedef") { michael@0: switch (val) { michael@0: case "true": michael@0: state.option.latedef = true; michael@0: break; michael@0: case "false": michael@0: state.option.latedef = false; michael@0: break; michael@0: case "nofunc": michael@0: state.option.latedef = "nofunc"; michael@0: break; michael@0: default: michael@0: error("E002", nt); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: var match = /^([+-])(W\d{3})$/g.exec(key); michael@0: if (match) { michael@0: // ignore for -W..., unignore for +W... michael@0: state.ignored[match[2]] = (match[1] === "-"); michael@0: return; michael@0: } michael@0: michael@0: var tn; michael@0: if (val === "true" || val === "false") { michael@0: if (nt.type === "jslint") { michael@0: tn = renamedOptions[key] || key; michael@0: state.option[tn] = (val === "true"); michael@0: michael@0: if (invertedOptions[tn] !== undefined) { michael@0: state.option[tn] = !state.option[tn]; michael@0: } michael@0: } else { michael@0: state.option[key] = (val === "true"); michael@0: } michael@0: michael@0: if (key === "newcap") { michael@0: state.option["(explicitNewcap)"] = true; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: error("E002", nt); michael@0: }); michael@0: michael@0: assume(); michael@0: } michael@0: } michael@0: michael@0: // We need a peek function. If it has an argument, it peeks that much farther michael@0: // ahead. It is used to distinguish michael@0: // for ( var i in ... michael@0: // from michael@0: // for ( var i = ... michael@0: michael@0: function peek(p) { michael@0: var i = p || 0, j = 0, t; michael@0: michael@0: while (j <= i) { michael@0: t = lookahead[j]; michael@0: if (!t) { michael@0: t = lookahead[j] = lex.token(); michael@0: } michael@0: j += 1; michael@0: } michael@0: return t; michael@0: } michael@0: michael@0: // Produce the next token. It looks for programming errors. michael@0: michael@0: function advance(id, t) { michael@0: switch (state.tokens.curr.id) { michael@0: case "(number)": michael@0: if (state.tokens.next.id === ".") { michael@0: warning("W005", state.tokens.curr); michael@0: } michael@0: break; michael@0: case "-": michael@0: if (state.tokens.next.id === "-" || state.tokens.next.id === "--") { michael@0: warning("W006"); michael@0: } michael@0: break; michael@0: case "+": michael@0: if (state.tokens.next.id === "+" || state.tokens.next.id === "++") { michael@0: warning("W007"); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) { michael@0: anonname = state.tokens.curr.value; michael@0: } michael@0: michael@0: if (id && state.tokens.next.id !== id) { michael@0: if (t) { michael@0: if (state.tokens.next.id === "(end)") { michael@0: error("E019", t, t.id); michael@0: } else { michael@0: error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value); michael@0: } michael@0: } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) { michael@0: warning("W116", state.tokens.next, id, state.tokens.next.value); michael@0: } michael@0: } michael@0: michael@0: state.tokens.prev = state.tokens.curr; michael@0: state.tokens.curr = state.tokens.next; michael@0: for (;;) { michael@0: state.tokens.next = lookahead.shift() || lex.token(); michael@0: michael@0: if (!state.tokens.next) { // No more tokens left, give up michael@0: quit("E041", state.tokens.curr.line); michael@0: } michael@0: michael@0: if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { michael@0: return; michael@0: } michael@0: michael@0: if (state.tokens.next.check) { michael@0: state.tokens.next.check(); michael@0: } michael@0: michael@0: if (state.tokens.next.isSpecial) { michael@0: doOption(); michael@0: } else { michael@0: if (state.tokens.next.id !== "(endline)") { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it michael@0: // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is michael@0: // like .nud except that it is only used on the first token of a statement. michael@0: // Having .fud makes it much easier to define statement-oriented languages like michael@0: // JavaScript. I retained Pratt's nomenclature. michael@0: michael@0: // .nud Null denotation michael@0: // .fud First null denotation michael@0: // .led Left denotation michael@0: // lbp Left binding power michael@0: // rbp Right binding power michael@0: michael@0: // They are elements of the parsing method called Top Down Operator Precedence. michael@0: michael@0: function expression(rbp, initial) { michael@0: var left, isArray = false, isObject = false, isLetExpr = false; michael@0: michael@0: // if current expression is a let expression michael@0: if (!initial && state.tokens.next.value === "let" && peek(0).value === "(") { michael@0: if (!state.option.inMoz(true)) { michael@0: warning("W118", state.tokens.next, "let expressions"); michael@0: } michael@0: isLetExpr = true; michael@0: // create a new block scope we use only for the current expression michael@0: funct["(blockscope)"].stack(); michael@0: advance("let"); michael@0: advance("("); michael@0: state.syntax["let"].fud.call(state.syntax["let"].fud, false); michael@0: advance(")"); michael@0: } michael@0: michael@0: if (state.tokens.next.id === "(end)") michael@0: error("E006", state.tokens.curr); michael@0: michael@0: advance(); michael@0: michael@0: if (initial) { michael@0: anonname = "anonymous"; michael@0: funct["(verb)"] = state.tokens.curr.value; michael@0: } michael@0: michael@0: if (initial === true && state.tokens.curr.fud) { michael@0: left = state.tokens.curr.fud(); michael@0: } else { michael@0: if (state.tokens.curr.nud) { michael@0: left = state.tokens.curr.nud(); michael@0: } else { michael@0: error("E030", state.tokens.curr, state.tokens.curr.id); michael@0: } michael@0: michael@0: var end_of_expr = state.tokens.next.identifier && michael@0: !state.tokens.curr.led && michael@0: state.tokens.curr.line !== state.tokens.next.line; michael@0: while (rbp < state.tokens.next.lbp && !end_of_expr) { michael@0: isArray = state.tokens.curr.value === "Array"; michael@0: isObject = state.tokens.curr.value === "Object"; michael@0: michael@0: // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object() michael@0: // Line breaks in IfStatement heads exist to satisfy the checkJSHint michael@0: // "Line too long." error. michael@0: if (left && (left.value || (left.first && left.first.value))) { michael@0: // If the left.value is not "new", or the left.first.value is a "." michael@0: // then safely assume that this is not "new Array()" and possibly michael@0: // not "new Object()"... michael@0: if (left.value !== "new" || michael@0: (left.first && left.first.value && left.first.value === ".")) { michael@0: isArray = false; michael@0: // ...In the case of Object, if the left.value and state.tokens.curr.value michael@0: // are not equal, then safely assume that this not "new Object()" michael@0: if (left.value !== state.tokens.curr.value) { michael@0: isObject = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: advance(); michael@0: michael@0: if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { michael@0: warning("W009", state.tokens.curr); michael@0: } michael@0: michael@0: if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { michael@0: warning("W010", state.tokens.curr); michael@0: } michael@0: michael@0: if (left && state.tokens.curr.led) { michael@0: left = state.tokens.curr.led(left); michael@0: } else { michael@0: error("E033", state.tokens.curr, state.tokens.curr.id); michael@0: } michael@0: } michael@0: } michael@0: if (isLetExpr) { michael@0: funct["(blockscope)"].unstack(); michael@0: } michael@0: return left; michael@0: } michael@0: michael@0: michael@0: // Functions for conformance of style. michael@0: michael@0: function adjacent(left, right) { michael@0: left = left || state.tokens.curr; michael@0: right = right || state.tokens.next; michael@0: if (state.option.white) { michael@0: if (left.character !== right.from && left.line === right.line) { michael@0: left.from += (left.character - left.from); michael@0: warning("W011", left, left.value); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function nobreak(left, right) { michael@0: left = left || state.tokens.curr; michael@0: right = right || state.tokens.next; michael@0: if (state.option.white && (left.character !== right.from || left.line !== right.line)) { michael@0: warning("W012", right, right.value); michael@0: } michael@0: } michael@0: michael@0: function nospace(left, right) { michael@0: left = left || state.tokens.curr; michael@0: right = right || state.tokens.next; michael@0: if (state.option.white && !left.comment) { michael@0: if (left.line === right.line) { michael@0: adjacent(left, right); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function nonadjacent(left, right) { michael@0: if (state.option.white) { michael@0: left = left || state.tokens.curr; michael@0: right = right || state.tokens.next; michael@0: michael@0: if (left.value === ";" && right.value === ";") { michael@0: return; michael@0: } michael@0: michael@0: if (left.line === right.line && left.character === right.from) { michael@0: left.from += (left.character - left.from); michael@0: warning("W013", left, left.value); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function nobreaknonadjacent(left, right) { michael@0: left = left || state.tokens.curr; michael@0: right = right || state.tokens.next; michael@0: if (!state.option.laxbreak && left.line !== right.line) { michael@0: warning("W014", right, right.id); michael@0: } else if (state.option.white) { michael@0: left = left || state.tokens.curr; michael@0: right = right || state.tokens.next; michael@0: if (left.character === right.from) { michael@0: left.from += (left.character - left.from); michael@0: warning("W013", left, left.value); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function indentation(bias) { michael@0: if (!state.option.white && !state.option["(explicitIndent)"]) { michael@0: return; michael@0: } michael@0: michael@0: if (state.tokens.next.id === "(end)") { michael@0: return; michael@0: } michael@0: michael@0: var i = indent + (bias || 0); michael@0: if (state.tokens.next.from !== i) { michael@0: warning("W015", state.tokens.next, state.tokens.next.value, i, state.tokens.next.from); michael@0: } michael@0: } michael@0: michael@0: function nolinebreak(t) { michael@0: t = t || state.tokens.curr; michael@0: if (t.line !== state.tokens.next.line) { michael@0: warning("E022", t, t.value); michael@0: } michael@0: } michael@0: michael@0: michael@0: function comma(opts) { michael@0: opts = opts || {}; michael@0: michael@0: if (!opts.peek) { michael@0: if (state.tokens.curr.line !== state.tokens.next.line) { michael@0: if (!state.option.laxcomma) { michael@0: if (comma.first) { michael@0: warning("I001"); michael@0: comma.first = false; michael@0: } michael@0: warning("W014", state.tokens.curr, state.tokens.next.value); michael@0: } michael@0: } else if (!state.tokens.curr.comment && michael@0: state.tokens.curr.character !== state.tokens.next.from && state.option.white) { michael@0: state.tokens.curr.from += (state.tokens.curr.character - state.tokens.curr.from); michael@0: warning("W011", state.tokens.curr, state.tokens.curr.value); michael@0: } michael@0: michael@0: advance(","); michael@0: } michael@0: michael@0: // TODO: This is a temporary solution to fight against false-positives in michael@0: // arrays and objects with trailing commas (see GH-363). The best solution michael@0: // would be to extract all whitespace rules out of parser. michael@0: michael@0: if (state.tokens.next.value !== "]" && state.tokens.next.value !== "}") { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: } michael@0: michael@0: if (state.tokens.next.identifier && !(opts.property && state.option.inES5())) { michael@0: // Keywords that cannot follow a comma operator. michael@0: switch (state.tokens.next.value) { michael@0: case "break": michael@0: case "case": michael@0: case "catch": michael@0: case "continue": michael@0: case "default": michael@0: case "do": michael@0: case "else": michael@0: case "finally": michael@0: case "for": michael@0: case "if": michael@0: case "in": michael@0: case "instanceof": michael@0: case "return": michael@0: case "yield": michael@0: case "switch": michael@0: case "throw": michael@0: case "try": michael@0: case "var": michael@0: case "let": michael@0: case "while": michael@0: case "with": michael@0: error("E024", state.tokens.next, state.tokens.next.value); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (state.tokens.next.type === "(punctuator)") { michael@0: switch (state.tokens.next.value) { michael@0: case "}": michael@0: case "]": michael@0: case ",": michael@0: if (opts.allowTrailing) { michael@0: return true; michael@0: } michael@0: michael@0: /* falls through */ michael@0: case ")": michael@0: error("E024", state.tokens.next, state.tokens.next.value); michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Functional constructors for making the symbols that will be inherited by michael@0: // tokens. michael@0: michael@0: function symbol(s, p) { michael@0: var x = state.syntax[s]; michael@0: if (!x || typeof x !== "object") { michael@0: state.syntax[s] = x = { michael@0: id: s, michael@0: lbp: p, michael@0: value: s michael@0: }; michael@0: } michael@0: return x; michael@0: } michael@0: michael@0: function delim(s) { michael@0: return symbol(s, 0); michael@0: } michael@0: michael@0: function stmt(s, f) { michael@0: var x = delim(s); michael@0: x.identifier = x.reserved = true; michael@0: x.fud = f; michael@0: return x; michael@0: } michael@0: michael@0: function blockstmt(s, f) { michael@0: var x = stmt(s, f); michael@0: x.block = true; michael@0: return x; michael@0: } michael@0: michael@0: function reserveName(x) { michael@0: var c = x.id.charAt(0); michael@0: if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) { michael@0: x.identifier = x.reserved = true; michael@0: } michael@0: return x; michael@0: } michael@0: michael@0: function prefix(s, f) { michael@0: var x = symbol(s, 150); michael@0: reserveName(x); michael@0: x.nud = (typeof f === "function") ? f : function () { michael@0: this.right = expression(150); michael@0: this.arity = "unary"; michael@0: if (this.id === "++" || this.id === "--") { michael@0: if (state.option.plusplus) { michael@0: warning("W016", this, this.id); michael@0: } else if ((!this.right.identifier || isReserved(this.right)) && michael@0: this.right.id !== "." && this.right.id !== "[") { michael@0: warning("W017", this); michael@0: } michael@0: } michael@0: return this; michael@0: }; michael@0: return x; michael@0: } michael@0: michael@0: function type(s, f) { michael@0: var x = delim(s); michael@0: x.type = s; michael@0: x.nud = f; michael@0: return x; michael@0: } michael@0: michael@0: function reserve(name, func) { michael@0: var x = type(name, func); michael@0: x.identifier = true; michael@0: x.reserved = true; michael@0: return x; michael@0: } michael@0: michael@0: function FutureReservedWord(name, meta) { michael@0: var x = type(name, (meta && meta.nud) || function () { michael@0: return this; michael@0: }); michael@0: michael@0: meta = meta || {}; michael@0: meta.isFutureReservedWord = true; michael@0: michael@0: x.value = name; michael@0: x.identifier = true; michael@0: x.reserved = true; michael@0: x.meta = meta; michael@0: michael@0: return x; michael@0: } michael@0: michael@0: function reservevar(s, v) { michael@0: return reserve(s, function () { michael@0: if (typeof v === "function") { michael@0: v(this); michael@0: } michael@0: return this; michael@0: }); michael@0: } michael@0: michael@0: function infix(s, f, p, w) { michael@0: var x = symbol(s, p); michael@0: reserveName(x); michael@0: x.led = function (left) { michael@0: if (!w) { michael@0: nobreaknonadjacent(state.tokens.prev, state.tokens.curr); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: } michael@0: if (s === "in" && left.id === "!") { michael@0: warning("W018", left, "!"); michael@0: } michael@0: if (typeof f === "function") { michael@0: return f(left, this); michael@0: } else { michael@0: this.left = left; michael@0: this.right = expression(p); michael@0: return this; michael@0: } michael@0: }; michael@0: return x; michael@0: } michael@0: michael@0: michael@0: function application(s) { michael@0: var x = symbol(s, 42); michael@0: michael@0: x.led = function (left) { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "arrow function syntax (=>)"); michael@0: } michael@0: michael@0: nobreaknonadjacent(state.tokens.prev, state.tokens.curr); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: michael@0: this.left = left; michael@0: this.right = doFunction(undefined, undefined, false, left); michael@0: return this; michael@0: }; michael@0: return x; michael@0: } michael@0: michael@0: function relation(s, f) { michael@0: var x = symbol(s, 100); michael@0: michael@0: x.led = function (left) { michael@0: nobreaknonadjacent(state.tokens.prev, state.tokens.curr); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: var right = expression(100); michael@0: michael@0: if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { michael@0: warning("W019", this); michael@0: } else if (f) { michael@0: f.apply(this, [left, right]); michael@0: } michael@0: michael@0: if (!left || !right) { michael@0: quit("E041", state.tokens.curr.line); michael@0: } michael@0: michael@0: if (left.id === "!") { michael@0: warning("W018", left, "!"); michael@0: } michael@0: michael@0: if (right.id === "!") { michael@0: warning("W018", right, "!"); michael@0: } michael@0: michael@0: this.left = left; michael@0: this.right = right; michael@0: return this; michael@0: }; michael@0: return x; michael@0: } michael@0: michael@0: function isPoorRelation(node) { michael@0: return node && michael@0: ((node.type === "(number)" && +node.value === 0) || michael@0: (node.type === "(string)" && node.value === "") || michael@0: (node.type === "null" && !state.option.eqnull) || michael@0: node.type === "true" || michael@0: node.type === "false" || michael@0: node.type === "undefined"); michael@0: } michael@0: michael@0: function assignop(s) { michael@0: symbol(s, 20).exps = true; michael@0: michael@0: return infix(s, function (left, that) { michael@0: that.left = left; michael@0: michael@0: if (left) { michael@0: if (predefined[left.value] === false && michael@0: scope[left.value]["(global)"] === true) { michael@0: warning("W020", left); michael@0: } else if (left["function"]) { michael@0: warning("W021", left, left.value); michael@0: } michael@0: michael@0: if (funct[left.value] === "const") { michael@0: error("E013", left, left.value); michael@0: } michael@0: michael@0: if (left.id === ".") { michael@0: if (!left.left) { michael@0: warning("E031", that); michael@0: } else if (left.left.value === "arguments" && !state.directive["use strict"]) { michael@0: warning("E031", that); michael@0: } michael@0: michael@0: that.right = expression(19); michael@0: return that; michael@0: } else if (left.id === "[") { michael@0: if (state.tokens.curr.left.first) { michael@0: state.tokens.curr.left.first.forEach(function (t) { michael@0: if (funct[t.value] === "const") { michael@0: error("E013", t, t.value); michael@0: } michael@0: }); michael@0: } else if (!left.left) { michael@0: warning("E031", that); michael@0: } else if (left.left.value === "arguments" && !state.directive["use strict"]) { michael@0: warning("E031", that); michael@0: } michael@0: that.right = expression(19); michael@0: return that; michael@0: } else if (left.identifier && !isReserved(left)) { michael@0: if (funct[left.value] === "exception") { michael@0: warning("W022", left); michael@0: } michael@0: that.right = expression(19); michael@0: return that; michael@0: } michael@0: michael@0: if (left === state.syntax["function"]) { michael@0: warning("W023", state.tokens.curr); michael@0: } michael@0: } michael@0: michael@0: error("E031", that); michael@0: }, 20); michael@0: } michael@0: michael@0: michael@0: function bitwise(s, f, p) { michael@0: var x = symbol(s, p); michael@0: reserveName(x); michael@0: x.led = (typeof f === "function") ? f : function (left) { michael@0: if (state.option.bitwise) { michael@0: warning("W016", this, this.id); michael@0: } michael@0: this.left = left; michael@0: this.right = expression(p); michael@0: return this; michael@0: }; michael@0: return x; michael@0: } michael@0: michael@0: michael@0: function bitwiseassignop(s) { michael@0: symbol(s, 20).exps = true; michael@0: return infix(s, function (left, that) { michael@0: if (state.option.bitwise) { michael@0: warning("W016", that, that.id); michael@0: } michael@0: nonadjacent(state.tokens.prev, state.tokens.curr); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (left) { michael@0: if (left.id === "." || left.id === "[" || michael@0: (left.identifier && !isReserved(left))) { michael@0: expression(19); michael@0: return that; michael@0: } michael@0: if (left === state.syntax["function"]) { michael@0: warning("W023", state.tokens.curr); michael@0: } michael@0: return that; michael@0: } michael@0: error("E031", that); michael@0: }, 20); michael@0: } michael@0: michael@0: michael@0: function suffix(s) { michael@0: var x = symbol(s, 150); michael@0: michael@0: x.led = function (left) { michael@0: if (state.option.plusplus) { michael@0: warning("W016", this, this.id); michael@0: } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") { michael@0: warning("W017", this); michael@0: } michael@0: michael@0: this.left = left; michael@0: return this; michael@0: }; michael@0: return x; michael@0: } michael@0: michael@0: // fnparam means that this identifier is being defined as a function michael@0: // argument (see identifier()) michael@0: // prop means that this identifier is that of an object property michael@0: michael@0: function optionalidentifier(fnparam, prop) { michael@0: if (!state.tokens.next.identifier) { michael@0: return; michael@0: } michael@0: michael@0: advance(); michael@0: michael@0: var curr = state.tokens.curr; michael@0: var meta = curr.meta || {}; michael@0: var val = state.tokens.curr.value; michael@0: michael@0: if (!isReserved(curr)) { michael@0: return val; michael@0: } michael@0: michael@0: if (prop) { michael@0: if (state.option.inES5() || meta.isFutureReservedWord) { michael@0: return val; michael@0: } michael@0: } michael@0: michael@0: if (fnparam && val === "undefined") { michael@0: return val; michael@0: } michael@0: michael@0: // Display an info message about reserved words as properties michael@0: // and ES5 but do it only once. michael@0: if (prop && !api.getCache("displayed:I002")) { michael@0: api.setCache("displayed:I002", true); michael@0: warning("I002"); michael@0: } michael@0: michael@0: warning("W024", state.tokens.curr, state.tokens.curr.id); michael@0: return val; michael@0: } michael@0: michael@0: // fnparam means that this identifier is being defined as a function michael@0: // argument michael@0: // prop means that this identifier is that of an object property michael@0: function identifier(fnparam, prop) { michael@0: var i = optionalidentifier(fnparam, prop); michael@0: if (i) { michael@0: return i; michael@0: } michael@0: if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") { michael@0: warning("W025"); michael@0: } else { michael@0: error("E030", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: } michael@0: michael@0: michael@0: function reachable(s) { michael@0: var i = 0, t; michael@0: if (state.tokens.next.id !== ";" || noreach) { michael@0: return; michael@0: } michael@0: for (;;) { michael@0: t = peek(i); michael@0: if (t.reach) { michael@0: return; michael@0: } michael@0: if (t.id !== "(endline)") { michael@0: if (t.id === "function") { michael@0: if (!state.option.latedef) { michael@0: break; michael@0: } michael@0: michael@0: warning("W026", t); michael@0: break; michael@0: } michael@0: michael@0: warning("W027", t, t.value, s); michael@0: break; michael@0: } michael@0: i += 1; michael@0: } michael@0: } michael@0: michael@0: michael@0: function statement(noindent) { michael@0: var values; michael@0: var i = indent, r, s = scope, t = state.tokens.next; michael@0: michael@0: if (t.id === ";") { michael@0: advance(";"); michael@0: return; michael@0: } michael@0: michael@0: // Is this a labelled statement? michael@0: var res = isReserved(t); michael@0: michael@0: // We're being more tolerant here: if someone uses michael@0: // a FutureReservedWord as a label, we warn but proceed michael@0: // anyway. michael@0: michael@0: if (res && t.meta && t.meta.isFutureReservedWord && peek().id === ":") { michael@0: warning("W024", t, t.id); michael@0: res = false; michael@0: } michael@0: michael@0: // detect a destructuring assignment michael@0: if (_.has(["[", "{"], t.value)) { michael@0: if (lookupBlockType().isDestAssign) { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "destructuring expression"); michael@0: } michael@0: values = destructuringExpression(); michael@0: values.forEach(function (tok) { michael@0: isundef(funct, "W117", tok.token, tok.id); michael@0: }); michael@0: advance("="); michael@0: destructuringExpressionMatch(values, expression(5, true)); michael@0: advance(";"); michael@0: return; michael@0: } michael@0: } michael@0: if (t.identifier && !res && peek().id === ":") { michael@0: advance(); michael@0: advance(":"); michael@0: scope = Object.create(s); michael@0: addlabel(t.value, "label"); michael@0: michael@0: if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { michael@0: warning("W028", state.tokens.next, t.value, state.tokens.next.value); michael@0: } michael@0: michael@0: state.tokens.next.label = t.value; michael@0: t = state.tokens.next; michael@0: } michael@0: michael@0: // Is it a lonely block? michael@0: michael@0: if (t.id === "{") { michael@0: // Is it a switch case block? michael@0: // michael@0: // switch (foo) { michael@0: // case bar: { <= here. michael@0: // ... michael@0: // } michael@0: // } michael@0: var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":"); michael@0: block(true, true, false, false, iscase); michael@0: return; michael@0: } michael@0: michael@0: // Parse the statement. michael@0: michael@0: if (!noindent) { michael@0: indentation(); michael@0: } michael@0: r = expression(0, true); michael@0: michael@0: // Look for the final semicolon. michael@0: michael@0: if (!t.block) { michael@0: if (!state.option.expr && (!r || !r.exps)) { michael@0: warning("W030", state.tokens.curr); michael@0: } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") { michael@0: warning("W031", t); michael@0: } michael@0: michael@0: if (state.tokens.next.id !== ";") { michael@0: if (!state.option.asi) { michael@0: // If this is the last statement in a block that ends on michael@0: // the same line *and* option lastsemic is on, ignore the warning. michael@0: // Otherwise, complain about missing semicolon. michael@0: if (!state.option.lastsemic || state.tokens.next.id !== "}" || michael@0: state.tokens.next.line !== state.tokens.curr.line) { michael@0: warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); michael@0: } michael@0: } michael@0: } else { michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: advance(";"); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: } michael@0: } michael@0: michael@0: // Restore the indentation. michael@0: michael@0: indent = i; michael@0: scope = s; michael@0: return r; michael@0: } michael@0: michael@0: michael@0: function statements(startLine) { michael@0: var a = [], p; michael@0: michael@0: while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") { michael@0: if (state.tokens.next.id === ";") { michael@0: p = peek(); michael@0: michael@0: if (!p || (p.id !== "(" && p.id !== "[")) { michael@0: warning("W032"); michael@0: } michael@0: michael@0: advance(";"); michael@0: } else { michael@0: a.push(statement(startLine === state.tokens.next.line)); michael@0: } michael@0: } michael@0: return a; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * read all directives michael@0: * recognizes a simple form of asi, but always michael@0: * warns, if it is used michael@0: */ michael@0: function directives() { michael@0: var i, p, pn; michael@0: michael@0: for (;;) { michael@0: if (state.tokens.next.id === "(string)") { michael@0: p = peek(0); michael@0: if (p.id === "(endline)") { michael@0: i = 1; michael@0: do { michael@0: pn = peek(i); michael@0: i = i + 1; michael@0: } while (pn.id === "(endline)"); michael@0: michael@0: if (pn.id !== ";") { michael@0: if (pn.id !== "(string)" && pn.id !== "(number)" && michael@0: pn.id !== "(regexp)" && pn.identifier !== true && michael@0: pn.id !== "}") { michael@0: break; michael@0: } michael@0: warning("W033", state.tokens.next); michael@0: } else { michael@0: p = pn; michael@0: } michael@0: } else if (p.id === "}") { michael@0: // Directive with no other statements, warn about missing semicolon michael@0: warning("W033", p); michael@0: } else if (p.id !== ";") { michael@0: break; michael@0: } michael@0: michael@0: indentation(); michael@0: advance(); michael@0: if (state.directive[state.tokens.curr.value]) { michael@0: warning("W034", state.tokens.curr, state.tokens.curr.value); michael@0: } michael@0: michael@0: if (state.tokens.curr.value === "use strict") { michael@0: if (!state.option["(explicitNewcap)"]) michael@0: state.option.newcap = true; michael@0: state.option.undef = true; michael@0: } michael@0: michael@0: // there's no directive negation, so always set to true michael@0: state.directive[state.tokens.curr.value] = true; michael@0: michael@0: if (p.id === ";") { michael@0: advance(";"); michael@0: } michael@0: continue; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Parses a single block. A block is a sequence of statements wrapped in michael@0: * braces. michael@0: * michael@0: * ordinary - true for everything but function bodies and try blocks. michael@0: * stmt - true if block can be a single statement (e.g. in if/for/while). michael@0: * isfunc - true if block is a function body michael@0: * isfatarrow - michael@0: * iscase - true if block is a switch case block michael@0: */ michael@0: function block(ordinary, stmt, isfunc, isfatarrow, iscase) { michael@0: var a, michael@0: b = inblock, michael@0: old_indent = indent, michael@0: m, michael@0: s = scope, michael@0: t, michael@0: line, michael@0: d; michael@0: michael@0: inblock = ordinary; michael@0: michael@0: if (!ordinary || !state.option.funcscope) michael@0: scope = Object.create(scope); michael@0: michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: t = state.tokens.next; michael@0: michael@0: var metrics = funct["(metrics)"]; michael@0: metrics.nestedBlockDepth += 1; michael@0: metrics.verifyMaxNestedBlockDepthPerFunction(); michael@0: michael@0: if (state.tokens.next.id === "{") { michael@0: advance("{"); michael@0: michael@0: // create a new block scope michael@0: funct["(blockscope)"].stack(); michael@0: michael@0: line = state.tokens.curr.line; michael@0: if (state.tokens.next.id !== "}") { michael@0: indent += state.option.indent; michael@0: while (!ordinary && state.tokens.next.from > indent) { michael@0: indent += state.option.indent; michael@0: } michael@0: michael@0: if (isfunc) { michael@0: m = {}; michael@0: for (d in state.directive) { michael@0: if (_.has(state.directive, d)) { michael@0: m[d] = state.directive[d]; michael@0: } michael@0: } michael@0: directives(); michael@0: michael@0: if (state.option.strict && funct["(context)"]["(global)"]) { michael@0: if (!m["use strict"] && !state.directive["use strict"]) { michael@0: warning("E007"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: a = statements(line); michael@0: michael@0: metrics.statementCount += a.length; michael@0: michael@0: if (isfunc) { michael@0: state.directive = m; michael@0: } michael@0: michael@0: indent -= state.option.indent; michael@0: if (line !== state.tokens.next.line) { michael@0: indentation(); michael@0: } michael@0: } else if (line !== state.tokens.next.line) { michael@0: indentation(); michael@0: } michael@0: advance("}", t); michael@0: michael@0: funct["(blockscope)"].unstack(); michael@0: michael@0: indent = old_indent; michael@0: } else if (!ordinary) { michael@0: if (isfunc) { michael@0: m = {}; michael@0: if (stmt && !isfatarrow && !state.option.inMoz(true)) { michael@0: error("W118", state.tokens.curr, "function closure expressions"); michael@0: } michael@0: michael@0: if (!stmt) { michael@0: for (d in state.directive) { michael@0: if (_.has(state.directive, d)) { michael@0: m[d] = state.directive[d]; michael@0: } michael@0: } michael@0: } michael@0: expression(5); michael@0: michael@0: if (state.option.strict && funct["(context)"]["(global)"]) { michael@0: if (!m["use strict"] && !state.directive["use strict"]) { michael@0: warning("E007"); michael@0: } michael@0: } michael@0: } else { michael@0: error("E021", state.tokens.next, "{", state.tokens.next.value); michael@0: } michael@0: } else { michael@0: michael@0: // check to avoid let declaration not within a block michael@0: funct["(nolet)"] = true; michael@0: michael@0: if (!stmt || state.option.curly) { michael@0: warning("W116", state.tokens.next, "{", state.tokens.next.value); michael@0: } michael@0: michael@0: noreach = true; michael@0: indent += state.option.indent; michael@0: // test indentation only if statement is in new line michael@0: a = [statement(state.tokens.next.line === state.tokens.curr.line)]; michael@0: indent -= state.option.indent; michael@0: noreach = false; michael@0: michael@0: delete funct["(nolet)"]; michael@0: } michael@0: // If it is a "break" in switch case, don't clear and let it propagate out. michael@0: if (!(iscase && funct["(verb)"] === "break")) funct["(verb)"] = null; michael@0: michael@0: if (!ordinary || !state.option.funcscope) scope = s; michael@0: inblock = b; michael@0: if (ordinary && state.option.noempty && (!a || a.length === 0)) { michael@0: warning("W035"); michael@0: } michael@0: metrics.nestedBlockDepth -= 1; michael@0: return a; michael@0: } michael@0: michael@0: michael@0: function countMember(m) { michael@0: if (membersOnly && typeof membersOnly[m] !== "boolean") { michael@0: warning("W036", state.tokens.curr, m); michael@0: } michael@0: if (typeof member[m] === "number") { michael@0: member[m] += 1; michael@0: } else { michael@0: member[m] = 1; michael@0: } michael@0: } michael@0: michael@0: michael@0: function note_implied(tkn) { michael@0: var name = tkn.value, line = tkn.line, a = implied[name]; michael@0: if (typeof a === "function") { michael@0: a = false; michael@0: } michael@0: michael@0: if (!a) { michael@0: a = [line]; michael@0: implied[name] = a; michael@0: } else if (a[a.length - 1] !== line) { michael@0: a.push(line); michael@0: } michael@0: } michael@0: michael@0: michael@0: // Build the syntax table by declaring the syntactic elements of the language. michael@0: michael@0: type("(number)", function () { michael@0: return this; michael@0: }); michael@0: michael@0: type("(string)", function () { michael@0: return this; michael@0: }); michael@0: michael@0: state.syntax["(identifier)"] = { michael@0: type: "(identifier)", michael@0: lbp: 0, michael@0: identifier: true, michael@0: nud: function () { michael@0: var v = this.value, michael@0: s = scope[v], michael@0: f; michael@0: michael@0: if (typeof s === "function") { michael@0: // Protection against accidental inheritance. michael@0: s = undefined; michael@0: } else if (typeof s === "boolean") { michael@0: f = funct; michael@0: funct = functions[0]; michael@0: addlabel(v, "var"); michael@0: s = funct; michael@0: funct = f; michael@0: } michael@0: var block; michael@0: if (_.has(funct, "(blockscope)")) { michael@0: block = funct["(blockscope)"].getlabel(v); michael@0: } michael@0: michael@0: // The name is in scope and defined in the current function. michael@0: if (funct === s || block) { michael@0: // Change 'unused' to 'var', and reject labels. michael@0: // the name is in a block scope michael@0: switch (block ? block[v]["(type)"] : funct[v]) { michael@0: case "unused": michael@0: if (block) block[v]["(type)"] = "var"; michael@0: else funct[v] = "var"; michael@0: break; michael@0: case "unction": michael@0: if (block) block[v]["(type)"] = "function"; michael@0: else funct[v] = "function"; michael@0: this["function"] = true; michael@0: break; michael@0: case "function": michael@0: this["function"] = true; michael@0: break; michael@0: case "label": michael@0: warning("W037", state.tokens.curr, v); michael@0: break; michael@0: } michael@0: } else if (funct["(global)"]) { michael@0: // The name is not defined in the function. If we are in the global michael@0: // scope, then we have an undefined variable. michael@0: // michael@0: // Operators typeof and delete do not raise runtime errors even if michael@0: // the base object of a reference is null so no need to display warning michael@0: // if we're inside of typeof or delete. michael@0: michael@0: if (typeof predefined[v] !== "boolean") { michael@0: // Attempting to subscript a null reference will throw an michael@0: // error, even within the typeof and delete operators michael@0: if (!(anonname === "typeof" || anonname === "delete") || michael@0: (state.tokens.next && (state.tokens.next.value === "." || michael@0: state.tokens.next.value === "["))) { michael@0: michael@0: // if we're in a list comprehension, variables are declared michael@0: // locally and used before being defined. So we check michael@0: // the presence of the given variable in the comp array michael@0: // before declaring it undefined. michael@0: michael@0: if (!funct["(comparray)"].check(v)) { michael@0: isundef(funct, "W117", state.tokens.curr, v); michael@0: } michael@0: } michael@0: } michael@0: michael@0: note_implied(state.tokens.curr); michael@0: } else { michael@0: // If the name is already defined in the current michael@0: // function, but not as outer, then there is a scope error. michael@0: michael@0: switch (funct[v]) { michael@0: case "closure": michael@0: case "function": michael@0: case "var": michael@0: case "unused": michael@0: warning("W038", state.tokens.curr, v); michael@0: break; michael@0: case "label": michael@0: warning("W037", state.tokens.curr, v); michael@0: break; michael@0: case "outer": michael@0: case "global": michael@0: break; michael@0: default: michael@0: // If the name is defined in an outer function, make an outer entry, michael@0: // and if it was unused, make it var. michael@0: if (s === true) { michael@0: funct[v] = true; michael@0: } else if (s === null) { michael@0: warning("W039", state.tokens.curr, v); michael@0: note_implied(state.tokens.curr); michael@0: } else if (typeof s !== "object") { michael@0: // Operators typeof and delete do not raise runtime errors even michael@0: // if the base object of a reference is null so no need to michael@0: // michael@0: // display warning if we're inside of typeof or delete. michael@0: // Attempting to subscript a null reference will throw an michael@0: // error, even within the typeof and delete operators michael@0: if (!(anonname === "typeof" || anonname === "delete") || michael@0: (state.tokens.next && michael@0: (state.tokens.next.value === "." || state.tokens.next.value === "["))) { michael@0: michael@0: isundef(funct, "W117", state.tokens.curr, v); michael@0: } michael@0: funct[v] = true; michael@0: note_implied(state.tokens.curr); michael@0: } else { michael@0: switch (s[v]) { michael@0: case "function": michael@0: case "unction": michael@0: this["function"] = true; michael@0: s[v] = "closure"; michael@0: funct[v] = s["(global)"] ? "global" : "outer"; michael@0: break; michael@0: case "var": michael@0: case "unused": michael@0: s[v] = "closure"; michael@0: funct[v] = s["(global)"] ? "global" : "outer"; michael@0: break; michael@0: case "closure": michael@0: funct[v] = s["(global)"] ? "global" : "outer"; michael@0: break; michael@0: case "label": michael@0: warning("W037", state.tokens.curr, v); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return this; michael@0: }, michael@0: led: function () { michael@0: error("E033", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: }; michael@0: michael@0: type("(regexp)", function () { michael@0: return this; michael@0: }); michael@0: michael@0: // ECMAScript parser michael@0: michael@0: delim("(endline)"); michael@0: delim("(begin)"); michael@0: delim("(end)").reach = true; michael@0: delim("(error)").reach = true; michael@0: delim("}").reach = true; michael@0: delim(")"); michael@0: delim("]"); michael@0: delim("\"").reach = true; michael@0: delim("'").reach = true; michael@0: delim(";"); michael@0: delim(":").reach = true; michael@0: delim("#"); michael@0: michael@0: reserve("else"); michael@0: reserve("case").reach = true; michael@0: reserve("catch"); michael@0: reserve("default").reach = true; michael@0: reserve("finally"); michael@0: reservevar("arguments", function (x) { michael@0: if (state.directive["use strict"] && funct["(global)"]) { michael@0: warning("E008", x); michael@0: } michael@0: }); michael@0: reservevar("eval"); michael@0: reservevar("false"); michael@0: reservevar("Infinity"); michael@0: reservevar("null"); michael@0: reservevar("this", function (x) { michael@0: if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] && michael@0: funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { michael@0: warning("W040", x); michael@0: } michael@0: }); michael@0: reservevar("true"); michael@0: reservevar("undefined"); michael@0: michael@0: assignop("=", "assign", 20); michael@0: assignop("+=", "assignadd", 20); michael@0: assignop("-=", "assignsub", 20); michael@0: assignop("*=", "assignmult", 20); michael@0: assignop("/=", "assigndiv", 20).nud = function () { michael@0: error("E014"); michael@0: }; michael@0: assignop("%=", "assignmod", 20); michael@0: michael@0: bitwiseassignop("&=", "assignbitand", 20); michael@0: bitwiseassignop("|=", "assignbitor", 20); michael@0: bitwiseassignop("^=", "assignbitxor", 20); michael@0: bitwiseassignop("<<=", "assignshiftleft", 20); michael@0: bitwiseassignop(">>=", "assignshiftright", 20); michael@0: bitwiseassignop(">>>=", "assignshiftrightunsigned", 20); michael@0: infix(",", function (left, that) { michael@0: var expr; michael@0: that.exprs = [left]; michael@0: if (!comma({peek: true})) { michael@0: return that; michael@0: } michael@0: while (true) { michael@0: if (!(expr = expression(5))) { michael@0: break; michael@0: } michael@0: that.exprs.push(expr); michael@0: if (state.tokens.next.value !== "," || !comma()) { michael@0: break; michael@0: } michael@0: } michael@0: return that; michael@0: }, 5, true); michael@0: infix("?", function (left, that) { michael@0: that.left = left; michael@0: that.right = expression(10); michael@0: advance(":"); michael@0: that["else"] = expression(10); michael@0: return that; michael@0: }, 30); michael@0: michael@0: infix("||", "or", 40); michael@0: infix("&&", "and", 50); michael@0: bitwise("|", "bitor", 70); michael@0: bitwise("^", "bitxor", 80); michael@0: bitwise("&", "bitand", 90); michael@0: relation("==", function (left, right) { michael@0: var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); michael@0: michael@0: if (!eqnull && state.option.eqeqeq) michael@0: warning("W116", this, "===", "=="); michael@0: else if (isPoorRelation(left)) michael@0: warning("W041", this, "===", left.value); michael@0: else if (isPoorRelation(right)) michael@0: warning("W041", this, "===", right.value); michael@0: michael@0: return this; michael@0: }); michael@0: relation("==="); michael@0: relation("!=", function (left, right) { michael@0: var eqnull = state.option.eqnull && michael@0: (left.value === "null" || right.value === "null"); michael@0: michael@0: if (!eqnull && state.option.eqeqeq) { michael@0: warning("W116", this, "!==", "!="); michael@0: } else if (isPoorRelation(left)) { michael@0: warning("W041", this, "!==", left.value); michael@0: } else if (isPoorRelation(right)) { michael@0: warning("W041", this, "!==", right.value); michael@0: } michael@0: return this; michael@0: }); michael@0: relation("!=="); michael@0: relation("<"); michael@0: relation(">"); michael@0: relation("<="); michael@0: relation(">="); michael@0: bitwise("<<", "shiftleft", 120); michael@0: bitwise(">>", "shiftright", 120); michael@0: bitwise(">>>", "shiftrightunsigned", 120); michael@0: infix("in", "in", 120); michael@0: infix("instanceof", "instanceof", 120); michael@0: infix("+", function (left, that) { michael@0: var right = expression(130); michael@0: if (left && right && left.id === "(string)" && right.id === "(string)") { michael@0: left.value += right.value; michael@0: left.character = right.character; michael@0: if (!state.option.scripturl && reg.javascriptURL.test(left.value)) { michael@0: warning("W050", left); michael@0: } michael@0: return left; michael@0: } michael@0: that.left = left; michael@0: that.right = right; michael@0: return that; michael@0: }, 130); michael@0: prefix("+", "num"); michael@0: prefix("+++", function () { michael@0: warning("W007"); michael@0: this.right = expression(150); michael@0: this.arity = "unary"; michael@0: return this; michael@0: }); michael@0: infix("+++", function (left) { michael@0: warning("W007"); michael@0: this.left = left; michael@0: this.right = expression(130); michael@0: return this; michael@0: }, 130); michael@0: infix("-", "sub", 130); michael@0: prefix("-", "neg"); michael@0: prefix("---", function () { michael@0: warning("W006"); michael@0: this.right = expression(150); michael@0: this.arity = "unary"; michael@0: return this; michael@0: }); michael@0: infix("---", function (left) { michael@0: warning("W006"); michael@0: this.left = left; michael@0: this.right = expression(130); michael@0: return this; michael@0: }, 130); michael@0: infix("*", "mult", 140); michael@0: infix("/", "div", 140); michael@0: infix("%", "mod", 140); michael@0: michael@0: suffix("++", "postinc"); michael@0: prefix("++", "preinc"); michael@0: state.syntax["++"].exps = true; michael@0: michael@0: suffix("--", "postdec"); michael@0: prefix("--", "predec"); michael@0: state.syntax["--"].exps = true; michael@0: prefix("delete", function () { michael@0: var p = expression(5); michael@0: if (!p || (p.id !== "." && p.id !== "[")) { michael@0: warning("W051"); michael@0: } michael@0: this.first = p; michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: prefix("~", function () { michael@0: if (state.option.bitwise) { michael@0: warning("W052", this, "~"); michael@0: } michael@0: expression(150); michael@0: return this; michael@0: }); michael@0: michael@0: prefix("...", function () { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", this, "spread/rest operator"); michael@0: } michael@0: if (!state.tokens.next.identifier) { michael@0: error("E030", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: expression(150); michael@0: return this; michael@0: }); michael@0: michael@0: prefix("!", function () { michael@0: this.right = expression(150); michael@0: this.arity = "unary"; michael@0: michael@0: if (!this.right) { // '!' followed by nothing? Give up. michael@0: quit("E041", this.line || 0); michael@0: } michael@0: michael@0: if (bang[this.right.id] === true) { michael@0: warning("W018", this, "!"); michael@0: } michael@0: return this; michael@0: }); michael@0: michael@0: prefix("typeof", "typeof"); michael@0: prefix("new", function () { michael@0: var c = expression(155), i; michael@0: if (c && c.id !== "function") { michael@0: if (c.identifier) { michael@0: c["new"] = true; michael@0: switch (c.value) { michael@0: case "Number": michael@0: case "String": michael@0: case "Boolean": michael@0: case "Math": michael@0: case "JSON": michael@0: warning("W053", state.tokens.prev, c.value); michael@0: break; michael@0: case "Function": michael@0: if (!state.option.evil) { michael@0: warning("W054"); michael@0: } michael@0: break; michael@0: case "Date": michael@0: case "RegExp": michael@0: break; michael@0: default: michael@0: if (c.id !== "function") { michael@0: i = c.value.substr(0, 1); michael@0: if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { michael@0: warning("W055", state.tokens.curr); michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: if (c.id !== "." && c.id !== "[" && c.id !== "(") { michael@0: warning("W056", state.tokens.curr); michael@0: } michael@0: } michael@0: } else { michael@0: if (!state.option.supernew) michael@0: warning("W057", this); michael@0: } michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: if (state.tokens.next.id !== "(" && !state.option.supernew) { michael@0: warning("W058", state.tokens.curr, state.tokens.curr.value); michael@0: } michael@0: this.first = c; michael@0: return this; michael@0: }); michael@0: state.syntax["new"].exps = true; michael@0: michael@0: prefix("void").exps = true; michael@0: michael@0: infix(".", function (left, that) { michael@0: adjacent(state.tokens.prev, state.tokens.curr); michael@0: nobreak(); michael@0: var m = identifier(false, true); michael@0: michael@0: if (typeof m === "string") { michael@0: countMember(m); michael@0: } michael@0: michael@0: that.left = left; michael@0: that.right = m; michael@0: michael@0: if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") { michael@0: warning("W001"); michael@0: } michael@0: michael@0: if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { michael@0: if (state.option.noarg) michael@0: warning("W059", left, m); michael@0: else if (state.directive["use strict"]) michael@0: error("E008"); michael@0: } else if (!state.option.evil && left && left.value === "document" && michael@0: (m === "write" || m === "writeln")) { michael@0: warning("W060", left); michael@0: } michael@0: michael@0: if (!state.option.evil && (m === "eval" || m === "execScript")) { michael@0: warning("W061"); michael@0: } michael@0: michael@0: return that; michael@0: }, 160, true); michael@0: michael@0: infix("(", function (left, that) { michael@0: if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") { michael@0: nobreak(state.tokens.prev, state.tokens.curr); michael@0: } michael@0: michael@0: nospace(); michael@0: if (state.option.immed && left && !left.immed && left.id === "function") { michael@0: warning("W062"); michael@0: } michael@0: michael@0: var n = 0; michael@0: var p = []; michael@0: michael@0: if (left) { michael@0: if (left.type === "(identifier)") { michael@0: if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { michael@0: if ("Number String Boolean Date Object".indexOf(left.value) === -1) { michael@0: if (left.value === "Math") { michael@0: warning("W063", left); michael@0: } else if (state.option.newcap) { michael@0: warning("W064", left); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (state.tokens.next.id !== ")") { michael@0: for (;;) { michael@0: p[p.length] = expression(10); michael@0: n += 1; michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: } michael@0: michael@0: advance(")"); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: michael@0: if (typeof left === "object") { michael@0: if (left.value === "parseInt" && n === 1) { michael@0: warning("W065", state.tokens.curr); michael@0: } michael@0: if (!state.option.evil) { michael@0: if (left.value === "eval" || left.value === "Function" || michael@0: left.value === "execScript") { michael@0: warning("W061", left); michael@0: michael@0: if (p[0] && [0].id === "(string)") { michael@0: addInternalSrc(left, p[0].value); michael@0: } michael@0: } else if (p[0] && p[0].id === "(string)" && michael@0: (left.value === "setTimeout" || michael@0: left.value === "setInterval")) { michael@0: warning("W066", left); michael@0: addInternalSrc(left, p[0].value); michael@0: michael@0: // window.setTimeout/setInterval michael@0: } else if (p[0] && p[0].id === "(string)" && michael@0: left.value === "." && michael@0: left.left.value === "window" && michael@0: (left.right === "setTimeout" || michael@0: left.right === "setInterval")) { michael@0: warning("W066", left); michael@0: addInternalSrc(left, p[0].value); michael@0: } michael@0: } michael@0: if (!left.identifier && left.id !== "." && left.id !== "[" && michael@0: left.id !== "(" && left.id !== "&&" && left.id !== "||" && michael@0: left.id !== "?") { michael@0: warning("W067", left); michael@0: } michael@0: } michael@0: michael@0: that.left = left; michael@0: return that; michael@0: }, 155, true).exps = true; michael@0: michael@0: prefix("(", function () { michael@0: nospace(); michael@0: var bracket, brackets = []; michael@0: var pn, pn1, i = 0; michael@0: michael@0: do { michael@0: pn = peek(i); michael@0: i += 1; michael@0: pn1 = peek(i); michael@0: i += 1; michael@0: } while (pn.value !== ")" && pn1.value !== "=>" && pn1.value !== ";" && pn1.type !== "(end)"); michael@0: michael@0: if (state.tokens.next.id === "function") { michael@0: state.tokens.next.immed = true; michael@0: } michael@0: michael@0: var exprs = []; michael@0: michael@0: if (state.tokens.next.id !== ")") { michael@0: for (;;) { michael@0: if (pn1.value === "=>" && state.tokens.next.value === "{") { michael@0: bracket = state.tokens.next; michael@0: bracket.left = destructuringExpression(); michael@0: brackets.push(bracket); michael@0: for (var t in bracket.left) { michael@0: exprs.push(bracket.left[t].token); michael@0: } michael@0: } else { michael@0: exprs.push(expression(5)); michael@0: } michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: } michael@0: michael@0: advance(")", this); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: if (state.option.immed && exprs[0] && exprs[0].id === "function") { michael@0: if (state.tokens.next.id !== "(" && michael@0: (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { michael@0: warning("W068", this); michael@0: } michael@0: } michael@0: michael@0: if (state.tokens.next.value === "=>") { michael@0: return exprs; michael@0: } michael@0: if (!exprs.length) { michael@0: return; michael@0: } michael@0: exprs[exprs.length - 1].paren = true; michael@0: if (exprs.length > 1) { michael@0: return Object.create(state.syntax[","], { exprs: { value: exprs } }); michael@0: } michael@0: return exprs[0]; michael@0: }); michael@0: michael@0: application("=>"); michael@0: michael@0: infix("[", function (left, that) { michael@0: nobreak(state.tokens.prev, state.tokens.curr); michael@0: nospace(); michael@0: var e = expression(5), s; michael@0: if (e && e.type === "(string)") { michael@0: if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { michael@0: warning("W061", that); michael@0: } michael@0: michael@0: countMember(e.value); michael@0: if (!state.option.sub && reg.identifier.test(e.value)) { michael@0: s = state.syntax[e.value]; michael@0: if (!s || !isReserved(s)) { michael@0: warning("W069", state.tokens.prev, e.value); michael@0: } michael@0: } michael@0: } michael@0: advance("]", that); michael@0: michael@0: if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") { michael@0: warning("W001"); michael@0: } michael@0: michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: that.left = left; michael@0: that.right = e; michael@0: return that; michael@0: }, 160, true); michael@0: michael@0: function comprehensiveArrayExpression() { michael@0: var res = {}; michael@0: res.exps = true; michael@0: funct["(comparray)"].stack(); michael@0: michael@0: res.right = expression(5); michael@0: advance("for"); michael@0: if (state.tokens.next.value === "each") { michael@0: advance("each"); michael@0: if (!state.option.inMoz(true)) { michael@0: warning("W118", state.tokens.curr, "for each"); michael@0: } michael@0: } michael@0: advance("("); michael@0: funct["(comparray)"].setState("define"); michael@0: res.left = expression(5); michael@0: advance(")"); michael@0: if (state.tokens.next.value === "if") { michael@0: advance("if"); michael@0: advance("("); michael@0: funct["(comparray)"].setState("filter"); michael@0: res.filter = expression(5); michael@0: advance(")"); michael@0: } michael@0: advance("]"); michael@0: funct["(comparray)"].unstack(); michael@0: return res; michael@0: } michael@0: michael@0: prefix("[", function () { michael@0: var blocktype = lookupBlockType(true); michael@0: if (blocktype.isCompArray) { michael@0: if (!state.option.inMoz(true)) { michael@0: warning("W118", state.tokens.curr, "array comprehension"); michael@0: } michael@0: return comprehensiveArrayExpression(); michael@0: } else if (blocktype.isDestAssign && !state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "destructuring assignment"); michael@0: } michael@0: var b = state.tokens.curr.line !== state.tokens.next.line; michael@0: this.first = []; michael@0: if (b) { michael@0: indent += state.option.indent; michael@0: if (state.tokens.next.from === indent + state.option.indent) { michael@0: indent += state.option.indent; michael@0: } michael@0: } michael@0: while (state.tokens.next.id !== "(end)") { michael@0: while (state.tokens.next.id === ",") { michael@0: if (!state.option.inES5()) michael@0: warning("W070"); michael@0: advance(","); michael@0: } michael@0: if (state.tokens.next.id === "]") { michael@0: break; michael@0: } michael@0: if (b && state.tokens.curr.line !== state.tokens.next.line) { michael@0: indentation(); michael@0: } michael@0: this.first.push(expression(10)); michael@0: if (state.tokens.next.id === ",") { michael@0: comma({ allowTrailing: true }); michael@0: if (state.tokens.next.id === "]" && !state.option.inES5(true)) { michael@0: warning("W070", state.tokens.curr); michael@0: break; michael@0: } michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: if (b) { michael@0: indent -= state.option.indent; michael@0: indentation(); michael@0: } michael@0: advance("]", this); michael@0: return this; michael@0: }, 160); michael@0: michael@0: michael@0: function property_name() { michael@0: var id = optionalidentifier(false, true); michael@0: michael@0: if (!id) { michael@0: if (state.tokens.next.id === "(string)") { michael@0: id = state.tokens.next.value; michael@0: advance(); michael@0: } else if (state.tokens.next.id === "(number)") { michael@0: id = state.tokens.next.value.toString(); michael@0: advance(); michael@0: } michael@0: } michael@0: michael@0: if (id === "hasOwnProperty") { michael@0: warning("W001"); michael@0: } michael@0: michael@0: return id; michael@0: } michael@0: michael@0: michael@0: function functionparams(parsed) { michael@0: var curr, next; michael@0: var params = []; michael@0: var ident; michael@0: var tokens = []; michael@0: var t; michael@0: michael@0: if (parsed) { michael@0: if (parsed instanceof Array) { michael@0: for (var i in parsed) { michael@0: curr = parsed[i]; michael@0: if (_.contains(["{", "["], curr.id)) { michael@0: for (t in curr.left) { michael@0: t = tokens[t]; michael@0: if (t.id) { michael@0: params.push(t.id); michael@0: addlabel(t.id, "unused", t.token); michael@0: } michael@0: } michael@0: } else if (curr.value === "...") { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", curr, "spread/rest operator"); michael@0: } michael@0: continue; michael@0: } else { michael@0: addlabel(curr.value, "unused", curr); michael@0: } michael@0: } michael@0: return params; michael@0: } else { michael@0: if (parsed.identifier === true) { michael@0: addlabel(parsed.value, "unused", parsed); michael@0: return [parsed]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: next = state.tokens.next; michael@0: michael@0: advance("("); michael@0: nospace(); michael@0: michael@0: if (state.tokens.next.id === ")") { michael@0: advance(")"); michael@0: return; michael@0: } michael@0: michael@0: for (;;) { michael@0: if (_.contains(["{", "["], state.tokens.next.id)) { michael@0: tokens = destructuringExpression(); michael@0: for (t in tokens) { michael@0: t = tokens[t]; michael@0: if (t.id) { michael@0: params.push(t.id); michael@0: addlabel(t.id, "unused", t.token); michael@0: } michael@0: } michael@0: } else if (state.tokens.next.value === "...") { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.next, "spread/rest operator"); michael@0: } michael@0: advance("..."); michael@0: nospace(); michael@0: ident = identifier(true); michael@0: params.push(ident); michael@0: addlabel(ident, "unused", state.tokens.curr); michael@0: } else { michael@0: ident = identifier(true); michael@0: params.push(ident); michael@0: addlabel(ident, "unused", state.tokens.curr); michael@0: } michael@0: if (state.tokens.next.id === ",") { michael@0: comma(); michael@0: } else { michael@0: advance(")", next); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: return params; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: function doFunction(name, statement, generator, fatarrowparams) { michael@0: var f; michael@0: var oldOption = state.option; michael@0: var oldIgnored = state.ignored; michael@0: var oldScope = scope; michael@0: michael@0: state.option = Object.create(state.option); michael@0: state.ignored = Object.create(state.ignored); michael@0: scope = Object.create(scope); michael@0: michael@0: funct = { michael@0: "(name)" : name || "\"" + anonname + "\"", michael@0: "(line)" : state.tokens.next.line, michael@0: "(character)" : state.tokens.next.character, michael@0: "(context)" : funct, michael@0: "(breakage)" : 0, michael@0: "(loopage)" : 0, michael@0: "(metrics)" : createMetrics(state.tokens.next), michael@0: "(scope)" : scope, michael@0: "(statement)" : statement, michael@0: "(tokens)" : {}, michael@0: "(blockscope)": funct["(blockscope)"], michael@0: "(comparray)" : funct["(comparray)"] michael@0: }; michael@0: michael@0: if (generator) { michael@0: funct["(generator)"] = true; michael@0: } michael@0: michael@0: f = funct; michael@0: state.tokens.curr.funct = funct; michael@0: michael@0: functions.push(funct); michael@0: michael@0: if (name) { michael@0: addlabel(name, "function"); michael@0: } michael@0: michael@0: funct["(params)"] = functionparams(fatarrowparams); michael@0: michael@0: funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); michael@0: michael@0: block(false, true, true, fatarrowparams ? true:false); michael@0: michael@0: if (generator && funct["(generator)"] !== "yielded") { michael@0: error("E047", state.tokens.curr); michael@0: } michael@0: michael@0: funct["(metrics)"].verifyMaxStatementsPerFunction(); michael@0: funct["(metrics)"].verifyMaxComplexityPerFunction(); michael@0: funct["(unusedOption)"] = state.option.unused; michael@0: michael@0: scope = oldScope; michael@0: state.option = oldOption; michael@0: state.ignored = oldIgnored; michael@0: funct["(last)"] = state.tokens.curr.line; michael@0: funct["(lastcharacter)"] = state.tokens.curr.character; michael@0: funct = funct["(context)"]; michael@0: michael@0: return f; michael@0: } michael@0: michael@0: function createMetrics(functionStartToken) { michael@0: return { michael@0: statementCount: 0, michael@0: nestedBlockDepth: -1, michael@0: ComplexityCount: 1, michael@0: verifyMaxStatementsPerFunction: function () { michael@0: if (state.option.maxstatements && michael@0: this.statementCount > state.option.maxstatements) { michael@0: warning("W071", functionStartToken, this.statementCount); michael@0: } michael@0: }, michael@0: michael@0: verifyMaxParametersPerFunction: function (params) { michael@0: params = params || []; michael@0: michael@0: if (state.option.maxparams && params.length > state.option.maxparams) { michael@0: warning("W072", functionStartToken, params.length); michael@0: } michael@0: }, michael@0: michael@0: verifyMaxNestedBlockDepthPerFunction: function () { michael@0: if (state.option.maxdepth && michael@0: this.nestedBlockDepth > 0 && michael@0: this.nestedBlockDepth === state.option.maxdepth + 1) { michael@0: warning("W073", null, this.nestedBlockDepth); michael@0: } michael@0: }, michael@0: michael@0: verifyMaxComplexityPerFunction: function () { michael@0: var max = state.option.maxcomplexity; michael@0: var cc = this.ComplexityCount; michael@0: if (max && cc > max) { michael@0: warning("W074", functionStartToken, cc); michael@0: } michael@0: } michael@0: }; michael@0: } michael@0: michael@0: function increaseComplexityCount() { michael@0: funct["(metrics)"].ComplexityCount += 1; michael@0: } michael@0: michael@0: // Parse assignments that were found instead of conditionals. michael@0: // For example: if (a = 1) { ... } michael@0: michael@0: function checkCondAssignment(expr) { michael@0: var id = expr.id; michael@0: if (id === ",") { michael@0: expr = expr.exprs[expr.exprs.length - 1]; michael@0: id = expr.id; michael@0: } michael@0: switch (id) { michael@0: case "=": michael@0: case "+=": michael@0: case "-=": michael@0: case "*=": michael@0: case "%=": michael@0: case "&=": michael@0: case "|=": michael@0: case "^=": michael@0: case "/=": michael@0: if (!expr.paren && !state.option.boss) { michael@0: warning("W084"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: (function (x) { michael@0: x.nud = function (isclassdef) { michael@0: var b, f, i, p, t, g; michael@0: var props = {}; // All properties, including accessors michael@0: var tag = ""; michael@0: michael@0: function saveProperty(name, tkn) { michael@0: if (props[name] && _.has(props, name)) michael@0: warning("W075", state.tokens.next, i); michael@0: else michael@0: props[name] = {}; michael@0: michael@0: props[name].basic = true; michael@0: props[name].basictkn = tkn; michael@0: } michael@0: michael@0: function saveSetter(name, tkn) { michael@0: if (props[name] && _.has(props, name)) { michael@0: if (props[name].basic || props[name].setter) michael@0: warning("W075", state.tokens.next, i); michael@0: } else { michael@0: props[name] = {}; michael@0: } michael@0: michael@0: props[name].setter = true; michael@0: props[name].setterToken = tkn; michael@0: } michael@0: michael@0: function saveGetter(name) { michael@0: if (props[name] && _.has(props, name)) { michael@0: if (props[name].basic || props[name].getter) michael@0: warning("W075", state.tokens.next, i); michael@0: } else { michael@0: props[name] = {}; michael@0: } michael@0: michael@0: props[name].getter = true; michael@0: props[name].getterToken = state.tokens.curr; michael@0: } michael@0: michael@0: b = state.tokens.curr.line !== state.tokens.next.line; michael@0: if (b) { michael@0: indent += state.option.indent; michael@0: if (state.tokens.next.from === indent + state.option.indent) { michael@0: indent += state.option.indent; michael@0: } michael@0: } michael@0: michael@0: for (;;) { michael@0: if (state.tokens.next.id === "}") { michael@0: break; michael@0: } michael@0: michael@0: if (b) { michael@0: indentation(); michael@0: } michael@0: michael@0: if (isclassdef && state.tokens.next.value === "static") { michael@0: advance("static"); michael@0: tag = "static "; michael@0: } michael@0: michael@0: if (state.tokens.next.value === "get" && peek().id !== ":") { michael@0: advance("get"); michael@0: michael@0: if (!state.option.inES5(!isclassdef)) { michael@0: error("E034"); michael@0: } michael@0: michael@0: i = property_name(); michael@0: if (!i) { michael@0: error("E035"); michael@0: } michael@0: michael@0: // It is a Syntax Error if PropName of MethodDefinition is michael@0: // "constructor" and SpecialMethod of MethodDefinition is true. michael@0: if (isclassdef && i === "constructor") { michael@0: error("E049", state.tokens.next, "class getter method", i); michael@0: } michael@0: michael@0: saveGetter(tag + i); michael@0: t = state.tokens.next; michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: f = doFunction(); michael@0: p = f["(params)"]; michael@0: michael@0: if (p) { michael@0: warning("W076", t, p[0], i); michael@0: } michael@0: michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: } else if (state.tokens.next.value === "set" && peek().id !== ":") { michael@0: advance("set"); michael@0: michael@0: if (!state.option.inES5(!isclassdef)) { michael@0: error("E034"); michael@0: } michael@0: michael@0: i = property_name(); michael@0: if (!i) { michael@0: error("E035"); michael@0: } michael@0: michael@0: // It is a Syntax Error if PropName of MethodDefinition is michael@0: // "constructor" and SpecialMethod of MethodDefinition is true. michael@0: if (isclassdef && i === "constructor") { michael@0: error("E049", state.tokens.next, "class setter method", i); michael@0: } michael@0: michael@0: saveSetter(tag + i, state.tokens.next); michael@0: t = state.tokens.next; michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: f = doFunction(); michael@0: p = f["(params)"]; michael@0: michael@0: if (!p || p.length !== 1) { michael@0: warning("W077", t, i); michael@0: } michael@0: } else { michael@0: g = false; michael@0: if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.next, "generator functions"); michael@0: } michael@0: advance("*"); michael@0: g = true; michael@0: } michael@0: i = property_name(); michael@0: saveProperty(tag + i, state.tokens.next); michael@0: michael@0: if (typeof i !== "string") { michael@0: break; michael@0: } michael@0: michael@0: if (state.tokens.next.value === "(") { michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "concise methods"); michael@0: } michael@0: doFunction(i, undefined, g); michael@0: } else if (!isclassdef) { michael@0: advance(":"); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: expression(10); michael@0: } michael@0: } michael@0: // It is a Syntax Error if PropName of MethodDefinition is "prototype". michael@0: if (isclassdef && i === "prototype") { michael@0: error("E049", state.tokens.next, "class method", i); michael@0: } michael@0: michael@0: countMember(i); michael@0: if (isclassdef) { michael@0: tag = ""; michael@0: continue; michael@0: } michael@0: if (state.tokens.next.id === ",") { michael@0: comma({ allowTrailing: true, property: true }); michael@0: if (state.tokens.next.id === ",") { michael@0: warning("W070", state.tokens.curr); michael@0: } else if (state.tokens.next.id === "}" && !state.option.inES5(true)) { michael@0: warning("W070", state.tokens.curr); michael@0: } michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: if (b) { michael@0: indent -= state.option.indent; michael@0: indentation(); michael@0: } michael@0: advance("}", this); michael@0: michael@0: // Check for lonely setters if in the ES5 mode. michael@0: if (state.option.inES5()) { michael@0: for (var name in props) { michael@0: if (_.has(props, name) && props[name].setter && !props[name].getter) { michael@0: warning("W078", props[name].setterToken); michael@0: } michael@0: } michael@0: } michael@0: return this; michael@0: }; michael@0: x.fud = function () { michael@0: error("E036", state.tokens.curr); michael@0: }; michael@0: }(delim("{"))); michael@0: michael@0: function destructuringExpression() { michael@0: var id, ids; michael@0: var identifiers = []; michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "destructuring expression"); michael@0: } michael@0: var nextInnerDE = function () { michael@0: var ident; michael@0: if (_.contains(["[", "{"], state.tokens.next.value)) { michael@0: ids = destructuringExpression(); michael@0: for (var id in ids) { michael@0: id = ids[id]; michael@0: identifiers.push({ id: id.id, token: id.token }); michael@0: } michael@0: } else if (state.tokens.next.value === ",") { michael@0: identifiers.push({ id: null, token: state.tokens.curr }); michael@0: } else { michael@0: ident = identifier(); michael@0: if (ident) michael@0: identifiers.push({ id: ident, token: state.tokens.curr }); michael@0: } michael@0: }; michael@0: if (state.tokens.next.value === "[") { michael@0: advance("["); michael@0: nextInnerDE(); michael@0: while (state.tokens.next.value !== "]") { michael@0: advance(","); michael@0: nextInnerDE(); michael@0: } michael@0: advance("]"); michael@0: } else if (state.tokens.next.value === "{") { michael@0: advance("{"); michael@0: id = identifier(); michael@0: if (state.tokens.next.value === ":") { michael@0: advance(":"); michael@0: nextInnerDE(); michael@0: } else { michael@0: identifiers.push({ id: id, token: state.tokens.curr }); michael@0: } michael@0: while (state.tokens.next.value !== "}") { michael@0: advance(","); michael@0: id = identifier(); michael@0: if (state.tokens.next.value === ":") { michael@0: advance(":"); michael@0: nextInnerDE(); michael@0: } else { michael@0: identifiers.push({ id: id, token: state.tokens.curr }); michael@0: } michael@0: } michael@0: advance("}"); michael@0: } michael@0: return identifiers; michael@0: } michael@0: function destructuringExpressionMatch(tokens, value) { michael@0: if (value.first) { michael@0: _.zip(tokens, value.first).forEach(function (val) { michael@0: var token = val[0]; michael@0: var value = val[1]; michael@0: if (token && value) { michael@0: token.first = value; michael@0: } else if (token && token.first && !value) { michael@0: warning("W080", token.first, token.first.value); michael@0: } /* else { michael@0: XXX value is discarded: wouldn't it need a warning ? michael@0: } */ michael@0: }); michael@0: } michael@0: } michael@0: michael@0: var conststatement = stmt("const", function (prefix) { michael@0: var tokens, value; michael@0: // state variable to know if it is a lone identifier, or a destructuring statement. michael@0: var lone; michael@0: michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "const"); michael@0: } michael@0: michael@0: this.first = []; michael@0: for (;;) { michael@0: var names = []; michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (_.contains(["{", "["], state.tokens.next.value)) { michael@0: tokens = destructuringExpression(); michael@0: lone = false; michael@0: } else { michael@0: tokens = [ { id: identifier(), token: state.tokens.curr } ]; michael@0: lone = true; michael@0: } michael@0: for (var t in tokens) { michael@0: t = tokens[t]; michael@0: if (funct[t.id] === "const") { michael@0: warning("E011", null, t.id); michael@0: } michael@0: if (funct["(global)"] && predefined[t.id] === false) { michael@0: warning("W079", t.token, t.id); michael@0: } michael@0: if (t.id) { michael@0: addlabel(t.id, "const"); michael@0: names.push(t.token); michael@0: } michael@0: } michael@0: if (prefix) { michael@0: break; michael@0: } michael@0: michael@0: this.first = this.first.concat(names); michael@0: michael@0: if (state.tokens.next.id !== "=") { michael@0: warning("E012", state.tokens.curr, state.tokens.curr.value); michael@0: } michael@0: michael@0: if (state.tokens.next.id === "=") { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: advance("="); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (state.tokens.next.id === "undefined") { michael@0: warning("W080", state.tokens.prev, state.tokens.prev.value); michael@0: } michael@0: if (peek(0).id === "=" && state.tokens.next.identifier) { michael@0: error("E037", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: value = expression(5); michael@0: if (lone) { michael@0: tokens[0].first = value; michael@0: } else { michael@0: destructuringExpressionMatch(names, value); michael@0: } michael@0: } michael@0: michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: return this; michael@0: }); michael@0: conststatement.exps = true; michael@0: var varstatement = stmt("var", function (prefix) { michael@0: // JavaScript does not have block scope. It only has function scope. So, michael@0: // declaring a variable in a block can have unexpected consequences. michael@0: var tokens, lone, value; michael@0: michael@0: if (funct["(onevar)"] && state.option.onevar) { michael@0: warning("W081"); michael@0: } else if (!funct["(global)"]) { michael@0: funct["(onevar)"] = true; michael@0: } michael@0: michael@0: this.first = []; michael@0: for (;;) { michael@0: var names = []; michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (_.contains(["{", "["], state.tokens.next.value)) { michael@0: tokens = destructuringExpression(); michael@0: lone = false; michael@0: } else { michael@0: tokens = [ { id: identifier(), token: state.tokens.curr } ]; michael@0: lone = true; michael@0: } michael@0: for (var t in tokens) { michael@0: t = tokens[t]; michael@0: if (state.option.inESNext() && funct[t.id] === "const") { michael@0: warning("E011", null, t.id); michael@0: } michael@0: if (funct["(global)"] && predefined[t.id] === false) { michael@0: warning("W079", t.token, t.id); michael@0: } michael@0: if (t.id) { michael@0: addlabel(t.id, "unused", t.token); michael@0: names.push(t.token); michael@0: } michael@0: } michael@0: if (prefix) { michael@0: break; michael@0: } michael@0: michael@0: this.first = this.first.concat(names); michael@0: michael@0: if (state.tokens.next.id === "=") { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: advance("="); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (state.tokens.next.id === "undefined") { michael@0: warning("W080", state.tokens.prev, state.tokens.prev.value); michael@0: } michael@0: if (peek(0).id === "=" && state.tokens.next.identifier) { michael@0: error("E038", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: value = expression(5); michael@0: if (lone) { michael@0: tokens[0].first = value; michael@0: } else { michael@0: destructuringExpressionMatch(names, value); michael@0: } michael@0: } michael@0: michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: return this; michael@0: }); michael@0: varstatement.exps = true; michael@0: var letstatement = stmt("let", function (prefix) { michael@0: var tokens, lone, value, letblock; michael@0: michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "let"); michael@0: } michael@0: michael@0: if (state.tokens.next.value === "(") { michael@0: if (!state.option.inMoz(true)) { michael@0: warning("W118", state.tokens.next, "let block"); michael@0: } michael@0: advance("("); michael@0: funct["(blockscope)"].stack(); michael@0: letblock = true; michael@0: } else if (funct["(nolet)"]) { michael@0: error("E048", state.tokens.curr); michael@0: } michael@0: michael@0: if (funct["(onevar)"] && state.option.onevar) { michael@0: warning("W081"); michael@0: } else if (!funct["(global)"]) { michael@0: funct["(onevar)"] = true; michael@0: } michael@0: michael@0: this.first = []; michael@0: for (;;) { michael@0: var names = []; michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (_.contains(["{", "["], state.tokens.next.value)) { michael@0: tokens = destructuringExpression(); michael@0: lone = false; michael@0: } else { michael@0: tokens = [ { id: identifier(), token: state.tokens.curr.value } ]; michael@0: lone = true; michael@0: } michael@0: for (var t in tokens) { michael@0: t = tokens[t]; michael@0: if (state.option.inESNext() && funct[t.id] === "const") { michael@0: warning("E011", null, t.id); michael@0: } michael@0: if (funct["(global)"] && predefined[t.id] === false) { michael@0: warning("W079", t.token, t.id); michael@0: } michael@0: if (t.id && !funct["(nolet)"]) { michael@0: addlabel(t.id, "unused", t.token, true); michael@0: names.push(t.token); michael@0: } michael@0: } michael@0: if (prefix) { michael@0: break; michael@0: } michael@0: michael@0: this.first = this.first.concat(names); michael@0: michael@0: if (state.tokens.next.id === "=") { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: advance("="); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: if (state.tokens.next.id === "undefined") { michael@0: warning("W080", state.tokens.prev, state.tokens.prev.value); michael@0: } michael@0: if (peek(0).id === "=" && state.tokens.next.identifier) { michael@0: error("E037", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: value = expression(5); michael@0: if (lone) { michael@0: tokens[0].first = value; michael@0: } else { michael@0: destructuringExpressionMatch(names, value); michael@0: } michael@0: } michael@0: michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: if (letblock) { michael@0: advance(")"); michael@0: block(true, true); michael@0: this.block = true; michael@0: funct["(blockscope)"].unstack(); michael@0: } michael@0: michael@0: return this; michael@0: }); michael@0: letstatement.exps = true; michael@0: michael@0: blockstmt("class", function () { michael@0: return classdef.call(this, true); michael@0: }); michael@0: michael@0: function classdef(stmt) { michael@0: /*jshint validthis:true */ michael@0: if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "class"); michael@0: } michael@0: if (stmt) { michael@0: // BindingIdentifier michael@0: this.name = identifier(); michael@0: addlabel(this.name, "unused", state.tokens.curr); michael@0: } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { michael@0: // BindingIdentifier(opt) michael@0: this.name = identifier(); michael@0: } michael@0: classtail(this); michael@0: return this; michael@0: } michael@0: michael@0: function classtail(c) { michael@0: var strictness = state.directive["use strict"]; michael@0: michael@0: // ClassHeritage(opt) michael@0: if (state.tokens.next.value === "extends") { michael@0: advance("extends"); michael@0: c.heritage = expression(10); michael@0: } michael@0: michael@0: // A ClassBody is always strict code. michael@0: state.directive["use strict"] = true; michael@0: advance("{"); michael@0: // ClassBody(opt) michael@0: c.body = state.syntax["{"].nud(true); michael@0: state.directive["use strict"] = strictness; michael@0: } michael@0: michael@0: blockstmt("function", function () { michael@0: var generator = false; michael@0: if (state.tokens.next.value === "*") { michael@0: advance("*"); michael@0: if (state.option.inESNext(true)) { michael@0: generator = true; michael@0: } else { michael@0: warning("W119", state.tokens.curr, "function*"); michael@0: } michael@0: } michael@0: if (inblock) { michael@0: warning("W082", state.tokens.curr); michael@0: michael@0: } michael@0: var i = identifier(); michael@0: if (funct[i] === "const") { michael@0: warning("E011", null, i); michael@0: } michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: addlabel(i, "unction", state.tokens.curr); michael@0: michael@0: doFunction(i, { statement: true }, generator); michael@0: if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { michael@0: error("E039"); michael@0: } michael@0: return this; michael@0: }); michael@0: michael@0: prefix("function", function () { michael@0: var generator = false; michael@0: if (state.tokens.next.value === "*") { michael@0: if (!state.option.inESNext()) { michael@0: warning("W119", state.tokens.curr, "function*"); michael@0: } michael@0: advance("*"); michael@0: generator = true; michael@0: } michael@0: var i = optionalidentifier(); michael@0: if (i || state.option.gcl) { michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: } else { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: } michael@0: doFunction(i, undefined, generator); michael@0: if (!state.option.loopfunc && funct["(loopage)"]) { michael@0: warning("W083"); michael@0: } michael@0: return this; michael@0: }); michael@0: michael@0: blockstmt("if", function () { michael@0: var t = state.tokens.next; michael@0: increaseComplexityCount(); michael@0: state.condition = true; michael@0: advance("("); michael@0: nonadjacent(this, t); michael@0: nospace(); michael@0: checkCondAssignment(expression(0)); michael@0: advance(")", t); michael@0: state.condition = false; michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: block(true, true); michael@0: if (state.tokens.next.id === "else") { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: advance("else"); michael@0: if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") { michael@0: statement(true); michael@0: } else { michael@0: block(true, true); michael@0: } michael@0: } michael@0: return this; michael@0: }); michael@0: michael@0: blockstmt("try", function () { michael@0: var b; michael@0: michael@0: function doCatch() { michael@0: var oldScope = scope; michael@0: var e; michael@0: michael@0: advance("catch"); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: advance("("); michael@0: michael@0: scope = Object.create(oldScope); michael@0: michael@0: e = state.tokens.next.value; michael@0: if (state.tokens.next.type !== "(identifier)") { michael@0: e = null; michael@0: warning("E030", state.tokens.next, e); michael@0: } michael@0: michael@0: advance(); michael@0: michael@0: funct = { michael@0: "(name)" : "(catch)", michael@0: "(line)" : state.tokens.next.line, michael@0: "(character)": state.tokens.next.character, michael@0: "(context)" : funct, michael@0: "(breakage)" : funct["(breakage)"], michael@0: "(loopage)" : funct["(loopage)"], michael@0: "(scope)" : scope, michael@0: "(statement)": false, michael@0: "(metrics)" : createMetrics(state.tokens.next), michael@0: "(catch)" : true, michael@0: "(tokens)" : {}, michael@0: "(blockscope)": funct["(blockscope)"], michael@0: "(comparray)": funct["(comparray)"] michael@0: }; michael@0: michael@0: if (e) { michael@0: addlabel(e, "exception"); michael@0: } michael@0: michael@0: if (state.tokens.next.value === "if") { michael@0: if (!state.option.inMoz(true)) { michael@0: warning("W118", state.tokens.curr, "catch filter"); michael@0: } michael@0: advance("if"); michael@0: expression(0); michael@0: } michael@0: michael@0: advance(")"); michael@0: michael@0: state.tokens.curr.funct = funct; michael@0: functions.push(funct); michael@0: michael@0: block(false); michael@0: michael@0: scope = oldScope; michael@0: michael@0: funct["(last)"] = state.tokens.curr.line; michael@0: funct["(lastcharacter)"] = state.tokens.curr.character; michael@0: funct = funct["(context)"]; michael@0: } michael@0: michael@0: block(false); michael@0: michael@0: while (state.tokens.next.id === "catch") { michael@0: increaseComplexityCount(); michael@0: if (b && (!state.option.inMoz(true))) { michael@0: warning("W118", state.tokens.next, "multiple catch blocks"); michael@0: } michael@0: doCatch(); michael@0: b = true; michael@0: } michael@0: michael@0: if (state.tokens.next.id === "finally") { michael@0: advance("finally"); michael@0: block(false); michael@0: return; michael@0: } michael@0: michael@0: if (!b) { michael@0: error("E021", state.tokens.next, "catch", state.tokens.next.value); michael@0: } michael@0: michael@0: return this; michael@0: }); michael@0: michael@0: blockstmt("while", function () { michael@0: var t = state.tokens.next; michael@0: funct["(breakage)"] += 1; michael@0: funct["(loopage)"] += 1; michael@0: increaseComplexityCount(); michael@0: advance("("); michael@0: nonadjacent(this, t); michael@0: nospace(); michael@0: checkCondAssignment(expression(0)); michael@0: advance(")", t); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: block(true, true); michael@0: funct["(breakage)"] -= 1; michael@0: funct["(loopage)"] -= 1; michael@0: return this; michael@0: }).labelled = true; michael@0: michael@0: blockstmt("with", function () { michael@0: var t = state.tokens.next; michael@0: if (state.directive["use strict"]) { michael@0: error("E010", state.tokens.curr); michael@0: } else if (!state.option.withstmt) { michael@0: warning("W085", state.tokens.curr); michael@0: } michael@0: michael@0: advance("("); michael@0: nonadjacent(this, t); michael@0: nospace(); michael@0: expression(0); michael@0: advance(")", t); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: block(true, true); michael@0: michael@0: return this; michael@0: }); michael@0: michael@0: blockstmt("switch", function () { michael@0: var t = state.tokens.next, michael@0: g = false; michael@0: funct["(breakage)"] += 1; michael@0: advance("("); michael@0: nonadjacent(this, t); michael@0: nospace(); michael@0: checkCondAssignment(expression(0)); michael@0: advance(")", t); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: t = state.tokens.next; michael@0: advance("{"); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: indent += state.option.indent; michael@0: this.cases = []; michael@0: michael@0: for (;;) { michael@0: switch (state.tokens.next.id) { michael@0: case "case": michael@0: switch (funct["(verb)"]) { michael@0: case "yield": michael@0: case "break": michael@0: case "case": michael@0: case "continue": michael@0: case "return": michael@0: case "switch": michael@0: case "throw": michael@0: break; michael@0: default: michael@0: // You can tell JSHint that you don't use break intentionally by michael@0: // adding a comment /* falls through */ on a line just before michael@0: // the next `case`. michael@0: if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { michael@0: warning("W086", state.tokens.curr, "case"); michael@0: } michael@0: } michael@0: indentation(-state.option.indent); michael@0: advance("case"); michael@0: this.cases.push(expression(20)); michael@0: increaseComplexityCount(); michael@0: g = true; michael@0: advance(":"); michael@0: funct["(verb)"] = "case"; michael@0: break; michael@0: case "default": michael@0: switch (funct["(verb)"]) { michael@0: case "yield": michael@0: case "break": michael@0: case "continue": michael@0: case "return": michael@0: case "throw": michael@0: break; michael@0: default: michael@0: // Do not display a warning if 'default' is the first statement or if michael@0: // there is a special /* falls through */ comment. michael@0: if (this.cases.length) { michael@0: if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { michael@0: warning("W086", state.tokens.curr, "default"); michael@0: } michael@0: } michael@0: } michael@0: indentation(-state.option.indent); michael@0: advance("default"); michael@0: g = true; michael@0: advance(":"); michael@0: break; michael@0: case "}": michael@0: indent -= state.option.indent; michael@0: indentation(); michael@0: advance("}", t); michael@0: funct["(breakage)"] -= 1; michael@0: funct["(verb)"] = undefined; michael@0: return; michael@0: case "(end)": michael@0: error("E023", state.tokens.next, "}"); michael@0: return; michael@0: default: michael@0: if (g) { michael@0: switch (state.tokens.curr.id) { michael@0: case ",": michael@0: error("E040"); michael@0: return; michael@0: case ":": michael@0: g = false; michael@0: statements(); michael@0: break; michael@0: default: michael@0: error("E025", state.tokens.curr); michael@0: return; michael@0: } michael@0: } else { michael@0: if (state.tokens.curr.id === ":") { michael@0: advance(":"); michael@0: error("E024", state.tokens.curr, ":"); michael@0: statements(); michael@0: } else { michael@0: error("E021", state.tokens.next, "case", state.tokens.next.value); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: }).labelled = true; michael@0: michael@0: stmt("debugger", function () { michael@0: if (!state.option.debug) { michael@0: warning("W087"); michael@0: } michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: (function () { michael@0: var x = stmt("do", function () { michael@0: funct["(breakage)"] += 1; michael@0: funct["(loopage)"] += 1; michael@0: increaseComplexityCount(); michael@0: michael@0: this.first = block(true, true); michael@0: advance("while"); michael@0: var t = state.tokens.next; michael@0: nonadjacent(state.tokens.curr, t); michael@0: advance("("); michael@0: nospace(); michael@0: checkCondAssignment(expression(0)); michael@0: advance(")", t); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: funct["(breakage)"] -= 1; michael@0: funct["(loopage)"] -= 1; michael@0: return this; michael@0: }); michael@0: x.labelled = true; michael@0: x.exps = true; michael@0: }()); michael@0: michael@0: blockstmt("for", function () { michael@0: var s, t = state.tokens.next; michael@0: var letscope = false; michael@0: var foreachtok = null; michael@0: michael@0: if (t.value === "each") { michael@0: foreachtok = t; michael@0: advance("each"); michael@0: if (!state.option.inMoz(true)) { michael@0: warning("W118", state.tokens.curr, "for each"); michael@0: } michael@0: } michael@0: michael@0: funct["(breakage)"] += 1; michael@0: funct["(loopage)"] += 1; michael@0: increaseComplexityCount(); michael@0: advance("("); michael@0: nonadjacent(this, t); michael@0: nospace(); michael@0: michael@0: // what kind of for(…) statement it is? for(…of…)? for(…in…)? for(…;…;…)? michael@0: var nextop; // contains the token of the "in" or "of" operator michael@0: var i = 0; michael@0: var inof = ["in", "of"]; michael@0: do { michael@0: nextop = peek(i); michael@0: ++i; michael@0: } while (!_.contains(inof, nextop.value) && nextop.value !== ";" && michael@0: nextop.type !== "(end)"); michael@0: michael@0: // if we're in a for (… in|of …) statement michael@0: if (_.contains(inof, nextop.value)) { michael@0: if (!state.option.inESNext() && nextop.value === "of") { michael@0: error("W104", nextop, "for of"); michael@0: } michael@0: if (state.tokens.next.id === "var") { michael@0: advance("var"); michael@0: state.syntax["var"].fud.call(state.syntax["var"].fud, true); michael@0: } else if (state.tokens.next.id === "let") { michael@0: advance("let"); michael@0: // create a new block scope michael@0: letscope = true; michael@0: funct["(blockscope)"].stack(); michael@0: state.syntax["let"].fud.call(state.syntax["let"].fud, true); michael@0: } else { michael@0: switch (funct[state.tokens.next.value]) { michael@0: case "unused": michael@0: funct[state.tokens.next.value] = "var"; michael@0: break; michael@0: case "var": michael@0: break; michael@0: default: michael@0: if (!funct["(blockscope)"].getlabel(state.tokens.next.value)) michael@0: warning("W088", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: advance(); michael@0: } michael@0: advance(nextop.value); michael@0: expression(20); michael@0: advance(")", t); michael@0: s = block(true, true); michael@0: if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" || michael@0: s[0].value !== "if")) { michael@0: warning("W089", this); michael@0: } michael@0: funct["(breakage)"] -= 1; michael@0: funct["(loopage)"] -= 1; michael@0: } else { michael@0: if (foreachtok) { michael@0: error("E045", foreachtok); michael@0: } michael@0: if (state.tokens.next.id !== ";") { michael@0: if (state.tokens.next.id === "var") { michael@0: advance("var"); michael@0: state.syntax["var"].fud.call(state.syntax["var"].fud); michael@0: } else if (state.tokens.next.id === "let") { michael@0: advance("let"); michael@0: // create a new block scope michael@0: letscope = true; michael@0: funct["(blockscope)"].stack(); michael@0: state.syntax["let"].fud.call(state.syntax["let"].fud); michael@0: } else { michael@0: for (;;) { michael@0: expression(0, "for"); michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: } michael@0: } michael@0: nolinebreak(state.tokens.curr); michael@0: advance(";"); michael@0: if (state.tokens.next.id !== ";") { michael@0: checkCondAssignment(expression(0)); michael@0: } michael@0: nolinebreak(state.tokens.curr); michael@0: advance(";"); michael@0: if (state.tokens.next.id === ";") { michael@0: error("E021", state.tokens.next, ")", ";"); michael@0: } michael@0: if (state.tokens.next.id !== ")") { michael@0: for (;;) { michael@0: expression(0, "for"); michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: comma(); michael@0: } michael@0: } michael@0: advance(")", t); michael@0: nospace(state.tokens.prev, state.tokens.curr); michael@0: block(true, true); michael@0: funct["(breakage)"] -= 1; michael@0: funct["(loopage)"] -= 1; michael@0: michael@0: } michael@0: // unstack loop blockscope michael@0: if (letscope) { michael@0: funct["(blockscope)"].unstack(); michael@0: } michael@0: return this; michael@0: }).labelled = true; michael@0: michael@0: michael@0: stmt("break", function () { michael@0: var v = state.tokens.next.value; michael@0: michael@0: if (funct["(breakage)"] === 0) michael@0: warning("W052", state.tokens.next, this.value); michael@0: michael@0: if (!state.option.asi) michael@0: nolinebreak(this); michael@0: michael@0: if (state.tokens.next.id !== ";") { michael@0: if (state.tokens.curr.line === state.tokens.next.line) { michael@0: if (funct[v] !== "label") { michael@0: warning("W090", state.tokens.next, v); michael@0: } else if (scope[v] !== funct) { michael@0: warning("W091", state.tokens.next, v); michael@0: } michael@0: this.first = state.tokens.next; michael@0: advance(); michael@0: } michael@0: } michael@0: reachable("break"); michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: michael@0: stmt("continue", function () { michael@0: var v = state.tokens.next.value; michael@0: michael@0: if (funct["(breakage)"] === 0) michael@0: warning("W052", state.tokens.next, this.value); michael@0: michael@0: if (!state.option.asi) michael@0: nolinebreak(this); michael@0: michael@0: if (state.tokens.next.id !== ";") { michael@0: if (state.tokens.curr.line === state.tokens.next.line) { michael@0: if (funct[v] !== "label") { michael@0: warning("W090", state.tokens.next, v); michael@0: } else if (scope[v] !== funct) { michael@0: warning("W091", state.tokens.next, v); michael@0: } michael@0: this.first = state.tokens.next; michael@0: advance(); michael@0: } michael@0: } else if (!funct["(loopage)"]) { michael@0: warning("W052", state.tokens.next, this.value); michael@0: } michael@0: reachable("continue"); michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: michael@0: stmt("return", function () { michael@0: if (this.line === state.tokens.next.line) { michael@0: if (state.tokens.next.id === "(regexp)") michael@0: warning("W092"); michael@0: michael@0: if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: this.first = expression(0); michael@0: michael@0: if (this.first && michael@0: this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) { michael@0: warningAt("W093", this.first.line, this.first.character); michael@0: } michael@0: } michael@0: } else { michael@0: if (state.tokens.next.type === "(punctuator)" && michael@0: ["[", "{", "+", "-"].indexOf(state.tokens.next.value) > -1) { michael@0: nolinebreak(this); // always warn (Line breaking error) michael@0: } michael@0: } michael@0: reachable("return"); michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: stmt("yield", function () { michael@0: if (state.option.inESNext(true) && funct["(generator)"] !== true) { michael@0: error("E046", state.tokens.curr, "yield"); michael@0: } else if (!state.option.inESNext()) { michael@0: warning("W104", state.tokens.curr, "yield"); michael@0: } michael@0: funct["(generator)"] = "yielded"; michael@0: if (this.line === state.tokens.next.line) { michael@0: if (state.tokens.next.id === "(regexp)") michael@0: warning("W092"); michael@0: michael@0: if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: this.first = expression(0); michael@0: michael@0: if (this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) { michael@0: warningAt("W093", this.first.line, this.first.character); michael@0: } michael@0: } michael@0: } else if (!state.option.asi) { michael@0: nolinebreak(this); // always warn (Line breaking error) michael@0: } michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: michael@0: stmt("throw", function () { michael@0: nolinebreak(this); michael@0: nonadjacent(state.tokens.curr, state.tokens.next); michael@0: this.first = expression(20); michael@0: reachable("throw"); michael@0: return this; michael@0: }).exps = true; michael@0: michael@0: // Future Reserved Words michael@0: michael@0: FutureReservedWord("abstract"); michael@0: FutureReservedWord("boolean"); michael@0: FutureReservedWord("byte"); michael@0: FutureReservedWord("char"); michael@0: FutureReservedWord("class", { es5: true, nud: classdef }); michael@0: FutureReservedWord("double"); michael@0: FutureReservedWord("enum", { es5: true }); michael@0: FutureReservedWord("export", { es5: true }); michael@0: FutureReservedWord("extends", { es5: true }); michael@0: FutureReservedWord("final"); michael@0: FutureReservedWord("float"); michael@0: FutureReservedWord("goto"); michael@0: FutureReservedWord("implements", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("import", { es5: true }); michael@0: FutureReservedWord("int"); michael@0: FutureReservedWord("interface", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("long"); michael@0: FutureReservedWord("native"); michael@0: FutureReservedWord("package", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("private", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("protected", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("public", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("short"); michael@0: FutureReservedWord("static", { es5: true, strictOnly: true }); michael@0: FutureReservedWord("super", { es5: true }); michael@0: FutureReservedWord("synchronized"); michael@0: FutureReservedWord("throws"); michael@0: FutureReservedWord("transient"); michael@0: FutureReservedWord("volatile"); michael@0: michael@0: // this function is used to determine wether a squarebracket or a curlybracket michael@0: // expression is a comprehension array, destructuring assignment or a json value. michael@0: michael@0: var lookupBlockType = function () { michael@0: var pn, pn1; michael@0: var i = 0; michael@0: var bracketStack = 0; michael@0: var ret = {}; michael@0: if (_.contains(["[", "{"], state.tokens.curr.value)) michael@0: bracketStack += 1; michael@0: if (_.contains(["[", "{"], state.tokens.next.value)) michael@0: bracketStack += 1; michael@0: if (_.contains(["]", "}"], state.tokens.next.value)) michael@0: bracketStack -= 1; michael@0: do { michael@0: pn = peek(i); michael@0: pn1 = peek(i + 1); michael@0: i = i + 1; michael@0: if (_.contains(["[", "{"], pn.value)) { michael@0: bracketStack += 1; michael@0: } else if (_.contains(["]", "}"], pn.value)) { michael@0: bracketStack -= 1; michael@0: } michael@0: if (pn.identifier && pn.value === "for" && bracketStack === 1) { michael@0: ret.isCompArray = true; michael@0: ret.notJson = true; michael@0: break; michael@0: } michael@0: if (_.contains(["}", "]"], pn.value) && pn1.value === "=") { michael@0: ret.isDestAssign = true; michael@0: ret.notJson = true; michael@0: break; michael@0: } michael@0: if (pn.value === ";") { michael@0: ret.isBlock = true; michael@0: ret.notJson = true; michael@0: } michael@0: } while (bracketStack > 0 && pn.id !== "(end)" && i < 15); michael@0: return ret; michael@0: }; michael@0: michael@0: // Check whether this function has been reached for a destructuring assign with undeclared values michael@0: function destructuringAssignOrJsonValue() { michael@0: // lookup for the assignment (esnext only) michael@0: // if it has semicolons, it is a block, so go parse it as a block michael@0: // or it's not a block, but there are assignments, check for undeclared variables michael@0: michael@0: var block = lookupBlockType(); michael@0: if (block.notJson) { michael@0: if (!state.option.inESNext() && block.isDestAssign) { michael@0: warning("W104", state.tokens.curr, "destructuring assignment"); michael@0: } michael@0: statements(); michael@0: // otherwise parse json value michael@0: } else { michael@0: state.option.laxbreak = true; michael@0: state.jsonMode = true; michael@0: jsonValue(); michael@0: } michael@0: } michael@0: michael@0: // array comprehension parsing function michael@0: // parses and defines the three states of the list comprehension in order michael@0: // to avoid defining global variables, but keeping them to the list comprehension scope michael@0: // only. The order of the states are as follows: michael@0: // * "use" which will be the returned iterative part of the list comprehension michael@0: // * "define" which will define the variables local to the list comprehension michael@0: // * "filter" which will help filter out values michael@0: michael@0: var arrayComprehension = function () { michael@0: var CompArray = function () { michael@0: this.mode = "use"; michael@0: this.variables = []; michael@0: }; michael@0: var _carrays = []; michael@0: var _current; michael@0: function declare(v) { michael@0: var l = _current.variables.filter(function (elt) { michael@0: // if it has, change its undef state michael@0: if (elt.value === v) { michael@0: elt.undef = false; michael@0: return v; michael@0: } michael@0: }).length; michael@0: return l !== 0; michael@0: } michael@0: function use(v) { michael@0: var l = _current.variables.filter(function (elt) { michael@0: // and if it has been defined michael@0: if (elt.value === v && !elt.undef) { michael@0: if (elt.unused === true) { michael@0: elt.unused = false; michael@0: } michael@0: return v; michael@0: } michael@0: }).length; michael@0: // otherwise we warn about it michael@0: return (l === 0); michael@0: } michael@0: return {stack: function () { michael@0: _current = new CompArray(); michael@0: _carrays.push(_current); michael@0: }, michael@0: unstack: function () { michael@0: _current.variables.filter(function (v) { michael@0: if (v.unused) michael@0: warning("W098", v.token, v.value); michael@0: if (v.undef) michael@0: isundef(v.funct, "W117", v.token, v.value); michael@0: }); michael@0: _carrays.splice(_carrays[_carrays.length - 1], 1); michael@0: _current = _carrays[_carrays.length - 1]; michael@0: }, michael@0: setState: function (s) { michael@0: if (_.contains(["use", "define", "filter"], s)) michael@0: _current.mode = s; michael@0: }, michael@0: check: function (v) { michael@0: // When we are in "use" state of the list comp, we enqueue that var michael@0: if (_current && _current.mode === "use") { michael@0: _current.variables.push({funct: funct, michael@0: token: state.tokens.curr, michael@0: value: v, michael@0: undef: true, michael@0: unused: false}); michael@0: return true; michael@0: // When we are in "define" state of the list comp, michael@0: } else if (_current && _current.mode === "define") { michael@0: // check if the variable has been used previously michael@0: if (!declare(v)) { michael@0: _current.variables.push({funct: funct, michael@0: token: state.tokens.curr, michael@0: value: v, michael@0: undef: false, michael@0: unused: true}); michael@0: } michael@0: return true; michael@0: // When we are in "filter" state, michael@0: } else if (_current && _current.mode === "filter") { michael@0: // we check whether current variable has been declared michael@0: if (use(v)) { michael@0: // if not we warn about it michael@0: isundef(funct, "W117", state.tokens.curr, v); michael@0: } michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: michael@0: // Parse JSON michael@0: michael@0: function jsonValue() { michael@0: michael@0: function jsonObject() { michael@0: var o = {}, t = state.tokens.next; michael@0: advance("{"); michael@0: if (state.tokens.next.id !== "}") { michael@0: for (;;) { michael@0: if (state.tokens.next.id === "(end)") { michael@0: error("E026", state.tokens.next, t.line); michael@0: } else if (state.tokens.next.id === "}") { michael@0: warning("W094", state.tokens.curr); michael@0: break; michael@0: } else if (state.tokens.next.id === ",") { michael@0: error("E028", state.tokens.next); michael@0: } else if (state.tokens.next.id !== "(string)") { michael@0: warning("W095", state.tokens.next, state.tokens.next.value); michael@0: } michael@0: if (o[state.tokens.next.value] === true) { michael@0: warning("W075", state.tokens.next, state.tokens.next.value); michael@0: } else if ((state.tokens.next.value === "__proto__" && michael@0: !state.option.proto) || (state.tokens.next.value === "__iterator__" && michael@0: !state.option.iterator)) { michael@0: warning("W096", state.tokens.next, state.tokens.next.value); michael@0: } else { michael@0: o[state.tokens.next.value] = true; michael@0: } michael@0: advance(); michael@0: advance(":"); michael@0: jsonValue(); michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: advance(","); michael@0: } michael@0: } michael@0: advance("}"); michael@0: } michael@0: michael@0: function jsonArray() { michael@0: var t = state.tokens.next; michael@0: advance("["); michael@0: if (state.tokens.next.id !== "]") { michael@0: for (;;) { michael@0: if (state.tokens.next.id === "(end)") { michael@0: error("E027", state.tokens.next, t.line); michael@0: } else if (state.tokens.next.id === "]") { michael@0: warning("W094", state.tokens.curr); michael@0: break; michael@0: } else if (state.tokens.next.id === ",") { michael@0: error("E028", state.tokens.next); michael@0: } michael@0: jsonValue(); michael@0: if (state.tokens.next.id !== ",") { michael@0: break; michael@0: } michael@0: advance(","); michael@0: } michael@0: } michael@0: advance("]"); michael@0: } michael@0: michael@0: switch (state.tokens.next.id) { michael@0: case "{": michael@0: jsonObject(); michael@0: break; michael@0: case "[": michael@0: jsonArray(); michael@0: break; michael@0: case "true": michael@0: case "false": michael@0: case "null": michael@0: case "(number)": michael@0: case "(string)": michael@0: advance(); michael@0: break; michael@0: case "-": michael@0: advance("-"); michael@0: if (state.tokens.curr.character !== state.tokens.next.from) { michael@0: warning("W011", state.tokens.curr); michael@0: } michael@0: adjacent(state.tokens.curr, state.tokens.next); michael@0: advance("(number)"); michael@0: break; michael@0: default: michael@0: error("E003", state.tokens.next); michael@0: } michael@0: } michael@0: michael@0: var blockScope = function () { michael@0: var _current = {}; michael@0: var _variables = [_current]; michael@0: michael@0: function _checkBlockLabels() { michael@0: for (var t in _current) { michael@0: if (_current[t]["(type)"] === "unused") { michael@0: if (state.option.unused) { michael@0: var tkn = _current[t]["(token)"]; michael@0: var line = tkn.line; michael@0: var chr = tkn.character; michael@0: warningAt("W098", line, chr, t); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return { michael@0: stack: function () { michael@0: _current = {}; michael@0: _variables.push(_current); michael@0: }, michael@0: michael@0: unstack: function () { michael@0: _checkBlockLabels(); michael@0: _variables.splice(_variables.length - 1, 1); michael@0: _current = _.last(_variables); michael@0: }, michael@0: michael@0: getlabel: function (l) { michael@0: for (var i = _variables.length - 1 ; i >= 0; --i) { michael@0: if (_.has(_variables[i], l)) { michael@0: return _variables[i]; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: current: { michael@0: has: function (t) { michael@0: return _.has(_current, t); michael@0: }, michael@0: add: function (t, type, tok) { michael@0: _current[t] = { "(type)" : type, michael@0: "(token)": tok }; michael@0: } michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: // The actual JSHINT function itself. michael@0: var itself = function (s, o, g) { michael@0: var a, i, k, x; michael@0: var optionKeys; michael@0: var newOptionObj = {}; michael@0: var newIgnoredObj = {}; michael@0: michael@0: state.reset(); michael@0: michael@0: if (o && o.scope) { michael@0: JSHINT.scope = o.scope; michael@0: } else { michael@0: JSHINT.errors = []; michael@0: JSHINT.undefs = []; michael@0: JSHINT.internals = []; michael@0: JSHINT.blacklist = {}; michael@0: JSHINT.scope = "(main)"; michael@0: } michael@0: michael@0: predefined = Object.create(null); michael@0: combine(predefined, vars.ecmaIdentifiers); michael@0: combine(predefined, vars.reservedVars); michael@0: michael@0: combine(predefined, g || {}); michael@0: michael@0: declared = Object.create(null); michael@0: exported = Object.create(null); michael@0: michael@0: if (o) { michael@0: a = o.predef; michael@0: if (a) { michael@0: if (!Array.isArray(a) && typeof a === "object") { michael@0: a = Object.keys(a); michael@0: } michael@0: michael@0: a.forEach(function (item) { michael@0: var slice, prop; michael@0: michael@0: if (item[0] === "-") { michael@0: slice = item.slice(1); michael@0: JSHINT.blacklist[slice] = slice; michael@0: } else { michael@0: prop = Object.getOwnPropertyDescriptor(o.predef, item); michael@0: predefined[item] = prop ? prop.value : false; michael@0: } michael@0: }); michael@0: } michael@0: michael@0: optionKeys = Object.keys(o); michael@0: for (x = 0; x < optionKeys.length; x++) { michael@0: if (/^-W\d{3}$/g.test(optionKeys[x])) { michael@0: newIgnoredObj[optionKeys[x].slice(1)] = true; michael@0: } else { michael@0: newOptionObj[optionKeys[x]] = o[optionKeys[x]]; michael@0: michael@0: if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) michael@0: newOptionObj["(explicitNewcap)"] = true; michael@0: michael@0: if (optionKeys[x] === "indent") michael@0: newOptionObj["(explicitIndent)"] = o[optionKeys[x]] === false ? false : true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: state.option = newOptionObj; michael@0: state.ignored = newIgnoredObj; michael@0: michael@0: state.option.indent = state.option.indent || 4; michael@0: state.option.maxerr = state.option.maxerr || 50; michael@0: michael@0: indent = 1; michael@0: global = Object.create(predefined); michael@0: scope = global; michael@0: funct = { michael@0: "(global)": true, michael@0: "(name)": "(global)", michael@0: "(scope)": scope, michael@0: "(breakage)": 0, michael@0: "(loopage)": 0, michael@0: "(tokens)": {}, michael@0: "(metrics)": createMetrics(state.tokens.next), michael@0: "(blockscope)": blockScope(), michael@0: "(comparray)": arrayComprehension() michael@0: }; michael@0: functions = [funct]; michael@0: urls = []; michael@0: stack = null; michael@0: member = {}; michael@0: membersOnly = null; michael@0: implied = {}; michael@0: inblock = false; michael@0: lookahead = []; michael@0: warnings = 0; michael@0: unuseds = []; michael@0: michael@0: if (!isString(s) && !Array.isArray(s)) { michael@0: errorAt("E004", 0); michael@0: return false; michael@0: } michael@0: michael@0: api = { michael@0: get isJSON() { michael@0: return state.jsonMode; michael@0: }, michael@0: michael@0: getOption: function (name) { michael@0: return state.option[name] || null; michael@0: }, michael@0: michael@0: getCache: function (name) { michael@0: return state.cache[name]; michael@0: }, michael@0: michael@0: setCache: function (name, value) { michael@0: state.cache[name] = value; michael@0: }, michael@0: michael@0: warn: function (code, data) { michael@0: warningAt.apply(null, [ code, data.line, data.char ].concat(data.data)); michael@0: }, michael@0: michael@0: on: function (names, listener) { michael@0: names.split(" ").forEach(function (name) { michael@0: emitter.on(name, listener); michael@0: }.bind(this)); michael@0: } michael@0: }; michael@0: michael@0: emitter.removeAllListeners(); michael@0: (extraModules || []).forEach(function (func) { michael@0: func(api); michael@0: }); michael@0: michael@0: state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"]; michael@0: michael@0: lex = new Lexer(s); michael@0: michael@0: lex.on("warning", function (ev) { michael@0: warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data)); michael@0: }); michael@0: michael@0: lex.on("error", function (ev) { michael@0: errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data)); michael@0: }); michael@0: michael@0: lex.on("fatal", function (ev) { michael@0: quit("E041", ev.line, ev.from); michael@0: }); michael@0: michael@0: lex.on("Identifier", function (ev) { michael@0: emitter.emit("Identifier", ev); michael@0: }); michael@0: michael@0: lex.on("String", function (ev) { michael@0: emitter.emit("String", ev); michael@0: }); michael@0: michael@0: lex.on("Number", function (ev) { michael@0: emitter.emit("Number", ev); michael@0: }); michael@0: michael@0: lex.start(); michael@0: michael@0: // Check options michael@0: for (var name in o) { michael@0: if (_.has(o, name)) { michael@0: checkOption(name, state.tokens.curr); michael@0: } michael@0: } michael@0: michael@0: assume(); michael@0: michael@0: // combine the passed globals after we've assumed all our options michael@0: combine(predefined, g || {}); michael@0: michael@0: //reset values michael@0: comma.first = true; michael@0: michael@0: try { michael@0: advance(); michael@0: switch (state.tokens.next.id) { michael@0: case "{": michael@0: case "[": michael@0: destructuringAssignOrJsonValue(); michael@0: break; michael@0: default: michael@0: directives(); michael@0: michael@0: if (state.directive["use strict"]) { michael@0: if (!state.option.globalstrict && !state.option.node) { michael@0: warning("W097", state.tokens.prev); michael@0: } michael@0: } michael@0: michael@0: statements(); michael@0: } michael@0: advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined); michael@0: funct["(blockscope)"].unstack(); michael@0: michael@0: var markDefined = function (name, context) { michael@0: do { michael@0: if (typeof context[name] === "string") { michael@0: // JSHINT marks unused variables as 'unused' and michael@0: // unused function declaration as 'unction'. This michael@0: // code changes such instances back 'var' and michael@0: // 'closure' so that the code in JSHINT.data() michael@0: // doesn't think they're unused. michael@0: michael@0: if (context[name] === "unused") michael@0: context[name] = "var"; michael@0: else if (context[name] === "unction") michael@0: context[name] = "closure"; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: context = context["(context)"]; michael@0: } while (context); michael@0: michael@0: return false; michael@0: }; michael@0: michael@0: var clearImplied = function (name, line) { michael@0: if (!implied[name]) michael@0: return; michael@0: michael@0: var newImplied = []; michael@0: for (var i = 0; i < implied[name].length; i += 1) { michael@0: if (implied[name][i] !== line) michael@0: newImplied.push(implied[name][i]); michael@0: } michael@0: michael@0: if (newImplied.length === 0) michael@0: delete implied[name]; michael@0: else michael@0: implied[name] = newImplied; michael@0: }; michael@0: michael@0: var warnUnused = function (name, tkn, type, unused_opt) { michael@0: var line = tkn.line; michael@0: var chr = tkn.character; michael@0: michael@0: if (unused_opt === undefined) { michael@0: unused_opt = state.option.unused; michael@0: } michael@0: michael@0: if (unused_opt === true) { michael@0: unused_opt = "last-param"; michael@0: } michael@0: michael@0: var warnable_types = { michael@0: "vars": ["var"], michael@0: "last-param": ["var", "param"], michael@0: "strict": ["var", "param", "last-param"] michael@0: }; michael@0: michael@0: if (unused_opt) { michael@0: if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { michael@0: warningAt("W098", line, chr, name); michael@0: } michael@0: } michael@0: michael@0: unuseds.push({ michael@0: name: name, michael@0: line: line, michael@0: character: chr michael@0: }); michael@0: }; michael@0: michael@0: var checkUnused = function (func, key) { michael@0: var type = func[key]; michael@0: var tkn = func["(tokens)"][key]; michael@0: michael@0: if (key.charAt(0) === "(") michael@0: return; michael@0: michael@0: if (type !== "unused" && type !== "unction") michael@0: return; michael@0: michael@0: // Params are checked separately from other variables. michael@0: if (func["(params)"] && func["(params)"].indexOf(key) !== -1) michael@0: return; michael@0: michael@0: // Variable is in global scope and defined as exported. michael@0: if (func["(global)"] && _.has(exported, key)) { michael@0: return; michael@0: } michael@0: michael@0: warnUnused(key, tkn, "var"); michael@0: }; michael@0: michael@0: // Check queued 'x is not defined' instances to see if they're still undefined. michael@0: for (i = 0; i < JSHINT.undefs.length; i += 1) { michael@0: k = JSHINT.undefs[i].slice(0); michael@0: michael@0: if (markDefined(k[2].value, k[0])) { michael@0: clearImplied(k[2].value, k[2].line); michael@0: } else if (state.option.undef) { michael@0: warning.apply(warning, k.slice(1)); michael@0: } michael@0: } michael@0: michael@0: functions.forEach(function (func) { michael@0: if (func["(unusedOption)"] === false) { michael@0: return; michael@0: } michael@0: michael@0: for (var key in func) { michael@0: if (_.has(func, key)) { michael@0: checkUnused(func, key); michael@0: } michael@0: } michael@0: michael@0: if (!func["(params)"]) michael@0: return; michael@0: michael@0: var params = func["(params)"].slice(); michael@0: var param = params.pop(); michael@0: var type, unused_opt; michael@0: michael@0: while (param) { michael@0: type = func[param]; michael@0: unused_opt = func["(unusedOption)"] || state.option.unused; michael@0: unused_opt = unused_opt === true ? "last-param" : unused_opt; michael@0: michael@0: // 'undefined' is a special case for (function (window, undefined) { ... })(); michael@0: // patterns. michael@0: michael@0: if (param === "undefined") michael@0: return; michael@0: michael@0: if (type === "unused" || type === "unction") { michael@0: warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); michael@0: } else if (unused_opt === "last-param") { michael@0: return; michael@0: } michael@0: michael@0: param = params.pop(); michael@0: } michael@0: }); michael@0: michael@0: for (var key in declared) { michael@0: if (_.has(declared, key) && !_.has(global, key)) { michael@0: warnUnused(key, declared[key], "var"); michael@0: } michael@0: } michael@0: michael@0: } catch (err) { michael@0: if (err && err.name === "JSHintError") { michael@0: var nt = state.tokens.next || {}; michael@0: JSHINT.errors.push({ michael@0: scope : "(main)", michael@0: raw : err.raw, michael@0: reason : err.message, michael@0: line : err.line || nt.line, michael@0: character : err.character || nt.from michael@0: }, null); michael@0: } else { michael@0: throw err; michael@0: } michael@0: } michael@0: michael@0: // Loop over the listed "internals", and check them as well. michael@0: michael@0: if (JSHINT.scope === "(main)") { michael@0: o = o || {}; michael@0: michael@0: for (i = 0; i < JSHINT.internals.length; i += 1) { michael@0: k = JSHINT.internals[i]; michael@0: o.scope = k.elem; michael@0: itself(k.value, o, g); michael@0: } michael@0: } michael@0: michael@0: return JSHINT.errors.length === 0; michael@0: }; michael@0: michael@0: // Modules. michael@0: itself.addModule = function (func) { michael@0: extraModules.push(func); michael@0: }; michael@0: michael@0: itself.addModule(style.register); michael@0: michael@0: // Data summary. michael@0: itself.data = function () { michael@0: var data = { michael@0: functions: [], michael@0: options: state.option michael@0: }; michael@0: var implieds = []; michael@0: var members = []; michael@0: var fu, f, i, j, n, globals; michael@0: michael@0: if (itself.errors.length) { michael@0: data.errors = itself.errors; michael@0: } michael@0: michael@0: if (state.jsonMode) { michael@0: data.json = true; michael@0: } michael@0: michael@0: for (n in implied) { michael@0: if (_.has(implied, n)) { michael@0: implieds.push({ michael@0: name: n, michael@0: line: implied[n] michael@0: }); michael@0: } michael@0: } michael@0: michael@0: if (implieds.length > 0) { michael@0: data.implieds = implieds; michael@0: } michael@0: michael@0: if (urls.length > 0) { michael@0: data.urls = urls; michael@0: } michael@0: michael@0: globals = Object.keys(scope); michael@0: if (globals.length > 0) { michael@0: data.globals = globals; michael@0: } michael@0: michael@0: for (i = 1; i < functions.length; i += 1) { michael@0: f = functions[i]; michael@0: fu = {}; michael@0: michael@0: for (j = 0; j < functionicity.length; j += 1) { michael@0: fu[functionicity[j]] = []; michael@0: } michael@0: michael@0: for (j = 0; j < functionicity.length; j += 1) { michael@0: if (fu[functionicity[j]].length === 0) { michael@0: delete fu[functionicity[j]]; michael@0: } michael@0: } michael@0: michael@0: fu.name = f["(name)"]; michael@0: fu.param = f["(params)"]; michael@0: fu.line = f["(line)"]; michael@0: fu.character = f["(character)"]; michael@0: fu.last = f["(last)"]; michael@0: fu.lastcharacter = f["(lastcharacter)"]; michael@0: data.functions.push(fu); michael@0: } michael@0: michael@0: if (unuseds.length > 0) { michael@0: data.unused = unuseds; michael@0: } michael@0: michael@0: members = []; michael@0: for (n in member) { michael@0: if (typeof member[n] === "number") { michael@0: data.member = member; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return data; michael@0: }; michael@0: michael@0: itself.jshint = itself; michael@0: michael@0: return itself; michael@0: }()); michael@0: michael@0: // Make JSHINT a Node module, if possible. michael@0: if (typeof exports === "object" && exports) { michael@0: exports.JSHINT = JSHINT; michael@0: } michael@0: michael@0: })() michael@0: },{"events":2,"../shared/vars.js":3,"./lex.js":10,"./reg.js":6,"./state.js":4,"../shared/messages.js":12,"./style.js":5,"console-browserify":7,"underscore":11}],12:[function(require,module,exports){ michael@0: (function(){"use strict"; michael@0: michael@0: var _ = require("underscore"); michael@0: michael@0: var errors = { michael@0: // JSHint options michael@0: E001: "Bad option: '{a}'.", michael@0: E002: "Bad option value.", michael@0: michael@0: // JSHint input michael@0: E003: "Expected a JSON value.", michael@0: E004: "Input is neither a string nor an array of strings.", michael@0: E005: "Input is empty.", michael@0: E006: "Unexpected early end of program.", michael@0: michael@0: // Strict mode michael@0: E007: "Missing \"use strict\" statement.", michael@0: E008: "Strict violation.", michael@0: E009: "Option 'validthis' can't be used in a global scope.", michael@0: E010: "'with' is not allowed in strict mode.", michael@0: michael@0: // Constants michael@0: E011: "const '{a}' has already been declared.", michael@0: E012: "const '{a}' is initialized to 'undefined'.", michael@0: E013: "Attempting to override '{a}' which is a constant.", michael@0: michael@0: // Regular expressions michael@0: E014: "A regular expression literal can be confused with '/='.", michael@0: E015: "Unclosed regular expression.", michael@0: E016: "Invalid regular expression.", michael@0: michael@0: // Tokens michael@0: E017: "Unclosed comment.", michael@0: E018: "Unbegun comment.", michael@0: E019: "Unmatched '{a}'.", michael@0: E020: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", michael@0: E021: "Expected '{a}' and instead saw '{b}'.", michael@0: E022: "Line breaking error '{a}'.", michael@0: E023: "Missing '{a}'.", michael@0: E024: "Unexpected '{a}'.", michael@0: E025: "Missing ':' on a case clause.", michael@0: E026: "Missing '}' to match '{' from line {a}.", michael@0: E027: "Missing ']' to match '[' form line {a}.", michael@0: E028: "Illegal comma.", michael@0: E029: "Unclosed string.", michael@0: michael@0: // Everything else michael@0: E030: "Expected an identifier and instead saw '{a}'.", michael@0: E031: "Bad assignment.", // FIXME: Rephrase michael@0: E032: "Expected a small integer or 'false' and instead saw '{a}'.", michael@0: E033: "Expected an operator and instead saw '{a}'.", michael@0: E034: "get/set are ES5 features.", michael@0: E035: "Missing property name.", michael@0: E036: "Expected to see a statement and instead saw a block.", michael@0: E037: "Constant {a} was not declared correctly.", michael@0: E038: "Variable {a} was not declared correctly.", michael@0: E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.", michael@0: E040: "Each value should have its own case label.", michael@0: E041: "Unrecoverable syntax error.", michael@0: E042: "Stopping.", michael@0: E043: "Too many errors.", michael@0: E044: "'{a}' is already defined and can't be redefined.", michael@0: E045: "Invalid for each loop.", michael@0: E046: "A yield statement shall be within a generator function (with syntax: `function*`)", michael@0: E047: "A generator function shall contain a yield statement.", michael@0: E048: "Let declaration not directly within block.", michael@0: E049: "A {a} cannot be named '{b}'." michael@0: }; michael@0: michael@0: var warnings = { michael@0: W001: "'hasOwnProperty' is a really bad name.", michael@0: W002: "Value of '{a}' may be overwritten in IE.", michael@0: W003: "'{a}' was used before it was defined.", michael@0: W004: "'{a}' is already defined.", michael@0: W005: "A dot following a number can be confused with a decimal point.", michael@0: W006: "Confusing minuses.", michael@0: W007: "Confusing pluses.", michael@0: W008: "A leading decimal point can be confused with a dot: '{a}'.", michael@0: W009: "The array literal notation [] is preferrable.", michael@0: W010: "The object literal notation {} is preferrable.", michael@0: W011: "Unexpected space after '{a}'.", michael@0: W012: "Unexpected space before '{a}'.", michael@0: W013: "Missing space after '{a}'.", michael@0: W014: "Bad line breaking before '{a}'.", michael@0: W015: "Expected '{a}' to have an indentation at {b} instead at {c}.", michael@0: W016: "Unexpected use of '{a}'.", michael@0: W017: "Bad operand.", michael@0: W018: "Confusing use of '{a}'.", michael@0: W019: "Use the isNaN function to compare with NaN.", michael@0: W020: "Read only.", michael@0: W021: "'{a}' is a function.", michael@0: W022: "Do not assign to the exception parameter.", michael@0: W023: "Expected an identifier in an assignment and instead saw a function invocation.", michael@0: W024: "Expected an identifier and instead saw '{a}' (a reserved word).", michael@0: W025: "Missing name in function declaration.", michael@0: W026: "Inner functions should be listed at the top of the outer function.", michael@0: W027: "Unreachable '{a}' after '{b}'.", michael@0: W028: "Label '{a}' on {b} statement.", michael@0: W030: "Expected an assignment or function call and instead saw an expression.", michael@0: W031: "Do not use 'new' for side effects.", michael@0: W032: "Unnecessary semicolon.", michael@0: W033: "Missing semicolon.", michael@0: W034: "Unnecessary directive \"{a}\".", michael@0: W035: "Empty block.", michael@0: W036: "Unexpected /*member '{a}'.", michael@0: W037: "'{a}' is a statement label.", michael@0: W038: "'{a}' used out of scope.", michael@0: W039: "'{a}' is not allowed.", michael@0: W040: "Possible strict violation.", michael@0: W041: "Use '{a}' to compare with '{b}'.", michael@0: W042: "Avoid EOL escaping.", michael@0: W043: "Bad escaping of EOL. Use option multistr if needed.", michael@0: W044: "Bad or unnecessary escaping.", michael@0: W045: "Bad number '{a}'.", michael@0: W046: "Don't use extra leading zeros '{a}'.", michael@0: W047: "A trailing decimal point can be confused with a dot: '{a}'.", michael@0: W048: "Unexpected control character in regular expression.", michael@0: W049: "Unexpected escaped character '{a}' in regular expression.", michael@0: W050: "JavaScript URL.", michael@0: W051: "Variables should not be deleted.", michael@0: W052: "Unexpected '{a}'.", michael@0: W053: "Do not use {a} as a constructor.", michael@0: W054: "The Function constructor is a form of eval.", michael@0: W055: "A constructor name should start with an uppercase letter.", michael@0: W056: "Bad constructor.", michael@0: W057: "Weird construction. Is 'new' unnecessary?", michael@0: W058: "Missing '()' invoking a constructor.", michael@0: W059: "Avoid arguments.{a}.", michael@0: W060: "document.write can be a form of eval.", michael@0: W061: "eval can be harmful.", michael@0: W062: "Wrap an immediate function invocation in parens " + michael@0: "to assist the reader in understanding that the expression " + michael@0: "is the result of a function, and not the function itself.", michael@0: W063: "Math is not a function.", michael@0: W064: "Missing 'new' prefix when invoking a constructor.", michael@0: W065: "Missing radix parameter.", michael@0: W066: "Implied eval. Consider passing a function instead of a string.", michael@0: W067: "Bad invocation.", michael@0: W068: "Wrapping non-IIFE function literals in parens is unnecessary.", michael@0: W069: "['{a}'] is better written in dot notation.", michael@0: W070: "Extra comma. (it breaks older versions of IE)", michael@0: W071: "This function has too many statements. ({a})", michael@0: W072: "This function has too many parameters. ({a})", michael@0: W073: "Blocks are nested too deeply. ({a})", michael@0: W074: "This function's cyclomatic complexity is too high. ({a})", michael@0: W075: "Duplicate key '{a}'.", michael@0: W076: "Unexpected parameter '{a}' in get {b} function.", michael@0: W077: "Expected a single parameter in set {a} function.", michael@0: W078: "Setter is defined without getter.", michael@0: W079: "Redefinition of '{a}'.", michael@0: W080: "It's not necessary to initialize '{a}' to 'undefined'.", michael@0: W081: "Too many var statements.", michael@0: W082: "Function declarations should not be placed in blocks. " + michael@0: "Use a function expression or move the statement to the top of " + michael@0: "the outer function.", michael@0: W083: "Don't make functions within a loop.", michael@0: W084: "Expected a conditional expression and instead saw an assignment.", michael@0: W085: "Don't use 'with'.", michael@0: W086: "Expected a 'break' statement before '{a}'.", michael@0: W087: "Forgotten 'debugger' statement?", michael@0: W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.", michael@0: W089: "The body of a for in should be wrapped in an if statement to filter " + michael@0: "unwanted properties from the prototype.", michael@0: W090: "'{a}' is not a statement label.", michael@0: W091: "'{a}' is out of scope.", michael@0: W092: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.", michael@0: W093: "Did you mean to return a conditional instead of an assignment?", michael@0: W094: "Unexpected comma.", michael@0: W095: "Expected a string and instead saw {a}.", michael@0: W096: "The '{a}' key may produce unexpected results.", michael@0: W097: "Use the function form of \"use strict\".", michael@0: W098: "'{a}' is defined but never used.", michael@0: W099: "Mixed spaces and tabs.", michael@0: W100: "This character may get silently deleted by one or more browsers.", michael@0: W101: "Line is too long.", michael@0: W102: "Trailing whitespace.", michael@0: W103: "The '{a}' property is deprecated.", michael@0: W104: "'{a}' is only available in JavaScript 1.7.", michael@0: W105: "Unexpected {a} in '{b}'.", michael@0: W106: "Identifier '{a}' is not in camel case.", michael@0: W107: "Script URL.", michael@0: W108: "Strings must use doublequote.", michael@0: W109: "Strings must use singlequote.", michael@0: W110: "Mixed double and single quotes.", michael@0: W112: "Unclosed string.", michael@0: W113: "Control character in string: {a}.", michael@0: W114: "Avoid {a}.", michael@0: W115: "Octal literals are not allowed in strict mode.", michael@0: W116: "Expected '{a}' and instead saw '{b}'.", michael@0: W117: "'{a}' is not defined.", michael@0: W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", michael@0: W119: "'{a}' is only available in ES6 (use esnext option)." michael@0: }; michael@0: michael@0: var info = { michael@0: I001: "Comma warnings can be turned off with 'laxcomma'.", michael@0: I002: "Reserved words as properties can be used under the 'es5' option.", michael@0: I003: "ES5 option is now set per default" michael@0: }; michael@0: michael@0: exports.errors = {}; michael@0: exports.warnings = {}; michael@0: exports.info = {}; michael@0: michael@0: _.each(errors, function (desc, code) { michael@0: exports.errors[code] = { code: code, desc: desc }; michael@0: }); michael@0: michael@0: _.each(warnings, function (desc, code) { michael@0: exports.warnings[code] = { code: code, desc: desc }; michael@0: }); michael@0: michael@0: _.each(info, function (desc, code) { michael@0: exports.info[code] = { code: code, desc: desc }; michael@0: }); michael@0: michael@0: })() michael@0: },{"underscore":11}],8:[function(require,module,exports){ michael@0: var events = require('events'); michael@0: michael@0: exports.isArray = isArray; michael@0: exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'}; michael@0: exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'}; michael@0: michael@0: michael@0: exports.print = function () {}; michael@0: exports.puts = function () {}; michael@0: exports.debug = function() {}; michael@0: michael@0: exports.inspect = function(obj, showHidden, depth, colors) { michael@0: var seen = []; michael@0: michael@0: var stylize = function(str, styleType) { michael@0: // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics michael@0: var styles = michael@0: { 'bold' : [1, 22], michael@0: 'italic' : [3, 23], michael@0: 'underline' : [4, 24], michael@0: 'inverse' : [7, 27], michael@0: 'white' : [37, 39], michael@0: 'grey' : [90, 39], michael@0: 'black' : [30, 39], michael@0: 'blue' : [34, 39], michael@0: 'cyan' : [36, 39], michael@0: 'green' : [32, 39], michael@0: 'magenta' : [35, 39], michael@0: 'red' : [31, 39], michael@0: 'yellow' : [33, 39] }; michael@0: michael@0: var style = michael@0: { 'special': 'cyan', michael@0: 'number': 'blue', michael@0: 'boolean': 'yellow', michael@0: 'undefined': 'grey', michael@0: 'null': 'bold', michael@0: 'string': 'green', michael@0: 'date': 'magenta', michael@0: // "name": intentionally not styling michael@0: 'regexp': 'red' }[styleType]; michael@0: michael@0: if (style) { michael@0: return '\033[' + styles[style][0] + 'm' + str + michael@0: '\033[' + styles[style][1] + 'm'; michael@0: } else { michael@0: return str; michael@0: } michael@0: }; michael@0: if (! colors) { michael@0: stylize = function(str, styleType) { return str; }; michael@0: } michael@0: michael@0: function format(value, recurseTimes) { michael@0: // Provide a hook for user-specified inspect functions. michael@0: // Check that value is an object with an inspect function on it michael@0: if (value && typeof value.inspect === 'function' && michael@0: // Filter out the util module, it's inspect function is special michael@0: value !== exports && michael@0: // Also filter out any prototype objects using the circular check. michael@0: !(value.constructor && value.constructor.prototype === value)) { michael@0: return value.inspect(recurseTimes); michael@0: } michael@0: michael@0: // Primitive types cannot have properties michael@0: switch (typeof value) { michael@0: case 'undefined': michael@0: return stylize('undefined', 'undefined'); michael@0: michael@0: case 'string': michael@0: var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') michael@0: .replace(/'/g, "\\'") michael@0: .replace(/\\"/g, '"') + '\''; michael@0: return stylize(simple, 'string'); michael@0: michael@0: case 'number': michael@0: return stylize('' + value, 'number'); michael@0: michael@0: case 'boolean': michael@0: return stylize('' + value, 'boolean'); michael@0: } michael@0: // For some reason typeof null is "object", so special case here. michael@0: if (value === null) { michael@0: return stylize('null', 'null'); michael@0: } michael@0: michael@0: // Look up the keys of the object. michael@0: var visible_keys = Object_keys(value); michael@0: var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys; michael@0: michael@0: // Functions without properties can be shortcutted. michael@0: if (typeof value === 'function' && keys.length === 0) { michael@0: if (isRegExp(value)) { michael@0: return stylize('' + value, 'regexp'); michael@0: } else { michael@0: var name = value.name ? ': ' + value.name : ''; michael@0: return stylize('[Function' + name + ']', 'special'); michael@0: } michael@0: } michael@0: michael@0: // Dates without properties can be shortcutted michael@0: if (isDate(value) && keys.length === 0) { michael@0: return stylize(value.toUTCString(), 'date'); michael@0: } michael@0: michael@0: var base, type, braces; michael@0: // Determine the object type michael@0: if (isArray(value)) { michael@0: type = 'Array'; michael@0: braces = ['[', ']']; michael@0: } else { michael@0: type = 'Object'; michael@0: braces = ['{', '}']; michael@0: } michael@0: michael@0: // Make functions say that they are functions michael@0: if (typeof value === 'function') { michael@0: var n = value.name ? ': ' + value.name : ''; michael@0: base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; michael@0: } else { michael@0: base = ''; michael@0: } michael@0: michael@0: // Make dates with properties first say the date michael@0: if (isDate(value)) { michael@0: base = ' ' + value.toUTCString(); michael@0: } michael@0: michael@0: if (keys.length === 0) { michael@0: return braces[0] + base + braces[1]; michael@0: } michael@0: michael@0: if (recurseTimes < 0) { michael@0: if (isRegExp(value)) { michael@0: return stylize('' + value, 'regexp'); michael@0: } else { michael@0: return stylize('[Object]', 'special'); michael@0: } michael@0: } michael@0: michael@0: seen.push(value); michael@0: michael@0: var output = keys.map(function(key) { michael@0: var name, str; michael@0: if (value.__lookupGetter__) { michael@0: if (value.__lookupGetter__(key)) { michael@0: if (value.__lookupSetter__(key)) { michael@0: str = stylize('[Getter/Setter]', 'special'); michael@0: } else { michael@0: str = stylize('[Getter]', 'special'); michael@0: } michael@0: } else { michael@0: if (value.__lookupSetter__(key)) { michael@0: str = stylize('[Setter]', 'special'); michael@0: } michael@0: } michael@0: } michael@0: if (visible_keys.indexOf(key) < 0) { michael@0: name = '[' + key + ']'; michael@0: } michael@0: if (!str) { michael@0: if (seen.indexOf(value[key]) < 0) { michael@0: if (recurseTimes === null) { michael@0: str = format(value[key]); michael@0: } else { michael@0: str = format(value[key], recurseTimes - 1); michael@0: } michael@0: if (str.indexOf('\n') > -1) { michael@0: if (isArray(value)) { michael@0: str = str.split('\n').map(function(line) { michael@0: return ' ' + line; michael@0: }).join('\n').substr(2); michael@0: } else { michael@0: str = '\n' + str.split('\n').map(function(line) { michael@0: return ' ' + line; michael@0: }).join('\n'); michael@0: } michael@0: } michael@0: } else { michael@0: str = stylize('[Circular]', 'special'); michael@0: } michael@0: } michael@0: if (typeof name === 'undefined') { michael@0: if (type === 'Array' && key.match(/^\d+$/)) { michael@0: return str; michael@0: } michael@0: name = JSON.stringify('' + key); michael@0: if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { michael@0: name = name.substr(1, name.length - 2); michael@0: name = stylize(name, 'name'); michael@0: } else { michael@0: name = name.replace(/'/g, "\\'") michael@0: .replace(/\\"/g, '"') michael@0: .replace(/(^"|"$)/g, "'"); michael@0: name = stylize(name, 'string'); michael@0: } michael@0: } michael@0: michael@0: return name + ': ' + str; michael@0: }); michael@0: michael@0: seen.pop(); michael@0: michael@0: var numLinesEst = 0; michael@0: var length = output.reduce(function(prev, cur) { michael@0: numLinesEst++; michael@0: if (cur.indexOf('\n') >= 0) numLinesEst++; michael@0: return prev + cur.length + 1; michael@0: }, 0); michael@0: michael@0: if (length > 50) { michael@0: output = braces[0] + michael@0: (base === '' ? '' : base + '\n ') + michael@0: ' ' + michael@0: output.join(',\n ') + michael@0: ' ' + michael@0: braces[1]; michael@0: michael@0: } else { michael@0: output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; michael@0: } michael@0: michael@0: return output; michael@0: } michael@0: return format(obj, (typeof depth === 'undefined' ? 2 : depth)); michael@0: }; michael@0: michael@0: michael@0: function isArray(ar) { michael@0: return ar instanceof Array || michael@0: Array.isArray(ar) || michael@0: (ar && ar !== Object.prototype && isArray(ar.__proto__)); michael@0: } michael@0: michael@0: michael@0: function isRegExp(re) { michael@0: return re instanceof RegExp || michael@0: (typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]'); michael@0: } michael@0: michael@0: michael@0: function isDate(d) { michael@0: if (d instanceof Date) return true; michael@0: if (typeof d !== 'object') return false; michael@0: var properties = Date.prototype && Object_getOwnPropertyNames(Date.prototype); michael@0: var proto = d.__proto__ && Object_getOwnPropertyNames(d.__proto__); michael@0: return JSON.stringify(proto) === JSON.stringify(properties); michael@0: } michael@0: michael@0: function pad(n) { michael@0: return n < 10 ? '0' + n.toString(10) : n.toString(10); michael@0: } michael@0: michael@0: var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', michael@0: 'Oct', 'Nov', 'Dec']; michael@0: michael@0: // 26 Feb 16:19:34 michael@0: function timestamp() { michael@0: var d = new Date(); michael@0: var time = [pad(d.getHours()), michael@0: pad(d.getMinutes()), michael@0: pad(d.getSeconds())].join(':'); michael@0: return [d.getDate(), months[d.getMonth()], time].join(' '); michael@0: } michael@0: michael@0: exports.log = function (msg) {}; michael@0: michael@0: exports.pump = null; michael@0: michael@0: var Object_keys = Object.keys || function (obj) { michael@0: var res = []; michael@0: for (var key in obj) res.push(key); michael@0: return res; michael@0: }; michael@0: michael@0: var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) { michael@0: var res = []; michael@0: for (var key in obj) { michael@0: if (Object.hasOwnProperty.call(obj, key)) res.push(key); michael@0: } michael@0: return res; michael@0: }; michael@0: michael@0: var Object_create = Object.create || function (prototype, properties) { michael@0: // from es5-shim michael@0: var object; michael@0: if (prototype === null) { michael@0: object = { '__proto__' : null }; michael@0: } michael@0: else { michael@0: if (typeof prototype !== 'object') { michael@0: throw new TypeError( michael@0: 'typeof prototype[' + (typeof prototype) + '] != \'object\'' michael@0: ); michael@0: } michael@0: var Type = function () {}; michael@0: Type.prototype = prototype; michael@0: object = new Type(); michael@0: object.__proto__ = prototype; michael@0: } michael@0: if (typeof properties !== 'undefined' && Object.defineProperties) { michael@0: Object.defineProperties(object, properties); michael@0: } michael@0: return object; michael@0: }; michael@0: michael@0: exports.inherits = function(ctor, superCtor) { michael@0: ctor.super_ = superCtor; michael@0: ctor.prototype = Object_create(superCtor.prototype, { michael@0: constructor: { michael@0: value: ctor, michael@0: enumerable: false, michael@0: writable: true, michael@0: configurable: true michael@0: } michael@0: }); michael@0: }; michael@0: michael@0: var formatRegExp = /%[sdj%]/g; michael@0: exports.format = function(f) { michael@0: if (typeof f !== 'string') { michael@0: var objects = []; michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: objects.push(exports.inspect(arguments[i])); michael@0: } michael@0: return objects.join(' '); michael@0: } michael@0: michael@0: var i = 1; michael@0: var args = arguments; michael@0: var len = args.length; michael@0: var str = String(f).replace(formatRegExp, function(x) { michael@0: if (x === '%%') return '%'; michael@0: if (i >= len) return x; michael@0: switch (x) { michael@0: case '%s': return String(args[i++]); michael@0: case '%d': return Number(args[i++]); michael@0: case '%j': return JSON.stringify(args[i++]); michael@0: default: michael@0: return x; michael@0: } michael@0: }); michael@0: for(var x = args[i]; i < len; x = args[++i]){ michael@0: if (x === null || typeof x !== 'object') { michael@0: str += ' ' + x; michael@0: } else { michael@0: str += ' ' + exports.inspect(x); michael@0: } michael@0: } michael@0: return str; michael@0: }; michael@0: michael@0: },{"events":2}],9:[function(require,module,exports){ michael@0: (function(){// UTILITY michael@0: var util = require('util'); michael@0: var Buffer = require("buffer").Buffer; michael@0: var pSlice = Array.prototype.slice; michael@0: michael@0: function objectKeys(object) { michael@0: if (Object.keys) return Object.keys(object); michael@0: var result = []; michael@0: for (var name in object) { michael@0: if (Object.prototype.hasOwnProperty.call(object, name)) { michael@0: result.push(name); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // 1. The assert module provides functions that throw michael@0: // AssertionError's when particular conditions are not met. The michael@0: // assert module must conform to the following interface. michael@0: michael@0: var assert = module.exports = ok; michael@0: michael@0: // 2. The AssertionError is defined in assert. michael@0: // new assert.AssertionError({ message: message, michael@0: // actual: actual, michael@0: // expected: expected }) michael@0: michael@0: assert.AssertionError = function AssertionError(options) { michael@0: this.name = 'AssertionError'; michael@0: this.message = options.message; michael@0: this.actual = options.actual; michael@0: this.expected = options.expected; michael@0: this.operator = options.operator; michael@0: var stackStartFunction = options.stackStartFunction || fail; michael@0: michael@0: if (Error.captureStackTrace) { michael@0: Error.captureStackTrace(this, stackStartFunction); michael@0: } michael@0: }; michael@0: util.inherits(assert.AssertionError, Error); michael@0: michael@0: function replacer(key, value) { michael@0: if (value === undefined) { michael@0: return '' + value; michael@0: } michael@0: if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) { michael@0: return value.toString(); michael@0: } michael@0: if (typeof value === 'function' || value instanceof RegExp) { michael@0: return value.toString(); michael@0: } michael@0: return value; michael@0: } michael@0: michael@0: function truncate(s, n) { michael@0: if (typeof s == 'string') { michael@0: return s.length < n ? s : s.slice(0, n); michael@0: } else { michael@0: return s; michael@0: } michael@0: } michael@0: michael@0: assert.AssertionError.prototype.toString = function() { michael@0: if (this.message) { michael@0: return [this.name + ':', this.message].join(' '); michael@0: } else { michael@0: return [ michael@0: this.name + ':', michael@0: truncate(JSON.stringify(this.actual, replacer), 128), michael@0: this.operator, michael@0: truncate(JSON.stringify(this.expected, replacer), 128) michael@0: ].join(' '); michael@0: } michael@0: }; michael@0: michael@0: // assert.AssertionError instanceof Error michael@0: michael@0: assert.AssertionError.__proto__ = Error.prototype; michael@0: michael@0: // At present only the three keys mentioned above are used and michael@0: // understood by the spec. Implementations or sub modules can pass michael@0: // other keys to the AssertionError's constructor - they will be michael@0: // ignored. michael@0: michael@0: // 3. All of the following functions must throw an AssertionError michael@0: // when a corresponding condition is not met, with a message that michael@0: // may be undefined if not provided. All assertion methods provide michael@0: // both the actual and expected values to the assertion error for michael@0: // display purposes. michael@0: michael@0: function fail(actual, expected, message, operator, stackStartFunction) { michael@0: throw new assert.AssertionError({ michael@0: message: message, michael@0: actual: actual, michael@0: expected: expected, michael@0: operator: operator, michael@0: stackStartFunction: stackStartFunction michael@0: }); michael@0: } michael@0: michael@0: // EXTENSION! allows for well behaved errors defined elsewhere. michael@0: assert.fail = fail; michael@0: michael@0: // 4. Pure assertion tests whether a value is truthy, as determined michael@0: // by !!guard. michael@0: // assert.ok(guard, message_opt); michael@0: // This statement is equivalent to assert.equal(true, guard, michael@0: // message_opt);. To test strictly for the value true, use michael@0: // assert.strictEqual(true, guard, message_opt);. michael@0: michael@0: function ok(value, message) { michael@0: if (!!!value) fail(value, true, message, '==', assert.ok); michael@0: } michael@0: assert.ok = ok; michael@0: michael@0: // 5. The equality assertion tests shallow, coercive equality with michael@0: // ==. michael@0: // assert.equal(actual, expected, message_opt); michael@0: michael@0: assert.equal = function equal(actual, expected, message) { michael@0: if (actual != expected) fail(actual, expected, message, '==', assert.equal); michael@0: }; michael@0: michael@0: // 6. The non-equality assertion tests for whether two objects are not equal michael@0: // with != assert.notEqual(actual, expected, message_opt); michael@0: michael@0: assert.notEqual = function notEqual(actual, expected, message) { michael@0: if (actual == expected) { michael@0: fail(actual, expected, message, '!=', assert.notEqual); michael@0: } michael@0: }; michael@0: michael@0: // 7. The equivalence assertion tests a deep equality relation. michael@0: // assert.deepEqual(actual, expected, message_opt); michael@0: michael@0: assert.deepEqual = function deepEqual(actual, expected, message) { michael@0: if (!_deepEqual(actual, expected)) { michael@0: fail(actual, expected, message, 'deepEqual', assert.deepEqual); michael@0: } michael@0: }; michael@0: michael@0: function _deepEqual(actual, expected) { michael@0: // 7.1. All identical values are equivalent, as determined by ===. michael@0: if (actual === expected) { michael@0: return true; michael@0: michael@0: } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { michael@0: if (actual.length != expected.length) return false; michael@0: michael@0: for (var i = 0; i < actual.length; i++) { michael@0: if (actual[i] !== expected[i]) return false; michael@0: } michael@0: michael@0: return true; michael@0: michael@0: // 7.2. If the expected value is a Date object, the actual value is michael@0: // equivalent if it is also a Date object that refers to the same time. michael@0: } else if (actual instanceof Date && expected instanceof Date) { michael@0: return actual.getTime() === expected.getTime(); michael@0: michael@0: // 7.3. Other pairs that do not both pass typeof value == 'object', michael@0: // equivalence is determined by ==. michael@0: } else if (typeof actual != 'object' && typeof expected != 'object') { michael@0: return actual == expected; michael@0: michael@0: // 7.4. For all other Object pairs, including Array objects, equivalence is michael@0: // determined by having the same number of owned properties (as verified michael@0: // with Object.prototype.hasOwnProperty.call), the same set of keys michael@0: // (although not necessarily the same order), equivalent values for every michael@0: // corresponding key, and an identical 'prototype' property. Note: this michael@0: // accounts for both named and indexed properties on Arrays. michael@0: } else { michael@0: return objEquiv(actual, expected); michael@0: } michael@0: } michael@0: michael@0: function isUndefinedOrNull(value) { michael@0: return value === null || value === undefined; michael@0: } michael@0: michael@0: function isArguments(object) { michael@0: return Object.prototype.toString.call(object) == '[object Arguments]'; michael@0: } michael@0: michael@0: function objEquiv(a, b) { michael@0: if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) michael@0: return false; michael@0: // an identical 'prototype' property. michael@0: if (a.prototype !== b.prototype) return false; michael@0: //~~~I've managed to break Object.keys through screwy arguments passing. michael@0: // Converting to array solves the problem. michael@0: if (isArguments(a)) { michael@0: if (!isArguments(b)) { michael@0: return false; michael@0: } michael@0: a = pSlice.call(a); michael@0: b = pSlice.call(b); michael@0: return _deepEqual(a, b); michael@0: } michael@0: try { michael@0: var ka = objectKeys(a), michael@0: kb = objectKeys(b), michael@0: key, i; michael@0: } catch (e) {//happens when one is a string literal and the other isn't michael@0: return false; michael@0: } michael@0: // having the same number of owned properties (keys incorporates michael@0: // hasOwnProperty) michael@0: if (ka.length != kb.length) michael@0: return false; michael@0: //the same set of keys (although not necessarily the same order), michael@0: ka.sort(); michael@0: kb.sort(); michael@0: //~~~cheap key test michael@0: for (i = ka.length - 1; i >= 0; i--) { michael@0: if (ka[i] != kb[i]) michael@0: return false; michael@0: } michael@0: //equivalent values for every corresponding key, and michael@0: //~~~possibly expensive deep test michael@0: for (i = ka.length - 1; i >= 0; i--) { michael@0: key = ka[i]; michael@0: if (!_deepEqual(a[key], b[key])) return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // 8. The non-equivalence assertion tests for any deep inequality. michael@0: // assert.notDeepEqual(actual, expected, message_opt); michael@0: michael@0: assert.notDeepEqual = function notDeepEqual(actual, expected, message) { michael@0: if (_deepEqual(actual, expected)) { michael@0: fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); michael@0: } michael@0: }; michael@0: michael@0: // 9. The strict equality assertion tests strict equality, as determined by ===. michael@0: // assert.strictEqual(actual, expected, message_opt); michael@0: michael@0: assert.strictEqual = function strictEqual(actual, expected, message) { michael@0: if (actual !== expected) { michael@0: fail(actual, expected, message, '===', assert.strictEqual); michael@0: } michael@0: }; michael@0: michael@0: // 10. The strict non-equality assertion tests for strict inequality, as michael@0: // determined by !==. assert.notStrictEqual(actual, expected, message_opt); michael@0: michael@0: assert.notStrictEqual = function notStrictEqual(actual, expected, message) { michael@0: if (actual === expected) { michael@0: fail(actual, expected, message, '!==', assert.notStrictEqual); michael@0: } michael@0: }; michael@0: michael@0: function expectedException(actual, expected) { michael@0: if (!actual || !expected) { michael@0: return false; michael@0: } michael@0: michael@0: if (expected instanceof RegExp) { michael@0: return expected.test(actual); michael@0: } else if (actual instanceof expected) { michael@0: return true; michael@0: } else if (expected.call({}, actual) === true) { michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: function _throws(shouldThrow, block, expected, message) { michael@0: var actual; michael@0: michael@0: if (typeof expected === 'string') { michael@0: message = expected; michael@0: expected = null; michael@0: } michael@0: michael@0: try { michael@0: block(); michael@0: } catch (e) { michael@0: actual = e; michael@0: } michael@0: michael@0: message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + michael@0: (message ? ' ' + message : '.'); michael@0: michael@0: if (shouldThrow && !actual) { michael@0: fail('Missing expected exception' + message); michael@0: } michael@0: michael@0: if (!shouldThrow && expectedException(actual, expected)) { michael@0: fail('Got unwanted exception' + message); michael@0: } michael@0: michael@0: if ((shouldThrow && actual && expected && michael@0: !expectedException(actual, expected)) || (!shouldThrow && actual)) { michael@0: throw actual; michael@0: } michael@0: } michael@0: michael@0: // 11. Expected to throw an error: michael@0: // assert.throws(block, Error_opt, message_opt); michael@0: michael@0: assert.throws = function(block, /*optional*/error, /*optional*/message) { michael@0: _throws.apply(this, [true].concat(pSlice.call(arguments))); michael@0: }; michael@0: michael@0: // EXTENSION! This is annoying to write outside this module. michael@0: assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { michael@0: _throws.apply(this, [false].concat(pSlice.call(arguments))); michael@0: }; michael@0: michael@0: assert.ifError = function(err) { if (err) {throw err;}}; michael@0: michael@0: })() michael@0: },{"util":8,"buffer":13}],11:[function(require,module,exports){ michael@0: (function(){// Underscore.js 1.4.4 michael@0: // http://underscorejs.org michael@0: // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. michael@0: // Underscore may be freely distributed under the MIT license. michael@0: michael@0: (function() { michael@0: michael@0: // Baseline setup michael@0: // -------------- michael@0: michael@0: // Establish the root object, `window` in the browser, or `global` on the server. michael@0: var root = this; michael@0: michael@0: // Save the previous value of the `_` variable. michael@0: var previousUnderscore = root._; michael@0: michael@0: // Establish the object that gets returned to break out of a loop iteration. michael@0: var breaker = {}; michael@0: michael@0: // Save bytes in the minified (but not gzipped) version: michael@0: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; michael@0: michael@0: // Create quick reference variables for speed access to core prototypes. michael@0: var push = ArrayProto.push, michael@0: slice = ArrayProto.slice, michael@0: concat = ArrayProto.concat, michael@0: toString = ObjProto.toString, michael@0: hasOwnProperty = ObjProto.hasOwnProperty; michael@0: michael@0: // All **ECMAScript 5** native function implementations that we hope to use michael@0: // are declared here. michael@0: var michael@0: nativeForEach = ArrayProto.forEach, michael@0: nativeMap = ArrayProto.map, michael@0: nativeReduce = ArrayProto.reduce, michael@0: nativeReduceRight = ArrayProto.reduceRight, michael@0: nativeFilter = ArrayProto.filter, michael@0: nativeEvery = ArrayProto.every, michael@0: nativeSome = ArrayProto.some, michael@0: nativeIndexOf = ArrayProto.indexOf, michael@0: nativeLastIndexOf = ArrayProto.lastIndexOf, michael@0: nativeIsArray = Array.isArray, michael@0: nativeKeys = Object.keys, michael@0: nativeBind = FuncProto.bind; michael@0: michael@0: // Create a safe reference to the Underscore object for use below. michael@0: var _ = function(obj) { michael@0: if (obj instanceof _) return obj; michael@0: if (!(this instanceof _)) return new _(obj); michael@0: this._wrapped = obj; michael@0: }; michael@0: michael@0: // Export the Underscore object for **Node.js**, with michael@0: // backwards-compatibility for the old `require()` API. If we're in michael@0: // the browser, add `_` as a global object via a string identifier, michael@0: // for Closure Compiler "advanced" mode. michael@0: if (typeof exports !== 'undefined') { michael@0: if (typeof module !== 'undefined' && module.exports) { michael@0: exports = module.exports = _; michael@0: } michael@0: exports._ = _; michael@0: } else { michael@0: root._ = _; michael@0: } michael@0: michael@0: // Current version. michael@0: _.VERSION = '1.4.4'; michael@0: michael@0: // Collection Functions michael@0: // -------------------- michael@0: michael@0: // The cornerstone, an `each` implementation, aka `forEach`. michael@0: // Handles objects with the built-in `forEach`, arrays, and raw objects. michael@0: // Delegates to **ECMAScript 5**'s native `forEach` if available. michael@0: var each = _.each = _.forEach = function(obj, iterator, context) { michael@0: if (obj == null) return; michael@0: if (nativeForEach && obj.forEach === nativeForEach) { michael@0: obj.forEach(iterator, context); michael@0: } else if (obj.length === +obj.length) { michael@0: for (var i = 0, l = obj.length; i < l; i++) { michael@0: if (iterator.call(context, obj[i], i, obj) === breaker) return; michael@0: } michael@0: } else { michael@0: for (var key in obj) { michael@0: if (_.has(obj, key)) { michael@0: if (iterator.call(context, obj[key], key, obj) === breaker) return; michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: // Return the results of applying the iterator to each element. michael@0: // Delegates to **ECMAScript 5**'s native `map` if available. michael@0: _.map = _.collect = function(obj, iterator, context) { michael@0: var results = []; michael@0: if (obj == null) return results; michael@0: if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); michael@0: each(obj, function(value, index, list) { michael@0: results[results.length] = iterator.call(context, value, index, list); michael@0: }); michael@0: return results; michael@0: }; michael@0: michael@0: var reduceError = 'Reduce of empty array with no initial value'; michael@0: michael@0: // **Reduce** builds up a single result from a list of values, aka `inject`, michael@0: // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. michael@0: _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { michael@0: var initial = arguments.length > 2; michael@0: if (obj == null) obj = []; michael@0: if (nativeReduce && obj.reduce === nativeReduce) { michael@0: if (context) iterator = _.bind(iterator, context); michael@0: return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); michael@0: } michael@0: each(obj, function(value, index, list) { michael@0: if (!initial) { michael@0: memo = value; michael@0: initial = true; michael@0: } else { michael@0: memo = iterator.call(context, memo, value, index, list); michael@0: } michael@0: }); michael@0: if (!initial) throw new TypeError(reduceError); michael@0: return memo; michael@0: }; michael@0: michael@0: // The right-associative version of reduce, also known as `foldr`. michael@0: // Delegates to **ECMAScript 5**'s native `reduceRight` if available. michael@0: _.reduceRight = _.foldr = function(obj, iterator, memo, context) { michael@0: var initial = arguments.length > 2; michael@0: if (obj == null) obj = []; michael@0: if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { michael@0: if (context) iterator = _.bind(iterator, context); michael@0: return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); michael@0: } michael@0: var length = obj.length; michael@0: if (length !== +length) { michael@0: var keys = _.keys(obj); michael@0: length = keys.length; michael@0: } michael@0: each(obj, function(value, index, list) { michael@0: index = keys ? keys[--length] : --length; michael@0: if (!initial) { michael@0: memo = obj[index]; michael@0: initial = true; michael@0: } else { michael@0: memo = iterator.call(context, memo, obj[index], index, list); michael@0: } michael@0: }); michael@0: if (!initial) throw new TypeError(reduceError); michael@0: return memo; michael@0: }; michael@0: michael@0: // Return the first value which passes a truth test. Aliased as `detect`. michael@0: _.find = _.detect = function(obj, iterator, context) { michael@0: var result; michael@0: any(obj, function(value, index, list) { michael@0: if (iterator.call(context, value, index, list)) { michael@0: result = value; michael@0: return true; michael@0: } michael@0: }); michael@0: return result; michael@0: }; michael@0: michael@0: // Return all the elements that pass a truth test. michael@0: // Delegates to **ECMAScript 5**'s native `filter` if available. michael@0: // Aliased as `select`. michael@0: _.filter = _.select = function(obj, iterator, context) { michael@0: var results = []; michael@0: if (obj == null) return results; michael@0: if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); michael@0: each(obj, function(value, index, list) { michael@0: if (iterator.call(context, value, index, list)) results[results.length] = value; michael@0: }); michael@0: return results; michael@0: }; michael@0: michael@0: // Return all the elements for which a truth test fails. michael@0: _.reject = function(obj, iterator, context) { michael@0: return _.filter(obj, function(value, index, list) { michael@0: return !iterator.call(context, value, index, list); michael@0: }, context); michael@0: }; michael@0: michael@0: // Determine whether all of the elements match a truth test. michael@0: // Delegates to **ECMAScript 5**'s native `every` if available. michael@0: // Aliased as `all`. michael@0: _.every = _.all = function(obj, iterator, context) { michael@0: iterator || (iterator = _.identity); michael@0: var result = true; michael@0: if (obj == null) return result; michael@0: if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); michael@0: each(obj, function(value, index, list) { michael@0: if (!(result = result && iterator.call(context, value, index, list))) return breaker; michael@0: }); michael@0: return !!result; michael@0: }; michael@0: michael@0: // Determine if at least one element in the object matches a truth test. michael@0: // Delegates to **ECMAScript 5**'s native `some` if available. michael@0: // Aliased as `any`. michael@0: var any = _.some = _.any = function(obj, iterator, context) { michael@0: iterator || (iterator = _.identity); michael@0: var result = false; michael@0: if (obj == null) return result; michael@0: if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); michael@0: each(obj, function(value, index, list) { michael@0: if (result || (result = iterator.call(context, value, index, list))) return breaker; michael@0: }); michael@0: return !!result; michael@0: }; michael@0: michael@0: // Determine if the array or object contains a given value (using `===`). michael@0: // Aliased as `include`. michael@0: _.contains = _.include = function(obj, target) { michael@0: if (obj == null) return false; michael@0: if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; michael@0: return any(obj, function(value) { michael@0: return value === target; michael@0: }); michael@0: }; michael@0: michael@0: // Invoke a method (with arguments) on every item in a collection. michael@0: _.invoke = function(obj, method) { michael@0: var args = slice.call(arguments, 2); michael@0: var isFunc = _.isFunction(method); michael@0: return _.map(obj, function(value) { michael@0: return (isFunc ? method : value[method]).apply(value, args); michael@0: }); michael@0: }; michael@0: michael@0: // Convenience version of a common use case of `map`: fetching a property. michael@0: _.pluck = function(obj, key) { michael@0: return _.map(obj, function(value){ return value[key]; }); michael@0: }; michael@0: michael@0: // Convenience version of a common use case of `filter`: selecting only objects michael@0: // containing specific `key:value` pairs. michael@0: _.where = function(obj, attrs, first) { michael@0: if (_.isEmpty(attrs)) return first ? null : []; michael@0: return _[first ? 'find' : 'filter'](obj, function(value) { michael@0: for (var key in attrs) { michael@0: if (attrs[key] !== value[key]) return false; michael@0: } michael@0: return true; michael@0: }); michael@0: }; michael@0: michael@0: // Convenience version of a common use case of `find`: getting the first object michael@0: // containing specific `key:value` pairs. michael@0: _.findWhere = function(obj, attrs) { michael@0: return _.where(obj, attrs, true); michael@0: }; michael@0: michael@0: // Return the maximum element or (element-based computation). michael@0: // Can't optimize arrays of integers longer than 65,535 elements. michael@0: // See: https://bugs.webkit.org/show_bug.cgi?id=80797 michael@0: _.max = function(obj, iterator, context) { michael@0: if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { michael@0: return Math.max.apply(Math, obj); michael@0: } michael@0: if (!iterator && _.isEmpty(obj)) return -Infinity; michael@0: var result = {computed : -Infinity, value: -Infinity}; michael@0: each(obj, function(value, index, list) { michael@0: var computed = iterator ? iterator.call(context, value, index, list) : value; michael@0: computed >= result.computed && (result = {value : value, computed : computed}); michael@0: }); michael@0: return result.value; michael@0: }; michael@0: michael@0: // Return the minimum element (or element-based computation). michael@0: _.min = function(obj, iterator, context) { michael@0: if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { michael@0: return Math.min.apply(Math, obj); michael@0: } michael@0: if (!iterator && _.isEmpty(obj)) return Infinity; michael@0: var result = {computed : Infinity, value: Infinity}; michael@0: each(obj, function(value, index, list) { michael@0: var computed = iterator ? iterator.call(context, value, index, list) : value; michael@0: computed < result.computed && (result = {value : value, computed : computed}); michael@0: }); michael@0: return result.value; michael@0: }; michael@0: michael@0: // Shuffle an array. michael@0: _.shuffle = function(obj) { michael@0: var rand; michael@0: var index = 0; michael@0: var shuffled = []; michael@0: each(obj, function(value) { michael@0: rand = _.random(index++); michael@0: shuffled[index - 1] = shuffled[rand]; michael@0: shuffled[rand] = value; michael@0: }); michael@0: return shuffled; michael@0: }; michael@0: michael@0: // An internal function to generate lookup iterators. michael@0: var lookupIterator = function(value) { michael@0: return _.isFunction(value) ? value : function(obj){ return obj[value]; }; michael@0: }; michael@0: michael@0: // Sort the object's values by a criterion produced by an iterator. michael@0: _.sortBy = function(obj, value, context) { michael@0: var iterator = lookupIterator(value); michael@0: return _.pluck(_.map(obj, function(value, index, list) { michael@0: return { michael@0: value : value, michael@0: index : index, michael@0: criteria : iterator.call(context, value, index, list) michael@0: }; michael@0: }).sort(function(left, right) { michael@0: var a = left.criteria; michael@0: var b = right.criteria; michael@0: if (a !== b) { michael@0: if (a > b || a === void 0) return 1; michael@0: if (a < b || b === void 0) return -1; michael@0: } michael@0: return left.index < right.index ? -1 : 1; michael@0: }), 'value'); michael@0: }; michael@0: michael@0: // An internal function used for aggregate "group by" operations. michael@0: var group = function(obj, value, context, behavior) { michael@0: var result = {}; michael@0: var iterator = lookupIterator(value || _.identity); michael@0: each(obj, function(value, index) { michael@0: var key = iterator.call(context, value, index, obj); michael@0: behavior(result, key, value); michael@0: }); michael@0: return result; michael@0: }; michael@0: michael@0: // Groups the object's values by a criterion. Pass either a string attribute michael@0: // to group by, or a function that returns the criterion. michael@0: _.groupBy = function(obj, value, context) { michael@0: return group(obj, value, context, function(result, key, value) { michael@0: (_.has(result, key) ? result[key] : (result[key] = [])).push(value); michael@0: }); michael@0: }; michael@0: michael@0: // Counts instances of an object that group by a certain criterion. Pass michael@0: // either a string attribute to count by, or a function that returns the michael@0: // criterion. michael@0: _.countBy = function(obj, value, context) { michael@0: return group(obj, value, context, function(result, key) { michael@0: if (!_.has(result, key)) result[key] = 0; michael@0: result[key]++; michael@0: }); michael@0: }; michael@0: michael@0: // Use a comparator function to figure out the smallest index at which michael@0: // an object should be inserted so as to maintain order. Uses binary search. michael@0: _.sortedIndex = function(array, obj, iterator, context) { michael@0: iterator = iterator == null ? _.identity : lookupIterator(iterator); michael@0: var value = iterator.call(context, obj); michael@0: var low = 0, high = array.length; michael@0: while (low < high) { michael@0: var mid = (low + high) >>> 1; michael@0: iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; michael@0: } michael@0: return low; michael@0: }; michael@0: michael@0: // Safely convert anything iterable into a real, live array. michael@0: _.toArray = function(obj) { michael@0: if (!obj) return []; michael@0: if (_.isArray(obj)) return slice.call(obj); michael@0: if (obj.length === +obj.length) return _.map(obj, _.identity); michael@0: return _.values(obj); michael@0: }; michael@0: michael@0: // Return the number of elements in an object. michael@0: _.size = function(obj) { michael@0: if (obj == null) return 0; michael@0: return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; michael@0: }; michael@0: michael@0: // Array Functions michael@0: // --------------- michael@0: michael@0: // Get the first element of an array. Passing **n** will return the first N michael@0: // values in the array. Aliased as `head` and `take`. The **guard** check michael@0: // allows it to work with `_.map`. michael@0: _.first = _.head = _.take = function(array, n, guard) { michael@0: if (array == null) return void 0; michael@0: return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; michael@0: }; michael@0: michael@0: // Returns everything but the last entry of the array. Especially useful on michael@0: // the arguments object. Passing **n** will return all the values in michael@0: // the array, excluding the last N. The **guard** check allows it to work with michael@0: // `_.map`. michael@0: _.initial = function(array, n, guard) { michael@0: return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); michael@0: }; michael@0: michael@0: // Get the last element of an array. Passing **n** will return the last N michael@0: // values in the array. The **guard** check allows it to work with `_.map`. michael@0: _.last = function(array, n, guard) { michael@0: if (array == null) return void 0; michael@0: if ((n != null) && !guard) { michael@0: return slice.call(array, Math.max(array.length - n, 0)); michael@0: } else { michael@0: return array[array.length - 1]; michael@0: } michael@0: }; michael@0: michael@0: // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. michael@0: // Especially useful on the arguments object. Passing an **n** will return michael@0: // the rest N values in the array. The **guard** michael@0: // check allows it to work with `_.map`. michael@0: _.rest = _.tail = _.drop = function(array, n, guard) { michael@0: return slice.call(array, (n == null) || guard ? 1 : n); michael@0: }; michael@0: michael@0: // Trim out all falsy values from an array. michael@0: _.compact = function(array) { michael@0: return _.filter(array, _.identity); michael@0: }; michael@0: michael@0: // Internal implementation of a recursive `flatten` function. michael@0: var flatten = function(input, shallow, output) { michael@0: each(input, function(value) { michael@0: if (_.isArray(value)) { michael@0: shallow ? push.apply(output, value) : flatten(value, shallow, output); michael@0: } else { michael@0: output.push(value); michael@0: } michael@0: }); michael@0: return output; michael@0: }; michael@0: michael@0: // Return a completely flattened version of an array. michael@0: _.flatten = function(array, shallow) { michael@0: return flatten(array, shallow, []); michael@0: }; michael@0: michael@0: // Return a version of the array that does not contain the specified value(s). michael@0: _.without = function(array) { michael@0: return _.difference(array, slice.call(arguments, 1)); michael@0: }; michael@0: michael@0: // Produce a duplicate-free version of the array. If the array has already michael@0: // been sorted, you have the option of using a faster algorithm. michael@0: // Aliased as `unique`. michael@0: _.uniq = _.unique = function(array, isSorted, iterator, context) { michael@0: if (_.isFunction(isSorted)) { michael@0: context = iterator; michael@0: iterator = isSorted; michael@0: isSorted = false; michael@0: } michael@0: var initial = iterator ? _.map(array, iterator, context) : array; michael@0: var results = []; michael@0: var seen = []; michael@0: each(initial, function(value, index) { michael@0: if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { michael@0: seen.push(value); michael@0: results.push(array[index]); michael@0: } michael@0: }); michael@0: return results; michael@0: }; michael@0: michael@0: // Produce an array that contains the union: each distinct element from all of michael@0: // the passed-in arrays. michael@0: _.union = function() { michael@0: return _.uniq(concat.apply(ArrayProto, arguments)); michael@0: }; michael@0: michael@0: // Produce an array that contains every item shared between all the michael@0: // passed-in arrays. michael@0: _.intersection = function(array) { michael@0: var rest = slice.call(arguments, 1); michael@0: return _.filter(_.uniq(array), function(item) { michael@0: return _.every(rest, function(other) { michael@0: return _.indexOf(other, item) >= 0; michael@0: }); michael@0: }); michael@0: }; michael@0: michael@0: // Take the difference between one array and a number of other arrays. michael@0: // Only the elements present in just the first array will remain. michael@0: _.difference = function(array) { michael@0: var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); michael@0: return _.filter(array, function(value){ return !_.contains(rest, value); }); michael@0: }; michael@0: michael@0: // Zip together multiple lists into a single array -- elements that share michael@0: // an index go together. michael@0: _.zip = function() { michael@0: var args = slice.call(arguments); michael@0: var length = _.max(_.pluck(args, 'length')); michael@0: var results = new Array(length); michael@0: for (var i = 0; i < length; i++) { michael@0: results[i] = _.pluck(args, "" + i); michael@0: } michael@0: return results; michael@0: }; michael@0: michael@0: // Converts lists into objects. Pass either a single array of `[key, value]` michael@0: // pairs, or two parallel arrays of the same length -- one of keys, and one of michael@0: // the corresponding values. michael@0: _.object = function(list, values) { michael@0: if (list == null) return {}; michael@0: var result = {}; michael@0: for (var i = 0, l = list.length; i < l; i++) { michael@0: if (values) { michael@0: result[list[i]] = values[i]; michael@0: } else { michael@0: result[list[i][0]] = list[i][1]; michael@0: } michael@0: } michael@0: return result; michael@0: }; michael@0: michael@0: // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), michael@0: // we need this function. Return the position of the first occurrence of an michael@0: // item in an array, or -1 if the item is not included in the array. michael@0: // Delegates to **ECMAScript 5**'s native `indexOf` if available. michael@0: // If the array is large and already in sort order, pass `true` michael@0: // for **isSorted** to use binary search. michael@0: _.indexOf = function(array, item, isSorted) { michael@0: if (array == null) return -1; michael@0: var i = 0, l = array.length; michael@0: if (isSorted) { michael@0: if (typeof isSorted == 'number') { michael@0: i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); michael@0: } else { michael@0: i = _.sortedIndex(array, item); michael@0: return array[i] === item ? i : -1; michael@0: } michael@0: } michael@0: if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); michael@0: for (; i < l; i++) if (array[i] === item) return i; michael@0: return -1; michael@0: }; michael@0: michael@0: // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. michael@0: _.lastIndexOf = function(array, item, from) { michael@0: if (array == null) return -1; michael@0: var hasIndex = from != null; michael@0: if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { michael@0: return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); michael@0: } michael@0: var i = (hasIndex ? from : array.length); michael@0: while (i--) if (array[i] === item) return i; michael@0: return -1; michael@0: }; michael@0: michael@0: // Generate an integer Array containing an arithmetic progression. A port of michael@0: // the native Python `range()` function. See michael@0: // [the Python documentation](http://docs.python.org/library/functions.html#range). michael@0: _.range = function(start, stop, step) { michael@0: if (arguments.length <= 1) { michael@0: stop = start || 0; michael@0: start = 0; michael@0: } michael@0: step = arguments[2] || 1; michael@0: michael@0: var len = Math.max(Math.ceil((stop - start) / step), 0); michael@0: var idx = 0; michael@0: var range = new Array(len); michael@0: michael@0: while(idx < len) { michael@0: range[idx++] = start; michael@0: start += step; michael@0: } michael@0: michael@0: return range; michael@0: }; michael@0: michael@0: // Function (ahem) Functions michael@0: // ------------------ michael@0: michael@0: // Create a function bound to a given object (assigning `this`, and arguments, michael@0: // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if michael@0: // available. michael@0: _.bind = function(func, context) { michael@0: if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); michael@0: var args = slice.call(arguments, 2); michael@0: return function() { michael@0: return func.apply(context, args.concat(slice.call(arguments))); michael@0: }; michael@0: }; michael@0: michael@0: // Partially apply a function by creating a version that has had some of its michael@0: // arguments pre-filled, without changing its dynamic `this` context. michael@0: _.partial = function(func) { michael@0: var args = slice.call(arguments, 1); michael@0: return function() { michael@0: return func.apply(this, args.concat(slice.call(arguments))); michael@0: }; michael@0: }; michael@0: michael@0: // Bind all of an object's methods to that object. Useful for ensuring that michael@0: // all callbacks defined on an object belong to it. michael@0: _.bindAll = function(obj) { michael@0: var funcs = slice.call(arguments, 1); michael@0: if (funcs.length === 0) funcs = _.functions(obj); michael@0: each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); michael@0: return obj; michael@0: }; michael@0: michael@0: // Memoize an expensive function by storing its results. michael@0: _.memoize = function(func, hasher) { michael@0: var memo = {}; michael@0: hasher || (hasher = _.identity); michael@0: return function() { michael@0: var key = hasher.apply(this, arguments); michael@0: return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); michael@0: }; michael@0: }; michael@0: michael@0: // Delays a function for the given number of milliseconds, and then calls michael@0: // it with the arguments supplied. michael@0: _.delay = function(func, wait) { michael@0: var args = slice.call(arguments, 2); michael@0: return setTimeout(function(){ return func.apply(null, args); }, wait); michael@0: }; michael@0: michael@0: // Defers a function, scheduling it to run after the current call stack has michael@0: // cleared. michael@0: _.defer = function(func) { michael@0: return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); michael@0: }; michael@0: michael@0: // Returns a function, that, when invoked, will only be triggered at most once michael@0: // during a given window of time. michael@0: _.throttle = function(func, wait) { michael@0: var context, args, timeout, result; michael@0: var previous = 0; michael@0: var later = function() { michael@0: previous = new Date; michael@0: timeout = null; michael@0: result = func.apply(context, args); michael@0: }; michael@0: return function() { michael@0: var now = new Date; michael@0: var remaining = wait - (now - previous); michael@0: context = this; michael@0: args = arguments; michael@0: if (remaining <= 0) { michael@0: clearTimeout(timeout); michael@0: timeout = null; michael@0: previous = now; michael@0: result = func.apply(context, args); michael@0: } else if (!timeout) { michael@0: timeout = setTimeout(later, remaining); michael@0: } michael@0: return result; michael@0: }; michael@0: }; michael@0: michael@0: // Returns a function, that, as long as it continues to be invoked, will not michael@0: // be triggered. The function will be called after it stops being called for michael@0: // N milliseconds. If `immediate` is passed, trigger the function on the michael@0: // leading edge, instead of the trailing. michael@0: _.debounce = function(func, wait, immediate) { michael@0: var timeout, result; michael@0: return function() { michael@0: var context = this, args = arguments; michael@0: var later = function() { michael@0: timeout = null; michael@0: if (!immediate) result = func.apply(context, args); michael@0: }; michael@0: var callNow = immediate && !timeout; michael@0: clearTimeout(timeout); michael@0: timeout = setTimeout(later, wait); michael@0: if (callNow) result = func.apply(context, args); michael@0: return result; michael@0: }; michael@0: }; michael@0: michael@0: // Returns a function that will be executed at most one time, no matter how michael@0: // often you call it. Useful for lazy initialization. michael@0: _.once = function(func) { michael@0: var ran = false, memo; michael@0: return function() { michael@0: if (ran) return memo; michael@0: ran = true; michael@0: memo = func.apply(this, arguments); michael@0: func = null; michael@0: return memo; michael@0: }; michael@0: }; michael@0: michael@0: // Returns the first function passed as an argument to the second, michael@0: // allowing you to adjust arguments, run code before and after, and michael@0: // conditionally execute the original function. michael@0: _.wrap = function(func, wrapper) { michael@0: return function() { michael@0: var args = [func]; michael@0: push.apply(args, arguments); michael@0: return wrapper.apply(this, args); michael@0: }; michael@0: }; michael@0: michael@0: // Returns a function that is the composition of a list of functions, each michael@0: // consuming the return value of the function that follows. michael@0: _.compose = function() { michael@0: var funcs = arguments; michael@0: return function() { michael@0: var args = arguments; michael@0: for (var i = funcs.length - 1; i >= 0; i--) { michael@0: args = [funcs[i].apply(this, args)]; michael@0: } michael@0: return args[0]; michael@0: }; michael@0: }; michael@0: michael@0: // Returns a function that will only be executed after being called N times. michael@0: _.after = function(times, func) { michael@0: if (times <= 0) return func(); michael@0: return function() { michael@0: if (--times < 1) { michael@0: return func.apply(this, arguments); michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: // Object Functions michael@0: // ---------------- michael@0: michael@0: // Retrieve the names of an object's properties. michael@0: // Delegates to **ECMAScript 5**'s native `Object.keys` michael@0: _.keys = nativeKeys || function(obj) { michael@0: if (obj !== Object(obj)) throw new TypeError('Invalid object'); michael@0: var keys = []; michael@0: for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; michael@0: return keys; michael@0: }; michael@0: michael@0: // Retrieve the values of an object's properties. michael@0: _.values = function(obj) { michael@0: var values = []; michael@0: for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); michael@0: return values; michael@0: }; michael@0: michael@0: // Convert an object into a list of `[key, value]` pairs. michael@0: _.pairs = function(obj) { michael@0: var pairs = []; michael@0: for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); michael@0: return pairs; michael@0: }; michael@0: michael@0: // Invert the keys and values of an object. The values must be serializable. michael@0: _.invert = function(obj) { michael@0: var result = {}; michael@0: for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; michael@0: return result; michael@0: }; michael@0: michael@0: // Return a sorted list of the function names available on the object. michael@0: // Aliased as `methods` michael@0: _.functions = _.methods = function(obj) { michael@0: var names = []; michael@0: for (var key in obj) { michael@0: if (_.isFunction(obj[key])) names.push(key); michael@0: } michael@0: return names.sort(); michael@0: }; michael@0: michael@0: // Extend a given object with all the properties in passed-in object(s). michael@0: _.extend = function(obj) { michael@0: each(slice.call(arguments, 1), function(source) { michael@0: if (source) { michael@0: for (var prop in source) { michael@0: obj[prop] = source[prop]; michael@0: } michael@0: } michael@0: }); michael@0: return obj; michael@0: }; michael@0: michael@0: // Return a copy of the object only containing the whitelisted properties. michael@0: _.pick = function(obj) { michael@0: var copy = {}; michael@0: var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); michael@0: each(keys, function(key) { michael@0: if (key in obj) copy[key] = obj[key]; michael@0: }); michael@0: return copy; michael@0: }; michael@0: michael@0: // Return a copy of the object without the blacklisted properties. michael@0: _.omit = function(obj) { michael@0: var copy = {}; michael@0: var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); michael@0: for (var key in obj) { michael@0: if (!_.contains(keys, key)) copy[key] = obj[key]; michael@0: } michael@0: return copy; michael@0: }; michael@0: michael@0: // Fill in a given object with default properties. michael@0: _.defaults = function(obj) { michael@0: each(slice.call(arguments, 1), function(source) { michael@0: if (source) { michael@0: for (var prop in source) { michael@0: if (obj[prop] == null) obj[prop] = source[prop]; michael@0: } michael@0: } michael@0: }); michael@0: return obj; michael@0: }; michael@0: michael@0: // Create a (shallow-cloned) duplicate of an object. michael@0: _.clone = function(obj) { michael@0: if (!_.isObject(obj)) return obj; michael@0: return _.isArray(obj) ? obj.slice() : _.extend({}, obj); michael@0: }; michael@0: michael@0: // Invokes interceptor with the obj, and then returns obj. michael@0: // The primary purpose of this method is to "tap into" a method chain, in michael@0: // order to perform operations on intermediate results within the chain. michael@0: _.tap = function(obj, interceptor) { michael@0: interceptor(obj); michael@0: return obj; michael@0: }; michael@0: michael@0: // Internal recursive comparison function for `isEqual`. michael@0: var eq = function(a, b, aStack, bStack) { michael@0: // Identical objects are equal. `0 === -0`, but they aren't identical. michael@0: // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. michael@0: if (a === b) return a !== 0 || 1 / a == 1 / b; michael@0: // A strict comparison is necessary because `null == undefined`. michael@0: if (a == null || b == null) return a === b; michael@0: // Unwrap any wrapped objects. michael@0: if (a instanceof _) a = a._wrapped; michael@0: if (b instanceof _) b = b._wrapped; michael@0: // Compare `[[Class]]` names. michael@0: var className = toString.call(a); michael@0: if (className != toString.call(b)) return false; michael@0: switch (className) { michael@0: // Strings, numbers, dates, and booleans are compared by value. michael@0: case '[object String]': michael@0: // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is michael@0: // equivalent to `new String("5")`. michael@0: return a == String(b); michael@0: case '[object Number]': michael@0: // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for michael@0: // other numeric values. michael@0: return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); michael@0: case '[object Date]': michael@0: case '[object Boolean]': michael@0: // Coerce dates and booleans to numeric primitive values. Dates are compared by their michael@0: // millisecond representations. Note that invalid dates with millisecond representations michael@0: // of `NaN` are not equivalent. michael@0: return +a == +b; michael@0: // RegExps are compared by their source patterns and flags. michael@0: case '[object RegExp]': michael@0: return a.source == b.source && michael@0: a.global == b.global && michael@0: a.multiline == b.multiline && michael@0: a.ignoreCase == b.ignoreCase; michael@0: } michael@0: if (typeof a != 'object' || typeof b != 'object') return false; michael@0: // Assume equality for cyclic structures. The algorithm for detecting cyclic michael@0: // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. michael@0: var length = aStack.length; michael@0: while (length--) { michael@0: // Linear search. Performance is inversely proportional to the number of michael@0: // unique nested structures. michael@0: if (aStack[length] == a) return bStack[length] == b; michael@0: } michael@0: // Add the first object to the stack of traversed objects. michael@0: aStack.push(a); michael@0: bStack.push(b); michael@0: var size = 0, result = true; michael@0: // Recursively compare objects and arrays. michael@0: if (className == '[object Array]') { michael@0: // Compare array lengths to determine if a deep comparison is necessary. michael@0: size = a.length; michael@0: result = size == b.length; michael@0: if (result) { michael@0: // Deep compare the contents, ignoring non-numeric properties. michael@0: while (size--) { michael@0: if (!(result = eq(a[size], b[size], aStack, bStack))) break; michael@0: } michael@0: } michael@0: } else { michael@0: // Objects with different constructors are not equivalent, but `Object`s michael@0: // from different frames are. michael@0: var aCtor = a.constructor, bCtor = b.constructor; michael@0: if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && michael@0: _.isFunction(bCtor) && (bCtor instanceof bCtor))) { michael@0: return false; michael@0: } michael@0: // Deep compare objects. michael@0: for (var key in a) { michael@0: if (_.has(a, key)) { michael@0: // Count the expected number of properties. michael@0: size++; michael@0: // Deep compare each member. michael@0: if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; michael@0: } michael@0: } michael@0: // Ensure that both objects contain the same number of properties. michael@0: if (result) { michael@0: for (key in b) { michael@0: if (_.has(b, key) && !(size--)) break; michael@0: } michael@0: result = !size; michael@0: } michael@0: } michael@0: // Remove the first object from the stack of traversed objects. michael@0: aStack.pop(); michael@0: bStack.pop(); michael@0: return result; michael@0: }; michael@0: michael@0: // Perform a deep comparison to check if two objects are equal. michael@0: _.isEqual = function(a, b) { michael@0: return eq(a, b, [], []); michael@0: }; michael@0: michael@0: // Is a given array, string, or object empty? michael@0: // An "empty" object has no enumerable own-properties. michael@0: _.isEmpty = function(obj) { michael@0: if (obj == null) return true; michael@0: if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; michael@0: for (var key in obj) if (_.has(obj, key)) return false; michael@0: return true; michael@0: }; michael@0: michael@0: // Is a given value a DOM element? michael@0: _.isElement = function(obj) { michael@0: return !!(obj && obj.nodeType === 1); michael@0: }; michael@0: michael@0: // Is a given value an array? michael@0: // Delegates to ECMA5's native Array.isArray michael@0: _.isArray = nativeIsArray || function(obj) { michael@0: return toString.call(obj) == '[object Array]'; michael@0: }; michael@0: michael@0: // Is a given variable an object? michael@0: _.isObject = function(obj) { michael@0: return obj === Object(obj); michael@0: }; michael@0: michael@0: // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. michael@0: each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { michael@0: _['is' + name] = function(obj) { michael@0: return toString.call(obj) == '[object ' + name + ']'; michael@0: }; michael@0: }); michael@0: michael@0: // Define a fallback version of the method in browsers (ahem, IE), where michael@0: // there isn't any inspectable "Arguments" type. michael@0: if (!_.isArguments(arguments)) { michael@0: _.isArguments = function(obj) { michael@0: return !!(obj && _.has(obj, 'callee')); michael@0: }; michael@0: } michael@0: michael@0: // Optimize `isFunction` if appropriate. michael@0: if (typeof (/./) !== 'function') { michael@0: _.isFunction = function(obj) { michael@0: return typeof obj === 'function'; michael@0: }; michael@0: } michael@0: michael@0: // Is a given object a finite number? michael@0: _.isFinite = function(obj) { michael@0: return isFinite(obj) && !isNaN(parseFloat(obj)); michael@0: }; michael@0: michael@0: // Is the given value `NaN`? (NaN is the only number which does not equal itself). michael@0: _.isNaN = function(obj) { michael@0: return _.isNumber(obj) && obj != +obj; michael@0: }; michael@0: michael@0: // Is a given value a boolean? michael@0: _.isBoolean = function(obj) { michael@0: return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; michael@0: }; michael@0: michael@0: // Is a given value equal to null? michael@0: _.isNull = function(obj) { michael@0: return obj === null; michael@0: }; michael@0: michael@0: // Is a given variable undefined? michael@0: _.isUndefined = function(obj) { michael@0: return obj === void 0; michael@0: }; michael@0: michael@0: // Shortcut function for checking if an object has a given property directly michael@0: // on itself (in other words, not on a prototype). michael@0: _.has = function(obj, key) { michael@0: return hasOwnProperty.call(obj, key); michael@0: }; michael@0: michael@0: // Utility Functions michael@0: // ----------------- michael@0: michael@0: // Run Underscore.js in *noConflict* mode, returning the `_` variable to its michael@0: // previous owner. Returns a reference to the Underscore object. michael@0: _.noConflict = function() { michael@0: root._ = previousUnderscore; michael@0: return this; michael@0: }; michael@0: michael@0: // Keep the identity function around for default iterators. michael@0: _.identity = function(value) { michael@0: return value; michael@0: }; michael@0: michael@0: // Run a function **n** times. michael@0: _.times = function(n, iterator, context) { michael@0: var accum = Array(n); michael@0: for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); michael@0: return accum; michael@0: }; michael@0: michael@0: // Return a random integer between min and max (inclusive). michael@0: _.random = function(min, max) { michael@0: if (max == null) { michael@0: max = min; michael@0: min = 0; michael@0: } michael@0: return min + Math.floor(Math.random() * (max - min + 1)); michael@0: }; michael@0: michael@0: // List of HTML entities for escaping. michael@0: var entityMap = { michael@0: escape: { michael@0: '&': '&', michael@0: '<': '<', michael@0: '>': '>', michael@0: '"': '"', michael@0: "'": ''', michael@0: '/': '/' michael@0: } michael@0: }; michael@0: entityMap.unescape = _.invert(entityMap.escape); michael@0: michael@0: // Regexes containing the keys and values listed immediately above. michael@0: var entityRegexes = { michael@0: escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), michael@0: unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') michael@0: }; michael@0: michael@0: // Functions for escaping and unescaping strings to/from HTML interpolation. michael@0: _.each(['escape', 'unescape'], function(method) { michael@0: _[method] = function(string) { michael@0: if (string == null) return ''; michael@0: return ('' + string).replace(entityRegexes[method], function(match) { michael@0: return entityMap[method][match]; michael@0: }); michael@0: }; michael@0: }); michael@0: michael@0: // If the value of the named property is a function then invoke it; michael@0: // otherwise, return it. michael@0: _.result = function(object, property) { michael@0: if (object == null) return null; michael@0: var value = object[property]; michael@0: return _.isFunction(value) ? value.call(object) : value; michael@0: }; michael@0: michael@0: // Add your own custom functions to the Underscore object. michael@0: _.mixin = function(obj) { michael@0: each(_.functions(obj), function(name){ michael@0: var func = _[name] = obj[name]; michael@0: _.prototype[name] = function() { michael@0: var args = [this._wrapped]; michael@0: push.apply(args, arguments); michael@0: return result.call(this, func.apply(_, args)); michael@0: }; michael@0: }); michael@0: }; michael@0: michael@0: // Generate a unique integer id (unique within the entire client session). michael@0: // Useful for temporary DOM ids. michael@0: var idCounter = 0; michael@0: _.uniqueId = function(prefix) { michael@0: var id = ++idCounter + ''; michael@0: return prefix ? prefix + id : id; michael@0: }; michael@0: michael@0: // By default, Underscore uses ERB-style template delimiters, change the michael@0: // following template settings to use alternative delimiters. michael@0: _.templateSettings = { michael@0: evaluate : /<%([\s\S]+?)%>/g, michael@0: interpolate : /<%=([\s\S]+?)%>/g, michael@0: escape : /<%-([\s\S]+?)%>/g michael@0: }; michael@0: michael@0: // When customizing `templateSettings`, if you don't want to define an michael@0: // interpolation, evaluation or escaping regex, we need one that is michael@0: // guaranteed not to match. michael@0: var noMatch = /(.)^/; michael@0: michael@0: // Certain characters need to be escaped so that they can be put into a michael@0: // string literal. michael@0: var escapes = { michael@0: "'": "'", michael@0: '\\': '\\', michael@0: '\r': 'r', michael@0: '\n': 'n', michael@0: '\t': 't', michael@0: '\u2028': 'u2028', michael@0: '\u2029': 'u2029' michael@0: }; michael@0: michael@0: var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; michael@0: michael@0: // JavaScript micro-templating, similar to John Resig's implementation. michael@0: // Underscore templating handles arbitrary delimiters, preserves whitespace, michael@0: // and correctly escapes quotes within interpolated code. michael@0: _.template = function(text, data, settings) { michael@0: var render; michael@0: settings = _.defaults({}, settings, _.templateSettings); michael@0: michael@0: // Combine delimiters into one regular expression via alternation. michael@0: var matcher = new RegExp([ michael@0: (settings.escape || noMatch).source, michael@0: (settings.interpolate || noMatch).source, michael@0: (settings.evaluate || noMatch).source michael@0: ].join('|') + '|$', 'g'); michael@0: michael@0: // Compile the template source, escaping string literals appropriately. michael@0: var index = 0; michael@0: var source = "__p+='"; michael@0: text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { michael@0: source += text.slice(index, offset) michael@0: .replace(escaper, function(match) { return '\\' + escapes[match]; }); michael@0: michael@0: if (escape) { michael@0: source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; michael@0: } michael@0: if (interpolate) { michael@0: source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; michael@0: } michael@0: if (evaluate) { michael@0: source += "';\n" + evaluate + "\n__p+='"; michael@0: } michael@0: index = offset + match.length; michael@0: return match; michael@0: }); michael@0: source += "';\n"; michael@0: michael@0: // If a variable is not specified, place data values in local scope. michael@0: if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; michael@0: michael@0: source = "var __t,__p='',__j=Array.prototype.join," + michael@0: "print=function(){__p+=__j.call(arguments,'');};\n" + michael@0: source + "return __p;\n"; michael@0: michael@0: try { michael@0: render = new Function(settings.variable || 'obj', '_', source); michael@0: } catch (e) { michael@0: e.source = source; michael@0: throw e; michael@0: } michael@0: michael@0: if (data) return render(data, _); michael@0: var template = function(data) { michael@0: return render.call(this, data, _); michael@0: }; michael@0: michael@0: // Provide the compiled function source as a convenience for precompilation. michael@0: template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; michael@0: michael@0: return template; michael@0: }; michael@0: michael@0: // Add a "chain" function, which will delegate to the wrapper. michael@0: _.chain = function(obj) { michael@0: return _(obj).chain(); michael@0: }; michael@0: michael@0: // OOP michael@0: // --------------- michael@0: // If Underscore is called as a function, it returns a wrapped object that michael@0: // can be used OO-style. This wrapper holds altered versions of all the michael@0: // underscore functions. Wrapped objects may be chained. michael@0: michael@0: // Helper function to continue chaining intermediate results. michael@0: var result = function(obj) { michael@0: return this._chain ? _(obj).chain() : obj; michael@0: }; michael@0: michael@0: // Add all of the Underscore functions to the wrapper object. michael@0: _.mixin(_); michael@0: michael@0: // Add all mutator Array functions to the wrapper. michael@0: each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { michael@0: var method = ArrayProto[name]; michael@0: _.prototype[name] = function() { michael@0: var obj = this._wrapped; michael@0: method.apply(obj, arguments); michael@0: if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; michael@0: return result.call(this, obj); michael@0: }; michael@0: }); michael@0: michael@0: // Add all accessor Array functions to the wrapper. michael@0: each(['concat', 'join', 'slice'], function(name) { michael@0: var method = ArrayProto[name]; michael@0: _.prototype[name] = function() { michael@0: return result.call(this, method.apply(this._wrapped, arguments)); michael@0: }; michael@0: }); michael@0: michael@0: _.extend(_.prototype, { michael@0: michael@0: // Start chaining a wrapped Underscore object. michael@0: chain: function() { michael@0: this._chain = true; michael@0: return this; michael@0: }, michael@0: michael@0: // Extracts the result from a wrapped and chained object. michael@0: value: function() { michael@0: return this._wrapped; michael@0: } michael@0: michael@0: }); michael@0: michael@0: }).call(this); michael@0: michael@0: })() michael@0: },{}],14:[function(require,module,exports){ michael@0: exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) { michael@0: var e, m, michael@0: eLen = nBytes * 8 - mLen - 1, michael@0: eMax = (1 << eLen) - 1, michael@0: eBias = eMax >> 1, michael@0: nBits = -7, michael@0: i = isBE ? 0 : (nBytes - 1), michael@0: d = isBE ? 1 : -1, michael@0: s = buffer[offset + i]; michael@0: michael@0: i += d; michael@0: michael@0: e = s & ((1 << (-nBits)) - 1); michael@0: s >>= (-nBits); michael@0: nBits += eLen; michael@0: for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); michael@0: michael@0: m = e & ((1 << (-nBits)) - 1); michael@0: e >>= (-nBits); michael@0: nBits += mLen; michael@0: for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); michael@0: michael@0: if (e === 0) { michael@0: e = 1 - eBias; michael@0: } else if (e === eMax) { michael@0: return m ? NaN : ((s ? -1 : 1) * Infinity); michael@0: } else { michael@0: m = m + Math.pow(2, mLen); michael@0: e = e - eBias; michael@0: } michael@0: return (s ? -1 : 1) * m * Math.pow(2, e - mLen); michael@0: }; michael@0: michael@0: exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { michael@0: var e, m, c, michael@0: eLen = nBytes * 8 - mLen - 1, michael@0: eMax = (1 << eLen) - 1, michael@0: eBias = eMax >> 1, michael@0: rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), michael@0: i = isBE ? (nBytes - 1) : 0, michael@0: d = isBE ? -1 : 1, michael@0: s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; michael@0: michael@0: value = Math.abs(value); michael@0: michael@0: if (isNaN(value) || value === Infinity) { michael@0: m = isNaN(value) ? 1 : 0; michael@0: e = eMax; michael@0: } else { michael@0: e = Math.floor(Math.log(value) / Math.LN2); michael@0: if (value * (c = Math.pow(2, -e)) < 1) { michael@0: e--; michael@0: c *= 2; michael@0: } michael@0: if (e + eBias >= 1) { michael@0: value += rt / c; michael@0: } else { michael@0: value += rt * Math.pow(2, 1 - eBias); michael@0: } michael@0: if (value * c >= 2) { michael@0: e++; michael@0: c /= 2; michael@0: } michael@0: michael@0: if (e + eBias >= eMax) { michael@0: m = 0; michael@0: e = eMax; michael@0: } else if (e + eBias >= 1) { michael@0: m = (value * c - 1) * Math.pow(2, mLen); michael@0: e = e + eBias; michael@0: } else { michael@0: m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); michael@0: e = 0; michael@0: } michael@0: } michael@0: michael@0: for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); michael@0: michael@0: e = (e << mLen) | m; michael@0: eLen += mLen; michael@0: for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); michael@0: michael@0: buffer[offset + i - d] |= s * 128; michael@0: }; michael@0: michael@0: },{}],13:[function(require,module,exports){ michael@0: (function(){function SlowBuffer (size) { michael@0: this.length = size; michael@0: }; michael@0: michael@0: var assert = require('assert'); michael@0: michael@0: exports.INSPECT_MAX_BYTES = 50; michael@0: michael@0: michael@0: function toHex(n) { michael@0: if (n < 16) return '0' + n.toString(16); michael@0: return n.toString(16); michael@0: } michael@0: michael@0: function utf8ToBytes(str) { michael@0: var byteArray = []; michael@0: for (var i = 0; i < str.length; i++) michael@0: if (str.charCodeAt(i) <= 0x7F) michael@0: byteArray.push(str.charCodeAt(i)); michael@0: else { michael@0: var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); michael@0: for (var j = 0; j < h.length; j++) michael@0: byteArray.push(parseInt(h[j], 16)); michael@0: } michael@0: michael@0: return byteArray; michael@0: } michael@0: michael@0: function asciiToBytes(str) { michael@0: var byteArray = [] michael@0: for (var i = 0; i < str.length; i++ ) michael@0: // Node's code seems to be doing this and not & 0x7F.. michael@0: byteArray.push( str.charCodeAt(i) & 0xFF ); michael@0: michael@0: return byteArray; michael@0: } michael@0: michael@0: function base64ToBytes(str) { michael@0: return require("base64-js").toByteArray(str); michael@0: } michael@0: michael@0: SlowBuffer.byteLength = function (str, encoding) { michael@0: switch (encoding || "utf8") { michael@0: case 'hex': michael@0: return str.length / 2; michael@0: michael@0: case 'utf8': michael@0: case 'utf-8': michael@0: return utf8ToBytes(str).length; michael@0: michael@0: case 'ascii': michael@0: case 'binary': michael@0: return str.length; michael@0: michael@0: case 'base64': michael@0: return base64ToBytes(str).length; michael@0: michael@0: default: michael@0: throw new Error('Unknown encoding'); michael@0: } michael@0: }; michael@0: michael@0: function blitBuffer(src, dst, offset, length) { michael@0: var pos, i = 0; michael@0: while (i < length) { michael@0: if ((i+offset >= dst.length) || (i >= src.length)) michael@0: break; michael@0: michael@0: dst[i + offset] = src[i]; michael@0: i++; michael@0: } michael@0: return i; michael@0: } michael@0: michael@0: SlowBuffer.prototype.utf8Write = function (string, offset, length) { michael@0: var bytes, pos; michael@0: return SlowBuffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); michael@0: }; michael@0: michael@0: SlowBuffer.prototype.asciiWrite = function (string, offset, length) { michael@0: var bytes, pos; michael@0: return SlowBuffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); michael@0: }; michael@0: michael@0: SlowBuffer.prototype.binaryWrite = SlowBuffer.prototype.asciiWrite; michael@0: michael@0: SlowBuffer.prototype.base64Write = function (string, offset, length) { michael@0: var bytes, pos; michael@0: return SlowBuffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); michael@0: }; michael@0: michael@0: SlowBuffer.prototype.base64Slice = function (start, end) { michael@0: var bytes = Array.prototype.slice.apply(this, arguments) michael@0: return require("base64-js").fromByteArray(bytes); michael@0: } michael@0: michael@0: function decodeUtf8Char(str) { michael@0: try { michael@0: return decodeURIComponent(str); michael@0: } catch (err) { michael@0: return String.fromCharCode(0xFFFD); // UTF 8 invalid char michael@0: } michael@0: } michael@0: michael@0: SlowBuffer.prototype.utf8Slice = function () { michael@0: var bytes = Array.prototype.slice.apply(this, arguments); michael@0: var res = ""; michael@0: var tmp = ""; michael@0: var i = 0; michael@0: while (i < bytes.length) { michael@0: if (bytes[i] <= 0x7F) { michael@0: res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); michael@0: tmp = ""; michael@0: } else michael@0: tmp += "%" + bytes[i].toString(16); michael@0: michael@0: i++; michael@0: } michael@0: michael@0: return res + decodeUtf8Char(tmp); michael@0: } michael@0: michael@0: SlowBuffer.prototype.asciiSlice = function () { michael@0: var bytes = Array.prototype.slice.apply(this, arguments); michael@0: var ret = ""; michael@0: for (var i = 0; i < bytes.length; i++) michael@0: ret += String.fromCharCode(bytes[i]); michael@0: return ret; michael@0: } michael@0: michael@0: SlowBuffer.prototype.binarySlice = SlowBuffer.prototype.asciiSlice; michael@0: michael@0: SlowBuffer.prototype.inspect = function() { michael@0: var out = [], michael@0: len = this.length; michael@0: for (var i = 0; i < len; i++) { michael@0: out[i] = toHex(this[i]); michael@0: if (i == exports.INSPECT_MAX_BYTES) { michael@0: out[i + 1] = '...'; michael@0: break; michael@0: } michael@0: } michael@0: return ''; michael@0: }; michael@0: michael@0: michael@0: SlowBuffer.prototype.hexSlice = function(start, end) { michael@0: var len = this.length; michael@0: michael@0: if (!start || start < 0) start = 0; michael@0: if (!end || end < 0 || end > len) end = len; michael@0: michael@0: var out = ''; michael@0: for (var i = start; i < end; i++) { michael@0: out += toHex(this[i]); michael@0: } michael@0: return out; michael@0: }; michael@0: michael@0: michael@0: SlowBuffer.prototype.toString = function(encoding, start, end) { michael@0: encoding = String(encoding || 'utf8').toLowerCase(); michael@0: start = +start || 0; michael@0: if (typeof end == 'undefined') end = this.length; michael@0: michael@0: // Fastpath empty strings michael@0: if (+end == start) { michael@0: return ''; michael@0: } michael@0: michael@0: switch (encoding) { michael@0: case 'hex': michael@0: return this.hexSlice(start, end); michael@0: michael@0: case 'utf8': michael@0: case 'utf-8': michael@0: return this.utf8Slice(start, end); michael@0: michael@0: case 'ascii': michael@0: return this.asciiSlice(start, end); michael@0: michael@0: case 'binary': michael@0: return this.binarySlice(start, end); michael@0: michael@0: case 'base64': michael@0: return this.base64Slice(start, end); michael@0: michael@0: case 'ucs2': michael@0: case 'ucs-2': michael@0: return this.ucs2Slice(start, end); michael@0: michael@0: default: michael@0: throw new Error('Unknown encoding'); michael@0: } michael@0: }; michael@0: michael@0: michael@0: SlowBuffer.prototype.hexWrite = function(string, offset, length) { michael@0: offset = +offset || 0; michael@0: var remaining = this.length - offset; michael@0: if (!length) { michael@0: length = remaining; michael@0: } else { michael@0: length = +length; michael@0: if (length > remaining) { michael@0: length = remaining; michael@0: } michael@0: } michael@0: michael@0: // must be an even number of digits michael@0: var strLen = string.length; michael@0: if (strLen % 2) { michael@0: throw new Error('Invalid hex string'); michael@0: } michael@0: if (length > strLen / 2) { michael@0: length = strLen / 2; michael@0: } michael@0: for (var i = 0; i < length; i++) { michael@0: var byte = parseInt(string.substr(i * 2, 2), 16); michael@0: if (isNaN(byte)) throw new Error('Invalid hex string'); michael@0: this[offset + i] = byte; michael@0: } michael@0: SlowBuffer._charsWritten = i * 2; michael@0: return i; michael@0: }; michael@0: michael@0: michael@0: SlowBuffer.prototype.write = function(string, offset, length, encoding) { michael@0: // Support both (string, offset, length, encoding) michael@0: // and the legacy (string, encoding, offset, length) michael@0: if (isFinite(offset)) { michael@0: if (!isFinite(length)) { michael@0: encoding = length; michael@0: length = undefined; michael@0: } michael@0: } else { // legacy michael@0: var swap = encoding; michael@0: encoding = offset; michael@0: offset = length; michael@0: length = swap; michael@0: } michael@0: michael@0: offset = +offset || 0; michael@0: var remaining = this.length - offset; michael@0: if (!length) { michael@0: length = remaining; michael@0: } else { michael@0: length = +length; michael@0: if (length > remaining) { michael@0: length = remaining; michael@0: } michael@0: } michael@0: encoding = String(encoding || 'utf8').toLowerCase(); michael@0: michael@0: switch (encoding) { michael@0: case 'hex': michael@0: return this.hexWrite(string, offset, length); michael@0: michael@0: case 'utf8': michael@0: case 'utf-8': michael@0: return this.utf8Write(string, offset, length); michael@0: michael@0: case 'ascii': michael@0: return this.asciiWrite(string, offset, length); michael@0: michael@0: case 'binary': michael@0: return this.binaryWrite(string, offset, length); michael@0: michael@0: case 'base64': michael@0: return this.base64Write(string, offset, length); michael@0: michael@0: case 'ucs2': michael@0: case 'ucs-2': michael@0: return this.ucs2Write(string, offset, length); michael@0: michael@0: default: michael@0: throw new Error('Unknown encoding'); michael@0: } michael@0: }; michael@0: michael@0: michael@0: // slice(start, end) michael@0: SlowBuffer.prototype.slice = function(start, end) { michael@0: if (end === undefined) end = this.length; michael@0: michael@0: if (end > this.length) { michael@0: throw new Error('oob'); michael@0: } michael@0: if (start > end) { michael@0: throw new Error('oob'); michael@0: } michael@0: michael@0: return new Buffer(this, end - start, +start); michael@0: }; michael@0: michael@0: SlowBuffer.prototype.copy = function(target, targetstart, sourcestart, sourceend) { michael@0: var temp = []; michael@0: for (var i=sourcestart; i this.length) { michael@0: throw new Error('oob'); michael@0: } michael@0: if (start > end) { michael@0: throw new Error('oob'); michael@0: } michael@0: michael@0: for (var i = start; i < end; i++) { michael@0: this[i] = value; michael@0: } michael@0: } michael@0: michael@0: function coerce(length) { michael@0: // Coerce length to a number (possibly NaN), round up michael@0: // in case it's fractional (e.g. 123.456) then do a michael@0: // double negate to coerce a NaN to 0. Easy, right? michael@0: length = ~~Math.ceil(+length); michael@0: return length < 0 ? 0 : length; michael@0: } michael@0: michael@0: michael@0: // Buffer michael@0: michael@0: function Buffer(subject, encoding, offset) { michael@0: if (!(this instanceof Buffer)) { michael@0: return new Buffer(subject, encoding, offset); michael@0: } michael@0: michael@0: var type; michael@0: michael@0: // Are we slicing? michael@0: if (typeof offset === 'number') { michael@0: this.length = coerce(encoding); michael@0: this.parent = subject; michael@0: this.offset = offset; michael@0: } else { michael@0: // Find the length michael@0: switch (type = typeof subject) { michael@0: case 'number': michael@0: this.length = coerce(subject); michael@0: break; michael@0: michael@0: case 'string': michael@0: this.length = Buffer.byteLength(subject, encoding); michael@0: break; michael@0: michael@0: case 'object': // Assume object is an array michael@0: this.length = coerce(subject.length); michael@0: break; michael@0: michael@0: default: michael@0: throw new Error('First argument needs to be a number, ' + michael@0: 'array or string.'); michael@0: } michael@0: michael@0: if (this.length > Buffer.poolSize) { michael@0: // Big buffer, just alloc one. michael@0: this.parent = new SlowBuffer(this.length); michael@0: this.offset = 0; michael@0: michael@0: } else { michael@0: // Small buffer. michael@0: if (!pool || pool.length - pool.used < this.length) allocPool(); michael@0: this.parent = pool; michael@0: this.offset = pool.used; michael@0: pool.used += this.length; michael@0: } michael@0: michael@0: // Treat array-ish objects as a byte array. michael@0: if (isArrayIsh(subject)) { michael@0: for (var i = 0; i < this.length; i++) { michael@0: if (subject instanceof Buffer) { michael@0: this.parent[i + this.offset] = subject.readUInt8(i); michael@0: } michael@0: else { michael@0: this.parent[i + this.offset] = subject[i]; michael@0: } michael@0: } michael@0: } else if (type == 'string') { michael@0: // We are a string michael@0: this.length = this.write(subject, 0, encoding); michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: function isArrayIsh(subject) { michael@0: return Array.isArray(subject) || Buffer.isBuffer(subject) || michael@0: subject && typeof subject === 'object' && michael@0: typeof subject.length === 'number'; michael@0: } michael@0: michael@0: exports.SlowBuffer = SlowBuffer; michael@0: exports.Buffer = Buffer; michael@0: michael@0: Buffer.poolSize = 8 * 1024; michael@0: var pool; michael@0: michael@0: function allocPool() { michael@0: pool = new SlowBuffer(Buffer.poolSize); michael@0: pool.used = 0; michael@0: } michael@0: michael@0: michael@0: // Static methods michael@0: Buffer.isBuffer = function isBuffer(b) { michael@0: return b instanceof Buffer || b instanceof SlowBuffer; michael@0: }; michael@0: michael@0: Buffer.concat = function (list, totalLength) { michael@0: if (!Array.isArray(list)) { michael@0: throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ michael@0: list should be an Array."); michael@0: } michael@0: michael@0: if (list.length === 0) { michael@0: return new Buffer(0); michael@0: } else if (list.length === 1) { michael@0: return list[0]; michael@0: } michael@0: michael@0: if (typeof totalLength !== 'number') { michael@0: totalLength = 0; michael@0: for (var i = 0; i < list.length; i++) { michael@0: var buf = list[i]; michael@0: totalLength += buf.length; michael@0: } michael@0: } michael@0: michael@0: var buffer = new Buffer(totalLength); michael@0: var pos = 0; michael@0: for (var i = 0; i < list.length; i++) { michael@0: var buf = list[i]; michael@0: buf.copy(buffer, pos); michael@0: pos += buf.length; michael@0: } michael@0: return buffer; michael@0: }; michael@0: michael@0: // Inspect michael@0: Buffer.prototype.inspect = function inspect() { michael@0: var out = [], michael@0: len = this.length; michael@0: michael@0: for (var i = 0; i < len; i++) { michael@0: out[i] = toHex(this.parent[i + this.offset]); michael@0: if (i == exports.INSPECT_MAX_BYTES) { michael@0: out[i + 1] = '...'; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return ''; michael@0: }; michael@0: michael@0: michael@0: Buffer.prototype.get = function get(i) { michael@0: if (i < 0 || i >= this.length) throw new Error('oob'); michael@0: return this.parent[this.offset + i]; michael@0: }; michael@0: michael@0: michael@0: Buffer.prototype.set = function set(i, v) { michael@0: if (i < 0 || i >= this.length) throw new Error('oob'); michael@0: return this.parent[this.offset + i] = v; michael@0: }; michael@0: michael@0: michael@0: // write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8') michael@0: Buffer.prototype.write = function(string, offset, length, encoding) { michael@0: // Support both (string, offset, length, encoding) michael@0: // and the legacy (string, encoding, offset, length) michael@0: if (isFinite(offset)) { michael@0: if (!isFinite(length)) { michael@0: encoding = length; michael@0: length = undefined; michael@0: } michael@0: } else { // legacy michael@0: var swap = encoding; michael@0: encoding = offset; michael@0: offset = length; michael@0: length = swap; michael@0: } michael@0: michael@0: offset = +offset || 0; michael@0: var remaining = this.length - offset; michael@0: if (!length) { michael@0: length = remaining; michael@0: } else { michael@0: length = +length; michael@0: if (length > remaining) { michael@0: length = remaining; michael@0: } michael@0: } michael@0: encoding = String(encoding || 'utf8').toLowerCase(); michael@0: michael@0: var ret; michael@0: switch (encoding) { michael@0: case 'hex': michael@0: ret = this.parent.hexWrite(string, this.offset + offset, length); michael@0: break; michael@0: michael@0: case 'utf8': michael@0: case 'utf-8': michael@0: ret = this.parent.utf8Write(string, this.offset + offset, length); michael@0: break; michael@0: michael@0: case 'ascii': michael@0: ret = this.parent.asciiWrite(string, this.offset + offset, length); michael@0: break; michael@0: michael@0: case 'binary': michael@0: ret = this.parent.binaryWrite(string, this.offset + offset, length); michael@0: break; michael@0: michael@0: case 'base64': michael@0: // Warning: maxLength not taken into account in base64Write michael@0: ret = this.parent.base64Write(string, this.offset + offset, length); michael@0: break; michael@0: michael@0: case 'ucs2': michael@0: case 'ucs-2': michael@0: ret = this.parent.ucs2Write(string, this.offset + offset, length); michael@0: break; michael@0: michael@0: default: michael@0: throw new Error('Unknown encoding'); michael@0: } michael@0: michael@0: Buffer._charsWritten = SlowBuffer._charsWritten; michael@0: michael@0: return ret; michael@0: }; michael@0: michael@0: michael@0: // toString(encoding, start=0, end=buffer.length) michael@0: Buffer.prototype.toString = function(encoding, start, end) { michael@0: encoding = String(encoding || 'utf8').toLowerCase(); michael@0: michael@0: if (typeof start == 'undefined' || start < 0) { michael@0: start = 0; michael@0: } else if (start > this.length) { michael@0: start = this.length; michael@0: } michael@0: michael@0: if (typeof end == 'undefined' || end > this.length) { michael@0: end = this.length; michael@0: } else if (end < 0) { michael@0: end = 0; michael@0: } michael@0: michael@0: start = start + this.offset; michael@0: end = end + this.offset; michael@0: michael@0: switch (encoding) { michael@0: case 'hex': michael@0: return this.parent.hexSlice(start, end); michael@0: michael@0: case 'utf8': michael@0: case 'utf-8': michael@0: return this.parent.utf8Slice(start, end); michael@0: michael@0: case 'ascii': michael@0: return this.parent.asciiSlice(start, end); michael@0: michael@0: case 'binary': michael@0: return this.parent.binarySlice(start, end); michael@0: michael@0: case 'base64': michael@0: return this.parent.base64Slice(start, end); michael@0: michael@0: case 'ucs2': michael@0: case 'ucs-2': michael@0: return this.parent.ucs2Slice(start, end); michael@0: michael@0: default: michael@0: throw new Error('Unknown encoding'); michael@0: } michael@0: }; michael@0: michael@0: michael@0: // byteLength michael@0: Buffer.byteLength = SlowBuffer.byteLength; michael@0: michael@0: michael@0: // fill(value, start=0, end=buffer.length) michael@0: Buffer.prototype.fill = function fill(value, start, end) { michael@0: value || (value = 0); michael@0: start || (start = 0); michael@0: end || (end = this.length); michael@0: michael@0: if (typeof value === 'string') { michael@0: value = value.charCodeAt(0); michael@0: } michael@0: if (!(typeof value === 'number') || isNaN(value)) { michael@0: throw new Error('value is not a number'); michael@0: } michael@0: michael@0: if (end < start) throw new Error('end < start'); michael@0: michael@0: // Fill 0 bytes; we're done michael@0: if (end === start) return 0; michael@0: if (this.length == 0) return 0; michael@0: michael@0: if (start < 0 || start >= this.length) { michael@0: throw new Error('start out of bounds'); michael@0: } michael@0: michael@0: if (end < 0 || end > this.length) { michael@0: throw new Error('end out of bounds'); michael@0: } michael@0: michael@0: return this.parent.fill(value, michael@0: start + this.offset, michael@0: end + this.offset); michael@0: }; michael@0: michael@0: michael@0: // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) michael@0: Buffer.prototype.copy = function(target, target_start, start, end) { michael@0: var source = this; michael@0: start || (start = 0); michael@0: end || (end = this.length); michael@0: target_start || (target_start = 0); michael@0: michael@0: if (end < start) throw new Error('sourceEnd < sourceStart'); michael@0: michael@0: // Copy 0 bytes; we're done michael@0: if (end === start) return 0; michael@0: if (target.length == 0 || source.length == 0) return 0; michael@0: michael@0: if (target_start < 0 || target_start >= target.length) { michael@0: throw new Error('targetStart out of bounds'); michael@0: } michael@0: michael@0: if (start < 0 || start >= source.length) { michael@0: throw new Error('sourceStart out of bounds'); michael@0: } michael@0: michael@0: if (end < 0 || end > source.length) { michael@0: throw new Error('sourceEnd out of bounds'); michael@0: } michael@0: michael@0: // Are we oob? michael@0: if (end > this.length) { michael@0: end = this.length; michael@0: } michael@0: michael@0: if (target.length - target_start < end - start) { michael@0: end = target.length - target_start + start; michael@0: } michael@0: michael@0: return this.parent.copy(target.parent, michael@0: target_start + target.offset, michael@0: start + this.offset, michael@0: end + this.offset); michael@0: }; michael@0: michael@0: michael@0: // slice(start, end) michael@0: Buffer.prototype.slice = function(start, end) { michael@0: if (end === undefined) end = this.length; michael@0: if (end > this.length) throw new Error('oob'); michael@0: if (start > end) throw new Error('oob'); michael@0: michael@0: return new Buffer(this.parent, end - start, +start + this.offset); michael@0: }; michael@0: michael@0: michael@0: // Legacy methods for backwards compatibility. michael@0: michael@0: Buffer.prototype.utf8Slice = function(start, end) { michael@0: return this.toString('utf8', start, end); michael@0: }; michael@0: michael@0: Buffer.prototype.binarySlice = function(start, end) { michael@0: return this.toString('binary', start, end); michael@0: }; michael@0: michael@0: Buffer.prototype.asciiSlice = function(start, end) { michael@0: return this.toString('ascii', start, end); michael@0: }; michael@0: michael@0: Buffer.prototype.utf8Write = function(string, offset) { michael@0: return this.write(string, offset, 'utf8'); michael@0: }; michael@0: michael@0: Buffer.prototype.binaryWrite = function(string, offset) { michael@0: return this.write(string, offset, 'binary'); michael@0: }; michael@0: michael@0: Buffer.prototype.asciiWrite = function(string, offset) { michael@0: return this.write(string, offset, 'ascii'); michael@0: }; michael@0: michael@0: Buffer.prototype.readUInt8 = function(offset, noAssert) { michael@0: var buffer = this; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: if (offset >= buffer.length) return; michael@0: michael@0: return buffer.parent[buffer.offset + offset]; michael@0: }; michael@0: michael@0: function readUInt16(buffer, offset, isBigEndian, noAssert) { michael@0: var val = 0; michael@0: michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 1 < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: if (offset >= buffer.length) return 0; michael@0: michael@0: if (isBigEndian) { michael@0: val = buffer.parent[buffer.offset + offset] << 8; michael@0: if (offset + 1 < buffer.length) { michael@0: val |= buffer.parent[buffer.offset + offset + 1]; michael@0: } michael@0: } else { michael@0: val = buffer.parent[buffer.offset + offset]; michael@0: if (offset + 1 < buffer.length) { michael@0: val |= buffer.parent[buffer.offset + offset + 1] << 8; michael@0: } michael@0: } michael@0: michael@0: return val; michael@0: } michael@0: michael@0: Buffer.prototype.readUInt16LE = function(offset, noAssert) { michael@0: return readUInt16(this, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.readUInt16BE = function(offset, noAssert) { michael@0: return readUInt16(this, offset, true, noAssert); michael@0: }; michael@0: michael@0: function readUInt32(buffer, offset, isBigEndian, noAssert) { michael@0: var val = 0; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 3 < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: if (offset >= buffer.length) return 0; michael@0: michael@0: if (isBigEndian) { michael@0: if (offset + 1 < buffer.length) michael@0: val = buffer.parent[buffer.offset + offset + 1] << 16; michael@0: if (offset + 2 < buffer.length) michael@0: val |= buffer.parent[buffer.offset + offset + 2] << 8; michael@0: if (offset + 3 < buffer.length) michael@0: val |= buffer.parent[buffer.offset + offset + 3]; michael@0: val = val + (buffer.parent[buffer.offset + offset] << 24 >>> 0); michael@0: } else { michael@0: if (offset + 2 < buffer.length) michael@0: val = buffer.parent[buffer.offset + offset + 2] << 16; michael@0: if (offset + 1 < buffer.length) michael@0: val |= buffer.parent[buffer.offset + offset + 1] << 8; michael@0: val |= buffer.parent[buffer.offset + offset]; michael@0: if (offset + 3 < buffer.length) michael@0: val = val + (buffer.parent[buffer.offset + offset + 3] << 24 >>> 0); michael@0: } michael@0: michael@0: return val; michael@0: } michael@0: michael@0: Buffer.prototype.readUInt32LE = function(offset, noAssert) { michael@0: return readUInt32(this, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.readUInt32BE = function(offset, noAssert) { michael@0: return readUInt32(this, offset, true, noAssert); michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * Signed integer types, yay team! A reminder on how two's complement actually michael@0: * works. The first bit is the signed bit, i.e. tells us whether or not the michael@0: * number should be positive or negative. If the two's complement value is michael@0: * positive, then we're done, as it's equivalent to the unsigned representation. michael@0: * michael@0: * Now if the number is positive, you're pretty much done, you can just leverage michael@0: * the unsigned translations and return those. Unfortunately, negative numbers michael@0: * aren't quite that straightforward. michael@0: * michael@0: * At first glance, one might be inclined to use the traditional formula to michael@0: * translate binary numbers between the positive and negative values in two's michael@0: * complement. (Though it doesn't quite work for the most negative value) michael@0: * Mainly: michael@0: * - invert all the bits michael@0: * - add one to the result michael@0: * michael@0: * Of course, this doesn't quite work in Javascript. Take for example the value michael@0: * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of michael@0: * course, Javascript will do the following: michael@0: * michael@0: * > ~0xff80 michael@0: * -65409 michael@0: * michael@0: * Whoh there, Javascript, that's not quite right. But wait, according to michael@0: * Javascript that's perfectly correct. When Javascript ends up seeing the michael@0: * constant 0xff80, it has no notion that it is actually a signed number. It michael@0: * assumes that we've input the unsigned value 0xff80. Thus, when it does the michael@0: * binary negation, it casts it into a signed value, (positive 0xff80). Then michael@0: * when you perform binary negation on that, it turns it into a negative number. michael@0: * michael@0: * Instead, we're going to have to use the following general formula, that works michael@0: * in a rather Javascript friendly way. I'm glad we don't support this kind of michael@0: * weird numbering scheme in the kernel. michael@0: * michael@0: * (BIT-MAX - (unsigned)val + 1) * -1 michael@0: * michael@0: * The astute observer, may think that this doesn't make sense for 8-bit numbers michael@0: * (really it isn't necessary for them). However, when you get 16-bit numbers, michael@0: * you do. Let's go back to our prior example and see how this will look: michael@0: * michael@0: * (0xffff - 0xff80 + 1) * -1 michael@0: * (0x007f + 1) * -1 michael@0: * (0x0080) * -1 michael@0: */ michael@0: Buffer.prototype.readInt8 = function(offset, noAssert) { michael@0: var buffer = this; michael@0: var neg; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: if (offset >= buffer.length) return; michael@0: michael@0: neg = buffer.parent[buffer.offset + offset] & 0x80; michael@0: if (!neg) { michael@0: return (buffer.parent[buffer.offset + offset]); michael@0: } michael@0: michael@0: return ((0xff - buffer.parent[buffer.offset + offset] + 1) * -1); michael@0: }; michael@0: michael@0: function readInt16(buffer, offset, isBigEndian, noAssert) { michael@0: var neg, val; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 1 < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: val = readUInt16(buffer, offset, isBigEndian, noAssert); michael@0: neg = val & 0x8000; michael@0: if (!neg) { michael@0: return val; michael@0: } michael@0: michael@0: return (0xffff - val + 1) * -1; michael@0: } michael@0: michael@0: Buffer.prototype.readInt16LE = function(offset, noAssert) { michael@0: return readInt16(this, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.readInt16BE = function(offset, noAssert) { michael@0: return readInt16(this, offset, true, noAssert); michael@0: }; michael@0: michael@0: function readInt32(buffer, offset, isBigEndian, noAssert) { michael@0: var neg, val; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 3 < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: val = readUInt32(buffer, offset, isBigEndian, noAssert); michael@0: neg = val & 0x80000000; michael@0: if (!neg) { michael@0: return (val); michael@0: } michael@0: michael@0: return (0xffffffff - val + 1) * -1; michael@0: } michael@0: michael@0: Buffer.prototype.readInt32LE = function(offset, noAssert) { michael@0: return readInt32(this, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.readInt32BE = function(offset, noAssert) { michael@0: return readInt32(this, offset, true, noAssert); michael@0: }; michael@0: michael@0: function readFloat(buffer, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset + 3 < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, michael@0: 23, 4); michael@0: } michael@0: michael@0: Buffer.prototype.readFloatLE = function(offset, noAssert) { michael@0: return readFloat(this, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.readFloatBE = function(offset, noAssert) { michael@0: return readFloat(this, offset, true, noAssert); michael@0: }; michael@0: michael@0: function readDouble(buffer, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset + 7 < buffer.length, michael@0: 'Trying to read beyond buffer length'); michael@0: } michael@0: michael@0: return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, michael@0: 52, 8); michael@0: } michael@0: michael@0: Buffer.prototype.readDoubleLE = function(offset, noAssert) { michael@0: return readDouble(this, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.readDoubleBE = function(offset, noAssert) { michael@0: return readDouble(this, offset, true, noAssert); michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * We have to make sure that the value is a valid integer. This means that it is michael@0: * non-negative. It has no fractional component and that it does not exceed the michael@0: * maximum allowed value. michael@0: * michael@0: * value The number to check for validity michael@0: * michael@0: * max The maximum value michael@0: */ michael@0: function verifuint(value, max) { michael@0: assert.ok(typeof (value) == 'number', michael@0: 'cannot write a non-number as a number'); michael@0: michael@0: assert.ok(value >= 0, michael@0: 'specified a negative value for writing an unsigned value'); michael@0: michael@0: assert.ok(value <= max, 'value is larger than maximum value for type'); michael@0: michael@0: assert.ok(Math.floor(value) === value, 'value has a fractional component'); michael@0: } michael@0: michael@0: Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { michael@0: var buffer = this; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset < buffer.length, michael@0: 'trying to write beyond buffer length'); michael@0: michael@0: verifuint(value, 0xff); michael@0: } michael@0: michael@0: if (offset < buffer.length) { michael@0: buffer.parent[buffer.offset + offset] = value; michael@0: } michael@0: }; michael@0: michael@0: function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 1 < buffer.length, michael@0: 'trying to write beyond buffer length'); michael@0: michael@0: verifuint(value, 0xffff); michael@0: } michael@0: michael@0: for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) { michael@0: buffer.parent[buffer.offset + offset + i] = michael@0: (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>> michael@0: (isBigEndian ? 1 - i : i) * 8; michael@0: } michael@0: michael@0: } michael@0: michael@0: Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { michael@0: writeUInt16(this, value, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { michael@0: writeUInt16(this, value, offset, true, noAssert); michael@0: }; michael@0: michael@0: function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 3 < buffer.length, michael@0: 'trying to write beyond buffer length'); michael@0: michael@0: verifuint(value, 0xffffffff); michael@0: } michael@0: michael@0: for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { michael@0: buffer.parent[buffer.offset + offset + i] = michael@0: (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; michael@0: } michael@0: } michael@0: michael@0: Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { michael@0: writeUInt32(this, value, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { michael@0: writeUInt32(this, value, offset, true, noAssert); michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * We now move onto our friends in the signed number category. Unlike unsigned michael@0: * numbers, we're going to have to worry a bit more about how we put values into michael@0: * arrays. Since we are only worrying about signed 32-bit values, we're in michael@0: * slightly better shape. Unfortunately, we really can't do our favorite binary michael@0: * & in this system. It really seems to do the wrong thing. For example: michael@0: * michael@0: * > -32 & 0xff michael@0: * 224 michael@0: * michael@0: * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of michael@0: * this aren't treated as a signed number. Ultimately a bad thing. michael@0: * michael@0: * What we're going to want to do is basically create the unsigned equivalent of michael@0: * our representation and pass that off to the wuint* functions. To do that michael@0: * we're going to do the following: michael@0: * michael@0: * - if the value is positive michael@0: * we can pass it directly off to the equivalent wuint michael@0: * - if the value is negative michael@0: * we do the following computation: michael@0: * mb + val + 1, where michael@0: * mb is the maximum unsigned value in that byte size michael@0: * val is the Javascript negative integer michael@0: * michael@0: * michael@0: * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If michael@0: * you do out the computations: michael@0: * michael@0: * 0xffff - 128 + 1 michael@0: * 0xffff - 127 michael@0: * 0xff80 michael@0: * michael@0: * You can then encode this value as the signed version. This is really rather michael@0: * hacky, but it should work and get the job done which is our goal here. michael@0: */ michael@0: michael@0: /* michael@0: * A series of checks to make sure we actually have a signed 32-bit number michael@0: */ michael@0: function verifsint(value, max, min) { michael@0: assert.ok(typeof (value) == 'number', michael@0: 'cannot write a non-number as a number'); michael@0: michael@0: assert.ok(value <= max, 'value larger than maximum allowed value'); michael@0: michael@0: assert.ok(value >= min, 'value smaller than minimum allowed value'); michael@0: michael@0: assert.ok(Math.floor(value) === value, 'value has a fractional component'); michael@0: } michael@0: michael@0: function verifIEEE754(value, max, min) { michael@0: assert.ok(typeof (value) == 'number', michael@0: 'cannot write a non-number as a number'); michael@0: michael@0: assert.ok(value <= max, 'value larger than maximum allowed value'); michael@0: michael@0: assert.ok(value >= min, 'value smaller than minimum allowed value'); michael@0: } michael@0: michael@0: Buffer.prototype.writeInt8 = function(value, offset, noAssert) { michael@0: var buffer = this; michael@0: michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset < buffer.length, michael@0: 'Trying to write beyond buffer length'); michael@0: michael@0: verifsint(value, 0x7f, -0x80); michael@0: } michael@0: michael@0: if (value >= 0) { michael@0: buffer.writeUInt8(value, offset, noAssert); michael@0: } else { michael@0: buffer.writeUInt8(0xff + value + 1, offset, noAssert); michael@0: } michael@0: }; michael@0: michael@0: function writeInt16(buffer, value, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 1 < buffer.length, michael@0: 'Trying to write beyond buffer length'); michael@0: michael@0: verifsint(value, 0x7fff, -0x8000); michael@0: } michael@0: michael@0: if (value >= 0) { michael@0: writeUInt16(buffer, value, offset, isBigEndian, noAssert); michael@0: } else { michael@0: writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); michael@0: } michael@0: } michael@0: michael@0: Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { michael@0: writeInt16(this, value, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { michael@0: writeInt16(this, value, offset, true, noAssert); michael@0: }; michael@0: michael@0: function writeInt32(buffer, value, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 3 < buffer.length, michael@0: 'Trying to write beyond buffer length'); michael@0: michael@0: verifsint(value, 0x7fffffff, -0x80000000); michael@0: } michael@0: michael@0: if (value >= 0) { michael@0: writeUInt32(buffer, value, offset, isBigEndian, noAssert); michael@0: } else { michael@0: writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); michael@0: } michael@0: } michael@0: michael@0: Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { michael@0: writeInt32(this, value, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { michael@0: writeInt32(this, value, offset, true, noAssert); michael@0: }; michael@0: michael@0: function writeFloat(buffer, value, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 3 < buffer.length, michael@0: 'Trying to write beyond buffer length'); michael@0: michael@0: verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); michael@0: } michael@0: michael@0: require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, michael@0: 23, 4); michael@0: } michael@0: michael@0: Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { michael@0: writeFloat(this, value, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { michael@0: writeFloat(this, value, offset, true, noAssert); michael@0: }; michael@0: michael@0: function writeDouble(buffer, value, offset, isBigEndian, noAssert) { michael@0: if (!noAssert) { michael@0: assert.ok(value !== undefined && value !== null, michael@0: 'missing value'); michael@0: michael@0: assert.ok(typeof (isBigEndian) === 'boolean', michael@0: 'missing or invalid endian'); michael@0: michael@0: assert.ok(offset !== undefined && offset !== null, michael@0: 'missing offset'); michael@0: michael@0: assert.ok(offset + 7 < buffer.length, michael@0: 'Trying to write beyond buffer length'); michael@0: michael@0: verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); michael@0: } michael@0: michael@0: require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, michael@0: 52, 8); michael@0: } michael@0: michael@0: Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { michael@0: writeDouble(this, value, offset, false, noAssert); michael@0: }; michael@0: michael@0: Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { michael@0: writeDouble(this, value, offset, true, noAssert); michael@0: }; michael@0: michael@0: SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8; michael@0: SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE; michael@0: SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE; michael@0: SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE; michael@0: SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE; michael@0: SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8; michael@0: SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE; michael@0: SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE; michael@0: SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE; michael@0: SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE; michael@0: SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE; michael@0: SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE; michael@0: SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE; michael@0: SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE; michael@0: SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8; michael@0: SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE; michael@0: SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE; michael@0: SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE; michael@0: SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE; michael@0: SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8; michael@0: SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE; michael@0: SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE; michael@0: SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE; michael@0: SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE; michael@0: SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE; michael@0: SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE; michael@0: SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE; michael@0: SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE; michael@0: michael@0: })() michael@0: },{"assert":9,"./buffer_ieee754":14,"base64-js":15}],15:[function(require,module,exports){ michael@0: (function (exports) { michael@0: 'use strict'; michael@0: michael@0: var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; michael@0: michael@0: function b64ToByteArray(b64) { michael@0: var i, j, l, tmp, placeHolders, arr; michael@0: michael@0: if (b64.length % 4 > 0) { michael@0: throw 'Invalid string. Length must be a multiple of 4'; michael@0: } michael@0: michael@0: // the number of equal signs (place holders) michael@0: // if there are two placeholders, than the two characters before it michael@0: // represent one byte michael@0: // if there is only one, then the three characters before it represent 2 bytes michael@0: // this is just a cheap hack to not do indexOf twice michael@0: placeHolders = b64.indexOf('='); michael@0: placeHolders = placeHolders > 0 ? b64.length - placeHolders : 0; michael@0: michael@0: // base64 is 4/3 + up to two characters of the original data michael@0: arr = [];//new Uint8Array(b64.length * 3 / 4 - placeHolders); michael@0: michael@0: // if there are placeholders, only get up to the last complete 4 chars michael@0: l = placeHolders > 0 ? b64.length - 4 : b64.length; michael@0: michael@0: for (i = 0, j = 0; i < l; i += 4, j += 3) { michael@0: tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12) | (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]); michael@0: arr.push((tmp & 0xFF0000) >> 16); michael@0: arr.push((tmp & 0xFF00) >> 8); michael@0: arr.push(tmp & 0xFF); michael@0: } michael@0: michael@0: if (placeHolders === 2) { michael@0: tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4); michael@0: arr.push(tmp & 0xFF); michael@0: } else if (placeHolders === 1) { michael@0: tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4) | (lookup.indexOf(b64[i + 2]) >> 2); michael@0: arr.push((tmp >> 8) & 0xFF); michael@0: arr.push(tmp & 0xFF); michael@0: } michael@0: michael@0: return arr; michael@0: } michael@0: michael@0: function uint8ToBase64(uint8) { michael@0: var i, michael@0: extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes michael@0: output = "", michael@0: temp, length; michael@0: michael@0: function tripletToBase64 (num) { michael@0: return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]; michael@0: }; michael@0: michael@0: // go through the array every three bytes, we'll deal with trailing stuff later michael@0: for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { michael@0: temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]); michael@0: output += tripletToBase64(temp); michael@0: } michael@0: michael@0: // pad the end with zeros, but make sure to not forget the extra bytes michael@0: switch (extraBytes) { michael@0: case 1: michael@0: temp = uint8[uint8.length - 1]; michael@0: output += lookup[temp >> 2]; michael@0: output += lookup[(temp << 4) & 0x3F]; michael@0: output += '=='; michael@0: break; michael@0: case 2: michael@0: temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]); michael@0: output += lookup[temp >> 10]; michael@0: output += lookup[(temp >> 4) & 0x3F]; michael@0: output += lookup[(temp << 2) & 0x3F]; michael@0: output += '='; michael@0: break; michael@0: } michael@0: michael@0: return output; michael@0: } michael@0: michael@0: module.exports.toByteArray = b64ToByteArray; michael@0: module.exports.fromByteArray = uint8ToBase64; michael@0: }()); michael@0: michael@0: },{}]},{},["E/GbHF"]) michael@0: ; michael@0: JSHINT = require('jshint').JSHINT; michael@0: }());