Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | //2.1.3 |
michael@0 | 2 | var JSHINT; |
michael@0 | 3 | (function () { |
michael@0 | 4 | var require; |
michael@0 | 5 | 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<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){ |
michael@0 | 6 | // shim for using process in browser |
michael@0 | 7 | |
michael@0 | 8 | var process = module.exports = {}; |
michael@0 | 9 | |
michael@0 | 10 | process.nextTick = (function () { |
michael@0 | 11 | var canSetImmediate = typeof window !== 'undefined' |
michael@0 | 12 | && window.setImmediate; |
michael@0 | 13 | var canPost = typeof window !== 'undefined' |
michael@0 | 14 | && window.postMessage && window.addEventListener |
michael@0 | 15 | ; |
michael@0 | 16 | |
michael@0 | 17 | if (canSetImmediate) { |
michael@0 | 18 | return function (f) { return window.setImmediate(f) }; |
michael@0 | 19 | } |
michael@0 | 20 | |
michael@0 | 21 | if (canPost) { |
michael@0 | 22 | var queue = []; |
michael@0 | 23 | window.addEventListener('message', function (ev) { |
michael@0 | 24 | if (ev.source === window && ev.data === 'process-tick') { |
michael@0 | 25 | ev.stopPropagation(); |
michael@0 | 26 | if (queue.length > 0) { |
michael@0 | 27 | var fn = queue.shift(); |
michael@0 | 28 | fn(); |
michael@0 | 29 | } |
michael@0 | 30 | } |
michael@0 | 31 | }, true); |
michael@0 | 32 | |
michael@0 | 33 | return function nextTick(fn) { |
michael@0 | 34 | queue.push(fn); |
michael@0 | 35 | window.postMessage('process-tick', '*'); |
michael@0 | 36 | }; |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | return function nextTick(fn) { |
michael@0 | 40 | setTimeout(fn, 0); |
michael@0 | 41 | }; |
michael@0 | 42 | })(); |
michael@0 | 43 | |
michael@0 | 44 | process.title = 'browser'; |
michael@0 | 45 | process.browser = true; |
michael@0 | 46 | process.env = {}; |
michael@0 | 47 | process.argv = []; |
michael@0 | 48 | |
michael@0 | 49 | process.binding = function (name) { |
michael@0 | 50 | throw new Error('process.binding is not supported'); |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | // TODO(shtylman) |
michael@0 | 54 | process.cwd = function () { return '/' }; |
michael@0 | 55 | process.chdir = function (dir) { |
michael@0 | 56 | throw new Error('process.chdir is not supported'); |
michael@0 | 57 | }; |
michael@0 | 58 | |
michael@0 | 59 | },{}],2:[function(require,module,exports){ |
michael@0 | 60 | (function(process){if (!process.EventEmitter) process.EventEmitter = function () {}; |
michael@0 | 61 | |
michael@0 | 62 | var EventEmitter = exports.EventEmitter = process.EventEmitter; |
michael@0 | 63 | var isArray = typeof Array.isArray === 'function' |
michael@0 | 64 | ? Array.isArray |
michael@0 | 65 | : function (xs) { |
michael@0 | 66 | return Object.prototype.toString.call(xs) === '[object Array]' |
michael@0 | 67 | } |
michael@0 | 68 | ; |
michael@0 | 69 | function indexOf (xs, x) { |
michael@0 | 70 | if (xs.indexOf) return xs.indexOf(x); |
michael@0 | 71 | for (var i = 0; i < xs.length; i++) { |
michael@0 | 72 | if (x === xs[i]) return i; |
michael@0 | 73 | } |
michael@0 | 74 | return -1; |
michael@0 | 75 | } |
michael@0 | 76 | |
michael@0 | 77 | // By default EventEmitters will print a warning if more than |
michael@0 | 78 | // 10 listeners are added to it. This is a useful default which |
michael@0 | 79 | // helps finding memory leaks. |
michael@0 | 80 | // |
michael@0 | 81 | // Obviously not all Emitters should be limited to 10. This function allows |
michael@0 | 82 | // that to be increased. Set to zero for unlimited. |
michael@0 | 83 | var defaultMaxListeners = 10; |
michael@0 | 84 | EventEmitter.prototype.setMaxListeners = function(n) { |
michael@0 | 85 | if (!this._events) this._events = {}; |
michael@0 | 86 | this._events.maxListeners = n; |
michael@0 | 87 | }; |
michael@0 | 88 | |
michael@0 | 89 | |
michael@0 | 90 | EventEmitter.prototype.emit = function(type) { |
michael@0 | 91 | // If there is no 'error' event listener then throw. |
michael@0 | 92 | if (type === 'error') { |
michael@0 | 93 | if (!this._events || !this._events.error || |
michael@0 | 94 | (isArray(this._events.error) && !this._events.error.length)) |
michael@0 | 95 | { |
michael@0 | 96 | if (arguments[1] instanceof Error) { |
michael@0 | 97 | throw arguments[1]; // Unhandled 'error' event |
michael@0 | 98 | } else { |
michael@0 | 99 | throw new Error("Uncaught, unspecified 'error' event."); |
michael@0 | 100 | } |
michael@0 | 101 | return false; |
michael@0 | 102 | } |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | if (!this._events) return false; |
michael@0 | 106 | var handler = this._events[type]; |
michael@0 | 107 | if (!handler) return false; |
michael@0 | 108 | |
michael@0 | 109 | if (typeof handler == 'function') { |
michael@0 | 110 | switch (arguments.length) { |
michael@0 | 111 | // fast cases |
michael@0 | 112 | case 1: |
michael@0 | 113 | handler.call(this); |
michael@0 | 114 | break; |
michael@0 | 115 | case 2: |
michael@0 | 116 | handler.call(this, arguments[1]); |
michael@0 | 117 | break; |
michael@0 | 118 | case 3: |
michael@0 | 119 | handler.call(this, arguments[1], arguments[2]); |
michael@0 | 120 | break; |
michael@0 | 121 | // slower |
michael@0 | 122 | default: |
michael@0 | 123 | var args = Array.prototype.slice.call(arguments, 1); |
michael@0 | 124 | handler.apply(this, args); |
michael@0 | 125 | } |
michael@0 | 126 | return true; |
michael@0 | 127 | |
michael@0 | 128 | } else if (isArray(handler)) { |
michael@0 | 129 | var args = Array.prototype.slice.call(arguments, 1); |
michael@0 | 130 | |
michael@0 | 131 | var listeners = handler.slice(); |
michael@0 | 132 | for (var i = 0, l = listeners.length; i < l; i++) { |
michael@0 | 133 | listeners[i].apply(this, args); |
michael@0 | 134 | } |
michael@0 | 135 | return true; |
michael@0 | 136 | |
michael@0 | 137 | } else { |
michael@0 | 138 | return false; |
michael@0 | 139 | } |
michael@0 | 140 | }; |
michael@0 | 141 | |
michael@0 | 142 | // EventEmitter is defined in src/node_events.cc |
michael@0 | 143 | // EventEmitter.prototype.emit() is also defined there. |
michael@0 | 144 | EventEmitter.prototype.addListener = function(type, listener) { |
michael@0 | 145 | if ('function' !== typeof listener) { |
michael@0 | 146 | throw new Error('addListener only takes instances of Function'); |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | if (!this._events) this._events = {}; |
michael@0 | 150 | |
michael@0 | 151 | // To avoid recursion in the case that type == "newListeners"! Before |
michael@0 | 152 | // adding it to the listeners, first emit "newListeners". |
michael@0 | 153 | this.emit('newListener', type, listener); |
michael@0 | 154 | |
michael@0 | 155 | if (!this._events[type]) { |
michael@0 | 156 | // Optimize the case of one listener. Don't need the extra array object. |
michael@0 | 157 | this._events[type] = listener; |
michael@0 | 158 | } else if (isArray(this._events[type])) { |
michael@0 | 159 | |
michael@0 | 160 | // Check for listener leak |
michael@0 | 161 | if (!this._events[type].warned) { |
michael@0 | 162 | var m; |
michael@0 | 163 | if (this._events.maxListeners !== undefined) { |
michael@0 | 164 | m = this._events.maxListeners; |
michael@0 | 165 | } else { |
michael@0 | 166 | m = defaultMaxListeners; |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | if (m && m > 0 && this._events[type].length > m) { |
michael@0 | 170 | this._events[type].warned = true; |
michael@0 | 171 | console.error('(node) warning: possible EventEmitter memory ' + |
michael@0 | 172 | 'leak detected. %d listeners added. ' + |
michael@0 | 173 | 'Use emitter.setMaxListeners() to increase limit.', |
michael@0 | 174 | this._events[type].length); |
michael@0 | 175 | console.trace(); |
michael@0 | 176 | } |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | // If we've already got an array, just append. |
michael@0 | 180 | this._events[type].push(listener); |
michael@0 | 181 | } else { |
michael@0 | 182 | // Adding the second element, need to change to array. |
michael@0 | 183 | this._events[type] = [this._events[type], listener]; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | return this; |
michael@0 | 187 | }; |
michael@0 | 188 | |
michael@0 | 189 | EventEmitter.prototype.on = EventEmitter.prototype.addListener; |
michael@0 | 190 | |
michael@0 | 191 | EventEmitter.prototype.once = function(type, listener) { |
michael@0 | 192 | var self = this; |
michael@0 | 193 | self.on(type, function g() { |
michael@0 | 194 | self.removeListener(type, g); |
michael@0 | 195 | listener.apply(this, arguments); |
michael@0 | 196 | }); |
michael@0 | 197 | |
michael@0 | 198 | return this; |
michael@0 | 199 | }; |
michael@0 | 200 | |
michael@0 | 201 | EventEmitter.prototype.removeListener = function(type, listener) { |
michael@0 | 202 | if ('function' !== typeof listener) { |
michael@0 | 203 | throw new Error('removeListener only takes instances of Function'); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | // does not use listeners(), so no side effect of creating _events[type] |
michael@0 | 207 | if (!this._events || !this._events[type]) return this; |
michael@0 | 208 | |
michael@0 | 209 | var list = this._events[type]; |
michael@0 | 210 | |
michael@0 | 211 | if (isArray(list)) { |
michael@0 | 212 | var i = indexOf(list, listener); |
michael@0 | 213 | if (i < 0) return this; |
michael@0 | 214 | list.splice(i, 1); |
michael@0 | 215 | if (list.length == 0) |
michael@0 | 216 | delete this._events[type]; |
michael@0 | 217 | } else if (this._events[type] === listener) { |
michael@0 | 218 | delete this._events[type]; |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | return this; |
michael@0 | 222 | }; |
michael@0 | 223 | |
michael@0 | 224 | EventEmitter.prototype.removeAllListeners = function(type) { |
michael@0 | 225 | if (arguments.length === 0) { |
michael@0 | 226 | this._events = {}; |
michael@0 | 227 | return this; |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | // does not use listeners(), so no side effect of creating _events[type] |
michael@0 | 231 | if (type && this._events && this._events[type]) this._events[type] = null; |
michael@0 | 232 | return this; |
michael@0 | 233 | }; |
michael@0 | 234 | |
michael@0 | 235 | EventEmitter.prototype.listeners = function(type) { |
michael@0 | 236 | if (!this._events) this._events = {}; |
michael@0 | 237 | if (!this._events[type]) this._events[type] = []; |
michael@0 | 238 | if (!isArray(this._events[type])) { |
michael@0 | 239 | this._events[type] = [this._events[type]]; |
michael@0 | 240 | } |
michael@0 | 241 | return this._events[type]; |
michael@0 | 242 | }; |
michael@0 | 243 | |
michael@0 | 244 | })(require("__browserify_process")) |
michael@0 | 245 | },{"__browserify_process":1}],3:[function(require,module,exports){ |
michael@0 | 246 | (function(){// jshint -W001 |
michael@0 | 247 | |
michael@0 | 248 | "use strict"; |
michael@0 | 249 | |
michael@0 | 250 | // Identifiers provided by the ECMAScript standard. |
michael@0 | 251 | |
michael@0 | 252 | exports.reservedVars = { |
michael@0 | 253 | arguments : false, |
michael@0 | 254 | NaN : false |
michael@0 | 255 | }; |
michael@0 | 256 | |
michael@0 | 257 | exports.ecmaIdentifiers = { |
michael@0 | 258 | Array : false, |
michael@0 | 259 | Boolean : false, |
michael@0 | 260 | Date : false, |
michael@0 | 261 | decodeURI : false, |
michael@0 | 262 | decodeURIComponent : false, |
michael@0 | 263 | encodeURI : false, |
michael@0 | 264 | encodeURIComponent : false, |
michael@0 | 265 | Error : false, |
michael@0 | 266 | "eval" : false, |
michael@0 | 267 | EvalError : false, |
michael@0 | 268 | Function : false, |
michael@0 | 269 | hasOwnProperty : false, |
michael@0 | 270 | isFinite : false, |
michael@0 | 271 | isNaN : false, |
michael@0 | 272 | JSON : false, |
michael@0 | 273 | Math : false, |
michael@0 | 274 | Map : false, |
michael@0 | 275 | Number : false, |
michael@0 | 276 | Object : false, |
michael@0 | 277 | parseInt : false, |
michael@0 | 278 | parseFloat : false, |
michael@0 | 279 | RangeError : false, |
michael@0 | 280 | ReferenceError : false, |
michael@0 | 281 | RegExp : false, |
michael@0 | 282 | Set : false, |
michael@0 | 283 | String : false, |
michael@0 | 284 | SyntaxError : false, |
michael@0 | 285 | TypeError : false, |
michael@0 | 286 | URIError : false, |
michael@0 | 287 | WeakMap : false |
michael@0 | 288 | }; |
michael@0 | 289 | |
michael@0 | 290 | // Global variables commonly provided by a web browser environment. |
michael@0 | 291 | |
michael@0 | 292 | exports.browser = { |
michael@0 | 293 | ArrayBuffer : false, |
michael@0 | 294 | ArrayBufferView : false, |
michael@0 | 295 | Audio : false, |
michael@0 | 296 | Blob : false, |
michael@0 | 297 | addEventListener : false, |
michael@0 | 298 | applicationCache : false, |
michael@0 | 299 | atob : false, |
michael@0 | 300 | blur : false, |
michael@0 | 301 | btoa : false, |
michael@0 | 302 | clearInterval : false, |
michael@0 | 303 | clearTimeout : false, |
michael@0 | 304 | close : false, |
michael@0 | 305 | closed : false, |
michael@0 | 306 | DataView : false, |
michael@0 | 307 | DOMParser : false, |
michael@0 | 308 | defaultStatus : false, |
michael@0 | 309 | document : false, |
michael@0 | 310 | Element : false, |
michael@0 | 311 | ElementTimeControl : false, |
michael@0 | 312 | event : false, |
michael@0 | 313 | FileReader : false, |
michael@0 | 314 | Float32Array : false, |
michael@0 | 315 | Float64Array : false, |
michael@0 | 316 | FormData : false, |
michael@0 | 317 | focus : false, |
michael@0 | 318 | frames : false, |
michael@0 | 319 | getComputedStyle : false, |
michael@0 | 320 | HTMLElement : false, |
michael@0 | 321 | HTMLAnchorElement : false, |
michael@0 | 322 | HTMLBaseElement : false, |
michael@0 | 323 | HTMLBlockquoteElement: false, |
michael@0 | 324 | HTMLBodyElement : false, |
michael@0 | 325 | HTMLBRElement : false, |
michael@0 | 326 | HTMLButtonElement : false, |
michael@0 | 327 | HTMLCanvasElement : false, |
michael@0 | 328 | HTMLDirectoryElement : false, |
michael@0 | 329 | HTMLDivElement : false, |
michael@0 | 330 | HTMLDListElement : false, |
michael@0 | 331 | HTMLFieldSetElement : false, |
michael@0 | 332 | HTMLFontElement : false, |
michael@0 | 333 | HTMLFormElement : false, |
michael@0 | 334 | HTMLFrameElement : false, |
michael@0 | 335 | HTMLFrameSetElement : false, |
michael@0 | 336 | HTMLHeadElement : false, |
michael@0 | 337 | HTMLHeadingElement : false, |
michael@0 | 338 | HTMLHRElement : false, |
michael@0 | 339 | HTMLHtmlElement : false, |
michael@0 | 340 | HTMLIFrameElement : false, |
michael@0 | 341 | HTMLImageElement : false, |
michael@0 | 342 | HTMLInputElement : false, |
michael@0 | 343 | HTMLIsIndexElement : false, |
michael@0 | 344 | HTMLLabelElement : false, |
michael@0 | 345 | HTMLLayerElement : false, |
michael@0 | 346 | HTMLLegendElement : false, |
michael@0 | 347 | HTMLLIElement : false, |
michael@0 | 348 | HTMLLinkElement : false, |
michael@0 | 349 | HTMLMapElement : false, |
michael@0 | 350 | HTMLMenuElement : false, |
michael@0 | 351 | HTMLMetaElement : false, |
michael@0 | 352 | HTMLModElement : false, |
michael@0 | 353 | HTMLObjectElement : false, |
michael@0 | 354 | HTMLOListElement : false, |
michael@0 | 355 | HTMLOptGroupElement : false, |
michael@0 | 356 | HTMLOptionElement : false, |
michael@0 | 357 | HTMLParagraphElement : false, |
michael@0 | 358 | HTMLParamElement : false, |
michael@0 | 359 | HTMLPreElement : false, |
michael@0 | 360 | HTMLQuoteElement : false, |
michael@0 | 361 | HTMLScriptElement : false, |
michael@0 | 362 | HTMLSelectElement : false, |
michael@0 | 363 | HTMLStyleElement : false, |
michael@0 | 364 | HTMLTableCaptionElement: false, |
michael@0 | 365 | HTMLTableCellElement : false, |
michael@0 | 366 | HTMLTableColElement : false, |
michael@0 | 367 | HTMLTableElement : false, |
michael@0 | 368 | HTMLTableRowElement : false, |
michael@0 | 369 | HTMLTableSectionElement: false, |
michael@0 | 370 | HTMLTextAreaElement : false, |
michael@0 | 371 | HTMLTitleElement : false, |
michael@0 | 372 | HTMLUListElement : false, |
michael@0 | 373 | HTMLVideoElement : false, |
michael@0 | 374 | history : false, |
michael@0 | 375 | Int16Array : false, |
michael@0 | 376 | Int32Array : false, |
michael@0 | 377 | Int8Array : false, |
michael@0 | 378 | Image : false, |
michael@0 | 379 | length : false, |
michael@0 | 380 | localStorage : false, |
michael@0 | 381 | location : false, |
michael@0 | 382 | MessageChannel : false, |
michael@0 | 383 | MessageEvent : false, |
michael@0 | 384 | MessagePort : false, |
michael@0 | 385 | moveBy : false, |
michael@0 | 386 | moveTo : false, |
michael@0 | 387 | MutationObserver : false, |
michael@0 | 388 | name : false, |
michael@0 | 389 | Node : false, |
michael@0 | 390 | NodeFilter : false, |
michael@0 | 391 | navigator : false, |
michael@0 | 392 | onbeforeunload : true, |
michael@0 | 393 | onblur : true, |
michael@0 | 394 | onerror : true, |
michael@0 | 395 | onfocus : true, |
michael@0 | 396 | onload : true, |
michael@0 | 397 | onresize : true, |
michael@0 | 398 | onunload : true, |
michael@0 | 399 | open : false, |
michael@0 | 400 | openDatabase : false, |
michael@0 | 401 | opener : false, |
michael@0 | 402 | Option : false, |
michael@0 | 403 | parent : false, |
michael@0 | 404 | print : false, |
michael@0 | 405 | removeEventListener : false, |
michael@0 | 406 | resizeBy : false, |
michael@0 | 407 | resizeTo : false, |
michael@0 | 408 | screen : false, |
michael@0 | 409 | scroll : false, |
michael@0 | 410 | scrollBy : false, |
michael@0 | 411 | scrollTo : false, |
michael@0 | 412 | sessionStorage : false, |
michael@0 | 413 | setInterval : false, |
michael@0 | 414 | setTimeout : false, |
michael@0 | 415 | SharedWorker : false, |
michael@0 | 416 | status : false, |
michael@0 | 417 | SVGAElement : false, |
michael@0 | 418 | SVGAltGlyphDefElement: false, |
michael@0 | 419 | SVGAltGlyphElement : false, |
michael@0 | 420 | SVGAltGlyphItemElement: false, |
michael@0 | 421 | SVGAngle : false, |
michael@0 | 422 | SVGAnimateColorElement: false, |
michael@0 | 423 | SVGAnimateElement : false, |
michael@0 | 424 | SVGAnimateMotionElement: false, |
michael@0 | 425 | SVGAnimateTransformElement: false, |
michael@0 | 426 | SVGAnimatedAngle : false, |
michael@0 | 427 | SVGAnimatedBoolean : false, |
michael@0 | 428 | SVGAnimatedEnumeration: false, |
michael@0 | 429 | SVGAnimatedInteger : false, |
michael@0 | 430 | SVGAnimatedLength : false, |
michael@0 | 431 | SVGAnimatedLengthList: false, |
michael@0 | 432 | SVGAnimatedNumber : false, |
michael@0 | 433 | SVGAnimatedNumberList: false, |
michael@0 | 434 | SVGAnimatedPathData : false, |
michael@0 | 435 | SVGAnimatedPoints : false, |
michael@0 | 436 | SVGAnimatedPreserveAspectRatio: false, |
michael@0 | 437 | SVGAnimatedRect : false, |
michael@0 | 438 | SVGAnimatedString : false, |
michael@0 | 439 | SVGAnimatedTransformList: false, |
michael@0 | 440 | SVGAnimationElement : false, |
michael@0 | 441 | SVGCSSRule : false, |
michael@0 | 442 | SVGCircleElement : false, |
michael@0 | 443 | SVGClipPathElement : false, |
michael@0 | 444 | SVGColor : false, |
michael@0 | 445 | SVGColorProfileElement: false, |
michael@0 | 446 | SVGColorProfileRule : false, |
michael@0 | 447 | SVGComponentTransferFunctionElement: false, |
michael@0 | 448 | SVGCursorElement : false, |
michael@0 | 449 | SVGDefsElement : false, |
michael@0 | 450 | SVGDescElement : false, |
michael@0 | 451 | SVGDocument : false, |
michael@0 | 452 | SVGElement : false, |
michael@0 | 453 | SVGElementInstance : false, |
michael@0 | 454 | SVGElementInstanceList: false, |
michael@0 | 455 | SVGEllipseElement : false, |
michael@0 | 456 | SVGExternalResourcesRequired: false, |
michael@0 | 457 | SVGFEBlendElement : false, |
michael@0 | 458 | SVGFEColorMatrixElement: false, |
michael@0 | 459 | SVGFEComponentTransferElement: false, |
michael@0 | 460 | SVGFECompositeElement: false, |
michael@0 | 461 | SVGFEConvolveMatrixElement: false, |
michael@0 | 462 | SVGFEDiffuseLightingElement: false, |
michael@0 | 463 | SVGFEDisplacementMapElement: false, |
michael@0 | 464 | SVGFEDistantLightElement: false, |
michael@0 | 465 | SVGFEDropShadowElement: false, |
michael@0 | 466 | SVGFEFloodElement : false, |
michael@0 | 467 | SVGFEFuncAElement : false, |
michael@0 | 468 | SVGFEFuncBElement : false, |
michael@0 | 469 | SVGFEFuncGElement : false, |
michael@0 | 470 | SVGFEFuncRElement : false, |
michael@0 | 471 | SVGFEGaussianBlurElement: false, |
michael@0 | 472 | SVGFEImageElement : false, |
michael@0 | 473 | SVGFEMergeElement : false, |
michael@0 | 474 | SVGFEMergeNodeElement: false, |
michael@0 | 475 | SVGFEMorphologyElement: false, |
michael@0 | 476 | SVGFEOffsetElement : false, |
michael@0 | 477 | SVGFEPointLightElement: false, |
michael@0 | 478 | SVGFESpecularLightingElement: false, |
michael@0 | 479 | SVGFESpotLightElement: false, |
michael@0 | 480 | SVGFETileElement : false, |
michael@0 | 481 | SVGFETurbulenceElement: false, |
michael@0 | 482 | SVGFilterElement : false, |
michael@0 | 483 | SVGFilterPrimitiveStandardAttributes: false, |
michael@0 | 484 | SVGFitToViewBox : false, |
michael@0 | 485 | SVGFontElement : false, |
michael@0 | 486 | SVGFontFaceElement : false, |
michael@0 | 487 | SVGFontFaceFormatElement: false, |
michael@0 | 488 | SVGFontFaceNameElement: false, |
michael@0 | 489 | SVGFontFaceSrcElement: false, |
michael@0 | 490 | SVGFontFaceUriElement: false, |
michael@0 | 491 | SVGForeignObjectElement: false, |
michael@0 | 492 | SVGGElement : false, |
michael@0 | 493 | SVGGlyphElement : false, |
michael@0 | 494 | SVGGlyphRefElement : false, |
michael@0 | 495 | SVGGradientElement : false, |
michael@0 | 496 | SVGHKernElement : false, |
michael@0 | 497 | SVGICCColor : false, |
michael@0 | 498 | SVGImageElement : false, |
michael@0 | 499 | SVGLangSpace : false, |
michael@0 | 500 | SVGLength : false, |
michael@0 | 501 | SVGLengthList : false, |
michael@0 | 502 | SVGLineElement : false, |
michael@0 | 503 | SVGLinearGradientElement: false, |
michael@0 | 504 | SVGLocatable : false, |
michael@0 | 505 | SVGMPathElement : false, |
michael@0 | 506 | SVGMarkerElement : false, |
michael@0 | 507 | SVGMaskElement : false, |
michael@0 | 508 | SVGMatrix : false, |
michael@0 | 509 | SVGMetadataElement : false, |
michael@0 | 510 | SVGMissingGlyphElement: false, |
michael@0 | 511 | SVGNumber : false, |
michael@0 | 512 | SVGNumberList : false, |
michael@0 | 513 | SVGPaint : false, |
michael@0 | 514 | SVGPathElement : false, |
michael@0 | 515 | SVGPathSeg : false, |
michael@0 | 516 | SVGPathSegArcAbs : false, |
michael@0 | 517 | SVGPathSegArcRel : false, |
michael@0 | 518 | SVGPathSegClosePath : false, |
michael@0 | 519 | SVGPathSegCurvetoCubicAbs: false, |
michael@0 | 520 | SVGPathSegCurvetoCubicRel: false, |
michael@0 | 521 | SVGPathSegCurvetoCubicSmoothAbs: false, |
michael@0 | 522 | SVGPathSegCurvetoCubicSmoothRel: false, |
michael@0 | 523 | SVGPathSegCurvetoQuadraticAbs: false, |
michael@0 | 524 | SVGPathSegCurvetoQuadraticRel: false, |
michael@0 | 525 | SVGPathSegCurvetoQuadraticSmoothAbs: false, |
michael@0 | 526 | SVGPathSegCurvetoQuadraticSmoothRel: false, |
michael@0 | 527 | SVGPathSegLinetoAbs : false, |
michael@0 | 528 | SVGPathSegLinetoHorizontalAbs: false, |
michael@0 | 529 | SVGPathSegLinetoHorizontalRel: false, |
michael@0 | 530 | SVGPathSegLinetoRel : false, |
michael@0 | 531 | SVGPathSegLinetoVerticalAbs: false, |
michael@0 | 532 | SVGPathSegLinetoVerticalRel: false, |
michael@0 | 533 | SVGPathSegList : false, |
michael@0 | 534 | SVGPathSegMovetoAbs : false, |
michael@0 | 535 | SVGPathSegMovetoRel : false, |
michael@0 | 536 | SVGPatternElement : false, |
michael@0 | 537 | SVGPoint : false, |
michael@0 | 538 | SVGPointList : false, |
michael@0 | 539 | SVGPolygonElement : false, |
michael@0 | 540 | SVGPolylineElement : false, |
michael@0 | 541 | SVGPreserveAspectRatio: false, |
michael@0 | 542 | SVGRadialGradientElement: false, |
michael@0 | 543 | SVGRect : false, |
michael@0 | 544 | SVGRectElement : false, |
michael@0 | 545 | SVGRenderingIntent : false, |
michael@0 | 546 | SVGSVGElement : false, |
michael@0 | 547 | SVGScriptElement : false, |
michael@0 | 548 | SVGSetElement : false, |
michael@0 | 549 | SVGStopElement : false, |
michael@0 | 550 | SVGStringList : false, |
michael@0 | 551 | SVGStylable : false, |
michael@0 | 552 | SVGStyleElement : false, |
michael@0 | 553 | SVGSwitchElement : false, |
michael@0 | 554 | SVGSymbolElement : false, |
michael@0 | 555 | SVGTRefElement : false, |
michael@0 | 556 | SVGTSpanElement : false, |
michael@0 | 557 | SVGTests : false, |
michael@0 | 558 | SVGTextContentElement: false, |
michael@0 | 559 | SVGTextElement : false, |
michael@0 | 560 | SVGTextPathElement : false, |
michael@0 | 561 | SVGTextPositioningElement: false, |
michael@0 | 562 | SVGTitleElement : false, |
michael@0 | 563 | SVGTransform : false, |
michael@0 | 564 | SVGTransformList : false, |
michael@0 | 565 | SVGTransformable : false, |
michael@0 | 566 | SVGURIReference : false, |
michael@0 | 567 | SVGUnitTypes : false, |
michael@0 | 568 | SVGUseElement : false, |
michael@0 | 569 | SVGVKernElement : false, |
michael@0 | 570 | SVGViewElement : false, |
michael@0 | 571 | SVGViewSpec : false, |
michael@0 | 572 | SVGZoomAndPan : false, |
michael@0 | 573 | TimeEvent : false, |
michael@0 | 574 | top : false, |
michael@0 | 575 | Uint16Array : false, |
michael@0 | 576 | Uint32Array : false, |
michael@0 | 577 | Uint8Array : false, |
michael@0 | 578 | Uint8ClampedArray : false, |
michael@0 | 579 | WebSocket : false, |
michael@0 | 580 | window : false, |
michael@0 | 581 | Worker : false, |
michael@0 | 582 | XMLHttpRequest : false, |
michael@0 | 583 | XMLSerializer : false, |
michael@0 | 584 | XPathEvaluator : false, |
michael@0 | 585 | XPathException : false, |
michael@0 | 586 | XPathExpression : false, |
michael@0 | 587 | XPathNSResolver : false, |
michael@0 | 588 | XPathResult : false |
michael@0 | 589 | }; |
michael@0 | 590 | |
michael@0 | 591 | exports.devel = { |
michael@0 | 592 | alert : false, |
michael@0 | 593 | confirm: false, |
michael@0 | 594 | console: false, |
michael@0 | 595 | Debug : false, |
michael@0 | 596 | opera : false, |
michael@0 | 597 | prompt : false |
michael@0 | 598 | }; |
michael@0 | 599 | |
michael@0 | 600 | exports.worker = { |
michael@0 | 601 | importScripts: true, |
michael@0 | 602 | postMessage : true, |
michael@0 | 603 | self : true |
michael@0 | 604 | }; |
michael@0 | 605 | |
michael@0 | 606 | // Widely adopted global names that are not part of ECMAScript standard |
michael@0 | 607 | exports.nonstandard = { |
michael@0 | 608 | escape : false, |
michael@0 | 609 | unescape: false |
michael@0 | 610 | }; |
michael@0 | 611 | |
michael@0 | 612 | // Globals provided by popular JavaScript environments. |
michael@0 | 613 | |
michael@0 | 614 | exports.couch = { |
michael@0 | 615 | "require" : false, |
michael@0 | 616 | respond : false, |
michael@0 | 617 | getRow : false, |
michael@0 | 618 | emit : false, |
michael@0 | 619 | send : false, |
michael@0 | 620 | start : false, |
michael@0 | 621 | sum : false, |
michael@0 | 622 | log : false, |
michael@0 | 623 | exports : false, |
michael@0 | 624 | module : false, |
michael@0 | 625 | provides : false |
michael@0 | 626 | }; |
michael@0 | 627 | |
michael@0 | 628 | exports.node = { |
michael@0 | 629 | __filename : false, |
michael@0 | 630 | __dirname : false, |
michael@0 | 631 | Buffer : false, |
michael@0 | 632 | DataView : false, |
michael@0 | 633 | console : false, |
michael@0 | 634 | exports : true, // In Node it is ok to exports = module.exports = foo(); |
michael@0 | 635 | GLOBAL : false, |
michael@0 | 636 | global : false, |
michael@0 | 637 | module : false, |
michael@0 | 638 | process : false, |
michael@0 | 639 | require : false, |
michael@0 | 640 | setTimeout : false, |
michael@0 | 641 | clearTimeout : false, |
michael@0 | 642 | setInterval : false, |
michael@0 | 643 | clearInterval : false, |
michael@0 | 644 | setImmediate : false, // v0.9.1+ |
michael@0 | 645 | clearImmediate: false // v0.9.1+ |
michael@0 | 646 | }; |
michael@0 | 647 | |
michael@0 | 648 | exports.phantom = { |
michael@0 | 649 | phantom : true, |
michael@0 | 650 | require : true, |
michael@0 | 651 | WebPage : true |
michael@0 | 652 | }; |
michael@0 | 653 | |
michael@0 | 654 | exports.rhino = { |
michael@0 | 655 | defineClass : false, |
michael@0 | 656 | deserialize : false, |
michael@0 | 657 | gc : false, |
michael@0 | 658 | help : false, |
michael@0 | 659 | importPackage: false, |
michael@0 | 660 | "java" : false, |
michael@0 | 661 | load : false, |
michael@0 | 662 | loadClass : false, |
michael@0 | 663 | print : false, |
michael@0 | 664 | quit : false, |
michael@0 | 665 | readFile : false, |
michael@0 | 666 | readUrl : false, |
michael@0 | 667 | runCommand : false, |
michael@0 | 668 | seal : false, |
michael@0 | 669 | serialize : false, |
michael@0 | 670 | spawn : false, |
michael@0 | 671 | sync : false, |
michael@0 | 672 | toint32 : false, |
michael@0 | 673 | version : false |
michael@0 | 674 | }; |
michael@0 | 675 | |
michael@0 | 676 | exports.wsh = { |
michael@0 | 677 | ActiveXObject : true, |
michael@0 | 678 | Enumerator : true, |
michael@0 | 679 | GetObject : true, |
michael@0 | 680 | ScriptEngine : true, |
michael@0 | 681 | ScriptEngineBuildVersion : true, |
michael@0 | 682 | ScriptEngineMajorVersion : true, |
michael@0 | 683 | ScriptEngineMinorVersion : true, |
michael@0 | 684 | VBArray : true, |
michael@0 | 685 | WSH : true, |
michael@0 | 686 | WScript : true, |
michael@0 | 687 | XDomainRequest : true |
michael@0 | 688 | }; |
michael@0 | 689 | |
michael@0 | 690 | // Globals provided by popular JavaScript libraries. |
michael@0 | 691 | |
michael@0 | 692 | exports.dojo = { |
michael@0 | 693 | dojo : false, |
michael@0 | 694 | dijit : false, |
michael@0 | 695 | dojox : false, |
michael@0 | 696 | define : false, |
michael@0 | 697 | "require": false |
michael@0 | 698 | }; |
michael@0 | 699 | |
michael@0 | 700 | exports.jquery = { |
michael@0 | 701 | "$" : false, |
michael@0 | 702 | jQuery : false |
michael@0 | 703 | }; |
michael@0 | 704 | |
michael@0 | 705 | exports.mootools = { |
michael@0 | 706 | "$" : false, |
michael@0 | 707 | "$$" : false, |
michael@0 | 708 | Asset : false, |
michael@0 | 709 | Browser : false, |
michael@0 | 710 | Chain : false, |
michael@0 | 711 | Class : false, |
michael@0 | 712 | Color : false, |
michael@0 | 713 | Cookie : false, |
michael@0 | 714 | Core : false, |
michael@0 | 715 | Document : false, |
michael@0 | 716 | DomReady : false, |
michael@0 | 717 | DOMEvent : false, |
michael@0 | 718 | DOMReady : false, |
michael@0 | 719 | Drag : false, |
michael@0 | 720 | Element : false, |
michael@0 | 721 | Elements : false, |
michael@0 | 722 | Event : false, |
michael@0 | 723 | Events : false, |
michael@0 | 724 | Fx : false, |
michael@0 | 725 | Group : false, |
michael@0 | 726 | Hash : false, |
michael@0 | 727 | HtmlTable : false, |
michael@0 | 728 | Iframe : false, |
michael@0 | 729 | IframeShim : false, |
michael@0 | 730 | InputValidator: false, |
michael@0 | 731 | instanceOf : false, |
michael@0 | 732 | Keyboard : false, |
michael@0 | 733 | Locale : false, |
michael@0 | 734 | Mask : false, |
michael@0 | 735 | MooTools : false, |
michael@0 | 736 | Native : false, |
michael@0 | 737 | Options : false, |
michael@0 | 738 | OverText : false, |
michael@0 | 739 | Request : false, |
michael@0 | 740 | Scroller : false, |
michael@0 | 741 | Slick : false, |
michael@0 | 742 | Slider : false, |
michael@0 | 743 | Sortables : false, |
michael@0 | 744 | Spinner : false, |
michael@0 | 745 | Swiff : false, |
michael@0 | 746 | Tips : false, |
michael@0 | 747 | Type : false, |
michael@0 | 748 | typeOf : false, |
michael@0 | 749 | URI : false, |
michael@0 | 750 | Window : false |
michael@0 | 751 | }; |
michael@0 | 752 | |
michael@0 | 753 | exports.prototypejs = { |
michael@0 | 754 | "$" : false, |
michael@0 | 755 | "$$" : false, |
michael@0 | 756 | "$A" : false, |
michael@0 | 757 | "$F" : false, |
michael@0 | 758 | "$H" : false, |
michael@0 | 759 | "$R" : false, |
michael@0 | 760 | "$break" : false, |
michael@0 | 761 | "$continue" : false, |
michael@0 | 762 | "$w" : false, |
michael@0 | 763 | Abstract : false, |
michael@0 | 764 | Ajax : false, |
michael@0 | 765 | Class : false, |
michael@0 | 766 | Enumerable : false, |
michael@0 | 767 | Element : false, |
michael@0 | 768 | Event : false, |
michael@0 | 769 | Field : false, |
michael@0 | 770 | Form : false, |
michael@0 | 771 | Hash : false, |
michael@0 | 772 | Insertion : false, |
michael@0 | 773 | ObjectRange : false, |
michael@0 | 774 | PeriodicalExecuter: false, |
michael@0 | 775 | Position : false, |
michael@0 | 776 | Prototype : false, |
michael@0 | 777 | Selector : false, |
michael@0 | 778 | Template : false, |
michael@0 | 779 | Toggle : false, |
michael@0 | 780 | Try : false, |
michael@0 | 781 | Autocompleter : false, |
michael@0 | 782 | Builder : false, |
michael@0 | 783 | Control : false, |
michael@0 | 784 | Draggable : false, |
michael@0 | 785 | Draggables : false, |
michael@0 | 786 | Droppables : false, |
michael@0 | 787 | Effect : false, |
michael@0 | 788 | Sortable : false, |
michael@0 | 789 | SortableObserver : false, |
michael@0 | 790 | Sound : false, |
michael@0 | 791 | Scriptaculous : false |
michael@0 | 792 | }; |
michael@0 | 793 | |
michael@0 | 794 | exports.yui = { |
michael@0 | 795 | YUI : false, |
michael@0 | 796 | Y : false, |
michael@0 | 797 | YUI_config: false |
michael@0 | 798 | }; |
michael@0 | 799 | |
michael@0 | 800 | |
michael@0 | 801 | })() |
michael@0 | 802 | },{}],4:[function(require,module,exports){ |
michael@0 | 803 | "use strict"; |
michael@0 | 804 | |
michael@0 | 805 | var state = { |
michael@0 | 806 | syntax: {}, |
michael@0 | 807 | |
michael@0 | 808 | reset: function () { |
michael@0 | 809 | this.tokens = { |
michael@0 | 810 | prev: null, |
michael@0 | 811 | next: null, |
michael@0 | 812 | curr: null |
michael@0 | 813 | }; |
michael@0 | 814 | |
michael@0 | 815 | this.option = {}; |
michael@0 | 816 | this.ignored = {}; |
michael@0 | 817 | this.directive = {}; |
michael@0 | 818 | this.jsonMode = false; |
michael@0 | 819 | this.jsonWarnings = []; |
michael@0 | 820 | this.lines = []; |
michael@0 | 821 | this.tab = ""; |
michael@0 | 822 | this.cache = {}; // Node.JS doesn't have Map. Sniff. |
michael@0 | 823 | } |
michael@0 | 824 | }; |
michael@0 | 825 | |
michael@0 | 826 | exports.state = state; |
michael@0 | 827 | |
michael@0 | 828 | },{}],5:[function(require,module,exports){ |
michael@0 | 829 | (function(){"use strict"; |
michael@0 | 830 | |
michael@0 | 831 | exports.register = function (linter) { |
michael@0 | 832 | // Check for properties named __proto__. This special property was |
michael@0 | 833 | // deprecated and then re-introduced for ES6. |
michael@0 | 834 | |
michael@0 | 835 | linter.on("Identifier", function style_scanProto(data) { |
michael@0 | 836 | if (linter.getOption("proto")) { |
michael@0 | 837 | return; |
michael@0 | 838 | } |
michael@0 | 839 | |
michael@0 | 840 | if (data.name === "__proto__") { |
michael@0 | 841 | linter.warn("W103", { |
michael@0 | 842 | line: data.line, |
michael@0 | 843 | char: data.char, |
michael@0 | 844 | data: [ data.name ] |
michael@0 | 845 | }); |
michael@0 | 846 | } |
michael@0 | 847 | }); |
michael@0 | 848 | |
michael@0 | 849 | // Check for properties named __iterator__. This is a special property |
michael@0 | 850 | // available only in browsers with JavaScript 1.7 implementation. |
michael@0 | 851 | |
michael@0 | 852 | linter.on("Identifier", function style_scanIterator(data) { |
michael@0 | 853 | if (linter.getOption("iterator")) { |
michael@0 | 854 | return; |
michael@0 | 855 | } |
michael@0 | 856 | |
michael@0 | 857 | if (data.name === "__iterator__") { |
michael@0 | 858 | linter.warn("W104", { |
michael@0 | 859 | line: data.line, |
michael@0 | 860 | char: data.char, |
michael@0 | 861 | data: [ data.name ] |
michael@0 | 862 | }); |
michael@0 | 863 | } |
michael@0 | 864 | }); |
michael@0 | 865 | |
michael@0 | 866 | // Check for dangling underscores. |
michael@0 | 867 | |
michael@0 | 868 | linter.on("Identifier", function style_scanDangling(data) { |
michael@0 | 869 | if (!linter.getOption("nomen")) { |
michael@0 | 870 | return; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | // Underscore.js |
michael@0 | 874 | if (data.name === "_") { |
michael@0 | 875 | return; |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | // In Node, __dirname and __filename should be ignored. |
michael@0 | 879 | if (linter.getOption("node")) { |
michael@0 | 880 | if (/^(__dirname|__filename)$/.test(data.name) && !data.isProperty) { |
michael@0 | 881 | return; |
michael@0 | 882 | } |
michael@0 | 883 | } |
michael@0 | 884 | |
michael@0 | 885 | if (/^(_+.*|.*_+)$/.test(data.name)) { |
michael@0 | 886 | linter.warn("W105", { |
michael@0 | 887 | line: data.line, |
michael@0 | 888 | char: data.from, |
michael@0 | 889 | data: [ "dangling '_'", data.name ] |
michael@0 | 890 | }); |
michael@0 | 891 | } |
michael@0 | 892 | }); |
michael@0 | 893 | |
michael@0 | 894 | // Check that all identifiers are using camelCase notation. |
michael@0 | 895 | // Exceptions: names like MY_VAR and _myVar. |
michael@0 | 896 | |
michael@0 | 897 | linter.on("Identifier", function style_scanCamelCase(data) { |
michael@0 | 898 | if (!linter.getOption("camelcase")) { |
michael@0 | 899 | return; |
michael@0 | 900 | } |
michael@0 | 901 | |
michael@0 | 902 | if (data.name.replace(/^_+/, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { |
michael@0 | 903 | linter.warn("W106", { |
michael@0 | 904 | line: data.line, |
michael@0 | 905 | char: data.from, |
michael@0 | 906 | data: [ data.name ] |
michael@0 | 907 | }); |
michael@0 | 908 | } |
michael@0 | 909 | }); |
michael@0 | 910 | |
michael@0 | 911 | // Enforce consistency in style of quoting. |
michael@0 | 912 | |
michael@0 | 913 | linter.on("String", function style_scanQuotes(data) { |
michael@0 | 914 | var quotmark = linter.getOption("quotmark"); |
michael@0 | 915 | var code; |
michael@0 | 916 | |
michael@0 | 917 | if (!quotmark) { |
michael@0 | 918 | return; |
michael@0 | 919 | } |
michael@0 | 920 | |
michael@0 | 921 | // If quotmark is set to 'single' warn about all double-quotes. |
michael@0 | 922 | |
michael@0 | 923 | if (quotmark === "single" && data.quote !== "'") { |
michael@0 | 924 | code = "W109"; |
michael@0 | 925 | } |
michael@0 | 926 | |
michael@0 | 927 | // If quotmark is set to 'double' warn about all single-quotes. |
michael@0 | 928 | |
michael@0 | 929 | if (quotmark === "double" && data.quote !== "\"") { |
michael@0 | 930 | code = "W108"; |
michael@0 | 931 | } |
michael@0 | 932 | |
michael@0 | 933 | // If quotmark is set to true, remember the first quotation style |
michael@0 | 934 | // and then warn about all others. |
michael@0 | 935 | |
michael@0 | 936 | if (quotmark === true) { |
michael@0 | 937 | if (!linter.getCache("quotmark")) { |
michael@0 | 938 | linter.setCache("quotmark", data.quote); |
michael@0 | 939 | } |
michael@0 | 940 | |
michael@0 | 941 | if (linter.getCache("quotmark") !== data.quote) { |
michael@0 | 942 | code = "W110"; |
michael@0 | 943 | } |
michael@0 | 944 | } |
michael@0 | 945 | |
michael@0 | 946 | if (code) { |
michael@0 | 947 | linter.warn(code, { |
michael@0 | 948 | line: data.line, |
michael@0 | 949 | char: data.char, |
michael@0 | 950 | }); |
michael@0 | 951 | } |
michael@0 | 952 | }); |
michael@0 | 953 | |
michael@0 | 954 | linter.on("Number", function style_scanNumbers(data) { |
michael@0 | 955 | if (data.value.charAt(0) === ".") { |
michael@0 | 956 | // Warn about a leading decimal point. |
michael@0 | 957 | linter.warn("W008", { |
michael@0 | 958 | line: data.line, |
michael@0 | 959 | char: data.char, |
michael@0 | 960 | data: [ data.value ] |
michael@0 | 961 | }); |
michael@0 | 962 | } |
michael@0 | 963 | |
michael@0 | 964 | if (data.value.substr(data.value.length - 1) === ".") { |
michael@0 | 965 | // Warn about a trailing decimal point. |
michael@0 | 966 | linter.warn("W047", { |
michael@0 | 967 | line: data.line, |
michael@0 | 968 | char: data.char, |
michael@0 | 969 | data: [ data.value ] |
michael@0 | 970 | }); |
michael@0 | 971 | } |
michael@0 | 972 | |
michael@0 | 973 | if (/^00+/.test(data.value)) { |
michael@0 | 974 | // Multiple leading zeroes. |
michael@0 | 975 | linter.warn("W046", { |
michael@0 | 976 | line: data.line, |
michael@0 | 977 | char: data.char, |
michael@0 | 978 | data: [ data.value ] |
michael@0 | 979 | }); |
michael@0 | 980 | } |
michael@0 | 981 | }); |
michael@0 | 982 | |
michael@0 | 983 | // Warn about script URLs. |
michael@0 | 984 | |
michael@0 | 985 | linter.on("String", function style_scanJavaScriptURLs(data) { |
michael@0 | 986 | var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; |
michael@0 | 987 | |
michael@0 | 988 | if (linter.getOption("scripturl")) { |
michael@0 | 989 | return; |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | if (re.test(data.value)) { |
michael@0 | 993 | linter.warn("W107", { |
michael@0 | 994 | line: data.line, |
michael@0 | 995 | char: data.char |
michael@0 | 996 | }); |
michael@0 | 997 | } |
michael@0 | 998 | }); |
michael@0 | 999 | }; |
michael@0 | 1000 | })() |
michael@0 | 1001 | },{}],6:[function(require,module,exports){ |
michael@0 | 1002 | /* |
michael@0 | 1003 | * Regular expressions. Some of these are stupidly long. |
michael@0 | 1004 | */ |
michael@0 | 1005 | |
michael@0 | 1006 | /*jshint maxlen:1000 */ |
michael@0 | 1007 | |
michael@0 | 1008 | "use string"; |
michael@0 | 1009 | |
michael@0 | 1010 | // Unsafe comment or string (ax) |
michael@0 | 1011 | exports.unsafeString = |
michael@0 | 1012 | /@cc|<\/?|script|\]\s*\]|<\s*!|</i; |
michael@0 | 1013 | |
michael@0 | 1014 | // Unsafe characters that are silently deleted by one or more browsers (cx) |
michael@0 | 1015 | exports.unsafeChars = |
michael@0 | 1016 | /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; |
michael@0 | 1017 | |
michael@0 | 1018 | // Characters in strings that need escaping (nx and nxg) |
michael@0 | 1019 | exports.needEsc = |
michael@0 | 1020 | /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; |
michael@0 | 1021 | |
michael@0 | 1022 | exports.needEscGlobal = |
michael@0 | 1023 | /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; |
michael@0 | 1024 | |
michael@0 | 1025 | // Star slash (lx) |
michael@0 | 1026 | exports.starSlash = /\*\//; |
michael@0 | 1027 | |
michael@0 | 1028 | // Identifier (ix) |
michael@0 | 1029 | exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; |
michael@0 | 1030 | |
michael@0 | 1031 | // JavaScript URL (jx) |
michael@0 | 1032 | exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; |
michael@0 | 1033 | |
michael@0 | 1034 | // Catches /* falls through */ comments (ft) |
michael@0 | 1035 | //exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; |
michael@0 | 1036 | exports.fallsThrough = /^\s*\/\/\s*Falls?\sthrough.*\s*$/; |
michael@0 | 1037 | |
michael@0 | 1038 | },{}],7:[function(require,module,exports){ |
michael@0 | 1039 | (function(global){/*global window, global*/ |
michael@0 | 1040 | var util = require("util") |
michael@0 | 1041 | var assert = require("assert") |
michael@0 | 1042 | |
michael@0 | 1043 | var slice = Array.prototype.slice |
michael@0 | 1044 | var console |
michael@0 | 1045 | var times = {} |
michael@0 | 1046 | |
michael@0 | 1047 | if (typeof global !== "undefined" && global.console) { |
michael@0 | 1048 | console = global.console |
michael@0 | 1049 | } else if (typeof window !== "undefined" && window.console) { |
michael@0 | 1050 | console = window.console |
michael@0 | 1051 | } else { |
michael@0 | 1052 | console = window.console = {} |
michael@0 | 1053 | } |
michael@0 | 1054 | |
michael@0 | 1055 | var functions = [ |
michael@0 | 1056 | [log, "log"] |
michael@0 | 1057 | , [info, "info"] |
michael@0 | 1058 | , [warn, "warn"] |
michael@0 | 1059 | , [error, "error"] |
michael@0 | 1060 | , [time, "time"] |
michael@0 | 1061 | , [timeEnd, "timeEnd"] |
michael@0 | 1062 | , [trace, "trace"] |
michael@0 | 1063 | , [dir, "dir"] |
michael@0 | 1064 | , [assert, "assert"] |
michael@0 | 1065 | ] |
michael@0 | 1066 | |
michael@0 | 1067 | for (var i = 0; i < functions.length; i++) { |
michael@0 | 1068 | var tuple = functions[i] |
michael@0 | 1069 | var f = tuple[0] |
michael@0 | 1070 | var name = tuple[1] |
michael@0 | 1071 | |
michael@0 | 1072 | if (!console[name]) { |
michael@0 | 1073 | console[name] = f |
michael@0 | 1074 | } |
michael@0 | 1075 | } |
michael@0 | 1076 | |
michael@0 | 1077 | module.exports = console |
michael@0 | 1078 | |
michael@0 | 1079 | function log() {} |
michael@0 | 1080 | |
michael@0 | 1081 | function info() { |
michael@0 | 1082 | console.log.apply(console, arguments) |
michael@0 | 1083 | } |
michael@0 | 1084 | |
michael@0 | 1085 | function warn() { |
michael@0 | 1086 | console.log.apply(console, arguments) |
michael@0 | 1087 | } |
michael@0 | 1088 | |
michael@0 | 1089 | function error() { |
michael@0 | 1090 | console.warn.apply(console, arguments) |
michael@0 | 1091 | } |
michael@0 | 1092 | |
michael@0 | 1093 | function time(label) { |
michael@0 | 1094 | times[label] = Date.now() |
michael@0 | 1095 | } |
michael@0 | 1096 | |
michael@0 | 1097 | function timeEnd(label) { |
michael@0 | 1098 | var time = times[label] |
michael@0 | 1099 | if (!time) { |
michael@0 | 1100 | throw new Error("No such label: " + label) |
michael@0 | 1101 | } |
michael@0 | 1102 | |
michael@0 | 1103 | var duration = Date.now() - time |
michael@0 | 1104 | console.log(label + ": " + duration + "ms") |
michael@0 | 1105 | } |
michael@0 | 1106 | |
michael@0 | 1107 | function trace() { |
michael@0 | 1108 | var err = new Error() |
michael@0 | 1109 | err.name = "Trace" |
michael@0 | 1110 | err.message = util.format.apply(null, arguments) |
michael@0 | 1111 | console.error(err.stack) |
michael@0 | 1112 | } |
michael@0 | 1113 | |
michael@0 | 1114 | function dir(object) { |
michael@0 | 1115 | console.log(util.inspect(object) + "\n") |
michael@0 | 1116 | } |
michael@0 | 1117 | |
michael@0 | 1118 | function assert(expression) { |
michael@0 | 1119 | if (!expression) { |
michael@0 | 1120 | var arr = slice.call(arguments, 1) |
michael@0 | 1121 | assert.ok(false, util.format.apply(null, arr)) |
michael@0 | 1122 | } |
michael@0 | 1123 | } |
michael@0 | 1124 | |
michael@0 | 1125 | })(window) |
michael@0 | 1126 | },{"util":8,"assert":9}],10:[function(require,module,exports){ |
michael@0 | 1127 | (function(){/* |
michael@0 | 1128 | * Lexical analysis and token construction. |
michael@0 | 1129 | */ |
michael@0 | 1130 | |
michael@0 | 1131 | "use strict"; |
michael@0 | 1132 | |
michael@0 | 1133 | var _ = require("underscore"); |
michael@0 | 1134 | var events = require("events"); |
michael@0 | 1135 | var reg = require("./reg.js"); |
michael@0 | 1136 | var state = require("./state.js").state; |
michael@0 | 1137 | |
michael@0 | 1138 | // Some of these token types are from JavaScript Parser API |
michael@0 | 1139 | // while others are specific to JSHint parser. |
michael@0 | 1140 | // JS Parser API: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API |
michael@0 | 1141 | |
michael@0 | 1142 | var Token = { |
michael@0 | 1143 | Identifier: 1, |
michael@0 | 1144 | Punctuator: 2, |
michael@0 | 1145 | NumericLiteral: 3, |
michael@0 | 1146 | StringLiteral: 4, |
michael@0 | 1147 | Comment: 5, |
michael@0 | 1148 | Keyword: 6, |
michael@0 | 1149 | NullLiteral: 7, |
michael@0 | 1150 | BooleanLiteral: 8, |
michael@0 | 1151 | RegExp: 9 |
michael@0 | 1152 | }; |
michael@0 | 1153 | |
michael@0 | 1154 | // This is auto generated from the unicode tables. |
michael@0 | 1155 | // The tables are at: |
michael@0 | 1156 | // http://www.fileformat.info/info/unicode/category/Lu/list.htm |
michael@0 | 1157 | // http://www.fileformat.info/info/unicode/category/Ll/list.htm |
michael@0 | 1158 | // http://www.fileformat.info/info/unicode/category/Lt/list.htm |
michael@0 | 1159 | // http://www.fileformat.info/info/unicode/category/Lm/list.htm |
michael@0 | 1160 | // http://www.fileformat.info/info/unicode/category/Lo/list.htm |
michael@0 | 1161 | // http://www.fileformat.info/info/unicode/category/Nl/list.htm |
michael@0 | 1162 | |
michael@0 | 1163 | var unicodeLetterTable = [ |
michael@0 | 1164 | 170, 170, 181, 181, 186, 186, 192, 214, |
michael@0 | 1165 | 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, |
michael@0 | 1166 | 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, |
michael@0 | 1167 | 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, |
michael@0 | 1168 | 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, |
michael@0 | 1169 | 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, |
michael@0 | 1170 | 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, |
michael@0 | 1171 | 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, |
michael@0 | 1172 | 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361, |
michael@0 | 1173 | 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, |
michael@0 | 1174 | 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, |
michael@0 | 1175 | 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, |
michael@0 | 1176 | 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, |
michael@0 | 1177 | 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, |
michael@0 | 1178 | 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, |
michael@0 | 1179 | 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, |
michael@0 | 1180 | 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, |
michael@0 | 1181 | 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, |
michael@0 | 1182 | 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, |
michael@0 | 1183 | 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, |
michael@0 | 1184 | 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, |
michael@0 | 1185 | 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, |
michael@0 | 1186 | 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, |
michael@0 | 1187 | 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, |
michael@0 | 1188 | 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, |
michael@0 | 1189 | 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, |
michael@0 | 1190 | 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, |
michael@0 | 1191 | 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, |
michael@0 | 1192 | 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, |
michael@0 | 1193 | 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, |
michael@0 | 1194 | 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, |
michael@0 | 1195 | 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, |
michael@0 | 1196 | 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346, |
michael@0 | 1197 | 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696, |
michael@0 | 1198 | 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, |
michael@0 | 1199 | 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, |
michael@0 | 1200 | 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, |
michael@0 | 1201 | 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, |
michael@0 | 1202 | 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, |
michael@0 | 1203 | 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, |
michael@0 | 1204 | 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, |
michael@0 | 1205 | 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, |
michael@0 | 1206 | 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141, |
michael@0 | 1207 | 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, |
michael@0 | 1208 | 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, |
michael@0 | 1209 | 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, |
michael@0 | 1210 | 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, |
michael@0 | 1211 | 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, |
michael@0 | 1212 | 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, |
michael@0 | 1213 | 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, |
michael@0 | 1214 | 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, |
michael@0 | 1215 | 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, |
michael@0 | 1216 | 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621, |
michael@0 | 1217 | 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, |
michael@0 | 1218 | 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, |
michael@0 | 1219 | 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, |
michael@0 | 1220 | 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, |
michael@0 | 1221 | 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, |
michael@0 | 1222 | 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312, |
michael@0 | 1223 | 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124, |
michael@0 | 1224 | 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, |
michael@0 | 1225 | 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, |
michael@0 | 1226 | 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921, |
michael@0 | 1227 | 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042, |
michael@0 | 1228 | 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, |
michael@0 | 1229 | 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, |
michael@0 | 1230 | 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, |
michael@0 | 1231 | 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, |
michael@0 | 1232 | 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, |
michael@0 | 1233 | 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798, |
michael@0 | 1234 | 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032, |
michael@0 | 1235 | 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045, |
michael@0 | 1236 | 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279, |
michael@0 | 1237 | 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, |
michael@0 | 1238 | 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, |
michael@0 | 1239 | 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, |
michael@0 | 1240 | 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, |
michael@0 | 1241 | 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, |
michael@0 | 1242 | 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, |
michael@0 | 1243 | 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, |
michael@0 | 1244 | 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334, |
michael@0 | 1245 | 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511, |
michael@0 | 1246 | 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592, |
michael@0 | 1247 | 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, |
michael@0 | 1248 | 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115, |
michael@0 | 1249 | 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405, |
michael@0 | 1250 | 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687, |
michael@0 | 1251 | 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894, |
michael@0 | 1252 | 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964, |
michael@0 | 1253 | 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, |
michael@0 | 1254 | 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, |
michael@0 | 1255 | 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, |
michael@0 | 1256 | 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, |
michael@0 | 1257 | 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, |
michael@0 | 1258 | 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, |
michael@0 | 1259 | 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, |
michael@0 | 1260 | 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972, |
michael@0 | 1261 | 177984, 177984, 178205, 178205, 194560, 195101 |
michael@0 | 1262 | ]; |
michael@0 | 1263 | |
michael@0 | 1264 | var identifierStartTable = []; |
michael@0 | 1265 | |
michael@0 | 1266 | for (var i = 0; i < 128; i++) { |
michael@0 | 1267 | identifierStartTable[i] = |
michael@0 | 1268 | i === 36 || // $ |
michael@0 | 1269 | i >= 65 && i <= 90 || // A-Z |
michael@0 | 1270 | i === 95 || // _ |
michael@0 | 1271 | i >= 97 && i <= 122; // a-z |
michael@0 | 1272 | } |
michael@0 | 1273 | |
michael@0 | 1274 | var identifierPartTable = []; |
michael@0 | 1275 | |
michael@0 | 1276 | for (var i = 0; i < 128; i++) { |
michael@0 | 1277 | identifierPartTable[i] = |
michael@0 | 1278 | identifierStartTable[i] || // $, _, A-Z, a-z |
michael@0 | 1279 | i >= 48 && i <= 57; // 0-9 |
michael@0 | 1280 | } |
michael@0 | 1281 | |
michael@0 | 1282 | // Object that handles postponed lexing verifications that checks the parsed |
michael@0 | 1283 | // environment state. |
michael@0 | 1284 | |
michael@0 | 1285 | function asyncTrigger() { |
michael@0 | 1286 | var _checks = []; |
michael@0 | 1287 | |
michael@0 | 1288 | return { |
michael@0 | 1289 | push: function (fn) { |
michael@0 | 1290 | _checks.push(fn); |
michael@0 | 1291 | }, |
michael@0 | 1292 | |
michael@0 | 1293 | check: function () { |
michael@0 | 1294 | for (var check in _checks) { |
michael@0 | 1295 | _checks[check](); |
michael@0 | 1296 | } |
michael@0 | 1297 | |
michael@0 | 1298 | _checks.splice(0, _checks.length); |
michael@0 | 1299 | } |
michael@0 | 1300 | }; |
michael@0 | 1301 | } |
michael@0 | 1302 | |
michael@0 | 1303 | /* |
michael@0 | 1304 | * Lexer for JSHint. |
michael@0 | 1305 | * |
michael@0 | 1306 | * This object does a char-by-char scan of the provided source code |
michael@0 | 1307 | * and produces a sequence of tokens. |
michael@0 | 1308 | * |
michael@0 | 1309 | * var lex = new Lexer("var i = 0;"); |
michael@0 | 1310 | * lex.start(); |
michael@0 | 1311 | * lex.token(); // returns the next token |
michael@0 | 1312 | * |
michael@0 | 1313 | * You have to use the token() method to move the lexer forward |
michael@0 | 1314 | * but you don't have to use its return value to get tokens. In addition |
michael@0 | 1315 | * to token() method returning the next token, the Lexer object also |
michael@0 | 1316 | * emits events. |
michael@0 | 1317 | * |
michael@0 | 1318 | * lex.on("Identifier", function (data) { |
michael@0 | 1319 | * if (data.name.indexOf("_") >= 0) { |
michael@0 | 1320 | * // Produce a warning. |
michael@0 | 1321 | * } |
michael@0 | 1322 | * }); |
michael@0 | 1323 | * |
michael@0 | 1324 | * Note that the token() method returns tokens in a JSLint-compatible |
michael@0 | 1325 | * format while the event emitter uses a slightly modified version of |
michael@0 | 1326 | * Mozilla's JavaScript Parser API. Eventually, we will move away from |
michael@0 | 1327 | * JSLint format. |
michael@0 | 1328 | */ |
michael@0 | 1329 | function Lexer(source) { |
michael@0 | 1330 | var lines = source; |
michael@0 | 1331 | |
michael@0 | 1332 | if (typeof lines === "string") { |
michael@0 | 1333 | lines = lines |
michael@0 | 1334 | .replace(/\r\n/g, "\n") |
michael@0 | 1335 | .replace(/\r/g, "\n") |
michael@0 | 1336 | .split("\n"); |
michael@0 | 1337 | } |
michael@0 | 1338 | |
michael@0 | 1339 | // If the first line is a shebang (#!), make it a blank and move on. |
michael@0 | 1340 | // Shebangs are used by Node scripts. |
michael@0 | 1341 | |
michael@0 | 1342 | if (lines[0] && lines[0].substr(0, 2) === "#!") { |
michael@0 | 1343 | lines[0] = ""; |
michael@0 | 1344 | } |
michael@0 | 1345 | |
michael@0 | 1346 | this.emitter = new events.EventEmitter(); |
michael@0 | 1347 | this.source = source; |
michael@0 | 1348 | this.lines = lines; |
michael@0 | 1349 | this.prereg = true; |
michael@0 | 1350 | |
michael@0 | 1351 | this.line = 0; |
michael@0 | 1352 | this.char = 1; |
michael@0 | 1353 | this.from = 1; |
michael@0 | 1354 | this.input = ""; |
michael@0 | 1355 | |
michael@0 | 1356 | for (var i = 0; i < state.option.indent; i += 1) { |
michael@0 | 1357 | state.tab += " "; |
michael@0 | 1358 | } |
michael@0 | 1359 | } |
michael@0 | 1360 | |
michael@0 | 1361 | Lexer.prototype = { |
michael@0 | 1362 | _lines: [], |
michael@0 | 1363 | |
michael@0 | 1364 | get lines() { |
michael@0 | 1365 | this._lines = state.lines; |
michael@0 | 1366 | return this._lines; |
michael@0 | 1367 | }, |
michael@0 | 1368 | |
michael@0 | 1369 | set lines(val) { |
michael@0 | 1370 | this._lines = val; |
michael@0 | 1371 | state.lines = this._lines; |
michael@0 | 1372 | }, |
michael@0 | 1373 | |
michael@0 | 1374 | /* |
michael@0 | 1375 | * Return the next i character without actually moving the |
michael@0 | 1376 | * char pointer. |
michael@0 | 1377 | */ |
michael@0 | 1378 | peek: function (i) { |
michael@0 | 1379 | return this.input.charAt(i || 0); |
michael@0 | 1380 | }, |
michael@0 | 1381 | |
michael@0 | 1382 | /* |
michael@0 | 1383 | * Move the char pointer forward i times. |
michael@0 | 1384 | */ |
michael@0 | 1385 | skip: function (i) { |
michael@0 | 1386 | i = i || 1; |
michael@0 | 1387 | this.char += i; |
michael@0 | 1388 | this.input = this.input.slice(i); |
michael@0 | 1389 | }, |
michael@0 | 1390 | |
michael@0 | 1391 | /* |
michael@0 | 1392 | * Subscribe to a token event. The API for this method is similar |
michael@0 | 1393 | * Underscore.js i.e. you can subscribe to multiple events with |
michael@0 | 1394 | * one call: |
michael@0 | 1395 | * |
michael@0 | 1396 | * lex.on("Identifier Number", function (data) { |
michael@0 | 1397 | * // ... |
michael@0 | 1398 | * }); |
michael@0 | 1399 | */ |
michael@0 | 1400 | on: function (names, listener) { |
michael@0 | 1401 | names.split(" ").forEach(function (name) { |
michael@0 | 1402 | this.emitter.on(name, listener); |
michael@0 | 1403 | }.bind(this)); |
michael@0 | 1404 | }, |
michael@0 | 1405 | |
michael@0 | 1406 | /* |
michael@0 | 1407 | * Trigger a token event. All arguments will be passed to each |
michael@0 | 1408 | * listener. |
michael@0 | 1409 | */ |
michael@0 | 1410 | trigger: function () { |
michael@0 | 1411 | this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments)); |
michael@0 | 1412 | }, |
michael@0 | 1413 | |
michael@0 | 1414 | /* |
michael@0 | 1415 | * Postpone a token event. the checking condition is set as |
michael@0 | 1416 | * last parameter, and the trigger function is called in a |
michael@0 | 1417 | * stored callback. To be later called using the check() function |
michael@0 | 1418 | * by the parser. This avoids parser's peek() to give the lexer |
michael@0 | 1419 | * a false context. |
michael@0 | 1420 | */ |
michael@0 | 1421 | triggerAsync: function (type, args, checks, fn) { |
michael@0 | 1422 | checks.push(function () { |
michael@0 | 1423 | if (fn()) { |
michael@0 | 1424 | this.trigger(type, args); |
michael@0 | 1425 | } |
michael@0 | 1426 | }.bind(this)); |
michael@0 | 1427 | }, |
michael@0 | 1428 | |
michael@0 | 1429 | /* |
michael@0 | 1430 | * Extract a punctuator out of the next sequence of characters |
michael@0 | 1431 | * or return 'null' if its not possible. |
michael@0 | 1432 | * |
michael@0 | 1433 | * This method's implementation was heavily influenced by the |
michael@0 | 1434 | * scanPunctuator function in the Esprima parser's source code. |
michael@0 | 1435 | */ |
michael@0 | 1436 | scanPunctuator: function () { |
michael@0 | 1437 | var ch1 = this.peek(); |
michael@0 | 1438 | var ch2, ch3, ch4; |
michael@0 | 1439 | |
michael@0 | 1440 | switch (ch1) { |
michael@0 | 1441 | // Most common single-character punctuators |
michael@0 | 1442 | case ".": |
michael@0 | 1443 | if ((/^[0-9]$/).test(this.peek(1))) { |
michael@0 | 1444 | return null; |
michael@0 | 1445 | } |
michael@0 | 1446 | if (this.peek(1) === "." && this.peek(2) === ".") { |
michael@0 | 1447 | return { |
michael@0 | 1448 | type: Token.Punctuator, |
michael@0 | 1449 | value: "..." |
michael@0 | 1450 | }; |
michael@0 | 1451 | } |
michael@0 | 1452 | /* falls through */ |
michael@0 | 1453 | case "(": |
michael@0 | 1454 | case ")": |
michael@0 | 1455 | case ";": |
michael@0 | 1456 | case ",": |
michael@0 | 1457 | case "{": |
michael@0 | 1458 | case "}": |
michael@0 | 1459 | case "[": |
michael@0 | 1460 | case "]": |
michael@0 | 1461 | case ":": |
michael@0 | 1462 | case "~": |
michael@0 | 1463 | case "?": |
michael@0 | 1464 | return { |
michael@0 | 1465 | type: Token.Punctuator, |
michael@0 | 1466 | value: ch1 |
michael@0 | 1467 | }; |
michael@0 | 1468 | |
michael@0 | 1469 | // A pound sign (for Node shebangs) |
michael@0 | 1470 | case "#": |
michael@0 | 1471 | return { |
michael@0 | 1472 | type: Token.Punctuator, |
michael@0 | 1473 | value: ch1 |
michael@0 | 1474 | }; |
michael@0 | 1475 | |
michael@0 | 1476 | // We're at the end of input |
michael@0 | 1477 | case "": |
michael@0 | 1478 | return null; |
michael@0 | 1479 | } |
michael@0 | 1480 | |
michael@0 | 1481 | // Peek more characters |
michael@0 | 1482 | |
michael@0 | 1483 | ch2 = this.peek(1); |
michael@0 | 1484 | ch3 = this.peek(2); |
michael@0 | 1485 | ch4 = this.peek(3); |
michael@0 | 1486 | |
michael@0 | 1487 | // 4-character punctuator: >>>= |
michael@0 | 1488 | |
michael@0 | 1489 | if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") { |
michael@0 | 1490 | return { |
michael@0 | 1491 | type: Token.Punctuator, |
michael@0 | 1492 | value: ">>>=" |
michael@0 | 1493 | }; |
michael@0 | 1494 | } |
michael@0 | 1495 | |
michael@0 | 1496 | // 3-character punctuators: === !== >>> <<= >>= |
michael@0 | 1497 | |
michael@0 | 1498 | if (ch1 === "=" && ch2 === "=" && ch3 === "=") { |
michael@0 | 1499 | return { |
michael@0 | 1500 | type: Token.Punctuator, |
michael@0 | 1501 | value: "===" |
michael@0 | 1502 | }; |
michael@0 | 1503 | } |
michael@0 | 1504 | |
michael@0 | 1505 | if (ch1 === "!" && ch2 === "=" && ch3 === "=") { |
michael@0 | 1506 | return { |
michael@0 | 1507 | type: Token.Punctuator, |
michael@0 | 1508 | value: "!==" |
michael@0 | 1509 | }; |
michael@0 | 1510 | } |
michael@0 | 1511 | |
michael@0 | 1512 | if (ch1 === ">" && ch2 === ">" && ch3 === ">") { |
michael@0 | 1513 | return { |
michael@0 | 1514 | type: Token.Punctuator, |
michael@0 | 1515 | value: ">>>" |
michael@0 | 1516 | }; |
michael@0 | 1517 | } |
michael@0 | 1518 | |
michael@0 | 1519 | if (ch1 === "<" && ch2 === "<" && ch3 === "=") { |
michael@0 | 1520 | return { |
michael@0 | 1521 | type: Token.Punctuator, |
michael@0 | 1522 | value: "<<=" |
michael@0 | 1523 | }; |
michael@0 | 1524 | } |
michael@0 | 1525 | |
michael@0 | 1526 | if (ch1 === ">" && ch2 === ">" && ch3 === "=") { |
michael@0 | 1527 | return { |
michael@0 | 1528 | type: Token.Punctuator, |
michael@0 | 1529 | value: ">>=" |
michael@0 | 1530 | }; |
michael@0 | 1531 | } |
michael@0 | 1532 | |
michael@0 | 1533 | // Fat arrow punctuator |
michael@0 | 1534 | if (ch1 === "=" && ch2 === ">") { |
michael@0 | 1535 | return { |
michael@0 | 1536 | type: Token.Punctuator, |
michael@0 | 1537 | value: ch1 + ch2 |
michael@0 | 1538 | }; |
michael@0 | 1539 | } |
michael@0 | 1540 | |
michael@0 | 1541 | // 2-character punctuators: <= >= == != ++ -- << >> && || |
michael@0 | 1542 | // += -= *= %= &= |= ^= (but not /=, see below) |
michael@0 | 1543 | if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { |
michael@0 | 1544 | return { |
michael@0 | 1545 | type: Token.Punctuator, |
michael@0 | 1546 | value: ch1 + ch2 |
michael@0 | 1547 | }; |
michael@0 | 1548 | } |
michael@0 | 1549 | |
michael@0 | 1550 | if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { |
michael@0 | 1551 | if (ch2 === "=") { |
michael@0 | 1552 | return { |
michael@0 | 1553 | type: Token.Punctuator, |
michael@0 | 1554 | value: ch1 + ch2 |
michael@0 | 1555 | }; |
michael@0 | 1556 | } |
michael@0 | 1557 | |
michael@0 | 1558 | return { |
michael@0 | 1559 | type: Token.Punctuator, |
michael@0 | 1560 | value: ch1 |
michael@0 | 1561 | }; |
michael@0 | 1562 | } |
michael@0 | 1563 | |
michael@0 | 1564 | // Special case: /=. We need to make sure that this is an |
michael@0 | 1565 | // operator and not a regular expression. |
michael@0 | 1566 | |
michael@0 | 1567 | if (ch1 === "/") { |
michael@0 | 1568 | if (ch2 === "=" && /\/=(?!(\S*\/[gim]?))/.test(this.input)) { |
michael@0 | 1569 | // /= is not a part of a regular expression, return it as a |
michael@0 | 1570 | // punctuator. |
michael@0 | 1571 | return { |
michael@0 | 1572 | type: Token.Punctuator, |
michael@0 | 1573 | value: "/=" |
michael@0 | 1574 | }; |
michael@0 | 1575 | } |
michael@0 | 1576 | |
michael@0 | 1577 | return { |
michael@0 | 1578 | type: Token.Punctuator, |
michael@0 | 1579 | value: "/" |
michael@0 | 1580 | }; |
michael@0 | 1581 | } |
michael@0 | 1582 | |
michael@0 | 1583 | return null; |
michael@0 | 1584 | }, |
michael@0 | 1585 | |
michael@0 | 1586 | /* |
michael@0 | 1587 | * Extract a comment out of the next sequence of characters and/or |
michael@0 | 1588 | * lines or return 'null' if its not possible. Since comments can |
michael@0 | 1589 | * span across multiple lines this method has to move the char |
michael@0 | 1590 | * pointer. |
michael@0 | 1591 | * |
michael@0 | 1592 | * In addition to normal JavaScript comments (// and /*) this method |
michael@0 | 1593 | * also recognizes JSHint- and JSLint-specific comments such as |
michael@0 | 1594 | * /*jshint, /*jslint, /*globals and so on. |
michael@0 | 1595 | */ |
michael@0 | 1596 | scanComments: function () { |
michael@0 | 1597 | var ch1 = this.peek(); |
michael@0 | 1598 | var ch2 = this.peek(1); |
michael@0 | 1599 | var rest = this.input.substr(2); |
michael@0 | 1600 | var startLine = this.line; |
michael@0 | 1601 | var startChar = this.char; |
michael@0 | 1602 | |
michael@0 | 1603 | // Create a comment token object and make sure it |
michael@0 | 1604 | // has all the data JSHint needs to work with special |
michael@0 | 1605 | // comments. |
michael@0 | 1606 | |
michael@0 | 1607 | function commentToken(label, body, opt) { |
michael@0 | 1608 | var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"]; |
michael@0 | 1609 | var isSpecial = false; |
michael@0 | 1610 | var value = label + body; |
michael@0 | 1611 | var commentType = "plain"; |
michael@0 | 1612 | opt = opt || {}; |
michael@0 | 1613 | |
michael@0 | 1614 | if (opt.isMultiline) { |
michael@0 | 1615 | value += "*/"; |
michael@0 | 1616 | } |
michael@0 | 1617 | |
michael@0 | 1618 | special.forEach(function (str) { |
michael@0 | 1619 | if (isSpecial) { |
michael@0 | 1620 | return; |
michael@0 | 1621 | } |
michael@0 | 1622 | |
michael@0 | 1623 | // Don't recognize any special comments other than jshint for single-line |
michael@0 | 1624 | // comments. This introduced many problems with legit comments. |
michael@0 | 1625 | if (label === "//" && str !== "jshint") { |
michael@0 | 1626 | return; |
michael@0 | 1627 | } |
michael@0 | 1628 | |
michael@0 | 1629 | if (body.substr(0, str.length) === str) { |
michael@0 | 1630 | isSpecial = true; |
michael@0 | 1631 | label = label + str; |
michael@0 | 1632 | body = body.substr(str.length); |
michael@0 | 1633 | } |
michael@0 | 1634 | |
michael@0 | 1635 | if (!isSpecial && body.charAt(0) === " " && body.substr(1, str.length) === str) { |
michael@0 | 1636 | isSpecial = true; |
michael@0 | 1637 | label = label + " " + str; |
michael@0 | 1638 | body = body.substr(str.length + 1); |
michael@0 | 1639 | } |
michael@0 | 1640 | |
michael@0 | 1641 | if (!isSpecial) { |
michael@0 | 1642 | return; |
michael@0 | 1643 | } |
michael@0 | 1644 | |
michael@0 | 1645 | switch (str) { |
michael@0 | 1646 | case "member": |
michael@0 | 1647 | commentType = "members"; |
michael@0 | 1648 | break; |
michael@0 | 1649 | case "global": |
michael@0 | 1650 | commentType = "globals"; |
michael@0 | 1651 | break; |
michael@0 | 1652 | default: |
michael@0 | 1653 | commentType = str; |
michael@0 | 1654 | } |
michael@0 | 1655 | }); |
michael@0 | 1656 | |
michael@0 | 1657 | return { |
michael@0 | 1658 | type: Token.Comment, |
michael@0 | 1659 | commentType: commentType, |
michael@0 | 1660 | value: value, |
michael@0 | 1661 | body: body, |
michael@0 | 1662 | isSpecial: isSpecial, |
michael@0 | 1663 | isMultiline: opt.isMultiline || false, |
michael@0 | 1664 | isMalformed: opt.isMalformed || false |
michael@0 | 1665 | }; |
michael@0 | 1666 | } |
michael@0 | 1667 | |
michael@0 | 1668 | // End of unbegun comment. Raise an error and skip that input. |
michael@0 | 1669 | if (ch1 === "*" && ch2 === "/") { |
michael@0 | 1670 | this.trigger("error", { |
michael@0 | 1671 | code: "E018", |
michael@0 | 1672 | line: startLine, |
michael@0 | 1673 | character: startChar |
michael@0 | 1674 | }); |
michael@0 | 1675 | |
michael@0 | 1676 | this.skip(2); |
michael@0 | 1677 | return null; |
michael@0 | 1678 | } |
michael@0 | 1679 | |
michael@0 | 1680 | // Comments must start either with // or /* |
michael@0 | 1681 | if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) { |
michael@0 | 1682 | return null; |
michael@0 | 1683 | } |
michael@0 | 1684 | |
michael@0 | 1685 | // One-line comment |
michael@0 | 1686 | if (ch2 === "/") { |
michael@0 | 1687 | this.skip(this.input.length); // Skip to the EOL. |
michael@0 | 1688 | return commentToken("//", rest); |
michael@0 | 1689 | } |
michael@0 | 1690 | |
michael@0 | 1691 | var body = ""; |
michael@0 | 1692 | |
michael@0 | 1693 | /* Multi-line comment */ |
michael@0 | 1694 | if (ch2 === "*") { |
michael@0 | 1695 | this.skip(2); |
michael@0 | 1696 | |
michael@0 | 1697 | while (this.peek() !== "*" || this.peek(1) !== "/") { |
michael@0 | 1698 | if (this.peek() === "") { // End of Line |
michael@0 | 1699 | body += "\n"; |
michael@0 | 1700 | |
michael@0 | 1701 | // If we hit EOF and our comment is still unclosed, |
michael@0 | 1702 | // trigger an error and end the comment implicitly. |
michael@0 | 1703 | if (!this.nextLine()) { |
michael@0 | 1704 | this.trigger("error", { |
michael@0 | 1705 | code: "E017", |
michael@0 | 1706 | line: startLine, |
michael@0 | 1707 | character: startChar |
michael@0 | 1708 | }); |
michael@0 | 1709 | |
michael@0 | 1710 | return commentToken("/*", body, { |
michael@0 | 1711 | isMultiline: true, |
michael@0 | 1712 | isMalformed: true |
michael@0 | 1713 | }); |
michael@0 | 1714 | } |
michael@0 | 1715 | } else { |
michael@0 | 1716 | body += this.peek(); |
michael@0 | 1717 | this.skip(); |
michael@0 | 1718 | } |
michael@0 | 1719 | } |
michael@0 | 1720 | |
michael@0 | 1721 | this.skip(2); |
michael@0 | 1722 | return commentToken("/*", body, { isMultiline: true }); |
michael@0 | 1723 | } |
michael@0 | 1724 | }, |
michael@0 | 1725 | |
michael@0 | 1726 | /* |
michael@0 | 1727 | * Extract a keyword out of the next sequence of characters or |
michael@0 | 1728 | * return 'null' if its not possible. |
michael@0 | 1729 | */ |
michael@0 | 1730 | scanKeyword: function () { |
michael@0 | 1731 | var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input); |
michael@0 | 1732 | var keywords = [ |
michael@0 | 1733 | "if", "in", "do", "var", "for", "new", |
michael@0 | 1734 | "try", "let", "this", "else", "case", |
michael@0 | 1735 | "void", "with", "enum", "while", "break", |
michael@0 | 1736 | "catch", "throw", "const", "yield", "class", |
michael@0 | 1737 | "super", "return", "typeof", "delete", |
michael@0 | 1738 | "switch", "export", "import", "default", |
michael@0 | 1739 | "finally", "extends", "function", "continue", |
michael@0 | 1740 | "debugger", "instanceof" |
michael@0 | 1741 | ]; |
michael@0 | 1742 | |
michael@0 | 1743 | if (result && keywords.indexOf(result[0]) >= 0) { |
michael@0 | 1744 | return { |
michael@0 | 1745 | type: Token.Keyword, |
michael@0 | 1746 | value: result[0] |
michael@0 | 1747 | }; |
michael@0 | 1748 | } |
michael@0 | 1749 | |
michael@0 | 1750 | return null; |
michael@0 | 1751 | }, |
michael@0 | 1752 | |
michael@0 | 1753 | /* |
michael@0 | 1754 | * Extract a JavaScript identifier out of the next sequence of |
michael@0 | 1755 | * characters or return 'null' if its not possible. In addition, |
michael@0 | 1756 | * to Identifier this method can also produce BooleanLiteral |
michael@0 | 1757 | * (true/false) and NullLiteral (null). |
michael@0 | 1758 | */ |
michael@0 | 1759 | scanIdentifier: function () { |
michael@0 | 1760 | var id = ""; |
michael@0 | 1761 | var index = 0; |
michael@0 | 1762 | var type, char; |
michael@0 | 1763 | |
michael@0 | 1764 | // Detects any character in the Unicode categories "Uppercase |
michael@0 | 1765 | // letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter |
michael@0 | 1766 | // (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or |
michael@0 | 1767 | // "Letter number (Nl)". |
michael@0 | 1768 | // |
michael@0 | 1769 | // Both approach and unicodeLetterTable were borrowed from |
michael@0 | 1770 | // Google's Traceur. |
michael@0 | 1771 | |
michael@0 | 1772 | function isUnicodeLetter(code) { |
michael@0 | 1773 | for (var i = 0; i < unicodeLetterTable.length;) { |
michael@0 | 1774 | if (code < unicodeLetterTable[i++]) { |
michael@0 | 1775 | return false; |
michael@0 | 1776 | } |
michael@0 | 1777 | |
michael@0 | 1778 | if (code <= unicodeLetterTable[i++]) { |
michael@0 | 1779 | return true; |
michael@0 | 1780 | } |
michael@0 | 1781 | } |
michael@0 | 1782 | |
michael@0 | 1783 | return false; |
michael@0 | 1784 | } |
michael@0 | 1785 | |
michael@0 | 1786 | function isHexDigit(str) { |
michael@0 | 1787 | return (/^[0-9a-fA-F]$/).test(str); |
michael@0 | 1788 | } |
michael@0 | 1789 | |
michael@0 | 1790 | var readUnicodeEscapeSequence = function () { |
michael@0 | 1791 | /*jshint validthis:true */ |
michael@0 | 1792 | index += 1; |
michael@0 | 1793 | |
michael@0 | 1794 | if (this.peek(index) !== "u") { |
michael@0 | 1795 | return null; |
michael@0 | 1796 | } |
michael@0 | 1797 | |
michael@0 | 1798 | var ch1 = this.peek(index + 1); |
michael@0 | 1799 | var ch2 = this.peek(index + 2); |
michael@0 | 1800 | var ch3 = this.peek(index + 3); |
michael@0 | 1801 | var ch4 = this.peek(index + 4); |
michael@0 | 1802 | var code; |
michael@0 | 1803 | |
michael@0 | 1804 | if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) { |
michael@0 | 1805 | code = parseInt(ch1 + ch2 + ch3 + ch4, 16); |
michael@0 | 1806 | |
michael@0 | 1807 | if (isUnicodeLetter(code)) { |
michael@0 | 1808 | index += 5; |
michael@0 | 1809 | return "\\u" + ch1 + ch2 + ch3 + ch4; |
michael@0 | 1810 | } |
michael@0 | 1811 | |
michael@0 | 1812 | return null; |
michael@0 | 1813 | } |
michael@0 | 1814 | |
michael@0 | 1815 | return null; |
michael@0 | 1816 | }.bind(this); |
michael@0 | 1817 | |
michael@0 | 1818 | var getIdentifierStart = function () { |
michael@0 | 1819 | /*jshint validthis:true */ |
michael@0 | 1820 | var chr = this.peek(index); |
michael@0 | 1821 | var code = chr.charCodeAt(0); |
michael@0 | 1822 | |
michael@0 | 1823 | if (code === 92) { |
michael@0 | 1824 | return readUnicodeEscapeSequence(); |
michael@0 | 1825 | } |
michael@0 | 1826 | |
michael@0 | 1827 | if (code < 128) { |
michael@0 | 1828 | if (identifierStartTable[code]) { |
michael@0 | 1829 | index += 1; |
michael@0 | 1830 | return chr; |
michael@0 | 1831 | } |
michael@0 | 1832 | |
michael@0 | 1833 | return null; |
michael@0 | 1834 | } |
michael@0 | 1835 | |
michael@0 | 1836 | if (isUnicodeLetter(code)) { |
michael@0 | 1837 | index += 1; |
michael@0 | 1838 | return chr; |
michael@0 | 1839 | } |
michael@0 | 1840 | |
michael@0 | 1841 | return null; |
michael@0 | 1842 | }.bind(this); |
michael@0 | 1843 | |
michael@0 | 1844 | var getIdentifierPart = function () { |
michael@0 | 1845 | /*jshint validthis:true */ |
michael@0 | 1846 | var chr = this.peek(index); |
michael@0 | 1847 | var code = chr.charCodeAt(0); |
michael@0 | 1848 | |
michael@0 | 1849 | if (code === 92) { |
michael@0 | 1850 | return readUnicodeEscapeSequence(); |
michael@0 | 1851 | } |
michael@0 | 1852 | |
michael@0 | 1853 | if (code < 128) { |
michael@0 | 1854 | if (identifierPartTable[code]) { |
michael@0 | 1855 | index += 1; |
michael@0 | 1856 | return chr; |
michael@0 | 1857 | } |
michael@0 | 1858 | |
michael@0 | 1859 | return null; |
michael@0 | 1860 | } |
michael@0 | 1861 | |
michael@0 | 1862 | if (isUnicodeLetter(code)) { |
michael@0 | 1863 | index += 1; |
michael@0 | 1864 | return chr; |
michael@0 | 1865 | } |
michael@0 | 1866 | |
michael@0 | 1867 | return null; |
michael@0 | 1868 | }.bind(this); |
michael@0 | 1869 | |
michael@0 | 1870 | char = getIdentifierStart(); |
michael@0 | 1871 | if (char === null) { |
michael@0 | 1872 | return null; |
michael@0 | 1873 | } |
michael@0 | 1874 | |
michael@0 | 1875 | id = char; |
michael@0 | 1876 | for (;;) { |
michael@0 | 1877 | char = getIdentifierPart(); |
michael@0 | 1878 | |
michael@0 | 1879 | if (char === null) { |
michael@0 | 1880 | break; |
michael@0 | 1881 | } |
michael@0 | 1882 | |
michael@0 | 1883 | id += char; |
michael@0 | 1884 | } |
michael@0 | 1885 | |
michael@0 | 1886 | switch (id) { |
michael@0 | 1887 | case "true": |
michael@0 | 1888 | case "false": |
michael@0 | 1889 | type = Token.BooleanLiteral; |
michael@0 | 1890 | break; |
michael@0 | 1891 | case "null": |
michael@0 | 1892 | type = Token.NullLiteral; |
michael@0 | 1893 | break; |
michael@0 | 1894 | default: |
michael@0 | 1895 | type = Token.Identifier; |
michael@0 | 1896 | } |
michael@0 | 1897 | |
michael@0 | 1898 | return { |
michael@0 | 1899 | type: type, |
michael@0 | 1900 | value: id |
michael@0 | 1901 | }; |
michael@0 | 1902 | }, |
michael@0 | 1903 | |
michael@0 | 1904 | /* |
michael@0 | 1905 | * Extract a numeric literal out of the next sequence of |
michael@0 | 1906 | * characters or return 'null' if its not possible. This method |
michael@0 | 1907 | * supports all numeric literals described in section 7.8.3 |
michael@0 | 1908 | * of the EcmaScript 5 specification. |
michael@0 | 1909 | * |
michael@0 | 1910 | * This method's implementation was heavily influenced by the |
michael@0 | 1911 | * scanNumericLiteral function in the Esprima parser's source code. |
michael@0 | 1912 | */ |
michael@0 | 1913 | scanNumericLiteral: function () { |
michael@0 | 1914 | var index = 0; |
michael@0 | 1915 | var value = ""; |
michael@0 | 1916 | var length = this.input.length; |
michael@0 | 1917 | var char = this.peek(index); |
michael@0 | 1918 | var bad; |
michael@0 | 1919 | |
michael@0 | 1920 | function isDecimalDigit(str) { |
michael@0 | 1921 | return (/^[0-9]$/).test(str); |
michael@0 | 1922 | } |
michael@0 | 1923 | |
michael@0 | 1924 | function isOctalDigit(str) { |
michael@0 | 1925 | return (/^[0-7]$/).test(str); |
michael@0 | 1926 | } |
michael@0 | 1927 | |
michael@0 | 1928 | function isHexDigit(str) { |
michael@0 | 1929 | return (/^[0-9a-fA-F]$/).test(str); |
michael@0 | 1930 | } |
michael@0 | 1931 | |
michael@0 | 1932 | function isIdentifierStart(ch) { |
michael@0 | 1933 | return (ch === "$") || (ch === "_") || (ch === "\\") || |
michael@0 | 1934 | (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z"); |
michael@0 | 1935 | } |
michael@0 | 1936 | |
michael@0 | 1937 | // Numbers must start either with a decimal digit or a point. |
michael@0 | 1938 | |
michael@0 | 1939 | if (char !== "." && !isDecimalDigit(char)) { |
michael@0 | 1940 | return null; |
michael@0 | 1941 | } |
michael@0 | 1942 | |
michael@0 | 1943 | if (char !== ".") { |
michael@0 | 1944 | value = this.peek(index); |
michael@0 | 1945 | index += 1; |
michael@0 | 1946 | char = this.peek(index); |
michael@0 | 1947 | |
michael@0 | 1948 | if (value === "0") { |
michael@0 | 1949 | // Base-16 numbers. |
michael@0 | 1950 | if (char === "x" || char === "X") { |
michael@0 | 1951 | index += 1; |
michael@0 | 1952 | value += char; |
michael@0 | 1953 | |
michael@0 | 1954 | while (index < length) { |
michael@0 | 1955 | char = this.peek(index); |
michael@0 | 1956 | if (!isHexDigit(char)) { |
michael@0 | 1957 | break; |
michael@0 | 1958 | } |
michael@0 | 1959 | value += char; |
michael@0 | 1960 | index += 1; |
michael@0 | 1961 | } |
michael@0 | 1962 | |
michael@0 | 1963 | if (value.length <= 2) { // 0x |
michael@0 | 1964 | return { |
michael@0 | 1965 | type: Token.NumericLiteral, |
michael@0 | 1966 | value: value, |
michael@0 | 1967 | isMalformed: true |
michael@0 | 1968 | }; |
michael@0 | 1969 | } |
michael@0 | 1970 | |
michael@0 | 1971 | if (index < length) { |
michael@0 | 1972 | char = this.peek(index); |
michael@0 | 1973 | if (isIdentifierStart(char)) { |
michael@0 | 1974 | return null; |
michael@0 | 1975 | } |
michael@0 | 1976 | } |
michael@0 | 1977 | |
michael@0 | 1978 | return { |
michael@0 | 1979 | type: Token.NumericLiteral, |
michael@0 | 1980 | value: value, |
michael@0 | 1981 | base: 16, |
michael@0 | 1982 | isMalformed: false |
michael@0 | 1983 | }; |
michael@0 | 1984 | } |
michael@0 | 1985 | |
michael@0 | 1986 | // Base-8 numbers. |
michael@0 | 1987 | if (isOctalDigit(char)) { |
michael@0 | 1988 | index += 1; |
michael@0 | 1989 | value += char; |
michael@0 | 1990 | bad = false; |
michael@0 | 1991 | |
michael@0 | 1992 | while (index < length) { |
michael@0 | 1993 | char = this.peek(index); |
michael@0 | 1994 | |
michael@0 | 1995 | // Numbers like '019' (note the 9) are not valid octals |
michael@0 | 1996 | // but we still parse them and mark as malformed. |
michael@0 | 1997 | |
michael@0 | 1998 | if (isDecimalDigit(char)) { |
michael@0 | 1999 | bad = true; |
michael@0 | 2000 | } else if (!isOctalDigit(char)) { |
michael@0 | 2001 | break; |
michael@0 | 2002 | } |
michael@0 | 2003 | value += char; |
michael@0 | 2004 | index += 1; |
michael@0 | 2005 | } |
michael@0 | 2006 | |
michael@0 | 2007 | if (index < length) { |
michael@0 | 2008 | char = this.peek(index); |
michael@0 | 2009 | if (isIdentifierStart(char)) { |
michael@0 | 2010 | return null; |
michael@0 | 2011 | } |
michael@0 | 2012 | } |
michael@0 | 2013 | |
michael@0 | 2014 | return { |
michael@0 | 2015 | type: Token.NumericLiteral, |
michael@0 | 2016 | value: value, |
michael@0 | 2017 | base: 8, |
michael@0 | 2018 | isMalformed: false |
michael@0 | 2019 | }; |
michael@0 | 2020 | } |
michael@0 | 2021 | |
michael@0 | 2022 | // Decimal numbers that start with '0' such as '09' are illegal |
michael@0 | 2023 | // but we still parse them and return as malformed. |
michael@0 | 2024 | |
michael@0 | 2025 | if (isDecimalDigit(char)) { |
michael@0 | 2026 | index += 1; |
michael@0 | 2027 | value += char; |
michael@0 | 2028 | } |
michael@0 | 2029 | } |
michael@0 | 2030 | |
michael@0 | 2031 | while (index < length) { |
michael@0 | 2032 | char = this.peek(index); |
michael@0 | 2033 | if (!isDecimalDigit(char)) { |
michael@0 | 2034 | break; |
michael@0 | 2035 | } |
michael@0 | 2036 | value += char; |
michael@0 | 2037 | index += 1; |
michael@0 | 2038 | } |
michael@0 | 2039 | } |
michael@0 | 2040 | |
michael@0 | 2041 | // Decimal digits. |
michael@0 | 2042 | |
michael@0 | 2043 | if (char === ".") { |
michael@0 | 2044 | value += char; |
michael@0 | 2045 | index += 1; |
michael@0 | 2046 | |
michael@0 | 2047 | while (index < length) { |
michael@0 | 2048 | char = this.peek(index); |
michael@0 | 2049 | if (!isDecimalDigit(char)) { |
michael@0 | 2050 | break; |
michael@0 | 2051 | } |
michael@0 | 2052 | value += char; |
michael@0 | 2053 | index += 1; |
michael@0 | 2054 | } |
michael@0 | 2055 | } |
michael@0 | 2056 | |
michael@0 | 2057 | // Exponent part. |
michael@0 | 2058 | |
michael@0 | 2059 | if (char === "e" || char === "E") { |
michael@0 | 2060 | value += char; |
michael@0 | 2061 | index += 1; |
michael@0 | 2062 | char = this.peek(index); |
michael@0 | 2063 | |
michael@0 | 2064 | if (char === "+" || char === "-") { |
michael@0 | 2065 | value += this.peek(index); |
michael@0 | 2066 | index += 1; |
michael@0 | 2067 | } |
michael@0 | 2068 | |
michael@0 | 2069 | char = this.peek(index); |
michael@0 | 2070 | if (isDecimalDigit(char)) { |
michael@0 | 2071 | value += char; |
michael@0 | 2072 | index += 1; |
michael@0 | 2073 | |
michael@0 | 2074 | while (index < length) { |
michael@0 | 2075 | char = this.peek(index); |
michael@0 | 2076 | if (!isDecimalDigit(char)) { |
michael@0 | 2077 | break; |
michael@0 | 2078 | } |
michael@0 | 2079 | value += char; |
michael@0 | 2080 | index += 1; |
michael@0 | 2081 | } |
michael@0 | 2082 | } else { |
michael@0 | 2083 | return null; |
michael@0 | 2084 | } |
michael@0 | 2085 | } |
michael@0 | 2086 | |
michael@0 | 2087 | if (index < length) { |
michael@0 | 2088 | char = this.peek(index); |
michael@0 | 2089 | if (isIdentifierStart(char)) { |
michael@0 | 2090 | return null; |
michael@0 | 2091 | } |
michael@0 | 2092 | } |
michael@0 | 2093 | |
michael@0 | 2094 | return { |
michael@0 | 2095 | type: Token.NumericLiteral, |
michael@0 | 2096 | value: value, |
michael@0 | 2097 | base: 10, |
michael@0 | 2098 | isMalformed: !isFinite(value) |
michael@0 | 2099 | }; |
michael@0 | 2100 | }, |
michael@0 | 2101 | |
michael@0 | 2102 | /* |
michael@0 | 2103 | * Extract a string out of the next sequence of characters and/or |
michael@0 | 2104 | * lines or return 'null' if its not possible. Since strings can |
michael@0 | 2105 | * span across multiple lines this method has to move the char |
michael@0 | 2106 | * pointer. |
michael@0 | 2107 | * |
michael@0 | 2108 | * This method recognizes pseudo-multiline JavaScript strings: |
michael@0 | 2109 | * |
michael@0 | 2110 | * var str = "hello\ |
michael@0 | 2111 | * world"; |
michael@0 | 2112 | */ |
michael@0 | 2113 | scanStringLiteral: function (checks) { |
michael@0 | 2114 | /*jshint loopfunc:true */ |
michael@0 | 2115 | var quote = this.peek(); |
michael@0 | 2116 | |
michael@0 | 2117 | // String must start with a quote. |
michael@0 | 2118 | if (quote !== "\"" && quote !== "'") { |
michael@0 | 2119 | return null; |
michael@0 | 2120 | } |
michael@0 | 2121 | |
michael@0 | 2122 | // In JSON strings must always use double quotes. |
michael@0 | 2123 | this.triggerAsync("warning", { |
michael@0 | 2124 | code: "W108", |
michael@0 | 2125 | line: this.line, |
michael@0 | 2126 | character: this.char // +1? |
michael@0 | 2127 | }, checks, function () { return state.jsonMode && quote !== "\""; }); |
michael@0 | 2128 | |
michael@0 | 2129 | var value = ""; |
michael@0 | 2130 | var startLine = this.line; |
michael@0 | 2131 | var startChar = this.char; |
michael@0 | 2132 | var allowNewLine = false; |
michael@0 | 2133 | |
michael@0 | 2134 | this.skip(); |
michael@0 | 2135 | |
michael@0 | 2136 | while (this.peek() !== quote) { |
michael@0 | 2137 | while (this.peek() === "") { // End Of Line |
michael@0 | 2138 | |
michael@0 | 2139 | // If an EOL is not preceded by a backslash, show a warning |
michael@0 | 2140 | // and proceed like it was a legit multi-line string where |
michael@0 | 2141 | // author simply forgot to escape the newline symbol. |
michael@0 | 2142 | // |
michael@0 | 2143 | // Another approach is to implicitly close a string on EOL |
michael@0 | 2144 | // but it generates too many false positives. |
michael@0 | 2145 | |
michael@0 | 2146 | if (!allowNewLine) { |
michael@0 | 2147 | this.trigger("warning", { |
michael@0 | 2148 | code: "W112", |
michael@0 | 2149 | line: this.line, |
michael@0 | 2150 | character: this.char |
michael@0 | 2151 | }); |
michael@0 | 2152 | } else { |
michael@0 | 2153 | allowNewLine = false; |
michael@0 | 2154 | |
michael@0 | 2155 | // Otherwise show a warning if multistr option was not set. |
michael@0 | 2156 | // For JSON, show warning no matter what. |
michael@0 | 2157 | |
michael@0 | 2158 | this.triggerAsync("warning", { |
michael@0 | 2159 | code: "W043", |
michael@0 | 2160 | line: this.line, |
michael@0 | 2161 | character: this.char |
michael@0 | 2162 | }, checks, function () { return !state.option.multistr; }); |
michael@0 | 2163 | |
michael@0 | 2164 | this.triggerAsync("warning", { |
michael@0 | 2165 | code: "W042", |
michael@0 | 2166 | line: this.line, |
michael@0 | 2167 | character: this.char |
michael@0 | 2168 | }, checks, function () { return state.jsonMode && state.option.multistr; }); |
michael@0 | 2169 | } |
michael@0 | 2170 | |
michael@0 | 2171 | // If we get an EOF inside of an unclosed string, show an |
michael@0 | 2172 | // error and implicitly close it at the EOF point. |
michael@0 | 2173 | |
michael@0 | 2174 | if (!this.nextLine()) { |
michael@0 | 2175 | this.trigger("error", { |
michael@0 | 2176 | code: "E029", |
michael@0 | 2177 | line: startLine, |
michael@0 | 2178 | character: startChar |
michael@0 | 2179 | }); |
michael@0 | 2180 | |
michael@0 | 2181 | return { |
michael@0 | 2182 | type: Token.StringLiteral, |
michael@0 | 2183 | value: value, |
michael@0 | 2184 | isUnclosed: true, |
michael@0 | 2185 | quote: quote |
michael@0 | 2186 | }; |
michael@0 | 2187 | } |
michael@0 | 2188 | } |
michael@0 | 2189 | |
michael@0 | 2190 | allowNewLine = false; |
michael@0 | 2191 | var char = this.peek(); |
michael@0 | 2192 | var jump = 1; // A length of a jump, after we're done |
michael@0 | 2193 | // parsing this character. |
michael@0 | 2194 | |
michael@0 | 2195 | if (char < " ") { |
michael@0 | 2196 | // Warn about a control character in a string. |
michael@0 | 2197 | this.trigger("warning", { |
michael@0 | 2198 | code: "W113", |
michael@0 | 2199 | line: this.line, |
michael@0 | 2200 | character: this.char, |
michael@0 | 2201 | data: [ "<non-printable>" ] |
michael@0 | 2202 | }); |
michael@0 | 2203 | } |
michael@0 | 2204 | |
michael@0 | 2205 | // Special treatment for some escaped characters. |
michael@0 | 2206 | |
michael@0 | 2207 | if (char === "\\") { |
michael@0 | 2208 | this.skip(); |
michael@0 | 2209 | char = this.peek(); |
michael@0 | 2210 | |
michael@0 | 2211 | switch (char) { |
michael@0 | 2212 | case "'": |
michael@0 | 2213 | this.triggerAsync("warning", { |
michael@0 | 2214 | code: "W114", |
michael@0 | 2215 | line: this.line, |
michael@0 | 2216 | character: this.char, |
michael@0 | 2217 | data: [ "\\'" ] |
michael@0 | 2218 | }, checks, function () {return state.jsonMode; }); |
michael@0 | 2219 | break; |
michael@0 | 2220 | case "b": |
michael@0 | 2221 | char = "\b"; |
michael@0 | 2222 | break; |
michael@0 | 2223 | case "f": |
michael@0 | 2224 | char = "\f"; |
michael@0 | 2225 | break; |
michael@0 | 2226 | case "n": |
michael@0 | 2227 | char = "\n"; |
michael@0 | 2228 | break; |
michael@0 | 2229 | case "r": |
michael@0 | 2230 | char = "\r"; |
michael@0 | 2231 | break; |
michael@0 | 2232 | case "t": |
michael@0 | 2233 | char = "\t"; |
michael@0 | 2234 | break; |
michael@0 | 2235 | case "0": |
michael@0 | 2236 | char = "\0"; |
michael@0 | 2237 | |
michael@0 | 2238 | // Octal literals fail in strict mode. |
michael@0 | 2239 | // Check if the number is between 00 and 07. |
michael@0 | 2240 | var n = parseInt(this.peek(1), 10); |
michael@0 | 2241 | this.triggerAsync("warning", { |
michael@0 | 2242 | code: "W115", |
michael@0 | 2243 | line: this.line, |
michael@0 | 2244 | character: this.char |
michael@0 | 2245 | }, checks, |
michael@0 | 2246 | function () { return n >= 0 && n <= 7 && state.directive["use strict"]; }); |
michael@0 | 2247 | break; |
michael@0 | 2248 | case "u": |
michael@0 | 2249 | char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16)); |
michael@0 | 2250 | jump = 5; |
michael@0 | 2251 | break; |
michael@0 | 2252 | case "v": |
michael@0 | 2253 | this.triggerAsync("warning", { |
michael@0 | 2254 | code: "W114", |
michael@0 | 2255 | line: this.line, |
michael@0 | 2256 | character: this.char, |
michael@0 | 2257 | data: [ "\\v" ] |
michael@0 | 2258 | }, checks, function () { return state.jsonMode; }); |
michael@0 | 2259 | |
michael@0 | 2260 | char = "\v"; |
michael@0 | 2261 | break; |
michael@0 | 2262 | case "x": |
michael@0 | 2263 | var x = parseInt(this.input.substr(1, 2), 16); |
michael@0 | 2264 | |
michael@0 | 2265 | this.triggerAsync("warning", { |
michael@0 | 2266 | code: "W114", |
michael@0 | 2267 | line: this.line, |
michael@0 | 2268 | character: this.char, |
michael@0 | 2269 | data: [ "\\x-" ] |
michael@0 | 2270 | }, checks, function () { return state.jsonMode; }); |
michael@0 | 2271 | |
michael@0 | 2272 | char = String.fromCharCode(x); |
michael@0 | 2273 | jump = 3; |
michael@0 | 2274 | break; |
michael@0 | 2275 | case "\\": |
michael@0 | 2276 | case "\"": |
michael@0 | 2277 | case "/": |
michael@0 | 2278 | break; |
michael@0 | 2279 | case "": |
michael@0 | 2280 | allowNewLine = true; |
michael@0 | 2281 | char = ""; |
michael@0 | 2282 | break; |
michael@0 | 2283 | case "!": |
michael@0 | 2284 | if (value.slice(value.length - 2) === "<") { |
michael@0 | 2285 | break; |
michael@0 | 2286 | } |
michael@0 | 2287 | |
michael@0 | 2288 | /*falls through */ |
michael@0 | 2289 | default: |
michael@0 | 2290 | // Weird escaping. |
michael@0 | 2291 | this.trigger("warning", { |
michael@0 | 2292 | code: "W044", |
michael@0 | 2293 | line: this.line, |
michael@0 | 2294 | character: this.char |
michael@0 | 2295 | }); |
michael@0 | 2296 | } |
michael@0 | 2297 | } |
michael@0 | 2298 | |
michael@0 | 2299 | value += char; |
michael@0 | 2300 | this.skip(jump); |
michael@0 | 2301 | } |
michael@0 | 2302 | |
michael@0 | 2303 | this.skip(); |
michael@0 | 2304 | return { |
michael@0 | 2305 | type: Token.StringLiteral, |
michael@0 | 2306 | value: value, |
michael@0 | 2307 | isUnclosed: false, |
michael@0 | 2308 | quote: quote |
michael@0 | 2309 | }; |
michael@0 | 2310 | }, |
michael@0 | 2311 | |
michael@0 | 2312 | /* |
michael@0 | 2313 | * Extract a regular expression out of the next sequence of |
michael@0 | 2314 | * characters and/or lines or return 'null' if its not possible. |
michael@0 | 2315 | * |
michael@0 | 2316 | * This method is platform dependent: it accepts almost any |
michael@0 | 2317 | * regular expression values but then tries to compile and run |
michael@0 | 2318 | * them using system's RegExp object. This means that there are |
michael@0 | 2319 | * rare edge cases where one JavaScript engine complains about |
michael@0 | 2320 | * your regular expression while others don't. |
michael@0 | 2321 | */ |
michael@0 | 2322 | scanRegExp: function () { |
michael@0 | 2323 | var index = 0; |
michael@0 | 2324 | var length = this.input.length; |
michael@0 | 2325 | var char = this.peek(); |
michael@0 | 2326 | var value = char; |
michael@0 | 2327 | var body = ""; |
michael@0 | 2328 | var flags = []; |
michael@0 | 2329 | var malformed = false; |
michael@0 | 2330 | var isCharSet = false; |
michael@0 | 2331 | var terminated; |
michael@0 | 2332 | |
michael@0 | 2333 | var scanUnexpectedChars = function () { |
michael@0 | 2334 | // Unexpected control character |
michael@0 | 2335 | if (char < " ") { |
michael@0 | 2336 | malformed = true; |
michael@0 | 2337 | this.trigger("warning", { |
michael@0 | 2338 | code: "W048", |
michael@0 | 2339 | line: this.line, |
michael@0 | 2340 | character: this.char |
michael@0 | 2341 | }); |
michael@0 | 2342 | } |
michael@0 | 2343 | |
michael@0 | 2344 | // Unexpected escaped character |
michael@0 | 2345 | if (char === "<") { |
michael@0 | 2346 | malformed = true; |
michael@0 | 2347 | this.trigger("warning", { |
michael@0 | 2348 | code: "W049", |
michael@0 | 2349 | line: this.line, |
michael@0 | 2350 | character: this.char, |
michael@0 | 2351 | data: [ char ] |
michael@0 | 2352 | }); |
michael@0 | 2353 | } |
michael@0 | 2354 | }.bind(this); |
michael@0 | 2355 | |
michael@0 | 2356 | // Regular expressions must start with '/' |
michael@0 | 2357 | if (!this.prereg || char !== "/") { |
michael@0 | 2358 | return null; |
michael@0 | 2359 | } |
michael@0 | 2360 | |
michael@0 | 2361 | index += 1; |
michael@0 | 2362 | terminated = false; |
michael@0 | 2363 | |
michael@0 | 2364 | // Try to get everything in between slashes. A couple of |
michael@0 | 2365 | // cases aside (see scanUnexpectedChars) we don't really |
michael@0 | 2366 | // care whether the resulting expression is valid or not. |
michael@0 | 2367 | // We will check that later using the RegExp object. |
michael@0 | 2368 | |
michael@0 | 2369 | while (index < length) { |
michael@0 | 2370 | char = this.peek(index); |
michael@0 | 2371 | value += char; |
michael@0 | 2372 | body += char; |
michael@0 | 2373 | |
michael@0 | 2374 | if (isCharSet) { |
michael@0 | 2375 | if (char === "]") { |
michael@0 | 2376 | if (this.peek(index - 1) !== "\\" || this.peek(index - 2) === "\\") { |
michael@0 | 2377 | isCharSet = false; |
michael@0 | 2378 | } |
michael@0 | 2379 | } |
michael@0 | 2380 | |
michael@0 | 2381 | if (char === "\\") { |
michael@0 | 2382 | index += 1; |
michael@0 | 2383 | char = this.peek(index); |
michael@0 | 2384 | body += char; |
michael@0 | 2385 | value += char; |
michael@0 | 2386 | |
michael@0 | 2387 | scanUnexpectedChars(); |
michael@0 | 2388 | } |
michael@0 | 2389 | |
michael@0 | 2390 | index += 1; |
michael@0 | 2391 | continue; |
michael@0 | 2392 | } |
michael@0 | 2393 | |
michael@0 | 2394 | if (char === "\\") { |
michael@0 | 2395 | index += 1; |
michael@0 | 2396 | char = this.peek(index); |
michael@0 | 2397 | body += char; |
michael@0 | 2398 | value += char; |
michael@0 | 2399 | |
michael@0 | 2400 | scanUnexpectedChars(); |
michael@0 | 2401 | |
michael@0 | 2402 | if (char === "/") { |
michael@0 | 2403 | index += 1; |
michael@0 | 2404 | continue; |
michael@0 | 2405 | } |
michael@0 | 2406 | |
michael@0 | 2407 | if (char === "[") { |
michael@0 | 2408 | index += 1; |
michael@0 | 2409 | continue; |
michael@0 | 2410 | } |
michael@0 | 2411 | } |
michael@0 | 2412 | |
michael@0 | 2413 | if (char === "[") { |
michael@0 | 2414 | isCharSet = true; |
michael@0 | 2415 | index += 1; |
michael@0 | 2416 | continue; |
michael@0 | 2417 | } |
michael@0 | 2418 | |
michael@0 | 2419 | if (char === "/") { |
michael@0 | 2420 | body = body.substr(0, body.length - 1); |
michael@0 | 2421 | terminated = true; |
michael@0 | 2422 | index += 1; |
michael@0 | 2423 | break; |
michael@0 | 2424 | } |
michael@0 | 2425 | |
michael@0 | 2426 | index += 1; |
michael@0 | 2427 | } |
michael@0 | 2428 | |
michael@0 | 2429 | // A regular expression that was never closed is an |
michael@0 | 2430 | // error from which we cannot recover. |
michael@0 | 2431 | |
michael@0 | 2432 | if (!terminated) { |
michael@0 | 2433 | this.trigger("error", { |
michael@0 | 2434 | code: "E015", |
michael@0 | 2435 | line: this.line, |
michael@0 | 2436 | character: this.from |
michael@0 | 2437 | }); |
michael@0 | 2438 | |
michael@0 | 2439 | return void this.trigger("fatal", { |
michael@0 | 2440 | line: this.line, |
michael@0 | 2441 | from: this.from |
michael@0 | 2442 | }); |
michael@0 | 2443 | } |
michael@0 | 2444 | |
michael@0 | 2445 | // Parse flags (if any). |
michael@0 | 2446 | |
michael@0 | 2447 | while (index < length) { |
michael@0 | 2448 | char = this.peek(index); |
michael@0 | 2449 | if (!/[gim]/.test(char)) { |
michael@0 | 2450 | break; |
michael@0 | 2451 | } |
michael@0 | 2452 | flags.push(char); |
michael@0 | 2453 | value += char; |
michael@0 | 2454 | index += 1; |
michael@0 | 2455 | } |
michael@0 | 2456 | |
michael@0 | 2457 | // Check regular expression for correctness. |
michael@0 | 2458 | |
michael@0 | 2459 | try { |
michael@0 | 2460 | new RegExp(body, flags.join("")); |
michael@0 | 2461 | } catch (err) { |
michael@0 | 2462 | malformed = true; |
michael@0 | 2463 | this.trigger("error", { |
michael@0 | 2464 | code: "E016", |
michael@0 | 2465 | line: this.line, |
michael@0 | 2466 | character: this.char, |
michael@0 | 2467 | data: [ err.message ] // Platform dependent! |
michael@0 | 2468 | }); |
michael@0 | 2469 | } |
michael@0 | 2470 | |
michael@0 | 2471 | return { |
michael@0 | 2472 | type: Token.RegExp, |
michael@0 | 2473 | value: value, |
michael@0 | 2474 | flags: flags, |
michael@0 | 2475 | isMalformed: malformed |
michael@0 | 2476 | }; |
michael@0 | 2477 | }, |
michael@0 | 2478 | |
michael@0 | 2479 | /* |
michael@0 | 2480 | * Scan for any occurence of mixed tabs and spaces. If smarttabs option |
michael@0 | 2481 | * is on, ignore tabs followed by spaces. |
michael@0 | 2482 | * |
michael@0 | 2483 | * Tabs followed by one space followed by a block comment are allowed. |
michael@0 | 2484 | */ |
michael@0 | 2485 | scanMixedSpacesAndTabs: function () { |
michael@0 | 2486 | var at, match; |
michael@0 | 2487 | |
michael@0 | 2488 | if (state.option.smarttabs) { |
michael@0 | 2489 | // Negative look-behind for "//" |
michael@0 | 2490 | match = this.input.match(/(\/\/|^\s?\*)? \t/); |
michael@0 | 2491 | at = match && !match[1] ? 0 : -1; |
michael@0 | 2492 | } else { |
michael@0 | 2493 | at = this.input.search(/ \t|\t [^\*]/); |
michael@0 | 2494 | } |
michael@0 | 2495 | |
michael@0 | 2496 | return at; |
michael@0 | 2497 | }, |
michael@0 | 2498 | |
michael@0 | 2499 | /* |
michael@0 | 2500 | * Scan for characters that get silently deleted by one or more browsers. |
michael@0 | 2501 | */ |
michael@0 | 2502 | scanUnsafeChars: function () { |
michael@0 | 2503 | return this.input.search(reg.unsafeChars); |
michael@0 | 2504 | }, |
michael@0 | 2505 | |
michael@0 | 2506 | /* |
michael@0 | 2507 | * Produce the next raw token or return 'null' if no tokens can be matched. |
michael@0 | 2508 | * This method skips over all space characters. |
michael@0 | 2509 | */ |
michael@0 | 2510 | next: function (checks) { |
michael@0 | 2511 | this.from = this.char; |
michael@0 | 2512 | |
michael@0 | 2513 | // Move to the next non-space character. |
michael@0 | 2514 | var start; |
michael@0 | 2515 | if (/\s/.test(this.peek())) { |
michael@0 | 2516 | start = this.char; |
michael@0 | 2517 | |
michael@0 | 2518 | while (/\s/.test(this.peek())) { |
michael@0 | 2519 | this.from += 1; |
michael@0 | 2520 | this.skip(); |
michael@0 | 2521 | } |
michael@0 | 2522 | |
michael@0 | 2523 | if (this.peek() === "") { // EOL |
michael@0 | 2524 | if (!/^\s*$/.test(this.lines[this.line - 1]) && state.option.trailing) { |
michael@0 | 2525 | this.trigger("warning", { code: "W102", line: this.line, character: start }); |
michael@0 | 2526 | } |
michael@0 | 2527 | } |
michael@0 | 2528 | } |
michael@0 | 2529 | |
michael@0 | 2530 | // Methods that work with multi-line structures and move the |
michael@0 | 2531 | // character pointer. |
michael@0 | 2532 | |
michael@0 | 2533 | var match = this.scanComments() || |
michael@0 | 2534 | this.scanStringLiteral(checks); |
michael@0 | 2535 | |
michael@0 | 2536 | if (match) { |
michael@0 | 2537 | return match; |
michael@0 | 2538 | } |
michael@0 | 2539 | |
michael@0 | 2540 | // Methods that don't move the character pointer. |
michael@0 | 2541 | |
michael@0 | 2542 | match = |
michael@0 | 2543 | this.scanRegExp() || |
michael@0 | 2544 | this.scanPunctuator() || |
michael@0 | 2545 | this.scanKeyword() || |
michael@0 | 2546 | this.scanIdentifier() || |
michael@0 | 2547 | this.scanNumericLiteral(); |
michael@0 | 2548 | |
michael@0 | 2549 | if (match) { |
michael@0 | 2550 | this.skip(match.value.length); |
michael@0 | 2551 | return match; |
michael@0 | 2552 | } |
michael@0 | 2553 | |
michael@0 | 2554 | // No token could be matched, give up. |
michael@0 | 2555 | |
michael@0 | 2556 | return null; |
michael@0 | 2557 | }, |
michael@0 | 2558 | |
michael@0 | 2559 | /* |
michael@0 | 2560 | * Switch to the next line and reset all char pointers. Once |
michael@0 | 2561 | * switched, this method also checks for mixed spaces and tabs |
michael@0 | 2562 | * and other minor warnings. |
michael@0 | 2563 | */ |
michael@0 | 2564 | nextLine: function () { |
michael@0 | 2565 | var char; |
michael@0 | 2566 | |
michael@0 | 2567 | if (this.line >= this.lines.length) { |
michael@0 | 2568 | return false; |
michael@0 | 2569 | } |
michael@0 | 2570 | |
michael@0 | 2571 | this.input = this.lines[this.line]; |
michael@0 | 2572 | this.line += 1; |
michael@0 | 2573 | this.char = 1; |
michael@0 | 2574 | this.from = 1; |
michael@0 | 2575 | |
michael@0 | 2576 | char = this.scanMixedSpacesAndTabs(); |
michael@0 | 2577 | if (char >= 0) { |
michael@0 | 2578 | this.trigger("warning", { code: "W099", line: this.line, character: char + 1 }); |
michael@0 | 2579 | } |
michael@0 | 2580 | |
michael@0 | 2581 | this.input = this.input.replace(/\t/g, state.tab); |
michael@0 | 2582 | char = this.scanUnsafeChars(); |
michael@0 | 2583 | |
michael@0 | 2584 | if (char >= 0) { |
michael@0 | 2585 | this.trigger("warning", { code: "W100", line: this.line, character: char }); |
michael@0 | 2586 | } |
michael@0 | 2587 | |
michael@0 | 2588 | // If there is a limit on line length, warn when lines get too |
michael@0 | 2589 | // long. |
michael@0 | 2590 | |
michael@0 | 2591 | if (state.option.maxlen && state.option.maxlen < this.input.length) { |
michael@0 | 2592 | this.trigger("warning", { code: "W101", line: this.line, character: this.input.length }); |
michael@0 | 2593 | } |
michael@0 | 2594 | |
michael@0 | 2595 | return true; |
michael@0 | 2596 | }, |
michael@0 | 2597 | |
michael@0 | 2598 | /* |
michael@0 | 2599 | * This is simply a synonym for nextLine() method with a friendlier |
michael@0 | 2600 | * public name. |
michael@0 | 2601 | */ |
michael@0 | 2602 | start: function () { |
michael@0 | 2603 | this.nextLine(); |
michael@0 | 2604 | }, |
michael@0 | 2605 | |
michael@0 | 2606 | /* |
michael@0 | 2607 | * Produce the next token. This function is called by advance() to get |
michael@0 | 2608 | * the next token. It retuns a token in a JSLint-compatible format. |
michael@0 | 2609 | */ |
michael@0 | 2610 | token: function () { |
michael@0 | 2611 | /*jshint loopfunc:true */ |
michael@0 | 2612 | var checks = asyncTrigger(); |
michael@0 | 2613 | var token; |
michael@0 | 2614 | |
michael@0 | 2615 | |
michael@0 | 2616 | function isReserved(token, isProperty) { |
michael@0 | 2617 | if (!token.reserved) { |
michael@0 | 2618 | return false; |
michael@0 | 2619 | } |
michael@0 | 2620 | |
michael@0 | 2621 | if (token.meta && token.meta.isFutureReservedWord) { |
michael@0 | 2622 | // ES3 FutureReservedWord in an ES5 environment. |
michael@0 | 2623 | if (state.option.inES5(true) && !token.meta.es5) { |
michael@0 | 2624 | return false; |
michael@0 | 2625 | } |
michael@0 | 2626 | |
michael@0 | 2627 | // Some ES5 FutureReservedWord identifiers are active only |
michael@0 | 2628 | // within a strict mode environment. |
michael@0 | 2629 | if (token.meta.strictOnly) { |
michael@0 | 2630 | if (!state.option.strict && !state.directive["use strict"]) { |
michael@0 | 2631 | return false; |
michael@0 | 2632 | } |
michael@0 | 2633 | } |
michael@0 | 2634 | |
michael@0 | 2635 | if (isProperty) { |
michael@0 | 2636 | return false; |
michael@0 | 2637 | } |
michael@0 | 2638 | } |
michael@0 | 2639 | |
michael@0 | 2640 | return true; |
michael@0 | 2641 | } |
michael@0 | 2642 | |
michael@0 | 2643 | // Produce a token object. |
michael@0 | 2644 | var create = function (type, value, isProperty) { |
michael@0 | 2645 | /*jshint validthis:true */ |
michael@0 | 2646 | var obj; |
michael@0 | 2647 | |
michael@0 | 2648 | if (type !== "(endline)" && type !== "(end)") { |
michael@0 | 2649 | this.prereg = false; |
michael@0 | 2650 | } |
michael@0 | 2651 | |
michael@0 | 2652 | if (type === "(punctuator)") { |
michael@0 | 2653 | switch (value) { |
michael@0 | 2654 | case ".": |
michael@0 | 2655 | case ")": |
michael@0 | 2656 | case "~": |
michael@0 | 2657 | case "#": |
michael@0 | 2658 | case "]": |
michael@0 | 2659 | this.prereg = false; |
michael@0 | 2660 | break; |
michael@0 | 2661 | default: |
michael@0 | 2662 | this.prereg = true; |
michael@0 | 2663 | } |
michael@0 | 2664 | |
michael@0 | 2665 | obj = Object.create(state.syntax[value] || state.syntax["(error)"]); |
michael@0 | 2666 | } |
michael@0 | 2667 | |
michael@0 | 2668 | if (type === "(identifier)") { |
michael@0 | 2669 | if (value === "return" || value === "case" || value === "typeof") { |
michael@0 | 2670 | this.prereg = true; |
michael@0 | 2671 | } |
michael@0 | 2672 | |
michael@0 | 2673 | if (_.has(state.syntax, value)) { |
michael@0 | 2674 | obj = Object.create(state.syntax[value] || state.syntax["(error)"]); |
michael@0 | 2675 | |
michael@0 | 2676 | // If this can't be a reserved keyword, reset the object. |
michael@0 | 2677 | if (!isReserved(obj, isProperty && type === "(identifier)")) { |
michael@0 | 2678 | obj = null; |
michael@0 | 2679 | } |
michael@0 | 2680 | } |
michael@0 | 2681 | } |
michael@0 | 2682 | |
michael@0 | 2683 | if (!obj) { |
michael@0 | 2684 | obj = Object.create(state.syntax[type]); |
michael@0 | 2685 | } |
michael@0 | 2686 | |
michael@0 | 2687 | obj.identifier = (type === "(identifier)"); |
michael@0 | 2688 | obj.type = obj.type || type; |
michael@0 | 2689 | obj.value = value; |
michael@0 | 2690 | obj.line = this.line; |
michael@0 | 2691 | obj.character = this.char; |
michael@0 | 2692 | obj.from = this.from; |
michael@0 | 2693 | |
michael@0 | 2694 | if (isProperty && obj.identifier) { |
michael@0 | 2695 | obj.isProperty = isProperty; |
michael@0 | 2696 | } |
michael@0 | 2697 | |
michael@0 | 2698 | obj.check = checks.check; |
michael@0 | 2699 | |
michael@0 | 2700 | return obj; |
michael@0 | 2701 | }.bind(this); |
michael@0 | 2702 | |
michael@0 | 2703 | for (;;) { |
michael@0 | 2704 | if (!this.input.length) { |
michael@0 | 2705 | return create(this.nextLine() ? "(endline)" : "(end)", ""); |
michael@0 | 2706 | } |
michael@0 | 2707 | |
michael@0 | 2708 | token = this.next(checks); |
michael@0 | 2709 | |
michael@0 | 2710 | if (!token) { |
michael@0 | 2711 | if (this.input.length) { |
michael@0 | 2712 | // Unexpected character. |
michael@0 | 2713 | this.trigger("error", { |
michael@0 | 2714 | code: "E024", |
michael@0 | 2715 | line: this.line, |
michael@0 | 2716 | character: this.char, |
michael@0 | 2717 | data: [ this.peek() ] |
michael@0 | 2718 | }); |
michael@0 | 2719 | |
michael@0 | 2720 | this.input = ""; |
michael@0 | 2721 | } |
michael@0 | 2722 | |
michael@0 | 2723 | continue; |
michael@0 | 2724 | } |
michael@0 | 2725 | |
michael@0 | 2726 | switch (token.type) { |
michael@0 | 2727 | case Token.StringLiteral: |
michael@0 | 2728 | this.triggerAsync("String", { |
michael@0 | 2729 | line: this.line, |
michael@0 | 2730 | char: this.char, |
michael@0 | 2731 | from: this.from, |
michael@0 | 2732 | value: token.value, |
michael@0 | 2733 | quote: token.quote |
michael@0 | 2734 | }, checks, function () { return true; }); |
michael@0 | 2735 | |
michael@0 | 2736 | return create("(string)", token.value); |
michael@0 | 2737 | case Token.Identifier: |
michael@0 | 2738 | this.trigger("Identifier", { |
michael@0 | 2739 | line: this.line, |
michael@0 | 2740 | char: this.char, |
michael@0 | 2741 | from: this.form, |
michael@0 | 2742 | name: token.value, |
michael@0 | 2743 | isProperty: state.tokens.curr.id === "." |
michael@0 | 2744 | }); |
michael@0 | 2745 | |
michael@0 | 2746 | /* falls through */ |
michael@0 | 2747 | case Token.Keyword: |
michael@0 | 2748 | case Token.NullLiteral: |
michael@0 | 2749 | case Token.BooleanLiteral: |
michael@0 | 2750 | return create("(identifier)", token.value, state.tokens.curr.id === "."); |
michael@0 | 2751 | |
michael@0 | 2752 | case Token.NumericLiteral: |
michael@0 | 2753 | if (token.isMalformed) { |
michael@0 | 2754 | this.trigger("warning", { |
michael@0 | 2755 | code: "W045", |
michael@0 | 2756 | line: this.line, |
michael@0 | 2757 | character: this.char, |
michael@0 | 2758 | data: [ token.value ] |
michael@0 | 2759 | }); |
michael@0 | 2760 | } |
michael@0 | 2761 | |
michael@0 | 2762 | this.triggerAsync("warning", { |
michael@0 | 2763 | code: "W114", |
michael@0 | 2764 | line: this.line, |
michael@0 | 2765 | character: this.char, |
michael@0 | 2766 | data: [ "0x-" ] |
michael@0 | 2767 | }, checks, function () { return token.base === 16 && state.jsonMode; }); |
michael@0 | 2768 | |
michael@0 | 2769 | this.triggerAsync("warning", { |
michael@0 | 2770 | code: "W115", |
michael@0 | 2771 | line: this.line, |
michael@0 | 2772 | character: this.char |
michael@0 | 2773 | }, checks, function () { |
michael@0 | 2774 | return state.directive["use strict"] && token.base === 8; |
michael@0 | 2775 | }); |
michael@0 | 2776 | |
michael@0 | 2777 | this.trigger("Number", { |
michael@0 | 2778 | line: this.line, |
michael@0 | 2779 | char: this.char, |
michael@0 | 2780 | from: this.from, |
michael@0 | 2781 | value: token.value, |
michael@0 | 2782 | base: token.base, |
michael@0 | 2783 | isMalformed: token.malformed |
michael@0 | 2784 | }); |
michael@0 | 2785 | |
michael@0 | 2786 | return create("(number)", token.value); |
michael@0 | 2787 | |
michael@0 | 2788 | case Token.RegExp: |
michael@0 | 2789 | return create("(regexp)", token.value); |
michael@0 | 2790 | |
michael@0 | 2791 | case Token.Comment: |
michael@0 | 2792 | state.tokens.curr.comment = true; |
michael@0 | 2793 | |
michael@0 | 2794 | if (token.isSpecial) { |
michael@0 | 2795 | return { |
michael@0 | 2796 | value: token.value, |
michael@0 | 2797 | body: token.body, |
michael@0 | 2798 | type: token.commentType, |
michael@0 | 2799 | isSpecial: token.isSpecial, |
michael@0 | 2800 | line: this.line, |
michael@0 | 2801 | character: this.char, |
michael@0 | 2802 | from: this.from |
michael@0 | 2803 | }; |
michael@0 | 2804 | } |
michael@0 | 2805 | |
michael@0 | 2806 | break; |
michael@0 | 2807 | |
michael@0 | 2808 | case "": |
michael@0 | 2809 | break; |
michael@0 | 2810 | |
michael@0 | 2811 | default: |
michael@0 | 2812 | return create("(punctuator)", token.value); |
michael@0 | 2813 | } |
michael@0 | 2814 | } |
michael@0 | 2815 | } |
michael@0 | 2816 | }; |
michael@0 | 2817 | |
michael@0 | 2818 | exports.Lexer = Lexer; |
michael@0 | 2819 | |
michael@0 | 2820 | })() |
michael@0 | 2821 | },{"events":2,"./reg.js":6,"./state.js":4,"underscore":11}],"jshint":[function(require,module,exports){ |
michael@0 | 2822 | module.exports=require('E/GbHF'); |
michael@0 | 2823 | },{}],"E/GbHF":[function(require,module,exports){ |
michael@0 | 2824 | (function(){/*! |
michael@0 | 2825 | * JSHint, by JSHint Community. |
michael@0 | 2826 | * |
michael@0 | 2827 | * This file (and this file only) is licensed under the same slightly modified |
michael@0 | 2828 | * MIT license that JSLint is. It stops evil-doers everywhere: |
michael@0 | 2829 | * |
michael@0 | 2830 | * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) |
michael@0 | 2831 | * |
michael@0 | 2832 | * Permission is hereby granted, free of charge, to any person obtaining |
michael@0 | 2833 | * a copy of this software and associated documentation files (the "Software"), |
michael@0 | 2834 | * to deal in the Software without restriction, including without limitation |
michael@0 | 2835 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
michael@0 | 2836 | * and/or sell copies of the Software, and to permit persons to whom |
michael@0 | 2837 | * the Software is furnished to do so, subject to the following conditions: |
michael@0 | 2838 | * |
michael@0 | 2839 | * The above copyright notice and this permission notice shall be included |
michael@0 | 2840 | * in all copies or substantial portions of the Software. |
michael@0 | 2841 | * |
michael@0 | 2842 | * The Software shall be used for Good, not Evil. |
michael@0 | 2843 | * |
michael@0 | 2844 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
michael@0 | 2845 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
michael@0 | 2846 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
michael@0 | 2847 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
michael@0 | 2848 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
michael@0 | 2849 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
michael@0 | 2850 | * DEALINGS IN THE SOFTWARE. |
michael@0 | 2851 | * |
michael@0 | 2852 | */ |
michael@0 | 2853 | |
michael@0 | 2854 | /*jshint quotmark:double */ |
michael@0 | 2855 | /*global console:true */ |
michael@0 | 2856 | /*exported console */ |
michael@0 | 2857 | |
michael@0 | 2858 | var _ = require("underscore"); |
michael@0 | 2859 | var events = require("events"); |
michael@0 | 2860 | var vars = require("../shared/vars.js"); |
michael@0 | 2861 | var messages = require("../shared/messages.js"); |
michael@0 | 2862 | var Lexer = require("./lex.js").Lexer; |
michael@0 | 2863 | var reg = require("./reg.js"); |
michael@0 | 2864 | var state = require("./state.js").state; |
michael@0 | 2865 | var style = require("./style.js"); |
michael@0 | 2866 | |
michael@0 | 2867 | // We need this module here because environments such as IE and Rhino |
michael@0 | 2868 | // don't necessarilly expose the 'console' API and browserify uses |
michael@0 | 2869 | // it to log things. It's a sad state of affair, really. |
michael@0 | 2870 | var console = require("console-browserify"); |
michael@0 | 2871 | |
michael@0 | 2872 | // We build the application inside a function so that we produce only a singleton |
michael@0 | 2873 | // variable. That function will be invoked immediately, and its return value is |
michael@0 | 2874 | // the JSHINT function itself. |
michael@0 | 2875 | |
michael@0 | 2876 | var JSHINT = (function () { |
michael@0 | 2877 | "use strict"; |
michael@0 | 2878 | |
michael@0 | 2879 | var anonname, // The guessed name for anonymous functions. |
michael@0 | 2880 | api, // Extension API |
michael@0 | 2881 | |
michael@0 | 2882 | // These are operators that should not be used with the ! operator. |
michael@0 | 2883 | bang = { |
michael@0 | 2884 | "<" : true, |
michael@0 | 2885 | "<=" : true, |
michael@0 | 2886 | "==" : true, |
michael@0 | 2887 | "===": true, |
michael@0 | 2888 | "!==": true, |
michael@0 | 2889 | "!=" : true, |
michael@0 | 2890 | ">" : true, |
michael@0 | 2891 | ">=" : true, |
michael@0 | 2892 | "+" : true, |
michael@0 | 2893 | "-" : true, |
michael@0 | 2894 | "*" : true, |
michael@0 | 2895 | "/" : true, |
michael@0 | 2896 | "%" : true |
michael@0 | 2897 | }, |
michael@0 | 2898 | |
michael@0 | 2899 | // These are the JSHint boolean options. |
michael@0 | 2900 | boolOptions = { |
michael@0 | 2901 | asi : true, // if automatic semicolon insertion should be tolerated |
michael@0 | 2902 | bitwise : true, // if bitwise operators should not be allowed |
michael@0 | 2903 | boss : true, // if advanced usage of assignments should be allowed |
michael@0 | 2904 | browser : true, // if the standard browser globals should be predefined |
michael@0 | 2905 | camelcase : true, // if identifiers should be required in camel case |
michael@0 | 2906 | couch : true, // if CouchDB globals should be predefined |
michael@0 | 2907 | curly : true, // if curly braces around all blocks should be required |
michael@0 | 2908 | debug : true, // if debugger statements should be allowed |
michael@0 | 2909 | devel : true, // if logging globals should be predefined (console, alert, etc.) |
michael@0 | 2910 | dojo : true, // if Dojo Toolkit globals should be predefined |
michael@0 | 2911 | eqeqeq : true, // if === should be required |
michael@0 | 2912 | eqnull : true, // if == null comparisons should be tolerated |
michael@0 | 2913 | es3 : true, // if ES3 syntax should be allowed |
michael@0 | 2914 | es5 : true, // if ES5 syntax should be allowed (is now set per default) |
michael@0 | 2915 | esnext : true, // if es.next specific syntax should be allowed |
michael@0 | 2916 | moz : true, // if mozilla specific syntax should be allowed |
michael@0 | 2917 | evil : true, // if eval should be allowed |
michael@0 | 2918 | expr : true, // if ExpressionStatement should be allowed as Programs |
michael@0 | 2919 | forin : true, // if for in statements must filter |
michael@0 | 2920 | funcscope : true, // if only function scope should be used for scope tests |
michael@0 | 2921 | gcl : true, // if JSHint should be compatible with Google Closure Linter |
michael@0 | 2922 | globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict') |
michael@0 | 2923 | immed : true, // if immediate invocations must be wrapped in parens |
michael@0 | 2924 | iterator : true, // if the `__iterator__` property should be allowed |
michael@0 | 2925 | jquery : true, // if jQuery globals should be predefined |
michael@0 | 2926 | lastsemic : true, // if semicolons may be ommitted for the trailing |
michael@0 | 2927 | // statements inside of a one-line blocks. |
michael@0 | 2928 | laxbreak : true, // if line breaks should not be checked |
michael@0 | 2929 | laxcomma : true, // if line breaks should not be checked around commas |
michael@0 | 2930 | loopfunc : true, // if functions should be allowed to be defined within |
michael@0 | 2931 | // loops |
michael@0 | 2932 | mootools : true, // if MooTools globals should be predefined |
michael@0 | 2933 | multistr : true, // allow multiline strings |
michael@0 | 2934 | newcap : true, // if constructor names must be capitalized |
michael@0 | 2935 | noarg : true, // if arguments.caller and arguments.callee should be |
michael@0 | 2936 | // disallowed |
michael@0 | 2937 | node : true, // if the Node.js environment globals should be |
michael@0 | 2938 | // predefined |
michael@0 | 2939 | noempty : true, // if empty blocks should be disallowed |
michael@0 | 2940 | nonew : true, // if using `new` for side-effects should be disallowed |
michael@0 | 2941 | nonstandard : true, // if non-standard (but widely adopted) globals should |
michael@0 | 2942 | // be predefined |
michael@0 | 2943 | nomen : true, // if names should be checked |
michael@0 | 2944 | onevar : true, // if only one var statement per function should be |
michael@0 | 2945 | // allowed |
michael@0 | 2946 | passfail : true, // if the scan should stop on first error |
michael@0 | 2947 | phantom : true, // if PhantomJS symbols should be allowed |
michael@0 | 2948 | plusplus : true, // if increment/decrement should not be allowed |
michael@0 | 2949 | proto : true, // if the `__proto__` property should be allowed |
michael@0 | 2950 | prototypejs : true, // if Prototype and Scriptaculous globals should be |
michael@0 | 2951 | // predefined |
michael@0 | 2952 | rhino : true, // if the Rhino environment globals should be predefined |
michael@0 | 2953 | undef : true, // if variables should be declared before used |
michael@0 | 2954 | scripturl : true, // if script-targeted URLs should be tolerated |
michael@0 | 2955 | shadow : true, // if variable shadowing should be tolerated |
michael@0 | 2956 | smarttabs : true, // if smarttabs should be tolerated |
michael@0 | 2957 | // (http://www.emacswiki.org/emacs/SmartTabs) |
michael@0 | 2958 | strict : true, // require the "use strict"; pragma |
michael@0 | 2959 | sub : true, // if all forms of subscript notation are tolerated |
michael@0 | 2960 | supernew : true, // if `new function () { ... };` and `new Object;` |
michael@0 | 2961 | // should be tolerated |
michael@0 | 2962 | trailing : true, // if trailing whitespace rules apply |
michael@0 | 2963 | validthis : true, // if 'this' inside a non-constructor function is valid. |
michael@0 | 2964 | // This is a function scoped option only. |
michael@0 | 2965 | withstmt : true, // if with statements should be allowed |
michael@0 | 2966 | white : true, // if strict whitespace rules apply |
michael@0 | 2967 | worker : true, // if Web Worker script symbols should be allowed |
michael@0 | 2968 | wsh : true, // if the Windows Scripting Host environment globals |
michael@0 | 2969 | // should be predefined |
michael@0 | 2970 | yui : true, // YUI variables should be predefined |
michael@0 | 2971 | |
michael@0 | 2972 | // Obsolete options |
michael@0 | 2973 | onecase : true, // if one case switch statements should be allowed |
michael@0 | 2974 | regexp : true, // if the . should not be allowed in regexp literals |
michael@0 | 2975 | regexdash : true // if unescaped first/last dash (-) inside brackets |
michael@0 | 2976 | // should be tolerated |
michael@0 | 2977 | }, |
michael@0 | 2978 | |
michael@0 | 2979 | // These are the JSHint options that can take any value |
michael@0 | 2980 | // (we use this object to detect invalid options) |
michael@0 | 2981 | valOptions = { |
michael@0 | 2982 | maxlen : false, |
michael@0 | 2983 | indent : false, |
michael@0 | 2984 | maxerr : false, |
michael@0 | 2985 | predef : false, |
michael@0 | 2986 | quotmark : false, //'single'|'double'|true |
michael@0 | 2987 | scope : false, |
michael@0 | 2988 | maxstatements: false, // {int} max statements per function |
michael@0 | 2989 | maxdepth : false, // {int} max nested block depth per function |
michael@0 | 2990 | maxparams : false, // {int} max params per function |
michael@0 | 2991 | maxcomplexity: false, // {int} max cyclomatic complexity per function |
michael@0 | 2992 | unused : true, // warn if variables are unused. Available options: |
michael@0 | 2993 | // false - don't check for unused variables |
michael@0 | 2994 | // true - "vars" + check last function param |
michael@0 | 2995 | // "vars" - skip checking unused function params |
michael@0 | 2996 | // "strict" - "vars" + check all function params |
michael@0 | 2997 | latedef : false // warn if the variable is used before its definition |
michael@0 | 2998 | // false - don't emit any warnings |
michael@0 | 2999 | // true - warn if any variable is used before its definition |
michael@0 | 3000 | // "nofunc" - warn for any variable but function declarations |
michael@0 | 3001 | }, |
michael@0 | 3002 | |
michael@0 | 3003 | // These are JSHint boolean options which are shared with JSLint |
michael@0 | 3004 | // where the definition in JSHint is opposite JSLint |
michael@0 | 3005 | invertedOptions = { |
michael@0 | 3006 | bitwise : true, |
michael@0 | 3007 | forin : true, |
michael@0 | 3008 | newcap : true, |
michael@0 | 3009 | nomen : true, |
michael@0 | 3010 | plusplus: true, |
michael@0 | 3011 | regexp : true, |
michael@0 | 3012 | undef : true, |
michael@0 | 3013 | white : true, |
michael@0 | 3014 | |
michael@0 | 3015 | // Inverted and renamed, use JSHint name here |
michael@0 | 3016 | eqeqeq : true, |
michael@0 | 3017 | onevar : true, |
michael@0 | 3018 | strict : true |
michael@0 | 3019 | }, |
michael@0 | 3020 | |
michael@0 | 3021 | // These are JSHint boolean options which are shared with JSLint |
michael@0 | 3022 | // where the name has been changed but the effect is unchanged |
michael@0 | 3023 | renamedOptions = { |
michael@0 | 3024 | eqeq : "eqeqeq", |
michael@0 | 3025 | vars : "onevar", |
michael@0 | 3026 | windows: "wsh", |
michael@0 | 3027 | sloppy : "strict" |
michael@0 | 3028 | }, |
michael@0 | 3029 | |
michael@0 | 3030 | declared, // Globals that were declared using /*global ... */ syntax. |
michael@0 | 3031 | exported, // Variables that are used outside of the current file. |
michael@0 | 3032 | |
michael@0 | 3033 | functionicity = [ |
michael@0 | 3034 | "closure", "exception", "global", "label", |
michael@0 | 3035 | "outer", "unused", "var" |
michael@0 | 3036 | ], |
michael@0 | 3037 | |
michael@0 | 3038 | funct, // The current function |
michael@0 | 3039 | functions, // All of the functions |
michael@0 | 3040 | |
michael@0 | 3041 | global, // The global scope |
michael@0 | 3042 | implied, // Implied globals |
michael@0 | 3043 | inblock, |
michael@0 | 3044 | indent, |
michael@0 | 3045 | lookahead, |
michael@0 | 3046 | lex, |
michael@0 | 3047 | member, |
michael@0 | 3048 | membersOnly, |
michael@0 | 3049 | noreach, |
michael@0 | 3050 | predefined, // Global variables defined by option |
michael@0 | 3051 | |
michael@0 | 3052 | scope, // The current scope |
michael@0 | 3053 | stack, |
michael@0 | 3054 | unuseds, |
michael@0 | 3055 | urls, |
michael@0 | 3056 | warnings, |
michael@0 | 3057 | |
michael@0 | 3058 | extraModules = [], |
michael@0 | 3059 | emitter = new events.EventEmitter(); |
michael@0 | 3060 | |
michael@0 | 3061 | function checkOption(name, t) { |
michael@0 | 3062 | name = name.trim(); |
michael@0 | 3063 | |
michael@0 | 3064 | if (/^[+-]W\d{3}$/g.test(name)) { |
michael@0 | 3065 | return true; |
michael@0 | 3066 | } |
michael@0 | 3067 | |
michael@0 | 3068 | if (valOptions[name] === undefined && boolOptions[name] === undefined) { |
michael@0 | 3069 | if (t.type !== "jslint") { |
michael@0 | 3070 | error("E001", t, name); |
michael@0 | 3071 | return false; |
michael@0 | 3072 | } |
michael@0 | 3073 | } |
michael@0 | 3074 | |
michael@0 | 3075 | return true; |
michael@0 | 3076 | } |
michael@0 | 3077 | |
michael@0 | 3078 | function isString(obj) { |
michael@0 | 3079 | return Object.prototype.toString.call(obj) === "[object String]"; |
michael@0 | 3080 | } |
michael@0 | 3081 | |
michael@0 | 3082 | function isIdentifier(tkn, value) { |
michael@0 | 3083 | if (!tkn) |
michael@0 | 3084 | return false; |
michael@0 | 3085 | |
michael@0 | 3086 | if (!tkn.identifier || tkn.value !== value) |
michael@0 | 3087 | return false; |
michael@0 | 3088 | |
michael@0 | 3089 | return true; |
michael@0 | 3090 | } |
michael@0 | 3091 | |
michael@0 | 3092 | function isReserved(token) { |
michael@0 | 3093 | if (!token.reserved) { |
michael@0 | 3094 | return false; |
michael@0 | 3095 | } |
michael@0 | 3096 | |
michael@0 | 3097 | if (token.meta && token.meta.isFutureReservedWord) { |
michael@0 | 3098 | // ES3 FutureReservedWord in an ES5 environment. |
michael@0 | 3099 | if (state.option.inES5(true) && !token.meta.es5) { |
michael@0 | 3100 | return false; |
michael@0 | 3101 | } |
michael@0 | 3102 | |
michael@0 | 3103 | // Some ES5 FutureReservedWord identifiers are active only |
michael@0 | 3104 | // within a strict mode environment. |
michael@0 | 3105 | if (token.meta.strictOnly) { |
michael@0 | 3106 | if (!state.option.strict && !state.directive["use strict"]) { |
michael@0 | 3107 | return false; |
michael@0 | 3108 | } |
michael@0 | 3109 | } |
michael@0 | 3110 | |
michael@0 | 3111 | if (token.isProperty) { |
michael@0 | 3112 | return false; |
michael@0 | 3113 | } |
michael@0 | 3114 | } |
michael@0 | 3115 | |
michael@0 | 3116 | return true; |
michael@0 | 3117 | } |
michael@0 | 3118 | |
michael@0 | 3119 | function supplant(str, data) { |
michael@0 | 3120 | return str.replace(/\{([^{}]*)\}/g, function (a, b) { |
michael@0 | 3121 | var r = data[b]; |
michael@0 | 3122 | return typeof r === "string" || typeof r === "number" ? r : a; |
michael@0 | 3123 | }); |
michael@0 | 3124 | } |
michael@0 | 3125 | |
michael@0 | 3126 | function combine(t, o) { |
michael@0 | 3127 | var n; |
michael@0 | 3128 | for (n in o) { |
michael@0 | 3129 | if (_.has(o, n) && !_.has(JSHINT.blacklist, n)) { |
michael@0 | 3130 | t[n] = o[n]; |
michael@0 | 3131 | } |
michael@0 | 3132 | } |
michael@0 | 3133 | } |
michael@0 | 3134 | |
michael@0 | 3135 | function updatePredefined() { |
michael@0 | 3136 | Object.keys(JSHINT.blacklist).forEach(function (key) { |
michael@0 | 3137 | delete predefined[key]; |
michael@0 | 3138 | }); |
michael@0 | 3139 | } |
michael@0 | 3140 | |
michael@0 | 3141 | function assume() { |
michael@0 | 3142 | if (state.option.es5) { |
michael@0 | 3143 | warning("I003"); |
michael@0 | 3144 | } |
michael@0 | 3145 | if (state.option.couch) { |
michael@0 | 3146 | combine(predefined, vars.couch); |
michael@0 | 3147 | } |
michael@0 | 3148 | |
michael@0 | 3149 | if (state.option.rhino) { |
michael@0 | 3150 | combine(predefined, vars.rhino); |
michael@0 | 3151 | } |
michael@0 | 3152 | |
michael@0 | 3153 | if (state.option.phantom) { |
michael@0 | 3154 | combine(predefined, vars.phantom); |
michael@0 | 3155 | } |
michael@0 | 3156 | |
michael@0 | 3157 | if (state.option.prototypejs) { |
michael@0 | 3158 | combine(predefined, vars.prototypejs); |
michael@0 | 3159 | } |
michael@0 | 3160 | |
michael@0 | 3161 | if (state.option.node) { |
michael@0 | 3162 | combine(predefined, vars.node); |
michael@0 | 3163 | } |
michael@0 | 3164 | |
michael@0 | 3165 | if (state.option.devel) { |
michael@0 | 3166 | combine(predefined, vars.devel); |
michael@0 | 3167 | } |
michael@0 | 3168 | |
michael@0 | 3169 | if (state.option.dojo) { |
michael@0 | 3170 | combine(predefined, vars.dojo); |
michael@0 | 3171 | } |
michael@0 | 3172 | |
michael@0 | 3173 | if (state.option.browser) { |
michael@0 | 3174 | combine(predefined, vars.browser); |
michael@0 | 3175 | } |
michael@0 | 3176 | |
michael@0 | 3177 | if (state.option.nonstandard) { |
michael@0 | 3178 | combine(predefined, vars.nonstandard); |
michael@0 | 3179 | } |
michael@0 | 3180 | |
michael@0 | 3181 | if (state.option.jquery) { |
michael@0 | 3182 | combine(predefined, vars.jquery); |
michael@0 | 3183 | } |
michael@0 | 3184 | |
michael@0 | 3185 | if (state.option.mootools) { |
michael@0 | 3186 | combine(predefined, vars.mootools); |
michael@0 | 3187 | } |
michael@0 | 3188 | |
michael@0 | 3189 | if (state.option.worker) { |
michael@0 | 3190 | combine(predefined, vars.worker); |
michael@0 | 3191 | } |
michael@0 | 3192 | |
michael@0 | 3193 | if (state.option.wsh) { |
michael@0 | 3194 | combine(predefined, vars.wsh); |
michael@0 | 3195 | } |
michael@0 | 3196 | |
michael@0 | 3197 | if (state.option.globalstrict && state.option.strict !== false) { |
michael@0 | 3198 | state.option.strict = true; |
michael@0 | 3199 | } |
michael@0 | 3200 | |
michael@0 | 3201 | if (state.option.yui) { |
michael@0 | 3202 | combine(predefined, vars.yui); |
michael@0 | 3203 | } |
michael@0 | 3204 | |
michael@0 | 3205 | // Let's assume that chronologically ES3 < ES5 < ES6/ESNext < Moz |
michael@0 | 3206 | |
michael@0 | 3207 | state.option.inMoz = function (strict) { |
michael@0 | 3208 | if (strict) { |
michael@0 | 3209 | return state.option.moz && !state.option.esnext; |
michael@0 | 3210 | } |
michael@0 | 3211 | return state.option.moz; |
michael@0 | 3212 | }; |
michael@0 | 3213 | |
michael@0 | 3214 | state.option.inESNext = function (strict) { |
michael@0 | 3215 | if (strict) { |
michael@0 | 3216 | return !state.option.moz && state.option.esnext; |
michael@0 | 3217 | } |
michael@0 | 3218 | return state.option.moz || state.option.esnext; |
michael@0 | 3219 | }; |
michael@0 | 3220 | |
michael@0 | 3221 | state.option.inES5 = function (/* strict */) { |
michael@0 | 3222 | return !state.option.es3; |
michael@0 | 3223 | }; |
michael@0 | 3224 | |
michael@0 | 3225 | state.option.inES3 = function (strict) { |
michael@0 | 3226 | if (strict) { |
michael@0 | 3227 | return !state.option.moz && !state.option.esnext && state.option.es3; |
michael@0 | 3228 | } |
michael@0 | 3229 | return state.option.es3; |
michael@0 | 3230 | }; |
michael@0 | 3231 | } |
michael@0 | 3232 | |
michael@0 | 3233 | // Produce an error warning. |
michael@0 | 3234 | function quit(code, line, chr) { |
michael@0 | 3235 | var percentage = Math.floor((line / state.lines.length) * 100); |
michael@0 | 3236 | var message = messages.errors[code].desc; |
michael@0 | 3237 | |
michael@0 | 3238 | throw { |
michael@0 | 3239 | name: "JSHintError", |
michael@0 | 3240 | line: line, |
michael@0 | 3241 | character: chr, |
michael@0 | 3242 | message: message + " (" + percentage + "% scanned).", |
michael@0 | 3243 | raw: message |
michael@0 | 3244 | }; |
michael@0 | 3245 | } |
michael@0 | 3246 | |
michael@0 | 3247 | function isundef(scope, code, token, a) { |
michael@0 | 3248 | return JSHINT.undefs.push([scope, code, token, a]); |
michael@0 | 3249 | } |
michael@0 | 3250 | |
michael@0 | 3251 | function warning(code, t, a, b, c, d) { |
michael@0 | 3252 | var ch, l, w, msg; |
michael@0 | 3253 | |
michael@0 | 3254 | if (/^W\d{3}$/.test(code)) { |
michael@0 | 3255 | if (state.ignored[code]) |
michael@0 | 3256 | return; |
michael@0 | 3257 | |
michael@0 | 3258 | msg = messages.warnings[code]; |
michael@0 | 3259 | } else if (/E\d{3}/.test(code)) { |
michael@0 | 3260 | msg = messages.errors[code]; |
michael@0 | 3261 | } else if (/I\d{3}/.test(code)) { |
michael@0 | 3262 | msg = messages.info[code]; |
michael@0 | 3263 | } |
michael@0 | 3264 | |
michael@0 | 3265 | t = t || state.tokens.next; |
michael@0 | 3266 | if (t.id === "(end)") { // `~ |
michael@0 | 3267 | t = state.tokens.curr; |
michael@0 | 3268 | } |
michael@0 | 3269 | |
michael@0 | 3270 | l = t.line || 0; |
michael@0 | 3271 | ch = t.from || 0; |
michael@0 | 3272 | |
michael@0 | 3273 | w = { |
michael@0 | 3274 | id: "(error)", |
michael@0 | 3275 | raw: msg.desc, |
michael@0 | 3276 | code: msg.code, |
michael@0 | 3277 | evidence: state.lines[l - 1] || "", |
michael@0 | 3278 | line: l, |
michael@0 | 3279 | character: ch, |
michael@0 | 3280 | scope: JSHINT.scope, |
michael@0 | 3281 | a: a, |
michael@0 | 3282 | b: b, |
michael@0 | 3283 | c: c, |
michael@0 | 3284 | d: d |
michael@0 | 3285 | }; |
michael@0 | 3286 | |
michael@0 | 3287 | w.reason = supplant(msg.desc, w); |
michael@0 | 3288 | JSHINT.errors.push(w); |
michael@0 | 3289 | |
michael@0 | 3290 | if (state.option.passfail) { |
michael@0 | 3291 | quit("E042", l, ch); |
michael@0 | 3292 | } |
michael@0 | 3293 | |
michael@0 | 3294 | warnings += 1; |
michael@0 | 3295 | if (warnings >= state.option.maxerr) { |
michael@0 | 3296 | quit("E043", l, ch); |
michael@0 | 3297 | } |
michael@0 | 3298 | |
michael@0 | 3299 | return w; |
michael@0 | 3300 | } |
michael@0 | 3301 | |
michael@0 | 3302 | function warningAt(m, l, ch, a, b, c, d) { |
michael@0 | 3303 | return warning(m, { |
michael@0 | 3304 | line: l, |
michael@0 | 3305 | from: ch |
michael@0 | 3306 | }, a, b, c, d); |
michael@0 | 3307 | } |
michael@0 | 3308 | |
michael@0 | 3309 | function error(m, t, a, b, c, d) { |
michael@0 | 3310 | warning(m, t, a, b, c, d); |
michael@0 | 3311 | } |
michael@0 | 3312 | |
michael@0 | 3313 | function errorAt(m, l, ch, a, b, c, d) { |
michael@0 | 3314 | return error(m, { |
michael@0 | 3315 | line: l, |
michael@0 | 3316 | from: ch |
michael@0 | 3317 | }, a, b, c, d); |
michael@0 | 3318 | } |
michael@0 | 3319 | |
michael@0 | 3320 | // Tracking of "internal" scripts, like eval containing a static string |
michael@0 | 3321 | function addInternalSrc(elem, src) { |
michael@0 | 3322 | var i; |
michael@0 | 3323 | i = { |
michael@0 | 3324 | id: "(internal)", |
michael@0 | 3325 | elem: elem, |
michael@0 | 3326 | value: src |
michael@0 | 3327 | }; |
michael@0 | 3328 | JSHINT.internals.push(i); |
michael@0 | 3329 | return i; |
michael@0 | 3330 | } |
michael@0 | 3331 | |
michael@0 | 3332 | function addlabel(t, type, tkn, islet) { |
michael@0 | 3333 | // Define t in the current function in the current scope. |
michael@0 | 3334 | if (type === "exception") { |
michael@0 | 3335 | if (_.has(funct["(context)"], t)) { |
michael@0 | 3336 | if (funct[t] !== true && !state.option.node) { |
michael@0 | 3337 | warning("W002", state.tokens.next, t); |
michael@0 | 3338 | } |
michael@0 | 3339 | } |
michael@0 | 3340 | } |
michael@0 | 3341 | |
michael@0 | 3342 | if (_.has(funct, t) && !funct["(global)"]) { |
michael@0 | 3343 | if (funct[t] === true) { |
michael@0 | 3344 | if (state.option.latedef) { |
michael@0 | 3345 | if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) || |
michael@0 | 3346 | !_.contains([funct[t], type], "unction")) { |
michael@0 | 3347 | warning("W003", state.tokens.next, t); |
michael@0 | 3348 | } |
michael@0 | 3349 | } |
michael@0 | 3350 | } else { |
michael@0 | 3351 | if (!state.option.shadow && type !== "exception" || |
michael@0 | 3352 | (funct["(blockscope)"].getlabel(t))) { |
michael@0 | 3353 | warning("W004", state.tokens.next, t); |
michael@0 | 3354 | } |
michael@0 | 3355 | } |
michael@0 | 3356 | } |
michael@0 | 3357 | |
michael@0 | 3358 | // a double definition of a let variable in same block throws a TypeError |
michael@0 | 3359 | //if (funct["(blockscope)"] && funct["(blockscope)"].current.has(t)) { |
michael@0 | 3360 | // error("E044", state.tokens.next, t); |
michael@0 | 3361 | //} |
michael@0 | 3362 | |
michael@0 | 3363 | // if the identifier is from a let, adds it only to the current blockscope |
michael@0 | 3364 | if (islet) { |
michael@0 | 3365 | funct["(blockscope)"].current.add(t, type, state.tokens.curr); |
michael@0 | 3366 | } else { |
michael@0 | 3367 | |
michael@0 | 3368 | funct[t] = type; |
michael@0 | 3369 | |
michael@0 | 3370 | if (tkn) { |
michael@0 | 3371 | funct["(tokens)"][t] = tkn; |
michael@0 | 3372 | } |
michael@0 | 3373 | |
michael@0 | 3374 | if (funct["(global)"]) { |
michael@0 | 3375 | global[t] = funct; |
michael@0 | 3376 | if (_.has(implied, t)) { |
michael@0 | 3377 | if (state.option.latedef) { |
michael@0 | 3378 | if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) || |
michael@0 | 3379 | !_.contains([funct[t], type], "unction")) { |
michael@0 | 3380 | warning("W003", state.tokens.next, t); |
michael@0 | 3381 | } |
michael@0 | 3382 | } |
michael@0 | 3383 | |
michael@0 | 3384 | delete implied[t]; |
michael@0 | 3385 | } |
michael@0 | 3386 | } else { |
michael@0 | 3387 | scope[t] = funct; |
michael@0 | 3388 | } |
michael@0 | 3389 | } |
michael@0 | 3390 | } |
michael@0 | 3391 | |
michael@0 | 3392 | function doOption() { |
michael@0 | 3393 | var nt = state.tokens.next; |
michael@0 | 3394 | var body = nt.body.split(",").map(function (s) { return s.trim(); }); |
michael@0 | 3395 | var predef = {}; |
michael@0 | 3396 | |
michael@0 | 3397 | if (nt.type === "globals") { |
michael@0 | 3398 | body.forEach(function (g) { |
michael@0 | 3399 | g = g.split(":"); |
michael@0 | 3400 | var key = g[0]; |
michael@0 | 3401 | var val = g[1]; |
michael@0 | 3402 | |
michael@0 | 3403 | if (key.charAt(0) === "-") { |
michael@0 | 3404 | key = key.slice(1); |
michael@0 | 3405 | val = false; |
michael@0 | 3406 | |
michael@0 | 3407 | JSHINT.blacklist[key] = key; |
michael@0 | 3408 | updatePredefined(); |
michael@0 | 3409 | } else { |
michael@0 | 3410 | predef[key] = (val === "true"); |
michael@0 | 3411 | } |
michael@0 | 3412 | }); |
michael@0 | 3413 | |
michael@0 | 3414 | combine(predefined, predef); |
michael@0 | 3415 | |
michael@0 | 3416 | for (var key in predef) { |
michael@0 | 3417 | if (_.has(predef, key)) { |
michael@0 | 3418 | declared[key] = nt; |
michael@0 | 3419 | } |
michael@0 | 3420 | } |
michael@0 | 3421 | } |
michael@0 | 3422 | |
michael@0 | 3423 | if (nt.type === "exported") { |
michael@0 | 3424 | body.forEach(function (e) { |
michael@0 | 3425 | exported[e] = true; |
michael@0 | 3426 | }); |
michael@0 | 3427 | } |
michael@0 | 3428 | |
michael@0 | 3429 | if (nt.type === "members") { |
michael@0 | 3430 | membersOnly = membersOnly || {}; |
michael@0 | 3431 | |
michael@0 | 3432 | body.forEach(function (m) { |
michael@0 | 3433 | var ch1 = m.charAt(0); |
michael@0 | 3434 | var ch2 = m.charAt(m.length - 1); |
michael@0 | 3435 | |
michael@0 | 3436 | if (ch1 === ch2 && (ch1 === "\"" || ch1 === "'")) { |
michael@0 | 3437 | m = m |
michael@0 | 3438 | .substr(1, m.length - 2) |
michael@0 | 3439 | .replace("\\b", "\b") |
michael@0 | 3440 | .replace("\\t", "\t") |
michael@0 | 3441 | .replace("\\n", "\n") |
michael@0 | 3442 | .replace("\\v", "\v") |
michael@0 | 3443 | .replace("\\f", "\f") |
michael@0 | 3444 | .replace("\\r", "\r") |
michael@0 | 3445 | .replace("\\\\", "\\") |
michael@0 | 3446 | .replace("\\\"", "\""); |
michael@0 | 3447 | } |
michael@0 | 3448 | |
michael@0 | 3449 | membersOnly[m] = false; |
michael@0 | 3450 | }); |
michael@0 | 3451 | } |
michael@0 | 3452 | |
michael@0 | 3453 | var numvals = [ |
michael@0 | 3454 | "maxstatements", |
michael@0 | 3455 | "maxparams", |
michael@0 | 3456 | "maxdepth", |
michael@0 | 3457 | "maxcomplexity", |
michael@0 | 3458 | "maxerr", |
michael@0 | 3459 | "maxlen", |
michael@0 | 3460 | "indent" |
michael@0 | 3461 | ]; |
michael@0 | 3462 | |
michael@0 | 3463 | if (nt.type === "jshint" || nt.type === "jslint") { |
michael@0 | 3464 | body.forEach(function (g) { |
michael@0 | 3465 | g = g.split(":"); |
michael@0 | 3466 | var key = (g[0] || "").trim(); |
michael@0 | 3467 | var val = (g[1] || "").trim(); |
michael@0 | 3468 | |
michael@0 | 3469 | if (!checkOption(key, nt)) { |
michael@0 | 3470 | return; |
michael@0 | 3471 | } |
michael@0 | 3472 | |
michael@0 | 3473 | if (numvals.indexOf(key) >= 0) { |
michael@0 | 3474 | |
michael@0 | 3475 | // GH988 - numeric options can be disabled by setting them to `false` |
michael@0 | 3476 | if (val !== "false") { |
michael@0 | 3477 | val = +val; |
michael@0 | 3478 | |
michael@0 | 3479 | if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) { |
michael@0 | 3480 | error("E032", nt, g[1].trim()); |
michael@0 | 3481 | return; |
michael@0 | 3482 | } |
michael@0 | 3483 | |
michael@0 | 3484 | if (key === "indent") { |
michael@0 | 3485 | state.option["(explicitIndent)"] = true; |
michael@0 | 3486 | } |
michael@0 | 3487 | state.option[key] = val; |
michael@0 | 3488 | } else { |
michael@0 | 3489 | if (key === "indent") { |
michael@0 | 3490 | state.option["(explicitIndent)"] = false; |
michael@0 | 3491 | } else { |
michael@0 | 3492 | state.option[key] = false; |
michael@0 | 3493 | } |
michael@0 | 3494 | } |
michael@0 | 3495 | |
michael@0 | 3496 | return; |
michael@0 | 3497 | } |
michael@0 | 3498 | |
michael@0 | 3499 | if (key === "validthis") { |
michael@0 | 3500 | // `validthis` is valid only within a function scope. |
michael@0 | 3501 | if (funct["(global)"]) { |
michael@0 | 3502 | error("E009"); |
michael@0 | 3503 | } else { |
michael@0 | 3504 | if (val === "true" || val === "false") { |
michael@0 | 3505 | state.option.validthis = (val === "true"); |
michael@0 | 3506 | } else { |
michael@0 | 3507 | error("E002", nt); |
michael@0 | 3508 | } |
michael@0 | 3509 | } |
michael@0 | 3510 | return; |
michael@0 | 3511 | } |
michael@0 | 3512 | |
michael@0 | 3513 | if (key === "quotmark") { |
michael@0 | 3514 | switch (val) { |
michael@0 | 3515 | case "true": |
michael@0 | 3516 | case "false": |
michael@0 | 3517 | state.option.quotmark = (val === "true"); |
michael@0 | 3518 | break; |
michael@0 | 3519 | case "double": |
michael@0 | 3520 | case "single": |
michael@0 | 3521 | state.option.quotmark = val; |
michael@0 | 3522 | break; |
michael@0 | 3523 | default: |
michael@0 | 3524 | error("E002", nt); |
michael@0 | 3525 | } |
michael@0 | 3526 | return; |
michael@0 | 3527 | } |
michael@0 | 3528 | |
michael@0 | 3529 | if (key === "unused") { |
michael@0 | 3530 | switch (val) { |
michael@0 | 3531 | case "true": |
michael@0 | 3532 | state.option.unused = true; |
michael@0 | 3533 | break; |
michael@0 | 3534 | case "false": |
michael@0 | 3535 | state.option.unused = false; |
michael@0 | 3536 | break; |
michael@0 | 3537 | case "vars": |
michael@0 | 3538 | case "strict": |
michael@0 | 3539 | state.option.unused = val; |
michael@0 | 3540 | break; |
michael@0 | 3541 | default: |
michael@0 | 3542 | error("E002", nt); |
michael@0 | 3543 | } |
michael@0 | 3544 | return; |
michael@0 | 3545 | } |
michael@0 | 3546 | |
michael@0 | 3547 | if (key === "latedef") { |
michael@0 | 3548 | switch (val) { |
michael@0 | 3549 | case "true": |
michael@0 | 3550 | state.option.latedef = true; |
michael@0 | 3551 | break; |
michael@0 | 3552 | case "false": |
michael@0 | 3553 | state.option.latedef = false; |
michael@0 | 3554 | break; |
michael@0 | 3555 | case "nofunc": |
michael@0 | 3556 | state.option.latedef = "nofunc"; |
michael@0 | 3557 | break; |
michael@0 | 3558 | default: |
michael@0 | 3559 | error("E002", nt); |
michael@0 | 3560 | } |
michael@0 | 3561 | return; |
michael@0 | 3562 | } |
michael@0 | 3563 | |
michael@0 | 3564 | var match = /^([+-])(W\d{3})$/g.exec(key); |
michael@0 | 3565 | if (match) { |
michael@0 | 3566 | // ignore for -W..., unignore for +W... |
michael@0 | 3567 | state.ignored[match[2]] = (match[1] === "-"); |
michael@0 | 3568 | return; |
michael@0 | 3569 | } |
michael@0 | 3570 | |
michael@0 | 3571 | var tn; |
michael@0 | 3572 | if (val === "true" || val === "false") { |
michael@0 | 3573 | if (nt.type === "jslint") { |
michael@0 | 3574 | tn = renamedOptions[key] || key; |
michael@0 | 3575 | state.option[tn] = (val === "true"); |
michael@0 | 3576 | |
michael@0 | 3577 | if (invertedOptions[tn] !== undefined) { |
michael@0 | 3578 | state.option[tn] = !state.option[tn]; |
michael@0 | 3579 | } |
michael@0 | 3580 | } else { |
michael@0 | 3581 | state.option[key] = (val === "true"); |
michael@0 | 3582 | } |
michael@0 | 3583 | |
michael@0 | 3584 | if (key === "newcap") { |
michael@0 | 3585 | state.option["(explicitNewcap)"] = true; |
michael@0 | 3586 | } |
michael@0 | 3587 | return; |
michael@0 | 3588 | } |
michael@0 | 3589 | |
michael@0 | 3590 | error("E002", nt); |
michael@0 | 3591 | }); |
michael@0 | 3592 | |
michael@0 | 3593 | assume(); |
michael@0 | 3594 | } |
michael@0 | 3595 | } |
michael@0 | 3596 | |
michael@0 | 3597 | // We need a peek function. If it has an argument, it peeks that much farther |
michael@0 | 3598 | // ahead. It is used to distinguish |
michael@0 | 3599 | // for ( var i in ... |
michael@0 | 3600 | // from |
michael@0 | 3601 | // for ( var i = ... |
michael@0 | 3602 | |
michael@0 | 3603 | function peek(p) { |
michael@0 | 3604 | var i = p || 0, j = 0, t; |
michael@0 | 3605 | |
michael@0 | 3606 | while (j <= i) { |
michael@0 | 3607 | t = lookahead[j]; |
michael@0 | 3608 | if (!t) { |
michael@0 | 3609 | t = lookahead[j] = lex.token(); |
michael@0 | 3610 | } |
michael@0 | 3611 | j += 1; |
michael@0 | 3612 | } |
michael@0 | 3613 | return t; |
michael@0 | 3614 | } |
michael@0 | 3615 | |
michael@0 | 3616 | // Produce the next token. It looks for programming errors. |
michael@0 | 3617 | |
michael@0 | 3618 | function advance(id, t) { |
michael@0 | 3619 | switch (state.tokens.curr.id) { |
michael@0 | 3620 | case "(number)": |
michael@0 | 3621 | if (state.tokens.next.id === ".") { |
michael@0 | 3622 | warning("W005", state.tokens.curr); |
michael@0 | 3623 | } |
michael@0 | 3624 | break; |
michael@0 | 3625 | case "-": |
michael@0 | 3626 | if (state.tokens.next.id === "-" || state.tokens.next.id === "--") { |
michael@0 | 3627 | warning("W006"); |
michael@0 | 3628 | } |
michael@0 | 3629 | break; |
michael@0 | 3630 | case "+": |
michael@0 | 3631 | if (state.tokens.next.id === "+" || state.tokens.next.id === "++") { |
michael@0 | 3632 | warning("W007"); |
michael@0 | 3633 | } |
michael@0 | 3634 | break; |
michael@0 | 3635 | } |
michael@0 | 3636 | |
michael@0 | 3637 | if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) { |
michael@0 | 3638 | anonname = state.tokens.curr.value; |
michael@0 | 3639 | } |
michael@0 | 3640 | |
michael@0 | 3641 | if (id && state.tokens.next.id !== id) { |
michael@0 | 3642 | if (t) { |
michael@0 | 3643 | if (state.tokens.next.id === "(end)") { |
michael@0 | 3644 | error("E019", t, t.id); |
michael@0 | 3645 | } else { |
michael@0 | 3646 | error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value); |
michael@0 | 3647 | } |
michael@0 | 3648 | } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) { |
michael@0 | 3649 | warning("W116", state.tokens.next, id, state.tokens.next.value); |
michael@0 | 3650 | } |
michael@0 | 3651 | } |
michael@0 | 3652 | |
michael@0 | 3653 | state.tokens.prev = state.tokens.curr; |
michael@0 | 3654 | state.tokens.curr = state.tokens.next; |
michael@0 | 3655 | for (;;) { |
michael@0 | 3656 | state.tokens.next = lookahead.shift() || lex.token(); |
michael@0 | 3657 | |
michael@0 | 3658 | if (!state.tokens.next) { // No more tokens left, give up |
michael@0 | 3659 | quit("E041", state.tokens.curr.line); |
michael@0 | 3660 | } |
michael@0 | 3661 | |
michael@0 | 3662 | if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { |
michael@0 | 3663 | return; |
michael@0 | 3664 | } |
michael@0 | 3665 | |
michael@0 | 3666 | if (state.tokens.next.check) { |
michael@0 | 3667 | state.tokens.next.check(); |
michael@0 | 3668 | } |
michael@0 | 3669 | |
michael@0 | 3670 | if (state.tokens.next.isSpecial) { |
michael@0 | 3671 | doOption(); |
michael@0 | 3672 | } else { |
michael@0 | 3673 | if (state.tokens.next.id !== "(endline)") { |
michael@0 | 3674 | break; |
michael@0 | 3675 | } |
michael@0 | 3676 | } |
michael@0 | 3677 | } |
michael@0 | 3678 | } |
michael@0 | 3679 | |
michael@0 | 3680 | // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it |
michael@0 | 3681 | // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is |
michael@0 | 3682 | // like .nud except that it is only used on the first token of a statement. |
michael@0 | 3683 | // Having .fud makes it much easier to define statement-oriented languages like |
michael@0 | 3684 | // JavaScript. I retained Pratt's nomenclature. |
michael@0 | 3685 | |
michael@0 | 3686 | // .nud Null denotation |
michael@0 | 3687 | // .fud First null denotation |
michael@0 | 3688 | // .led Left denotation |
michael@0 | 3689 | // lbp Left binding power |
michael@0 | 3690 | // rbp Right binding power |
michael@0 | 3691 | |
michael@0 | 3692 | // They are elements of the parsing method called Top Down Operator Precedence. |
michael@0 | 3693 | |
michael@0 | 3694 | function expression(rbp, initial) { |
michael@0 | 3695 | var left, isArray = false, isObject = false, isLetExpr = false; |
michael@0 | 3696 | |
michael@0 | 3697 | // if current expression is a let expression |
michael@0 | 3698 | if (!initial && state.tokens.next.value === "let" && peek(0).value === "(") { |
michael@0 | 3699 | if (!state.option.inMoz(true)) { |
michael@0 | 3700 | warning("W118", state.tokens.next, "let expressions"); |
michael@0 | 3701 | } |
michael@0 | 3702 | isLetExpr = true; |
michael@0 | 3703 | // create a new block scope we use only for the current expression |
michael@0 | 3704 | funct["(blockscope)"].stack(); |
michael@0 | 3705 | advance("let"); |
michael@0 | 3706 | advance("("); |
michael@0 | 3707 | state.syntax["let"].fud.call(state.syntax["let"].fud, false); |
michael@0 | 3708 | advance(")"); |
michael@0 | 3709 | } |
michael@0 | 3710 | |
michael@0 | 3711 | if (state.tokens.next.id === "(end)") |
michael@0 | 3712 | error("E006", state.tokens.curr); |
michael@0 | 3713 | |
michael@0 | 3714 | advance(); |
michael@0 | 3715 | |
michael@0 | 3716 | if (initial) { |
michael@0 | 3717 | anonname = "anonymous"; |
michael@0 | 3718 | funct["(verb)"] = state.tokens.curr.value; |
michael@0 | 3719 | } |
michael@0 | 3720 | |
michael@0 | 3721 | if (initial === true && state.tokens.curr.fud) { |
michael@0 | 3722 | left = state.tokens.curr.fud(); |
michael@0 | 3723 | } else { |
michael@0 | 3724 | if (state.tokens.curr.nud) { |
michael@0 | 3725 | left = state.tokens.curr.nud(); |
michael@0 | 3726 | } else { |
michael@0 | 3727 | error("E030", state.tokens.curr, state.tokens.curr.id); |
michael@0 | 3728 | } |
michael@0 | 3729 | |
michael@0 | 3730 | var end_of_expr = state.tokens.next.identifier && |
michael@0 | 3731 | !state.tokens.curr.led && |
michael@0 | 3732 | state.tokens.curr.line !== state.tokens.next.line; |
michael@0 | 3733 | while (rbp < state.tokens.next.lbp && !end_of_expr) { |
michael@0 | 3734 | isArray = state.tokens.curr.value === "Array"; |
michael@0 | 3735 | isObject = state.tokens.curr.value === "Object"; |
michael@0 | 3736 | |
michael@0 | 3737 | // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object() |
michael@0 | 3738 | // Line breaks in IfStatement heads exist to satisfy the checkJSHint |
michael@0 | 3739 | // "Line too long." error. |
michael@0 | 3740 | if (left && (left.value || (left.first && left.first.value))) { |
michael@0 | 3741 | // If the left.value is not "new", or the left.first.value is a "." |
michael@0 | 3742 | // then safely assume that this is not "new Array()" and possibly |
michael@0 | 3743 | // not "new Object()"... |
michael@0 | 3744 | if (left.value !== "new" || |
michael@0 | 3745 | (left.first && left.first.value && left.first.value === ".")) { |
michael@0 | 3746 | isArray = false; |
michael@0 | 3747 | // ...In the case of Object, if the left.value and state.tokens.curr.value |
michael@0 | 3748 | // are not equal, then safely assume that this not "new Object()" |
michael@0 | 3749 | if (left.value !== state.tokens.curr.value) { |
michael@0 | 3750 | isObject = false; |
michael@0 | 3751 | } |
michael@0 | 3752 | } |
michael@0 | 3753 | } |
michael@0 | 3754 | |
michael@0 | 3755 | advance(); |
michael@0 | 3756 | |
michael@0 | 3757 | if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { |
michael@0 | 3758 | warning("W009", state.tokens.curr); |
michael@0 | 3759 | } |
michael@0 | 3760 | |
michael@0 | 3761 | if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { |
michael@0 | 3762 | warning("W010", state.tokens.curr); |
michael@0 | 3763 | } |
michael@0 | 3764 | |
michael@0 | 3765 | if (left && state.tokens.curr.led) { |
michael@0 | 3766 | left = state.tokens.curr.led(left); |
michael@0 | 3767 | } else { |
michael@0 | 3768 | error("E033", state.tokens.curr, state.tokens.curr.id); |
michael@0 | 3769 | } |
michael@0 | 3770 | } |
michael@0 | 3771 | } |
michael@0 | 3772 | if (isLetExpr) { |
michael@0 | 3773 | funct["(blockscope)"].unstack(); |
michael@0 | 3774 | } |
michael@0 | 3775 | return left; |
michael@0 | 3776 | } |
michael@0 | 3777 | |
michael@0 | 3778 | |
michael@0 | 3779 | // Functions for conformance of style. |
michael@0 | 3780 | |
michael@0 | 3781 | function adjacent(left, right) { |
michael@0 | 3782 | left = left || state.tokens.curr; |
michael@0 | 3783 | right = right || state.tokens.next; |
michael@0 | 3784 | if (state.option.white) { |
michael@0 | 3785 | if (left.character !== right.from && left.line === right.line) { |
michael@0 | 3786 | left.from += (left.character - left.from); |
michael@0 | 3787 | warning("W011", left, left.value); |
michael@0 | 3788 | } |
michael@0 | 3789 | } |
michael@0 | 3790 | } |
michael@0 | 3791 | |
michael@0 | 3792 | function nobreak(left, right) { |
michael@0 | 3793 | left = left || state.tokens.curr; |
michael@0 | 3794 | right = right || state.tokens.next; |
michael@0 | 3795 | if (state.option.white && (left.character !== right.from || left.line !== right.line)) { |
michael@0 | 3796 | warning("W012", right, right.value); |
michael@0 | 3797 | } |
michael@0 | 3798 | } |
michael@0 | 3799 | |
michael@0 | 3800 | function nospace(left, right) { |
michael@0 | 3801 | left = left || state.tokens.curr; |
michael@0 | 3802 | right = right || state.tokens.next; |
michael@0 | 3803 | if (state.option.white && !left.comment) { |
michael@0 | 3804 | if (left.line === right.line) { |
michael@0 | 3805 | adjacent(left, right); |
michael@0 | 3806 | } |
michael@0 | 3807 | } |
michael@0 | 3808 | } |
michael@0 | 3809 | |
michael@0 | 3810 | function nonadjacent(left, right) { |
michael@0 | 3811 | if (state.option.white) { |
michael@0 | 3812 | left = left || state.tokens.curr; |
michael@0 | 3813 | right = right || state.tokens.next; |
michael@0 | 3814 | |
michael@0 | 3815 | if (left.value === ";" && right.value === ";") { |
michael@0 | 3816 | return; |
michael@0 | 3817 | } |
michael@0 | 3818 | |
michael@0 | 3819 | if (left.line === right.line && left.character === right.from) { |
michael@0 | 3820 | left.from += (left.character - left.from); |
michael@0 | 3821 | warning("W013", left, left.value); |
michael@0 | 3822 | } |
michael@0 | 3823 | } |
michael@0 | 3824 | } |
michael@0 | 3825 | |
michael@0 | 3826 | function nobreaknonadjacent(left, right) { |
michael@0 | 3827 | left = left || state.tokens.curr; |
michael@0 | 3828 | right = right || state.tokens.next; |
michael@0 | 3829 | if (!state.option.laxbreak && left.line !== right.line) { |
michael@0 | 3830 | warning("W014", right, right.id); |
michael@0 | 3831 | } else if (state.option.white) { |
michael@0 | 3832 | left = left || state.tokens.curr; |
michael@0 | 3833 | right = right || state.tokens.next; |
michael@0 | 3834 | if (left.character === right.from) { |
michael@0 | 3835 | left.from += (left.character - left.from); |
michael@0 | 3836 | warning("W013", left, left.value); |
michael@0 | 3837 | } |
michael@0 | 3838 | } |
michael@0 | 3839 | } |
michael@0 | 3840 | |
michael@0 | 3841 | function indentation(bias) { |
michael@0 | 3842 | if (!state.option.white && !state.option["(explicitIndent)"]) { |
michael@0 | 3843 | return; |
michael@0 | 3844 | } |
michael@0 | 3845 | |
michael@0 | 3846 | if (state.tokens.next.id === "(end)") { |
michael@0 | 3847 | return; |
michael@0 | 3848 | } |
michael@0 | 3849 | |
michael@0 | 3850 | var i = indent + (bias || 0); |
michael@0 | 3851 | if (state.tokens.next.from !== i) { |
michael@0 | 3852 | warning("W015", state.tokens.next, state.tokens.next.value, i, state.tokens.next.from); |
michael@0 | 3853 | } |
michael@0 | 3854 | } |
michael@0 | 3855 | |
michael@0 | 3856 | function nolinebreak(t) { |
michael@0 | 3857 | t = t || state.tokens.curr; |
michael@0 | 3858 | if (t.line !== state.tokens.next.line) { |
michael@0 | 3859 | warning("E022", t, t.value); |
michael@0 | 3860 | } |
michael@0 | 3861 | } |
michael@0 | 3862 | |
michael@0 | 3863 | |
michael@0 | 3864 | function comma(opts) { |
michael@0 | 3865 | opts = opts || {}; |
michael@0 | 3866 | |
michael@0 | 3867 | if (!opts.peek) { |
michael@0 | 3868 | if (state.tokens.curr.line !== state.tokens.next.line) { |
michael@0 | 3869 | if (!state.option.laxcomma) { |
michael@0 | 3870 | if (comma.first) { |
michael@0 | 3871 | warning("I001"); |
michael@0 | 3872 | comma.first = false; |
michael@0 | 3873 | } |
michael@0 | 3874 | warning("W014", state.tokens.curr, state.tokens.next.value); |
michael@0 | 3875 | } |
michael@0 | 3876 | } else if (!state.tokens.curr.comment && |
michael@0 | 3877 | state.tokens.curr.character !== state.tokens.next.from && state.option.white) { |
michael@0 | 3878 | state.tokens.curr.from += (state.tokens.curr.character - state.tokens.curr.from); |
michael@0 | 3879 | warning("W011", state.tokens.curr, state.tokens.curr.value); |
michael@0 | 3880 | } |
michael@0 | 3881 | |
michael@0 | 3882 | advance(","); |
michael@0 | 3883 | } |
michael@0 | 3884 | |
michael@0 | 3885 | // TODO: This is a temporary solution to fight against false-positives in |
michael@0 | 3886 | // arrays and objects with trailing commas (see GH-363). The best solution |
michael@0 | 3887 | // would be to extract all whitespace rules out of parser. |
michael@0 | 3888 | |
michael@0 | 3889 | if (state.tokens.next.value !== "]" && state.tokens.next.value !== "}") { |
michael@0 | 3890 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 3891 | } |
michael@0 | 3892 | |
michael@0 | 3893 | if (state.tokens.next.identifier && !(opts.property && state.option.inES5())) { |
michael@0 | 3894 | // Keywords that cannot follow a comma operator. |
michael@0 | 3895 | switch (state.tokens.next.value) { |
michael@0 | 3896 | case "break": |
michael@0 | 3897 | case "case": |
michael@0 | 3898 | case "catch": |
michael@0 | 3899 | case "continue": |
michael@0 | 3900 | case "default": |
michael@0 | 3901 | case "do": |
michael@0 | 3902 | case "else": |
michael@0 | 3903 | case "finally": |
michael@0 | 3904 | case "for": |
michael@0 | 3905 | case "if": |
michael@0 | 3906 | case "in": |
michael@0 | 3907 | case "instanceof": |
michael@0 | 3908 | case "return": |
michael@0 | 3909 | case "yield": |
michael@0 | 3910 | case "switch": |
michael@0 | 3911 | case "throw": |
michael@0 | 3912 | case "try": |
michael@0 | 3913 | case "var": |
michael@0 | 3914 | case "let": |
michael@0 | 3915 | case "while": |
michael@0 | 3916 | case "with": |
michael@0 | 3917 | error("E024", state.tokens.next, state.tokens.next.value); |
michael@0 | 3918 | return false; |
michael@0 | 3919 | } |
michael@0 | 3920 | } |
michael@0 | 3921 | |
michael@0 | 3922 | if (state.tokens.next.type === "(punctuator)") { |
michael@0 | 3923 | switch (state.tokens.next.value) { |
michael@0 | 3924 | case "}": |
michael@0 | 3925 | case "]": |
michael@0 | 3926 | case ",": |
michael@0 | 3927 | if (opts.allowTrailing) { |
michael@0 | 3928 | return true; |
michael@0 | 3929 | } |
michael@0 | 3930 | |
michael@0 | 3931 | /* falls through */ |
michael@0 | 3932 | case ")": |
michael@0 | 3933 | error("E024", state.tokens.next, state.tokens.next.value); |
michael@0 | 3934 | return false; |
michael@0 | 3935 | } |
michael@0 | 3936 | } |
michael@0 | 3937 | return true; |
michael@0 | 3938 | } |
michael@0 | 3939 | |
michael@0 | 3940 | // Functional constructors for making the symbols that will be inherited by |
michael@0 | 3941 | // tokens. |
michael@0 | 3942 | |
michael@0 | 3943 | function symbol(s, p) { |
michael@0 | 3944 | var x = state.syntax[s]; |
michael@0 | 3945 | if (!x || typeof x !== "object") { |
michael@0 | 3946 | state.syntax[s] = x = { |
michael@0 | 3947 | id: s, |
michael@0 | 3948 | lbp: p, |
michael@0 | 3949 | value: s |
michael@0 | 3950 | }; |
michael@0 | 3951 | } |
michael@0 | 3952 | return x; |
michael@0 | 3953 | } |
michael@0 | 3954 | |
michael@0 | 3955 | function delim(s) { |
michael@0 | 3956 | return symbol(s, 0); |
michael@0 | 3957 | } |
michael@0 | 3958 | |
michael@0 | 3959 | function stmt(s, f) { |
michael@0 | 3960 | var x = delim(s); |
michael@0 | 3961 | x.identifier = x.reserved = true; |
michael@0 | 3962 | x.fud = f; |
michael@0 | 3963 | return x; |
michael@0 | 3964 | } |
michael@0 | 3965 | |
michael@0 | 3966 | function blockstmt(s, f) { |
michael@0 | 3967 | var x = stmt(s, f); |
michael@0 | 3968 | x.block = true; |
michael@0 | 3969 | return x; |
michael@0 | 3970 | } |
michael@0 | 3971 | |
michael@0 | 3972 | function reserveName(x) { |
michael@0 | 3973 | var c = x.id.charAt(0); |
michael@0 | 3974 | if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) { |
michael@0 | 3975 | x.identifier = x.reserved = true; |
michael@0 | 3976 | } |
michael@0 | 3977 | return x; |
michael@0 | 3978 | } |
michael@0 | 3979 | |
michael@0 | 3980 | function prefix(s, f) { |
michael@0 | 3981 | var x = symbol(s, 150); |
michael@0 | 3982 | reserveName(x); |
michael@0 | 3983 | x.nud = (typeof f === "function") ? f : function () { |
michael@0 | 3984 | this.right = expression(150); |
michael@0 | 3985 | this.arity = "unary"; |
michael@0 | 3986 | if (this.id === "++" || this.id === "--") { |
michael@0 | 3987 | if (state.option.plusplus) { |
michael@0 | 3988 | warning("W016", this, this.id); |
michael@0 | 3989 | } else if ((!this.right.identifier || isReserved(this.right)) && |
michael@0 | 3990 | this.right.id !== "." && this.right.id !== "[") { |
michael@0 | 3991 | warning("W017", this); |
michael@0 | 3992 | } |
michael@0 | 3993 | } |
michael@0 | 3994 | return this; |
michael@0 | 3995 | }; |
michael@0 | 3996 | return x; |
michael@0 | 3997 | } |
michael@0 | 3998 | |
michael@0 | 3999 | function type(s, f) { |
michael@0 | 4000 | var x = delim(s); |
michael@0 | 4001 | x.type = s; |
michael@0 | 4002 | x.nud = f; |
michael@0 | 4003 | return x; |
michael@0 | 4004 | } |
michael@0 | 4005 | |
michael@0 | 4006 | function reserve(name, func) { |
michael@0 | 4007 | var x = type(name, func); |
michael@0 | 4008 | x.identifier = true; |
michael@0 | 4009 | x.reserved = true; |
michael@0 | 4010 | return x; |
michael@0 | 4011 | } |
michael@0 | 4012 | |
michael@0 | 4013 | function FutureReservedWord(name, meta) { |
michael@0 | 4014 | var x = type(name, (meta && meta.nud) || function () { |
michael@0 | 4015 | return this; |
michael@0 | 4016 | }); |
michael@0 | 4017 | |
michael@0 | 4018 | meta = meta || {}; |
michael@0 | 4019 | meta.isFutureReservedWord = true; |
michael@0 | 4020 | |
michael@0 | 4021 | x.value = name; |
michael@0 | 4022 | x.identifier = true; |
michael@0 | 4023 | x.reserved = true; |
michael@0 | 4024 | x.meta = meta; |
michael@0 | 4025 | |
michael@0 | 4026 | return x; |
michael@0 | 4027 | } |
michael@0 | 4028 | |
michael@0 | 4029 | function reservevar(s, v) { |
michael@0 | 4030 | return reserve(s, function () { |
michael@0 | 4031 | if (typeof v === "function") { |
michael@0 | 4032 | v(this); |
michael@0 | 4033 | } |
michael@0 | 4034 | return this; |
michael@0 | 4035 | }); |
michael@0 | 4036 | } |
michael@0 | 4037 | |
michael@0 | 4038 | function infix(s, f, p, w) { |
michael@0 | 4039 | var x = symbol(s, p); |
michael@0 | 4040 | reserveName(x); |
michael@0 | 4041 | x.led = function (left) { |
michael@0 | 4042 | if (!w) { |
michael@0 | 4043 | nobreaknonadjacent(state.tokens.prev, state.tokens.curr); |
michael@0 | 4044 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4045 | } |
michael@0 | 4046 | if (s === "in" && left.id === "!") { |
michael@0 | 4047 | warning("W018", left, "!"); |
michael@0 | 4048 | } |
michael@0 | 4049 | if (typeof f === "function") { |
michael@0 | 4050 | return f(left, this); |
michael@0 | 4051 | } else { |
michael@0 | 4052 | this.left = left; |
michael@0 | 4053 | this.right = expression(p); |
michael@0 | 4054 | return this; |
michael@0 | 4055 | } |
michael@0 | 4056 | }; |
michael@0 | 4057 | return x; |
michael@0 | 4058 | } |
michael@0 | 4059 | |
michael@0 | 4060 | |
michael@0 | 4061 | function application(s) { |
michael@0 | 4062 | var x = symbol(s, 42); |
michael@0 | 4063 | |
michael@0 | 4064 | x.led = function (left) { |
michael@0 | 4065 | if (!state.option.inESNext()) { |
michael@0 | 4066 | warning("W104", state.tokens.curr, "arrow function syntax (=>)"); |
michael@0 | 4067 | } |
michael@0 | 4068 | |
michael@0 | 4069 | nobreaknonadjacent(state.tokens.prev, state.tokens.curr); |
michael@0 | 4070 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4071 | |
michael@0 | 4072 | this.left = left; |
michael@0 | 4073 | this.right = doFunction(undefined, undefined, false, left); |
michael@0 | 4074 | return this; |
michael@0 | 4075 | }; |
michael@0 | 4076 | return x; |
michael@0 | 4077 | } |
michael@0 | 4078 | |
michael@0 | 4079 | function relation(s, f) { |
michael@0 | 4080 | var x = symbol(s, 100); |
michael@0 | 4081 | |
michael@0 | 4082 | x.led = function (left) { |
michael@0 | 4083 | nobreaknonadjacent(state.tokens.prev, state.tokens.curr); |
michael@0 | 4084 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4085 | var right = expression(100); |
michael@0 | 4086 | |
michael@0 | 4087 | if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { |
michael@0 | 4088 | warning("W019", this); |
michael@0 | 4089 | } else if (f) { |
michael@0 | 4090 | f.apply(this, [left, right]); |
michael@0 | 4091 | } |
michael@0 | 4092 | |
michael@0 | 4093 | if (!left || !right) { |
michael@0 | 4094 | quit("E041", state.tokens.curr.line); |
michael@0 | 4095 | } |
michael@0 | 4096 | |
michael@0 | 4097 | if (left.id === "!") { |
michael@0 | 4098 | warning("W018", left, "!"); |
michael@0 | 4099 | } |
michael@0 | 4100 | |
michael@0 | 4101 | if (right.id === "!") { |
michael@0 | 4102 | warning("W018", right, "!"); |
michael@0 | 4103 | } |
michael@0 | 4104 | |
michael@0 | 4105 | this.left = left; |
michael@0 | 4106 | this.right = right; |
michael@0 | 4107 | return this; |
michael@0 | 4108 | }; |
michael@0 | 4109 | return x; |
michael@0 | 4110 | } |
michael@0 | 4111 | |
michael@0 | 4112 | function isPoorRelation(node) { |
michael@0 | 4113 | return node && |
michael@0 | 4114 | ((node.type === "(number)" && +node.value === 0) || |
michael@0 | 4115 | (node.type === "(string)" && node.value === "") || |
michael@0 | 4116 | (node.type === "null" && !state.option.eqnull) || |
michael@0 | 4117 | node.type === "true" || |
michael@0 | 4118 | node.type === "false" || |
michael@0 | 4119 | node.type === "undefined"); |
michael@0 | 4120 | } |
michael@0 | 4121 | |
michael@0 | 4122 | function assignop(s) { |
michael@0 | 4123 | symbol(s, 20).exps = true; |
michael@0 | 4124 | |
michael@0 | 4125 | return infix(s, function (left, that) { |
michael@0 | 4126 | that.left = left; |
michael@0 | 4127 | |
michael@0 | 4128 | if (left) { |
michael@0 | 4129 | if (predefined[left.value] === false && |
michael@0 | 4130 | scope[left.value]["(global)"] === true) { |
michael@0 | 4131 | warning("W020", left); |
michael@0 | 4132 | } else if (left["function"]) { |
michael@0 | 4133 | warning("W021", left, left.value); |
michael@0 | 4134 | } |
michael@0 | 4135 | |
michael@0 | 4136 | if (funct[left.value] === "const") { |
michael@0 | 4137 | error("E013", left, left.value); |
michael@0 | 4138 | } |
michael@0 | 4139 | |
michael@0 | 4140 | if (left.id === ".") { |
michael@0 | 4141 | if (!left.left) { |
michael@0 | 4142 | warning("E031", that); |
michael@0 | 4143 | } else if (left.left.value === "arguments" && !state.directive["use strict"]) { |
michael@0 | 4144 | warning("E031", that); |
michael@0 | 4145 | } |
michael@0 | 4146 | |
michael@0 | 4147 | that.right = expression(19); |
michael@0 | 4148 | return that; |
michael@0 | 4149 | } else if (left.id === "[") { |
michael@0 | 4150 | if (state.tokens.curr.left.first) { |
michael@0 | 4151 | state.tokens.curr.left.first.forEach(function (t) { |
michael@0 | 4152 | if (funct[t.value] === "const") { |
michael@0 | 4153 | error("E013", t, t.value); |
michael@0 | 4154 | } |
michael@0 | 4155 | }); |
michael@0 | 4156 | } else if (!left.left) { |
michael@0 | 4157 | warning("E031", that); |
michael@0 | 4158 | } else if (left.left.value === "arguments" && !state.directive["use strict"]) { |
michael@0 | 4159 | warning("E031", that); |
michael@0 | 4160 | } |
michael@0 | 4161 | that.right = expression(19); |
michael@0 | 4162 | return that; |
michael@0 | 4163 | } else if (left.identifier && !isReserved(left)) { |
michael@0 | 4164 | if (funct[left.value] === "exception") { |
michael@0 | 4165 | warning("W022", left); |
michael@0 | 4166 | } |
michael@0 | 4167 | that.right = expression(19); |
michael@0 | 4168 | return that; |
michael@0 | 4169 | } |
michael@0 | 4170 | |
michael@0 | 4171 | if (left === state.syntax["function"]) { |
michael@0 | 4172 | warning("W023", state.tokens.curr); |
michael@0 | 4173 | } |
michael@0 | 4174 | } |
michael@0 | 4175 | |
michael@0 | 4176 | error("E031", that); |
michael@0 | 4177 | }, 20); |
michael@0 | 4178 | } |
michael@0 | 4179 | |
michael@0 | 4180 | |
michael@0 | 4181 | function bitwise(s, f, p) { |
michael@0 | 4182 | var x = symbol(s, p); |
michael@0 | 4183 | reserveName(x); |
michael@0 | 4184 | x.led = (typeof f === "function") ? f : function (left) { |
michael@0 | 4185 | if (state.option.bitwise) { |
michael@0 | 4186 | warning("W016", this, this.id); |
michael@0 | 4187 | } |
michael@0 | 4188 | this.left = left; |
michael@0 | 4189 | this.right = expression(p); |
michael@0 | 4190 | return this; |
michael@0 | 4191 | }; |
michael@0 | 4192 | return x; |
michael@0 | 4193 | } |
michael@0 | 4194 | |
michael@0 | 4195 | |
michael@0 | 4196 | function bitwiseassignop(s) { |
michael@0 | 4197 | symbol(s, 20).exps = true; |
michael@0 | 4198 | return infix(s, function (left, that) { |
michael@0 | 4199 | if (state.option.bitwise) { |
michael@0 | 4200 | warning("W016", that, that.id); |
michael@0 | 4201 | } |
michael@0 | 4202 | nonadjacent(state.tokens.prev, state.tokens.curr); |
michael@0 | 4203 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4204 | if (left) { |
michael@0 | 4205 | if (left.id === "." || left.id === "[" || |
michael@0 | 4206 | (left.identifier && !isReserved(left))) { |
michael@0 | 4207 | expression(19); |
michael@0 | 4208 | return that; |
michael@0 | 4209 | } |
michael@0 | 4210 | if (left === state.syntax["function"]) { |
michael@0 | 4211 | warning("W023", state.tokens.curr); |
michael@0 | 4212 | } |
michael@0 | 4213 | return that; |
michael@0 | 4214 | } |
michael@0 | 4215 | error("E031", that); |
michael@0 | 4216 | }, 20); |
michael@0 | 4217 | } |
michael@0 | 4218 | |
michael@0 | 4219 | |
michael@0 | 4220 | function suffix(s) { |
michael@0 | 4221 | var x = symbol(s, 150); |
michael@0 | 4222 | |
michael@0 | 4223 | x.led = function (left) { |
michael@0 | 4224 | if (state.option.plusplus) { |
michael@0 | 4225 | warning("W016", this, this.id); |
michael@0 | 4226 | } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") { |
michael@0 | 4227 | warning("W017", this); |
michael@0 | 4228 | } |
michael@0 | 4229 | |
michael@0 | 4230 | this.left = left; |
michael@0 | 4231 | return this; |
michael@0 | 4232 | }; |
michael@0 | 4233 | return x; |
michael@0 | 4234 | } |
michael@0 | 4235 | |
michael@0 | 4236 | // fnparam means that this identifier is being defined as a function |
michael@0 | 4237 | // argument (see identifier()) |
michael@0 | 4238 | // prop means that this identifier is that of an object property |
michael@0 | 4239 | |
michael@0 | 4240 | function optionalidentifier(fnparam, prop) { |
michael@0 | 4241 | if (!state.tokens.next.identifier) { |
michael@0 | 4242 | return; |
michael@0 | 4243 | } |
michael@0 | 4244 | |
michael@0 | 4245 | advance(); |
michael@0 | 4246 | |
michael@0 | 4247 | var curr = state.tokens.curr; |
michael@0 | 4248 | var meta = curr.meta || {}; |
michael@0 | 4249 | var val = state.tokens.curr.value; |
michael@0 | 4250 | |
michael@0 | 4251 | if (!isReserved(curr)) { |
michael@0 | 4252 | return val; |
michael@0 | 4253 | } |
michael@0 | 4254 | |
michael@0 | 4255 | if (prop) { |
michael@0 | 4256 | if (state.option.inES5() || meta.isFutureReservedWord) { |
michael@0 | 4257 | return val; |
michael@0 | 4258 | } |
michael@0 | 4259 | } |
michael@0 | 4260 | |
michael@0 | 4261 | if (fnparam && val === "undefined") { |
michael@0 | 4262 | return val; |
michael@0 | 4263 | } |
michael@0 | 4264 | |
michael@0 | 4265 | // Display an info message about reserved words as properties |
michael@0 | 4266 | // and ES5 but do it only once. |
michael@0 | 4267 | if (prop && !api.getCache("displayed:I002")) { |
michael@0 | 4268 | api.setCache("displayed:I002", true); |
michael@0 | 4269 | warning("I002"); |
michael@0 | 4270 | } |
michael@0 | 4271 | |
michael@0 | 4272 | warning("W024", state.tokens.curr, state.tokens.curr.id); |
michael@0 | 4273 | return val; |
michael@0 | 4274 | } |
michael@0 | 4275 | |
michael@0 | 4276 | // fnparam means that this identifier is being defined as a function |
michael@0 | 4277 | // argument |
michael@0 | 4278 | // prop means that this identifier is that of an object property |
michael@0 | 4279 | function identifier(fnparam, prop) { |
michael@0 | 4280 | var i = optionalidentifier(fnparam, prop); |
michael@0 | 4281 | if (i) { |
michael@0 | 4282 | return i; |
michael@0 | 4283 | } |
michael@0 | 4284 | if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") { |
michael@0 | 4285 | warning("W025"); |
michael@0 | 4286 | } else { |
michael@0 | 4287 | error("E030", state.tokens.next, state.tokens.next.value); |
michael@0 | 4288 | } |
michael@0 | 4289 | } |
michael@0 | 4290 | |
michael@0 | 4291 | |
michael@0 | 4292 | function reachable(s) { |
michael@0 | 4293 | var i = 0, t; |
michael@0 | 4294 | if (state.tokens.next.id !== ";" || noreach) { |
michael@0 | 4295 | return; |
michael@0 | 4296 | } |
michael@0 | 4297 | for (;;) { |
michael@0 | 4298 | t = peek(i); |
michael@0 | 4299 | if (t.reach) { |
michael@0 | 4300 | return; |
michael@0 | 4301 | } |
michael@0 | 4302 | if (t.id !== "(endline)") { |
michael@0 | 4303 | if (t.id === "function") { |
michael@0 | 4304 | if (!state.option.latedef) { |
michael@0 | 4305 | break; |
michael@0 | 4306 | } |
michael@0 | 4307 | |
michael@0 | 4308 | warning("W026", t); |
michael@0 | 4309 | break; |
michael@0 | 4310 | } |
michael@0 | 4311 | |
michael@0 | 4312 | warning("W027", t, t.value, s); |
michael@0 | 4313 | break; |
michael@0 | 4314 | } |
michael@0 | 4315 | i += 1; |
michael@0 | 4316 | } |
michael@0 | 4317 | } |
michael@0 | 4318 | |
michael@0 | 4319 | |
michael@0 | 4320 | function statement(noindent) { |
michael@0 | 4321 | var values; |
michael@0 | 4322 | var i = indent, r, s = scope, t = state.tokens.next; |
michael@0 | 4323 | |
michael@0 | 4324 | if (t.id === ";") { |
michael@0 | 4325 | advance(";"); |
michael@0 | 4326 | return; |
michael@0 | 4327 | } |
michael@0 | 4328 | |
michael@0 | 4329 | // Is this a labelled statement? |
michael@0 | 4330 | var res = isReserved(t); |
michael@0 | 4331 | |
michael@0 | 4332 | // We're being more tolerant here: if someone uses |
michael@0 | 4333 | // a FutureReservedWord as a label, we warn but proceed |
michael@0 | 4334 | // anyway. |
michael@0 | 4335 | |
michael@0 | 4336 | if (res && t.meta && t.meta.isFutureReservedWord && peek().id === ":") { |
michael@0 | 4337 | warning("W024", t, t.id); |
michael@0 | 4338 | res = false; |
michael@0 | 4339 | } |
michael@0 | 4340 | |
michael@0 | 4341 | // detect a destructuring assignment |
michael@0 | 4342 | if (_.has(["[", "{"], t.value)) { |
michael@0 | 4343 | if (lookupBlockType().isDestAssign) { |
michael@0 | 4344 | if (!state.option.inESNext()) { |
michael@0 | 4345 | warning("W104", state.tokens.curr, "destructuring expression"); |
michael@0 | 4346 | } |
michael@0 | 4347 | values = destructuringExpression(); |
michael@0 | 4348 | values.forEach(function (tok) { |
michael@0 | 4349 | isundef(funct, "W117", tok.token, tok.id); |
michael@0 | 4350 | }); |
michael@0 | 4351 | advance("="); |
michael@0 | 4352 | destructuringExpressionMatch(values, expression(5, true)); |
michael@0 | 4353 | advance(";"); |
michael@0 | 4354 | return; |
michael@0 | 4355 | } |
michael@0 | 4356 | } |
michael@0 | 4357 | if (t.identifier && !res && peek().id === ":") { |
michael@0 | 4358 | advance(); |
michael@0 | 4359 | advance(":"); |
michael@0 | 4360 | scope = Object.create(s); |
michael@0 | 4361 | addlabel(t.value, "label"); |
michael@0 | 4362 | |
michael@0 | 4363 | if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { |
michael@0 | 4364 | warning("W028", state.tokens.next, t.value, state.tokens.next.value); |
michael@0 | 4365 | } |
michael@0 | 4366 | |
michael@0 | 4367 | state.tokens.next.label = t.value; |
michael@0 | 4368 | t = state.tokens.next; |
michael@0 | 4369 | } |
michael@0 | 4370 | |
michael@0 | 4371 | // Is it a lonely block? |
michael@0 | 4372 | |
michael@0 | 4373 | if (t.id === "{") { |
michael@0 | 4374 | // Is it a switch case block? |
michael@0 | 4375 | // |
michael@0 | 4376 | // switch (foo) { |
michael@0 | 4377 | // case bar: { <= here. |
michael@0 | 4378 | // ... |
michael@0 | 4379 | // } |
michael@0 | 4380 | // } |
michael@0 | 4381 | var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":"); |
michael@0 | 4382 | block(true, true, false, false, iscase); |
michael@0 | 4383 | return; |
michael@0 | 4384 | } |
michael@0 | 4385 | |
michael@0 | 4386 | // Parse the statement. |
michael@0 | 4387 | |
michael@0 | 4388 | if (!noindent) { |
michael@0 | 4389 | indentation(); |
michael@0 | 4390 | } |
michael@0 | 4391 | r = expression(0, true); |
michael@0 | 4392 | |
michael@0 | 4393 | // Look for the final semicolon. |
michael@0 | 4394 | |
michael@0 | 4395 | if (!t.block) { |
michael@0 | 4396 | if (!state.option.expr && (!r || !r.exps)) { |
michael@0 | 4397 | warning("W030", state.tokens.curr); |
michael@0 | 4398 | } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") { |
michael@0 | 4399 | warning("W031", t); |
michael@0 | 4400 | } |
michael@0 | 4401 | |
michael@0 | 4402 | if (state.tokens.next.id !== ";") { |
michael@0 | 4403 | if (!state.option.asi) { |
michael@0 | 4404 | // If this is the last statement in a block that ends on |
michael@0 | 4405 | // the same line *and* option lastsemic is on, ignore the warning. |
michael@0 | 4406 | // Otherwise, complain about missing semicolon. |
michael@0 | 4407 | if (!state.option.lastsemic || state.tokens.next.id !== "}" || |
michael@0 | 4408 | state.tokens.next.line !== state.tokens.curr.line) { |
michael@0 | 4409 | warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); |
michael@0 | 4410 | } |
michael@0 | 4411 | } |
michael@0 | 4412 | } else { |
michael@0 | 4413 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4414 | advance(";"); |
michael@0 | 4415 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4416 | } |
michael@0 | 4417 | } |
michael@0 | 4418 | |
michael@0 | 4419 | // Restore the indentation. |
michael@0 | 4420 | |
michael@0 | 4421 | indent = i; |
michael@0 | 4422 | scope = s; |
michael@0 | 4423 | return r; |
michael@0 | 4424 | } |
michael@0 | 4425 | |
michael@0 | 4426 | |
michael@0 | 4427 | function statements(startLine) { |
michael@0 | 4428 | var a = [], p; |
michael@0 | 4429 | |
michael@0 | 4430 | while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") { |
michael@0 | 4431 | if (state.tokens.next.id === ";") { |
michael@0 | 4432 | p = peek(); |
michael@0 | 4433 | |
michael@0 | 4434 | if (!p || (p.id !== "(" && p.id !== "[")) { |
michael@0 | 4435 | warning("W032"); |
michael@0 | 4436 | } |
michael@0 | 4437 | |
michael@0 | 4438 | advance(";"); |
michael@0 | 4439 | } else { |
michael@0 | 4440 | a.push(statement(startLine === state.tokens.next.line)); |
michael@0 | 4441 | } |
michael@0 | 4442 | } |
michael@0 | 4443 | return a; |
michael@0 | 4444 | } |
michael@0 | 4445 | |
michael@0 | 4446 | |
michael@0 | 4447 | /* |
michael@0 | 4448 | * read all directives |
michael@0 | 4449 | * recognizes a simple form of asi, but always |
michael@0 | 4450 | * warns, if it is used |
michael@0 | 4451 | */ |
michael@0 | 4452 | function directives() { |
michael@0 | 4453 | var i, p, pn; |
michael@0 | 4454 | |
michael@0 | 4455 | for (;;) { |
michael@0 | 4456 | if (state.tokens.next.id === "(string)") { |
michael@0 | 4457 | p = peek(0); |
michael@0 | 4458 | if (p.id === "(endline)") { |
michael@0 | 4459 | i = 1; |
michael@0 | 4460 | do { |
michael@0 | 4461 | pn = peek(i); |
michael@0 | 4462 | i = i + 1; |
michael@0 | 4463 | } while (pn.id === "(endline)"); |
michael@0 | 4464 | |
michael@0 | 4465 | if (pn.id !== ";") { |
michael@0 | 4466 | if (pn.id !== "(string)" && pn.id !== "(number)" && |
michael@0 | 4467 | pn.id !== "(regexp)" && pn.identifier !== true && |
michael@0 | 4468 | pn.id !== "}") { |
michael@0 | 4469 | break; |
michael@0 | 4470 | } |
michael@0 | 4471 | warning("W033", state.tokens.next); |
michael@0 | 4472 | } else { |
michael@0 | 4473 | p = pn; |
michael@0 | 4474 | } |
michael@0 | 4475 | } else if (p.id === "}") { |
michael@0 | 4476 | // Directive with no other statements, warn about missing semicolon |
michael@0 | 4477 | warning("W033", p); |
michael@0 | 4478 | } else if (p.id !== ";") { |
michael@0 | 4479 | break; |
michael@0 | 4480 | } |
michael@0 | 4481 | |
michael@0 | 4482 | indentation(); |
michael@0 | 4483 | advance(); |
michael@0 | 4484 | if (state.directive[state.tokens.curr.value]) { |
michael@0 | 4485 | warning("W034", state.tokens.curr, state.tokens.curr.value); |
michael@0 | 4486 | } |
michael@0 | 4487 | |
michael@0 | 4488 | if (state.tokens.curr.value === "use strict") { |
michael@0 | 4489 | if (!state.option["(explicitNewcap)"]) |
michael@0 | 4490 | state.option.newcap = true; |
michael@0 | 4491 | state.option.undef = true; |
michael@0 | 4492 | } |
michael@0 | 4493 | |
michael@0 | 4494 | // there's no directive negation, so always set to true |
michael@0 | 4495 | state.directive[state.tokens.curr.value] = true; |
michael@0 | 4496 | |
michael@0 | 4497 | if (p.id === ";") { |
michael@0 | 4498 | advance(";"); |
michael@0 | 4499 | } |
michael@0 | 4500 | continue; |
michael@0 | 4501 | } |
michael@0 | 4502 | break; |
michael@0 | 4503 | } |
michael@0 | 4504 | } |
michael@0 | 4505 | |
michael@0 | 4506 | |
michael@0 | 4507 | /* |
michael@0 | 4508 | * Parses a single block. A block is a sequence of statements wrapped in |
michael@0 | 4509 | * braces. |
michael@0 | 4510 | * |
michael@0 | 4511 | * ordinary - true for everything but function bodies and try blocks. |
michael@0 | 4512 | * stmt - true if block can be a single statement (e.g. in if/for/while). |
michael@0 | 4513 | * isfunc - true if block is a function body |
michael@0 | 4514 | * isfatarrow - |
michael@0 | 4515 | * iscase - true if block is a switch case block |
michael@0 | 4516 | */ |
michael@0 | 4517 | function block(ordinary, stmt, isfunc, isfatarrow, iscase) { |
michael@0 | 4518 | var a, |
michael@0 | 4519 | b = inblock, |
michael@0 | 4520 | old_indent = indent, |
michael@0 | 4521 | m, |
michael@0 | 4522 | s = scope, |
michael@0 | 4523 | t, |
michael@0 | 4524 | line, |
michael@0 | 4525 | d; |
michael@0 | 4526 | |
michael@0 | 4527 | inblock = ordinary; |
michael@0 | 4528 | |
michael@0 | 4529 | if (!ordinary || !state.option.funcscope) |
michael@0 | 4530 | scope = Object.create(scope); |
michael@0 | 4531 | |
michael@0 | 4532 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 4533 | t = state.tokens.next; |
michael@0 | 4534 | |
michael@0 | 4535 | var metrics = funct["(metrics)"]; |
michael@0 | 4536 | metrics.nestedBlockDepth += 1; |
michael@0 | 4537 | metrics.verifyMaxNestedBlockDepthPerFunction(); |
michael@0 | 4538 | |
michael@0 | 4539 | if (state.tokens.next.id === "{") { |
michael@0 | 4540 | advance("{"); |
michael@0 | 4541 | |
michael@0 | 4542 | // create a new block scope |
michael@0 | 4543 | funct["(blockscope)"].stack(); |
michael@0 | 4544 | |
michael@0 | 4545 | line = state.tokens.curr.line; |
michael@0 | 4546 | if (state.tokens.next.id !== "}") { |
michael@0 | 4547 | indent += state.option.indent; |
michael@0 | 4548 | while (!ordinary && state.tokens.next.from > indent) { |
michael@0 | 4549 | indent += state.option.indent; |
michael@0 | 4550 | } |
michael@0 | 4551 | |
michael@0 | 4552 | if (isfunc) { |
michael@0 | 4553 | m = {}; |
michael@0 | 4554 | for (d in state.directive) { |
michael@0 | 4555 | if (_.has(state.directive, d)) { |
michael@0 | 4556 | m[d] = state.directive[d]; |
michael@0 | 4557 | } |
michael@0 | 4558 | } |
michael@0 | 4559 | directives(); |
michael@0 | 4560 | |
michael@0 | 4561 | if (state.option.strict && funct["(context)"]["(global)"]) { |
michael@0 | 4562 | if (!m["use strict"] && !state.directive["use strict"]) { |
michael@0 | 4563 | warning("E007"); |
michael@0 | 4564 | } |
michael@0 | 4565 | } |
michael@0 | 4566 | } |
michael@0 | 4567 | |
michael@0 | 4568 | a = statements(line); |
michael@0 | 4569 | |
michael@0 | 4570 | metrics.statementCount += a.length; |
michael@0 | 4571 | |
michael@0 | 4572 | if (isfunc) { |
michael@0 | 4573 | state.directive = m; |
michael@0 | 4574 | } |
michael@0 | 4575 | |
michael@0 | 4576 | indent -= state.option.indent; |
michael@0 | 4577 | if (line !== state.tokens.next.line) { |
michael@0 | 4578 | indentation(); |
michael@0 | 4579 | } |
michael@0 | 4580 | } else if (line !== state.tokens.next.line) { |
michael@0 | 4581 | indentation(); |
michael@0 | 4582 | } |
michael@0 | 4583 | advance("}", t); |
michael@0 | 4584 | |
michael@0 | 4585 | funct["(blockscope)"].unstack(); |
michael@0 | 4586 | |
michael@0 | 4587 | indent = old_indent; |
michael@0 | 4588 | } else if (!ordinary) { |
michael@0 | 4589 | if (isfunc) { |
michael@0 | 4590 | m = {}; |
michael@0 | 4591 | if (stmt && !isfatarrow && !state.option.inMoz(true)) { |
michael@0 | 4592 | error("W118", state.tokens.curr, "function closure expressions"); |
michael@0 | 4593 | } |
michael@0 | 4594 | |
michael@0 | 4595 | if (!stmt) { |
michael@0 | 4596 | for (d in state.directive) { |
michael@0 | 4597 | if (_.has(state.directive, d)) { |
michael@0 | 4598 | m[d] = state.directive[d]; |
michael@0 | 4599 | } |
michael@0 | 4600 | } |
michael@0 | 4601 | } |
michael@0 | 4602 | expression(5); |
michael@0 | 4603 | |
michael@0 | 4604 | if (state.option.strict && funct["(context)"]["(global)"]) { |
michael@0 | 4605 | if (!m["use strict"] && !state.directive["use strict"]) { |
michael@0 | 4606 | warning("E007"); |
michael@0 | 4607 | } |
michael@0 | 4608 | } |
michael@0 | 4609 | } else { |
michael@0 | 4610 | error("E021", state.tokens.next, "{", state.tokens.next.value); |
michael@0 | 4611 | } |
michael@0 | 4612 | } else { |
michael@0 | 4613 | |
michael@0 | 4614 | // check to avoid let declaration not within a block |
michael@0 | 4615 | funct["(nolet)"] = true; |
michael@0 | 4616 | |
michael@0 | 4617 | if (!stmt || state.option.curly) { |
michael@0 | 4618 | warning("W116", state.tokens.next, "{", state.tokens.next.value); |
michael@0 | 4619 | } |
michael@0 | 4620 | |
michael@0 | 4621 | noreach = true; |
michael@0 | 4622 | indent += state.option.indent; |
michael@0 | 4623 | // test indentation only if statement is in new line |
michael@0 | 4624 | a = [statement(state.tokens.next.line === state.tokens.curr.line)]; |
michael@0 | 4625 | indent -= state.option.indent; |
michael@0 | 4626 | noreach = false; |
michael@0 | 4627 | |
michael@0 | 4628 | delete funct["(nolet)"]; |
michael@0 | 4629 | } |
michael@0 | 4630 | // If it is a "break" in switch case, don't clear and let it propagate out. |
michael@0 | 4631 | if (!(iscase && funct["(verb)"] === "break")) funct["(verb)"] = null; |
michael@0 | 4632 | |
michael@0 | 4633 | if (!ordinary || !state.option.funcscope) scope = s; |
michael@0 | 4634 | inblock = b; |
michael@0 | 4635 | if (ordinary && state.option.noempty && (!a || a.length === 0)) { |
michael@0 | 4636 | warning("W035"); |
michael@0 | 4637 | } |
michael@0 | 4638 | metrics.nestedBlockDepth -= 1; |
michael@0 | 4639 | return a; |
michael@0 | 4640 | } |
michael@0 | 4641 | |
michael@0 | 4642 | |
michael@0 | 4643 | function countMember(m) { |
michael@0 | 4644 | if (membersOnly && typeof membersOnly[m] !== "boolean") { |
michael@0 | 4645 | warning("W036", state.tokens.curr, m); |
michael@0 | 4646 | } |
michael@0 | 4647 | if (typeof member[m] === "number") { |
michael@0 | 4648 | member[m] += 1; |
michael@0 | 4649 | } else { |
michael@0 | 4650 | member[m] = 1; |
michael@0 | 4651 | } |
michael@0 | 4652 | } |
michael@0 | 4653 | |
michael@0 | 4654 | |
michael@0 | 4655 | function note_implied(tkn) { |
michael@0 | 4656 | var name = tkn.value, line = tkn.line, a = implied[name]; |
michael@0 | 4657 | if (typeof a === "function") { |
michael@0 | 4658 | a = false; |
michael@0 | 4659 | } |
michael@0 | 4660 | |
michael@0 | 4661 | if (!a) { |
michael@0 | 4662 | a = [line]; |
michael@0 | 4663 | implied[name] = a; |
michael@0 | 4664 | } else if (a[a.length - 1] !== line) { |
michael@0 | 4665 | a.push(line); |
michael@0 | 4666 | } |
michael@0 | 4667 | } |
michael@0 | 4668 | |
michael@0 | 4669 | |
michael@0 | 4670 | // Build the syntax table by declaring the syntactic elements of the language. |
michael@0 | 4671 | |
michael@0 | 4672 | type("(number)", function () { |
michael@0 | 4673 | return this; |
michael@0 | 4674 | }); |
michael@0 | 4675 | |
michael@0 | 4676 | type("(string)", function () { |
michael@0 | 4677 | return this; |
michael@0 | 4678 | }); |
michael@0 | 4679 | |
michael@0 | 4680 | state.syntax["(identifier)"] = { |
michael@0 | 4681 | type: "(identifier)", |
michael@0 | 4682 | lbp: 0, |
michael@0 | 4683 | identifier: true, |
michael@0 | 4684 | nud: function () { |
michael@0 | 4685 | var v = this.value, |
michael@0 | 4686 | s = scope[v], |
michael@0 | 4687 | f; |
michael@0 | 4688 | |
michael@0 | 4689 | if (typeof s === "function") { |
michael@0 | 4690 | // Protection against accidental inheritance. |
michael@0 | 4691 | s = undefined; |
michael@0 | 4692 | } else if (typeof s === "boolean") { |
michael@0 | 4693 | f = funct; |
michael@0 | 4694 | funct = functions[0]; |
michael@0 | 4695 | addlabel(v, "var"); |
michael@0 | 4696 | s = funct; |
michael@0 | 4697 | funct = f; |
michael@0 | 4698 | } |
michael@0 | 4699 | var block; |
michael@0 | 4700 | if (_.has(funct, "(blockscope)")) { |
michael@0 | 4701 | block = funct["(blockscope)"].getlabel(v); |
michael@0 | 4702 | } |
michael@0 | 4703 | |
michael@0 | 4704 | // The name is in scope and defined in the current function. |
michael@0 | 4705 | if (funct === s || block) { |
michael@0 | 4706 | // Change 'unused' to 'var', and reject labels. |
michael@0 | 4707 | // the name is in a block scope |
michael@0 | 4708 | switch (block ? block[v]["(type)"] : funct[v]) { |
michael@0 | 4709 | case "unused": |
michael@0 | 4710 | if (block) block[v]["(type)"] = "var"; |
michael@0 | 4711 | else funct[v] = "var"; |
michael@0 | 4712 | break; |
michael@0 | 4713 | case "unction": |
michael@0 | 4714 | if (block) block[v]["(type)"] = "function"; |
michael@0 | 4715 | else funct[v] = "function"; |
michael@0 | 4716 | this["function"] = true; |
michael@0 | 4717 | break; |
michael@0 | 4718 | case "function": |
michael@0 | 4719 | this["function"] = true; |
michael@0 | 4720 | break; |
michael@0 | 4721 | case "label": |
michael@0 | 4722 | warning("W037", state.tokens.curr, v); |
michael@0 | 4723 | break; |
michael@0 | 4724 | } |
michael@0 | 4725 | } else if (funct["(global)"]) { |
michael@0 | 4726 | // The name is not defined in the function. If we are in the global |
michael@0 | 4727 | // scope, then we have an undefined variable. |
michael@0 | 4728 | // |
michael@0 | 4729 | // Operators typeof and delete do not raise runtime errors even if |
michael@0 | 4730 | // the base object of a reference is null so no need to display warning |
michael@0 | 4731 | // if we're inside of typeof or delete. |
michael@0 | 4732 | |
michael@0 | 4733 | if (typeof predefined[v] !== "boolean") { |
michael@0 | 4734 | // Attempting to subscript a null reference will throw an |
michael@0 | 4735 | // error, even within the typeof and delete operators |
michael@0 | 4736 | if (!(anonname === "typeof" || anonname === "delete") || |
michael@0 | 4737 | (state.tokens.next && (state.tokens.next.value === "." || |
michael@0 | 4738 | state.tokens.next.value === "["))) { |
michael@0 | 4739 | |
michael@0 | 4740 | // if we're in a list comprehension, variables are declared |
michael@0 | 4741 | // locally and used before being defined. So we check |
michael@0 | 4742 | // the presence of the given variable in the comp array |
michael@0 | 4743 | // before declaring it undefined. |
michael@0 | 4744 | |
michael@0 | 4745 | if (!funct["(comparray)"].check(v)) { |
michael@0 | 4746 | isundef(funct, "W117", state.tokens.curr, v); |
michael@0 | 4747 | } |
michael@0 | 4748 | } |
michael@0 | 4749 | } |
michael@0 | 4750 | |
michael@0 | 4751 | note_implied(state.tokens.curr); |
michael@0 | 4752 | } else { |
michael@0 | 4753 | // If the name is already defined in the current |
michael@0 | 4754 | // function, but not as outer, then there is a scope error. |
michael@0 | 4755 | |
michael@0 | 4756 | switch (funct[v]) { |
michael@0 | 4757 | case "closure": |
michael@0 | 4758 | case "function": |
michael@0 | 4759 | case "var": |
michael@0 | 4760 | case "unused": |
michael@0 | 4761 | warning("W038", state.tokens.curr, v); |
michael@0 | 4762 | break; |
michael@0 | 4763 | case "label": |
michael@0 | 4764 | warning("W037", state.tokens.curr, v); |
michael@0 | 4765 | break; |
michael@0 | 4766 | case "outer": |
michael@0 | 4767 | case "global": |
michael@0 | 4768 | break; |
michael@0 | 4769 | default: |
michael@0 | 4770 | // If the name is defined in an outer function, make an outer entry, |
michael@0 | 4771 | // and if it was unused, make it var. |
michael@0 | 4772 | if (s === true) { |
michael@0 | 4773 | funct[v] = true; |
michael@0 | 4774 | } else if (s === null) { |
michael@0 | 4775 | warning("W039", state.tokens.curr, v); |
michael@0 | 4776 | note_implied(state.tokens.curr); |
michael@0 | 4777 | } else if (typeof s !== "object") { |
michael@0 | 4778 | // Operators typeof and delete do not raise runtime errors even |
michael@0 | 4779 | // if the base object of a reference is null so no need to |
michael@0 | 4780 | // |
michael@0 | 4781 | // display warning if we're inside of typeof or delete. |
michael@0 | 4782 | // Attempting to subscript a null reference will throw an |
michael@0 | 4783 | // error, even within the typeof and delete operators |
michael@0 | 4784 | if (!(anonname === "typeof" || anonname === "delete") || |
michael@0 | 4785 | (state.tokens.next && |
michael@0 | 4786 | (state.tokens.next.value === "." || state.tokens.next.value === "["))) { |
michael@0 | 4787 | |
michael@0 | 4788 | isundef(funct, "W117", state.tokens.curr, v); |
michael@0 | 4789 | } |
michael@0 | 4790 | funct[v] = true; |
michael@0 | 4791 | note_implied(state.tokens.curr); |
michael@0 | 4792 | } else { |
michael@0 | 4793 | switch (s[v]) { |
michael@0 | 4794 | case "function": |
michael@0 | 4795 | case "unction": |
michael@0 | 4796 | this["function"] = true; |
michael@0 | 4797 | s[v] = "closure"; |
michael@0 | 4798 | funct[v] = s["(global)"] ? "global" : "outer"; |
michael@0 | 4799 | break; |
michael@0 | 4800 | case "var": |
michael@0 | 4801 | case "unused": |
michael@0 | 4802 | s[v] = "closure"; |
michael@0 | 4803 | funct[v] = s["(global)"] ? "global" : "outer"; |
michael@0 | 4804 | break; |
michael@0 | 4805 | case "closure": |
michael@0 | 4806 | funct[v] = s["(global)"] ? "global" : "outer"; |
michael@0 | 4807 | break; |
michael@0 | 4808 | case "label": |
michael@0 | 4809 | warning("W037", state.tokens.curr, v); |
michael@0 | 4810 | } |
michael@0 | 4811 | } |
michael@0 | 4812 | } |
michael@0 | 4813 | } |
michael@0 | 4814 | return this; |
michael@0 | 4815 | }, |
michael@0 | 4816 | led: function () { |
michael@0 | 4817 | error("E033", state.tokens.next, state.tokens.next.value); |
michael@0 | 4818 | } |
michael@0 | 4819 | }; |
michael@0 | 4820 | |
michael@0 | 4821 | type("(regexp)", function () { |
michael@0 | 4822 | return this; |
michael@0 | 4823 | }); |
michael@0 | 4824 | |
michael@0 | 4825 | // ECMAScript parser |
michael@0 | 4826 | |
michael@0 | 4827 | delim("(endline)"); |
michael@0 | 4828 | delim("(begin)"); |
michael@0 | 4829 | delim("(end)").reach = true; |
michael@0 | 4830 | delim("(error)").reach = true; |
michael@0 | 4831 | delim("}").reach = true; |
michael@0 | 4832 | delim(")"); |
michael@0 | 4833 | delim("]"); |
michael@0 | 4834 | delim("\"").reach = true; |
michael@0 | 4835 | delim("'").reach = true; |
michael@0 | 4836 | delim(";"); |
michael@0 | 4837 | delim(":").reach = true; |
michael@0 | 4838 | delim("#"); |
michael@0 | 4839 | |
michael@0 | 4840 | reserve("else"); |
michael@0 | 4841 | reserve("case").reach = true; |
michael@0 | 4842 | reserve("catch"); |
michael@0 | 4843 | reserve("default").reach = true; |
michael@0 | 4844 | reserve("finally"); |
michael@0 | 4845 | reservevar("arguments", function (x) { |
michael@0 | 4846 | if (state.directive["use strict"] && funct["(global)"]) { |
michael@0 | 4847 | warning("E008", x); |
michael@0 | 4848 | } |
michael@0 | 4849 | }); |
michael@0 | 4850 | reservevar("eval"); |
michael@0 | 4851 | reservevar("false"); |
michael@0 | 4852 | reservevar("Infinity"); |
michael@0 | 4853 | reservevar("null"); |
michael@0 | 4854 | reservevar("this", function (x) { |
michael@0 | 4855 | if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] && |
michael@0 | 4856 | funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { |
michael@0 | 4857 | warning("W040", x); |
michael@0 | 4858 | } |
michael@0 | 4859 | }); |
michael@0 | 4860 | reservevar("true"); |
michael@0 | 4861 | reservevar("undefined"); |
michael@0 | 4862 | |
michael@0 | 4863 | assignop("=", "assign", 20); |
michael@0 | 4864 | assignop("+=", "assignadd", 20); |
michael@0 | 4865 | assignop("-=", "assignsub", 20); |
michael@0 | 4866 | assignop("*=", "assignmult", 20); |
michael@0 | 4867 | assignop("/=", "assigndiv", 20).nud = function () { |
michael@0 | 4868 | error("E014"); |
michael@0 | 4869 | }; |
michael@0 | 4870 | assignop("%=", "assignmod", 20); |
michael@0 | 4871 | |
michael@0 | 4872 | bitwiseassignop("&=", "assignbitand", 20); |
michael@0 | 4873 | bitwiseassignop("|=", "assignbitor", 20); |
michael@0 | 4874 | bitwiseassignop("^=", "assignbitxor", 20); |
michael@0 | 4875 | bitwiseassignop("<<=", "assignshiftleft", 20); |
michael@0 | 4876 | bitwiseassignop(">>=", "assignshiftright", 20); |
michael@0 | 4877 | bitwiseassignop(">>>=", "assignshiftrightunsigned", 20); |
michael@0 | 4878 | infix(",", function (left, that) { |
michael@0 | 4879 | var expr; |
michael@0 | 4880 | that.exprs = [left]; |
michael@0 | 4881 | if (!comma({peek: true})) { |
michael@0 | 4882 | return that; |
michael@0 | 4883 | } |
michael@0 | 4884 | while (true) { |
michael@0 | 4885 | if (!(expr = expression(5))) { |
michael@0 | 4886 | break; |
michael@0 | 4887 | } |
michael@0 | 4888 | that.exprs.push(expr); |
michael@0 | 4889 | if (state.tokens.next.value !== "," || !comma()) { |
michael@0 | 4890 | break; |
michael@0 | 4891 | } |
michael@0 | 4892 | } |
michael@0 | 4893 | return that; |
michael@0 | 4894 | }, 5, true); |
michael@0 | 4895 | infix("?", function (left, that) { |
michael@0 | 4896 | that.left = left; |
michael@0 | 4897 | that.right = expression(10); |
michael@0 | 4898 | advance(":"); |
michael@0 | 4899 | that["else"] = expression(10); |
michael@0 | 4900 | return that; |
michael@0 | 4901 | }, 30); |
michael@0 | 4902 | |
michael@0 | 4903 | infix("||", "or", 40); |
michael@0 | 4904 | infix("&&", "and", 50); |
michael@0 | 4905 | bitwise("|", "bitor", 70); |
michael@0 | 4906 | bitwise("^", "bitxor", 80); |
michael@0 | 4907 | bitwise("&", "bitand", 90); |
michael@0 | 4908 | relation("==", function (left, right) { |
michael@0 | 4909 | var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); |
michael@0 | 4910 | |
michael@0 | 4911 | if (!eqnull && state.option.eqeqeq) |
michael@0 | 4912 | warning("W116", this, "===", "=="); |
michael@0 | 4913 | else if (isPoorRelation(left)) |
michael@0 | 4914 | warning("W041", this, "===", left.value); |
michael@0 | 4915 | else if (isPoorRelation(right)) |
michael@0 | 4916 | warning("W041", this, "===", right.value); |
michael@0 | 4917 | |
michael@0 | 4918 | return this; |
michael@0 | 4919 | }); |
michael@0 | 4920 | relation("==="); |
michael@0 | 4921 | relation("!=", function (left, right) { |
michael@0 | 4922 | var eqnull = state.option.eqnull && |
michael@0 | 4923 | (left.value === "null" || right.value === "null"); |
michael@0 | 4924 | |
michael@0 | 4925 | if (!eqnull && state.option.eqeqeq) { |
michael@0 | 4926 | warning("W116", this, "!==", "!="); |
michael@0 | 4927 | } else if (isPoorRelation(left)) { |
michael@0 | 4928 | warning("W041", this, "!==", left.value); |
michael@0 | 4929 | } else if (isPoorRelation(right)) { |
michael@0 | 4930 | warning("W041", this, "!==", right.value); |
michael@0 | 4931 | } |
michael@0 | 4932 | return this; |
michael@0 | 4933 | }); |
michael@0 | 4934 | relation("!=="); |
michael@0 | 4935 | relation("<"); |
michael@0 | 4936 | relation(">"); |
michael@0 | 4937 | relation("<="); |
michael@0 | 4938 | relation(">="); |
michael@0 | 4939 | bitwise("<<", "shiftleft", 120); |
michael@0 | 4940 | bitwise(">>", "shiftright", 120); |
michael@0 | 4941 | bitwise(">>>", "shiftrightunsigned", 120); |
michael@0 | 4942 | infix("in", "in", 120); |
michael@0 | 4943 | infix("instanceof", "instanceof", 120); |
michael@0 | 4944 | infix("+", function (left, that) { |
michael@0 | 4945 | var right = expression(130); |
michael@0 | 4946 | if (left && right && left.id === "(string)" && right.id === "(string)") { |
michael@0 | 4947 | left.value += right.value; |
michael@0 | 4948 | left.character = right.character; |
michael@0 | 4949 | if (!state.option.scripturl && reg.javascriptURL.test(left.value)) { |
michael@0 | 4950 | warning("W050", left); |
michael@0 | 4951 | } |
michael@0 | 4952 | return left; |
michael@0 | 4953 | } |
michael@0 | 4954 | that.left = left; |
michael@0 | 4955 | that.right = right; |
michael@0 | 4956 | return that; |
michael@0 | 4957 | }, 130); |
michael@0 | 4958 | prefix("+", "num"); |
michael@0 | 4959 | prefix("+++", function () { |
michael@0 | 4960 | warning("W007"); |
michael@0 | 4961 | this.right = expression(150); |
michael@0 | 4962 | this.arity = "unary"; |
michael@0 | 4963 | return this; |
michael@0 | 4964 | }); |
michael@0 | 4965 | infix("+++", function (left) { |
michael@0 | 4966 | warning("W007"); |
michael@0 | 4967 | this.left = left; |
michael@0 | 4968 | this.right = expression(130); |
michael@0 | 4969 | return this; |
michael@0 | 4970 | }, 130); |
michael@0 | 4971 | infix("-", "sub", 130); |
michael@0 | 4972 | prefix("-", "neg"); |
michael@0 | 4973 | prefix("---", function () { |
michael@0 | 4974 | warning("W006"); |
michael@0 | 4975 | this.right = expression(150); |
michael@0 | 4976 | this.arity = "unary"; |
michael@0 | 4977 | return this; |
michael@0 | 4978 | }); |
michael@0 | 4979 | infix("---", function (left) { |
michael@0 | 4980 | warning("W006"); |
michael@0 | 4981 | this.left = left; |
michael@0 | 4982 | this.right = expression(130); |
michael@0 | 4983 | return this; |
michael@0 | 4984 | }, 130); |
michael@0 | 4985 | infix("*", "mult", 140); |
michael@0 | 4986 | infix("/", "div", 140); |
michael@0 | 4987 | infix("%", "mod", 140); |
michael@0 | 4988 | |
michael@0 | 4989 | suffix("++", "postinc"); |
michael@0 | 4990 | prefix("++", "preinc"); |
michael@0 | 4991 | state.syntax["++"].exps = true; |
michael@0 | 4992 | |
michael@0 | 4993 | suffix("--", "postdec"); |
michael@0 | 4994 | prefix("--", "predec"); |
michael@0 | 4995 | state.syntax["--"].exps = true; |
michael@0 | 4996 | prefix("delete", function () { |
michael@0 | 4997 | var p = expression(5); |
michael@0 | 4998 | if (!p || (p.id !== "." && p.id !== "[")) { |
michael@0 | 4999 | warning("W051"); |
michael@0 | 5000 | } |
michael@0 | 5001 | this.first = p; |
michael@0 | 5002 | return this; |
michael@0 | 5003 | }).exps = true; |
michael@0 | 5004 | |
michael@0 | 5005 | prefix("~", function () { |
michael@0 | 5006 | if (state.option.bitwise) { |
michael@0 | 5007 | warning("W052", this, "~"); |
michael@0 | 5008 | } |
michael@0 | 5009 | expression(150); |
michael@0 | 5010 | return this; |
michael@0 | 5011 | }); |
michael@0 | 5012 | |
michael@0 | 5013 | prefix("...", function () { |
michael@0 | 5014 | if (!state.option.inESNext()) { |
michael@0 | 5015 | warning("W104", this, "spread/rest operator"); |
michael@0 | 5016 | } |
michael@0 | 5017 | if (!state.tokens.next.identifier) { |
michael@0 | 5018 | error("E030", state.tokens.next, state.tokens.next.value); |
michael@0 | 5019 | } |
michael@0 | 5020 | expression(150); |
michael@0 | 5021 | return this; |
michael@0 | 5022 | }); |
michael@0 | 5023 | |
michael@0 | 5024 | prefix("!", function () { |
michael@0 | 5025 | this.right = expression(150); |
michael@0 | 5026 | this.arity = "unary"; |
michael@0 | 5027 | |
michael@0 | 5028 | if (!this.right) { // '!' followed by nothing? Give up. |
michael@0 | 5029 | quit("E041", this.line || 0); |
michael@0 | 5030 | } |
michael@0 | 5031 | |
michael@0 | 5032 | if (bang[this.right.id] === true) { |
michael@0 | 5033 | warning("W018", this, "!"); |
michael@0 | 5034 | } |
michael@0 | 5035 | return this; |
michael@0 | 5036 | }); |
michael@0 | 5037 | |
michael@0 | 5038 | prefix("typeof", "typeof"); |
michael@0 | 5039 | prefix("new", function () { |
michael@0 | 5040 | var c = expression(155), i; |
michael@0 | 5041 | if (c && c.id !== "function") { |
michael@0 | 5042 | if (c.identifier) { |
michael@0 | 5043 | c["new"] = true; |
michael@0 | 5044 | switch (c.value) { |
michael@0 | 5045 | case "Number": |
michael@0 | 5046 | case "String": |
michael@0 | 5047 | case "Boolean": |
michael@0 | 5048 | case "Math": |
michael@0 | 5049 | case "JSON": |
michael@0 | 5050 | warning("W053", state.tokens.prev, c.value); |
michael@0 | 5051 | break; |
michael@0 | 5052 | case "Function": |
michael@0 | 5053 | if (!state.option.evil) { |
michael@0 | 5054 | warning("W054"); |
michael@0 | 5055 | } |
michael@0 | 5056 | break; |
michael@0 | 5057 | case "Date": |
michael@0 | 5058 | case "RegExp": |
michael@0 | 5059 | break; |
michael@0 | 5060 | default: |
michael@0 | 5061 | if (c.id !== "function") { |
michael@0 | 5062 | i = c.value.substr(0, 1); |
michael@0 | 5063 | if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { |
michael@0 | 5064 | warning("W055", state.tokens.curr); |
michael@0 | 5065 | } |
michael@0 | 5066 | } |
michael@0 | 5067 | } |
michael@0 | 5068 | } else { |
michael@0 | 5069 | if (c.id !== "." && c.id !== "[" && c.id !== "(") { |
michael@0 | 5070 | warning("W056", state.tokens.curr); |
michael@0 | 5071 | } |
michael@0 | 5072 | } |
michael@0 | 5073 | } else { |
michael@0 | 5074 | if (!state.option.supernew) |
michael@0 | 5075 | warning("W057", this); |
michael@0 | 5076 | } |
michael@0 | 5077 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5078 | if (state.tokens.next.id !== "(" && !state.option.supernew) { |
michael@0 | 5079 | warning("W058", state.tokens.curr, state.tokens.curr.value); |
michael@0 | 5080 | } |
michael@0 | 5081 | this.first = c; |
michael@0 | 5082 | return this; |
michael@0 | 5083 | }); |
michael@0 | 5084 | state.syntax["new"].exps = true; |
michael@0 | 5085 | |
michael@0 | 5086 | prefix("void").exps = true; |
michael@0 | 5087 | |
michael@0 | 5088 | infix(".", function (left, that) { |
michael@0 | 5089 | adjacent(state.tokens.prev, state.tokens.curr); |
michael@0 | 5090 | nobreak(); |
michael@0 | 5091 | var m = identifier(false, true); |
michael@0 | 5092 | |
michael@0 | 5093 | if (typeof m === "string") { |
michael@0 | 5094 | countMember(m); |
michael@0 | 5095 | } |
michael@0 | 5096 | |
michael@0 | 5097 | that.left = left; |
michael@0 | 5098 | that.right = m; |
michael@0 | 5099 | |
michael@0 | 5100 | if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") { |
michael@0 | 5101 | warning("W001"); |
michael@0 | 5102 | } |
michael@0 | 5103 | |
michael@0 | 5104 | if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { |
michael@0 | 5105 | if (state.option.noarg) |
michael@0 | 5106 | warning("W059", left, m); |
michael@0 | 5107 | else if (state.directive["use strict"]) |
michael@0 | 5108 | error("E008"); |
michael@0 | 5109 | } else if (!state.option.evil && left && left.value === "document" && |
michael@0 | 5110 | (m === "write" || m === "writeln")) { |
michael@0 | 5111 | warning("W060", left); |
michael@0 | 5112 | } |
michael@0 | 5113 | |
michael@0 | 5114 | if (!state.option.evil && (m === "eval" || m === "execScript")) { |
michael@0 | 5115 | warning("W061"); |
michael@0 | 5116 | } |
michael@0 | 5117 | |
michael@0 | 5118 | return that; |
michael@0 | 5119 | }, 160, true); |
michael@0 | 5120 | |
michael@0 | 5121 | infix("(", function (left, that) { |
michael@0 | 5122 | if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") { |
michael@0 | 5123 | nobreak(state.tokens.prev, state.tokens.curr); |
michael@0 | 5124 | } |
michael@0 | 5125 | |
michael@0 | 5126 | nospace(); |
michael@0 | 5127 | if (state.option.immed && left && !left.immed && left.id === "function") { |
michael@0 | 5128 | warning("W062"); |
michael@0 | 5129 | } |
michael@0 | 5130 | |
michael@0 | 5131 | var n = 0; |
michael@0 | 5132 | var p = []; |
michael@0 | 5133 | |
michael@0 | 5134 | if (left) { |
michael@0 | 5135 | if (left.type === "(identifier)") { |
michael@0 | 5136 | if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { |
michael@0 | 5137 | if ("Number String Boolean Date Object".indexOf(left.value) === -1) { |
michael@0 | 5138 | if (left.value === "Math") { |
michael@0 | 5139 | warning("W063", left); |
michael@0 | 5140 | } else if (state.option.newcap) { |
michael@0 | 5141 | warning("W064", left); |
michael@0 | 5142 | } |
michael@0 | 5143 | } |
michael@0 | 5144 | } |
michael@0 | 5145 | } |
michael@0 | 5146 | } |
michael@0 | 5147 | |
michael@0 | 5148 | if (state.tokens.next.id !== ")") { |
michael@0 | 5149 | for (;;) { |
michael@0 | 5150 | p[p.length] = expression(10); |
michael@0 | 5151 | n += 1; |
michael@0 | 5152 | if (state.tokens.next.id !== ",") { |
michael@0 | 5153 | break; |
michael@0 | 5154 | } |
michael@0 | 5155 | comma(); |
michael@0 | 5156 | } |
michael@0 | 5157 | } |
michael@0 | 5158 | |
michael@0 | 5159 | advance(")"); |
michael@0 | 5160 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 5161 | |
michael@0 | 5162 | if (typeof left === "object") { |
michael@0 | 5163 | if (left.value === "parseInt" && n === 1) { |
michael@0 | 5164 | warning("W065", state.tokens.curr); |
michael@0 | 5165 | } |
michael@0 | 5166 | if (!state.option.evil) { |
michael@0 | 5167 | if (left.value === "eval" || left.value === "Function" || |
michael@0 | 5168 | left.value === "execScript") { |
michael@0 | 5169 | warning("W061", left); |
michael@0 | 5170 | |
michael@0 | 5171 | if (p[0] && [0].id === "(string)") { |
michael@0 | 5172 | addInternalSrc(left, p[0].value); |
michael@0 | 5173 | } |
michael@0 | 5174 | } else if (p[0] && p[0].id === "(string)" && |
michael@0 | 5175 | (left.value === "setTimeout" || |
michael@0 | 5176 | left.value === "setInterval")) { |
michael@0 | 5177 | warning("W066", left); |
michael@0 | 5178 | addInternalSrc(left, p[0].value); |
michael@0 | 5179 | |
michael@0 | 5180 | // window.setTimeout/setInterval |
michael@0 | 5181 | } else if (p[0] && p[0].id === "(string)" && |
michael@0 | 5182 | left.value === "." && |
michael@0 | 5183 | left.left.value === "window" && |
michael@0 | 5184 | (left.right === "setTimeout" || |
michael@0 | 5185 | left.right === "setInterval")) { |
michael@0 | 5186 | warning("W066", left); |
michael@0 | 5187 | addInternalSrc(left, p[0].value); |
michael@0 | 5188 | } |
michael@0 | 5189 | } |
michael@0 | 5190 | if (!left.identifier && left.id !== "." && left.id !== "[" && |
michael@0 | 5191 | left.id !== "(" && left.id !== "&&" && left.id !== "||" && |
michael@0 | 5192 | left.id !== "?") { |
michael@0 | 5193 | warning("W067", left); |
michael@0 | 5194 | } |
michael@0 | 5195 | } |
michael@0 | 5196 | |
michael@0 | 5197 | that.left = left; |
michael@0 | 5198 | return that; |
michael@0 | 5199 | }, 155, true).exps = true; |
michael@0 | 5200 | |
michael@0 | 5201 | prefix("(", function () { |
michael@0 | 5202 | nospace(); |
michael@0 | 5203 | var bracket, brackets = []; |
michael@0 | 5204 | var pn, pn1, i = 0; |
michael@0 | 5205 | |
michael@0 | 5206 | do { |
michael@0 | 5207 | pn = peek(i); |
michael@0 | 5208 | i += 1; |
michael@0 | 5209 | pn1 = peek(i); |
michael@0 | 5210 | i += 1; |
michael@0 | 5211 | } while (pn.value !== ")" && pn1.value !== "=>" && pn1.value !== ";" && pn1.type !== "(end)"); |
michael@0 | 5212 | |
michael@0 | 5213 | if (state.tokens.next.id === "function") { |
michael@0 | 5214 | state.tokens.next.immed = true; |
michael@0 | 5215 | } |
michael@0 | 5216 | |
michael@0 | 5217 | var exprs = []; |
michael@0 | 5218 | |
michael@0 | 5219 | if (state.tokens.next.id !== ")") { |
michael@0 | 5220 | for (;;) { |
michael@0 | 5221 | if (pn1.value === "=>" && state.tokens.next.value === "{") { |
michael@0 | 5222 | bracket = state.tokens.next; |
michael@0 | 5223 | bracket.left = destructuringExpression(); |
michael@0 | 5224 | brackets.push(bracket); |
michael@0 | 5225 | for (var t in bracket.left) { |
michael@0 | 5226 | exprs.push(bracket.left[t].token); |
michael@0 | 5227 | } |
michael@0 | 5228 | } else { |
michael@0 | 5229 | exprs.push(expression(5)); |
michael@0 | 5230 | } |
michael@0 | 5231 | if (state.tokens.next.id !== ",") { |
michael@0 | 5232 | break; |
michael@0 | 5233 | } |
michael@0 | 5234 | comma(); |
michael@0 | 5235 | } |
michael@0 | 5236 | } |
michael@0 | 5237 | |
michael@0 | 5238 | advance(")", this); |
michael@0 | 5239 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 5240 | if (state.option.immed && exprs[0] && exprs[0].id === "function") { |
michael@0 | 5241 | if (state.tokens.next.id !== "(" && |
michael@0 | 5242 | (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { |
michael@0 | 5243 | warning("W068", this); |
michael@0 | 5244 | } |
michael@0 | 5245 | } |
michael@0 | 5246 | |
michael@0 | 5247 | if (state.tokens.next.value === "=>") { |
michael@0 | 5248 | return exprs; |
michael@0 | 5249 | } |
michael@0 | 5250 | if (!exprs.length) { |
michael@0 | 5251 | return; |
michael@0 | 5252 | } |
michael@0 | 5253 | exprs[exprs.length - 1].paren = true; |
michael@0 | 5254 | if (exprs.length > 1) { |
michael@0 | 5255 | return Object.create(state.syntax[","], { exprs: { value: exprs } }); |
michael@0 | 5256 | } |
michael@0 | 5257 | return exprs[0]; |
michael@0 | 5258 | }); |
michael@0 | 5259 | |
michael@0 | 5260 | application("=>"); |
michael@0 | 5261 | |
michael@0 | 5262 | infix("[", function (left, that) { |
michael@0 | 5263 | nobreak(state.tokens.prev, state.tokens.curr); |
michael@0 | 5264 | nospace(); |
michael@0 | 5265 | var e = expression(5), s; |
michael@0 | 5266 | if (e && e.type === "(string)") { |
michael@0 | 5267 | if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { |
michael@0 | 5268 | warning("W061", that); |
michael@0 | 5269 | } |
michael@0 | 5270 | |
michael@0 | 5271 | countMember(e.value); |
michael@0 | 5272 | if (!state.option.sub && reg.identifier.test(e.value)) { |
michael@0 | 5273 | s = state.syntax[e.value]; |
michael@0 | 5274 | if (!s || !isReserved(s)) { |
michael@0 | 5275 | warning("W069", state.tokens.prev, e.value); |
michael@0 | 5276 | } |
michael@0 | 5277 | } |
michael@0 | 5278 | } |
michael@0 | 5279 | advance("]", that); |
michael@0 | 5280 | |
michael@0 | 5281 | if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") { |
michael@0 | 5282 | warning("W001"); |
michael@0 | 5283 | } |
michael@0 | 5284 | |
michael@0 | 5285 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 5286 | that.left = left; |
michael@0 | 5287 | that.right = e; |
michael@0 | 5288 | return that; |
michael@0 | 5289 | }, 160, true); |
michael@0 | 5290 | |
michael@0 | 5291 | function comprehensiveArrayExpression() { |
michael@0 | 5292 | var res = {}; |
michael@0 | 5293 | res.exps = true; |
michael@0 | 5294 | funct["(comparray)"].stack(); |
michael@0 | 5295 | |
michael@0 | 5296 | res.right = expression(5); |
michael@0 | 5297 | advance("for"); |
michael@0 | 5298 | if (state.tokens.next.value === "each") { |
michael@0 | 5299 | advance("each"); |
michael@0 | 5300 | if (!state.option.inMoz(true)) { |
michael@0 | 5301 | warning("W118", state.tokens.curr, "for each"); |
michael@0 | 5302 | } |
michael@0 | 5303 | } |
michael@0 | 5304 | advance("("); |
michael@0 | 5305 | funct["(comparray)"].setState("define"); |
michael@0 | 5306 | res.left = expression(5); |
michael@0 | 5307 | advance(")"); |
michael@0 | 5308 | if (state.tokens.next.value === "if") { |
michael@0 | 5309 | advance("if"); |
michael@0 | 5310 | advance("("); |
michael@0 | 5311 | funct["(comparray)"].setState("filter"); |
michael@0 | 5312 | res.filter = expression(5); |
michael@0 | 5313 | advance(")"); |
michael@0 | 5314 | } |
michael@0 | 5315 | advance("]"); |
michael@0 | 5316 | funct["(comparray)"].unstack(); |
michael@0 | 5317 | return res; |
michael@0 | 5318 | } |
michael@0 | 5319 | |
michael@0 | 5320 | prefix("[", function () { |
michael@0 | 5321 | var blocktype = lookupBlockType(true); |
michael@0 | 5322 | if (blocktype.isCompArray) { |
michael@0 | 5323 | if (!state.option.inMoz(true)) { |
michael@0 | 5324 | warning("W118", state.tokens.curr, "array comprehension"); |
michael@0 | 5325 | } |
michael@0 | 5326 | return comprehensiveArrayExpression(); |
michael@0 | 5327 | } else if (blocktype.isDestAssign && !state.option.inESNext()) { |
michael@0 | 5328 | warning("W104", state.tokens.curr, "destructuring assignment"); |
michael@0 | 5329 | } |
michael@0 | 5330 | var b = state.tokens.curr.line !== state.tokens.next.line; |
michael@0 | 5331 | this.first = []; |
michael@0 | 5332 | if (b) { |
michael@0 | 5333 | indent += state.option.indent; |
michael@0 | 5334 | if (state.tokens.next.from === indent + state.option.indent) { |
michael@0 | 5335 | indent += state.option.indent; |
michael@0 | 5336 | } |
michael@0 | 5337 | } |
michael@0 | 5338 | while (state.tokens.next.id !== "(end)") { |
michael@0 | 5339 | while (state.tokens.next.id === ",") { |
michael@0 | 5340 | if (!state.option.inES5()) |
michael@0 | 5341 | warning("W070"); |
michael@0 | 5342 | advance(","); |
michael@0 | 5343 | } |
michael@0 | 5344 | if (state.tokens.next.id === "]") { |
michael@0 | 5345 | break; |
michael@0 | 5346 | } |
michael@0 | 5347 | if (b && state.tokens.curr.line !== state.tokens.next.line) { |
michael@0 | 5348 | indentation(); |
michael@0 | 5349 | } |
michael@0 | 5350 | this.first.push(expression(10)); |
michael@0 | 5351 | if (state.tokens.next.id === ",") { |
michael@0 | 5352 | comma({ allowTrailing: true }); |
michael@0 | 5353 | if (state.tokens.next.id === "]" && !state.option.inES5(true)) { |
michael@0 | 5354 | warning("W070", state.tokens.curr); |
michael@0 | 5355 | break; |
michael@0 | 5356 | } |
michael@0 | 5357 | } else { |
michael@0 | 5358 | break; |
michael@0 | 5359 | } |
michael@0 | 5360 | } |
michael@0 | 5361 | if (b) { |
michael@0 | 5362 | indent -= state.option.indent; |
michael@0 | 5363 | indentation(); |
michael@0 | 5364 | } |
michael@0 | 5365 | advance("]", this); |
michael@0 | 5366 | return this; |
michael@0 | 5367 | }, 160); |
michael@0 | 5368 | |
michael@0 | 5369 | |
michael@0 | 5370 | function property_name() { |
michael@0 | 5371 | var id = optionalidentifier(false, true); |
michael@0 | 5372 | |
michael@0 | 5373 | if (!id) { |
michael@0 | 5374 | if (state.tokens.next.id === "(string)") { |
michael@0 | 5375 | id = state.tokens.next.value; |
michael@0 | 5376 | advance(); |
michael@0 | 5377 | } else if (state.tokens.next.id === "(number)") { |
michael@0 | 5378 | id = state.tokens.next.value.toString(); |
michael@0 | 5379 | advance(); |
michael@0 | 5380 | } |
michael@0 | 5381 | } |
michael@0 | 5382 | |
michael@0 | 5383 | if (id === "hasOwnProperty") { |
michael@0 | 5384 | warning("W001"); |
michael@0 | 5385 | } |
michael@0 | 5386 | |
michael@0 | 5387 | return id; |
michael@0 | 5388 | } |
michael@0 | 5389 | |
michael@0 | 5390 | |
michael@0 | 5391 | function functionparams(parsed) { |
michael@0 | 5392 | var curr, next; |
michael@0 | 5393 | var params = []; |
michael@0 | 5394 | var ident; |
michael@0 | 5395 | var tokens = []; |
michael@0 | 5396 | var t; |
michael@0 | 5397 | |
michael@0 | 5398 | if (parsed) { |
michael@0 | 5399 | if (parsed instanceof Array) { |
michael@0 | 5400 | for (var i in parsed) { |
michael@0 | 5401 | curr = parsed[i]; |
michael@0 | 5402 | if (_.contains(["{", "["], curr.id)) { |
michael@0 | 5403 | for (t in curr.left) { |
michael@0 | 5404 | t = tokens[t]; |
michael@0 | 5405 | if (t.id) { |
michael@0 | 5406 | params.push(t.id); |
michael@0 | 5407 | addlabel(t.id, "unused", t.token); |
michael@0 | 5408 | } |
michael@0 | 5409 | } |
michael@0 | 5410 | } else if (curr.value === "...") { |
michael@0 | 5411 | if (!state.option.inESNext()) { |
michael@0 | 5412 | warning("W104", curr, "spread/rest operator"); |
michael@0 | 5413 | } |
michael@0 | 5414 | continue; |
michael@0 | 5415 | } else { |
michael@0 | 5416 | addlabel(curr.value, "unused", curr); |
michael@0 | 5417 | } |
michael@0 | 5418 | } |
michael@0 | 5419 | return params; |
michael@0 | 5420 | } else { |
michael@0 | 5421 | if (parsed.identifier === true) { |
michael@0 | 5422 | addlabel(parsed.value, "unused", parsed); |
michael@0 | 5423 | return [parsed]; |
michael@0 | 5424 | } |
michael@0 | 5425 | } |
michael@0 | 5426 | } |
michael@0 | 5427 | |
michael@0 | 5428 | next = state.tokens.next; |
michael@0 | 5429 | |
michael@0 | 5430 | advance("("); |
michael@0 | 5431 | nospace(); |
michael@0 | 5432 | |
michael@0 | 5433 | if (state.tokens.next.id === ")") { |
michael@0 | 5434 | advance(")"); |
michael@0 | 5435 | return; |
michael@0 | 5436 | } |
michael@0 | 5437 | |
michael@0 | 5438 | for (;;) { |
michael@0 | 5439 | if (_.contains(["{", "["], state.tokens.next.id)) { |
michael@0 | 5440 | tokens = destructuringExpression(); |
michael@0 | 5441 | for (t in tokens) { |
michael@0 | 5442 | t = tokens[t]; |
michael@0 | 5443 | if (t.id) { |
michael@0 | 5444 | params.push(t.id); |
michael@0 | 5445 | addlabel(t.id, "unused", t.token); |
michael@0 | 5446 | } |
michael@0 | 5447 | } |
michael@0 | 5448 | } else if (state.tokens.next.value === "...") { |
michael@0 | 5449 | if (!state.option.inESNext()) { |
michael@0 | 5450 | warning("W104", state.tokens.next, "spread/rest operator"); |
michael@0 | 5451 | } |
michael@0 | 5452 | advance("..."); |
michael@0 | 5453 | nospace(); |
michael@0 | 5454 | ident = identifier(true); |
michael@0 | 5455 | params.push(ident); |
michael@0 | 5456 | addlabel(ident, "unused", state.tokens.curr); |
michael@0 | 5457 | } else { |
michael@0 | 5458 | ident = identifier(true); |
michael@0 | 5459 | params.push(ident); |
michael@0 | 5460 | addlabel(ident, "unused", state.tokens.curr); |
michael@0 | 5461 | } |
michael@0 | 5462 | if (state.tokens.next.id === ",") { |
michael@0 | 5463 | comma(); |
michael@0 | 5464 | } else { |
michael@0 | 5465 | advance(")", next); |
michael@0 | 5466 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 5467 | return params; |
michael@0 | 5468 | } |
michael@0 | 5469 | } |
michael@0 | 5470 | } |
michael@0 | 5471 | |
michael@0 | 5472 | |
michael@0 | 5473 | function doFunction(name, statement, generator, fatarrowparams) { |
michael@0 | 5474 | var f; |
michael@0 | 5475 | var oldOption = state.option; |
michael@0 | 5476 | var oldIgnored = state.ignored; |
michael@0 | 5477 | var oldScope = scope; |
michael@0 | 5478 | |
michael@0 | 5479 | state.option = Object.create(state.option); |
michael@0 | 5480 | state.ignored = Object.create(state.ignored); |
michael@0 | 5481 | scope = Object.create(scope); |
michael@0 | 5482 | |
michael@0 | 5483 | funct = { |
michael@0 | 5484 | "(name)" : name || "\"" + anonname + "\"", |
michael@0 | 5485 | "(line)" : state.tokens.next.line, |
michael@0 | 5486 | "(character)" : state.tokens.next.character, |
michael@0 | 5487 | "(context)" : funct, |
michael@0 | 5488 | "(breakage)" : 0, |
michael@0 | 5489 | "(loopage)" : 0, |
michael@0 | 5490 | "(metrics)" : createMetrics(state.tokens.next), |
michael@0 | 5491 | "(scope)" : scope, |
michael@0 | 5492 | "(statement)" : statement, |
michael@0 | 5493 | "(tokens)" : {}, |
michael@0 | 5494 | "(blockscope)": funct["(blockscope)"], |
michael@0 | 5495 | "(comparray)" : funct["(comparray)"] |
michael@0 | 5496 | }; |
michael@0 | 5497 | |
michael@0 | 5498 | if (generator) { |
michael@0 | 5499 | funct["(generator)"] = true; |
michael@0 | 5500 | } |
michael@0 | 5501 | |
michael@0 | 5502 | f = funct; |
michael@0 | 5503 | state.tokens.curr.funct = funct; |
michael@0 | 5504 | |
michael@0 | 5505 | functions.push(funct); |
michael@0 | 5506 | |
michael@0 | 5507 | if (name) { |
michael@0 | 5508 | addlabel(name, "function"); |
michael@0 | 5509 | } |
michael@0 | 5510 | |
michael@0 | 5511 | funct["(params)"] = functionparams(fatarrowparams); |
michael@0 | 5512 | |
michael@0 | 5513 | funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); |
michael@0 | 5514 | |
michael@0 | 5515 | block(false, true, true, fatarrowparams ? true:false); |
michael@0 | 5516 | |
michael@0 | 5517 | if (generator && funct["(generator)"] !== "yielded") { |
michael@0 | 5518 | error("E047", state.tokens.curr); |
michael@0 | 5519 | } |
michael@0 | 5520 | |
michael@0 | 5521 | funct["(metrics)"].verifyMaxStatementsPerFunction(); |
michael@0 | 5522 | funct["(metrics)"].verifyMaxComplexityPerFunction(); |
michael@0 | 5523 | funct["(unusedOption)"] = state.option.unused; |
michael@0 | 5524 | |
michael@0 | 5525 | scope = oldScope; |
michael@0 | 5526 | state.option = oldOption; |
michael@0 | 5527 | state.ignored = oldIgnored; |
michael@0 | 5528 | funct["(last)"] = state.tokens.curr.line; |
michael@0 | 5529 | funct["(lastcharacter)"] = state.tokens.curr.character; |
michael@0 | 5530 | funct = funct["(context)"]; |
michael@0 | 5531 | |
michael@0 | 5532 | return f; |
michael@0 | 5533 | } |
michael@0 | 5534 | |
michael@0 | 5535 | function createMetrics(functionStartToken) { |
michael@0 | 5536 | return { |
michael@0 | 5537 | statementCount: 0, |
michael@0 | 5538 | nestedBlockDepth: -1, |
michael@0 | 5539 | ComplexityCount: 1, |
michael@0 | 5540 | verifyMaxStatementsPerFunction: function () { |
michael@0 | 5541 | if (state.option.maxstatements && |
michael@0 | 5542 | this.statementCount > state.option.maxstatements) { |
michael@0 | 5543 | warning("W071", functionStartToken, this.statementCount); |
michael@0 | 5544 | } |
michael@0 | 5545 | }, |
michael@0 | 5546 | |
michael@0 | 5547 | verifyMaxParametersPerFunction: function (params) { |
michael@0 | 5548 | params = params || []; |
michael@0 | 5549 | |
michael@0 | 5550 | if (state.option.maxparams && params.length > state.option.maxparams) { |
michael@0 | 5551 | warning("W072", functionStartToken, params.length); |
michael@0 | 5552 | } |
michael@0 | 5553 | }, |
michael@0 | 5554 | |
michael@0 | 5555 | verifyMaxNestedBlockDepthPerFunction: function () { |
michael@0 | 5556 | if (state.option.maxdepth && |
michael@0 | 5557 | this.nestedBlockDepth > 0 && |
michael@0 | 5558 | this.nestedBlockDepth === state.option.maxdepth + 1) { |
michael@0 | 5559 | warning("W073", null, this.nestedBlockDepth); |
michael@0 | 5560 | } |
michael@0 | 5561 | }, |
michael@0 | 5562 | |
michael@0 | 5563 | verifyMaxComplexityPerFunction: function () { |
michael@0 | 5564 | var max = state.option.maxcomplexity; |
michael@0 | 5565 | var cc = this.ComplexityCount; |
michael@0 | 5566 | if (max && cc > max) { |
michael@0 | 5567 | warning("W074", functionStartToken, cc); |
michael@0 | 5568 | } |
michael@0 | 5569 | } |
michael@0 | 5570 | }; |
michael@0 | 5571 | } |
michael@0 | 5572 | |
michael@0 | 5573 | function increaseComplexityCount() { |
michael@0 | 5574 | funct["(metrics)"].ComplexityCount += 1; |
michael@0 | 5575 | } |
michael@0 | 5576 | |
michael@0 | 5577 | // Parse assignments that were found instead of conditionals. |
michael@0 | 5578 | // For example: if (a = 1) { ... } |
michael@0 | 5579 | |
michael@0 | 5580 | function checkCondAssignment(expr) { |
michael@0 | 5581 | var id = expr.id; |
michael@0 | 5582 | if (id === ",") { |
michael@0 | 5583 | expr = expr.exprs[expr.exprs.length - 1]; |
michael@0 | 5584 | id = expr.id; |
michael@0 | 5585 | } |
michael@0 | 5586 | switch (id) { |
michael@0 | 5587 | case "=": |
michael@0 | 5588 | case "+=": |
michael@0 | 5589 | case "-=": |
michael@0 | 5590 | case "*=": |
michael@0 | 5591 | case "%=": |
michael@0 | 5592 | case "&=": |
michael@0 | 5593 | case "|=": |
michael@0 | 5594 | case "^=": |
michael@0 | 5595 | case "/=": |
michael@0 | 5596 | if (!expr.paren && !state.option.boss) { |
michael@0 | 5597 | warning("W084"); |
michael@0 | 5598 | } |
michael@0 | 5599 | } |
michael@0 | 5600 | } |
michael@0 | 5601 | |
michael@0 | 5602 | |
michael@0 | 5603 | (function (x) { |
michael@0 | 5604 | x.nud = function (isclassdef) { |
michael@0 | 5605 | var b, f, i, p, t, g; |
michael@0 | 5606 | var props = {}; // All properties, including accessors |
michael@0 | 5607 | var tag = ""; |
michael@0 | 5608 | |
michael@0 | 5609 | function saveProperty(name, tkn) { |
michael@0 | 5610 | if (props[name] && _.has(props, name)) |
michael@0 | 5611 | warning("W075", state.tokens.next, i); |
michael@0 | 5612 | else |
michael@0 | 5613 | props[name] = {}; |
michael@0 | 5614 | |
michael@0 | 5615 | props[name].basic = true; |
michael@0 | 5616 | props[name].basictkn = tkn; |
michael@0 | 5617 | } |
michael@0 | 5618 | |
michael@0 | 5619 | function saveSetter(name, tkn) { |
michael@0 | 5620 | if (props[name] && _.has(props, name)) { |
michael@0 | 5621 | if (props[name].basic || props[name].setter) |
michael@0 | 5622 | warning("W075", state.tokens.next, i); |
michael@0 | 5623 | } else { |
michael@0 | 5624 | props[name] = {}; |
michael@0 | 5625 | } |
michael@0 | 5626 | |
michael@0 | 5627 | props[name].setter = true; |
michael@0 | 5628 | props[name].setterToken = tkn; |
michael@0 | 5629 | } |
michael@0 | 5630 | |
michael@0 | 5631 | function saveGetter(name) { |
michael@0 | 5632 | if (props[name] && _.has(props, name)) { |
michael@0 | 5633 | if (props[name].basic || props[name].getter) |
michael@0 | 5634 | warning("W075", state.tokens.next, i); |
michael@0 | 5635 | } else { |
michael@0 | 5636 | props[name] = {}; |
michael@0 | 5637 | } |
michael@0 | 5638 | |
michael@0 | 5639 | props[name].getter = true; |
michael@0 | 5640 | props[name].getterToken = state.tokens.curr; |
michael@0 | 5641 | } |
michael@0 | 5642 | |
michael@0 | 5643 | b = state.tokens.curr.line !== state.tokens.next.line; |
michael@0 | 5644 | if (b) { |
michael@0 | 5645 | indent += state.option.indent; |
michael@0 | 5646 | if (state.tokens.next.from === indent + state.option.indent) { |
michael@0 | 5647 | indent += state.option.indent; |
michael@0 | 5648 | } |
michael@0 | 5649 | } |
michael@0 | 5650 | |
michael@0 | 5651 | for (;;) { |
michael@0 | 5652 | if (state.tokens.next.id === "}") { |
michael@0 | 5653 | break; |
michael@0 | 5654 | } |
michael@0 | 5655 | |
michael@0 | 5656 | if (b) { |
michael@0 | 5657 | indentation(); |
michael@0 | 5658 | } |
michael@0 | 5659 | |
michael@0 | 5660 | if (isclassdef && state.tokens.next.value === "static") { |
michael@0 | 5661 | advance("static"); |
michael@0 | 5662 | tag = "static "; |
michael@0 | 5663 | } |
michael@0 | 5664 | |
michael@0 | 5665 | if (state.tokens.next.value === "get" && peek().id !== ":") { |
michael@0 | 5666 | advance("get"); |
michael@0 | 5667 | |
michael@0 | 5668 | if (!state.option.inES5(!isclassdef)) { |
michael@0 | 5669 | error("E034"); |
michael@0 | 5670 | } |
michael@0 | 5671 | |
michael@0 | 5672 | i = property_name(); |
michael@0 | 5673 | if (!i) { |
michael@0 | 5674 | error("E035"); |
michael@0 | 5675 | } |
michael@0 | 5676 | |
michael@0 | 5677 | // It is a Syntax Error if PropName of MethodDefinition is |
michael@0 | 5678 | // "constructor" and SpecialMethod of MethodDefinition is true. |
michael@0 | 5679 | if (isclassdef && i === "constructor") { |
michael@0 | 5680 | error("E049", state.tokens.next, "class getter method", i); |
michael@0 | 5681 | } |
michael@0 | 5682 | |
michael@0 | 5683 | saveGetter(tag + i); |
michael@0 | 5684 | t = state.tokens.next; |
michael@0 | 5685 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5686 | f = doFunction(); |
michael@0 | 5687 | p = f["(params)"]; |
michael@0 | 5688 | |
michael@0 | 5689 | if (p) { |
michael@0 | 5690 | warning("W076", t, p[0], i); |
michael@0 | 5691 | } |
michael@0 | 5692 | |
michael@0 | 5693 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5694 | } else if (state.tokens.next.value === "set" && peek().id !== ":") { |
michael@0 | 5695 | advance("set"); |
michael@0 | 5696 | |
michael@0 | 5697 | if (!state.option.inES5(!isclassdef)) { |
michael@0 | 5698 | error("E034"); |
michael@0 | 5699 | } |
michael@0 | 5700 | |
michael@0 | 5701 | i = property_name(); |
michael@0 | 5702 | if (!i) { |
michael@0 | 5703 | error("E035"); |
michael@0 | 5704 | } |
michael@0 | 5705 | |
michael@0 | 5706 | // It is a Syntax Error if PropName of MethodDefinition is |
michael@0 | 5707 | // "constructor" and SpecialMethod of MethodDefinition is true. |
michael@0 | 5708 | if (isclassdef && i === "constructor") { |
michael@0 | 5709 | error("E049", state.tokens.next, "class setter method", i); |
michael@0 | 5710 | } |
michael@0 | 5711 | |
michael@0 | 5712 | saveSetter(tag + i, state.tokens.next); |
michael@0 | 5713 | t = state.tokens.next; |
michael@0 | 5714 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5715 | f = doFunction(); |
michael@0 | 5716 | p = f["(params)"]; |
michael@0 | 5717 | |
michael@0 | 5718 | if (!p || p.length !== 1) { |
michael@0 | 5719 | warning("W077", t, i); |
michael@0 | 5720 | } |
michael@0 | 5721 | } else { |
michael@0 | 5722 | g = false; |
michael@0 | 5723 | if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { |
michael@0 | 5724 | if (!state.option.inESNext()) { |
michael@0 | 5725 | warning("W104", state.tokens.next, "generator functions"); |
michael@0 | 5726 | } |
michael@0 | 5727 | advance("*"); |
michael@0 | 5728 | g = true; |
michael@0 | 5729 | } |
michael@0 | 5730 | i = property_name(); |
michael@0 | 5731 | saveProperty(tag + i, state.tokens.next); |
michael@0 | 5732 | |
michael@0 | 5733 | if (typeof i !== "string") { |
michael@0 | 5734 | break; |
michael@0 | 5735 | } |
michael@0 | 5736 | |
michael@0 | 5737 | if (state.tokens.next.value === "(") { |
michael@0 | 5738 | if (!state.option.inESNext()) { |
michael@0 | 5739 | warning("W104", state.tokens.curr, "concise methods"); |
michael@0 | 5740 | } |
michael@0 | 5741 | doFunction(i, undefined, g); |
michael@0 | 5742 | } else if (!isclassdef) { |
michael@0 | 5743 | advance(":"); |
michael@0 | 5744 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5745 | expression(10); |
michael@0 | 5746 | } |
michael@0 | 5747 | } |
michael@0 | 5748 | // It is a Syntax Error if PropName of MethodDefinition is "prototype". |
michael@0 | 5749 | if (isclassdef && i === "prototype") { |
michael@0 | 5750 | error("E049", state.tokens.next, "class method", i); |
michael@0 | 5751 | } |
michael@0 | 5752 | |
michael@0 | 5753 | countMember(i); |
michael@0 | 5754 | if (isclassdef) { |
michael@0 | 5755 | tag = ""; |
michael@0 | 5756 | continue; |
michael@0 | 5757 | } |
michael@0 | 5758 | if (state.tokens.next.id === ",") { |
michael@0 | 5759 | comma({ allowTrailing: true, property: true }); |
michael@0 | 5760 | if (state.tokens.next.id === ",") { |
michael@0 | 5761 | warning("W070", state.tokens.curr); |
michael@0 | 5762 | } else if (state.tokens.next.id === "}" && !state.option.inES5(true)) { |
michael@0 | 5763 | warning("W070", state.tokens.curr); |
michael@0 | 5764 | } |
michael@0 | 5765 | } else { |
michael@0 | 5766 | break; |
michael@0 | 5767 | } |
michael@0 | 5768 | } |
michael@0 | 5769 | if (b) { |
michael@0 | 5770 | indent -= state.option.indent; |
michael@0 | 5771 | indentation(); |
michael@0 | 5772 | } |
michael@0 | 5773 | advance("}", this); |
michael@0 | 5774 | |
michael@0 | 5775 | // Check for lonely setters if in the ES5 mode. |
michael@0 | 5776 | if (state.option.inES5()) { |
michael@0 | 5777 | for (var name in props) { |
michael@0 | 5778 | if (_.has(props, name) && props[name].setter && !props[name].getter) { |
michael@0 | 5779 | warning("W078", props[name].setterToken); |
michael@0 | 5780 | } |
michael@0 | 5781 | } |
michael@0 | 5782 | } |
michael@0 | 5783 | return this; |
michael@0 | 5784 | }; |
michael@0 | 5785 | x.fud = function () { |
michael@0 | 5786 | error("E036", state.tokens.curr); |
michael@0 | 5787 | }; |
michael@0 | 5788 | }(delim("{"))); |
michael@0 | 5789 | |
michael@0 | 5790 | function destructuringExpression() { |
michael@0 | 5791 | var id, ids; |
michael@0 | 5792 | var identifiers = []; |
michael@0 | 5793 | if (!state.option.inESNext()) { |
michael@0 | 5794 | warning("W104", state.tokens.curr, "destructuring expression"); |
michael@0 | 5795 | } |
michael@0 | 5796 | var nextInnerDE = function () { |
michael@0 | 5797 | var ident; |
michael@0 | 5798 | if (_.contains(["[", "{"], state.tokens.next.value)) { |
michael@0 | 5799 | ids = destructuringExpression(); |
michael@0 | 5800 | for (var id in ids) { |
michael@0 | 5801 | id = ids[id]; |
michael@0 | 5802 | identifiers.push({ id: id.id, token: id.token }); |
michael@0 | 5803 | } |
michael@0 | 5804 | } else if (state.tokens.next.value === ",") { |
michael@0 | 5805 | identifiers.push({ id: null, token: state.tokens.curr }); |
michael@0 | 5806 | } else { |
michael@0 | 5807 | ident = identifier(); |
michael@0 | 5808 | if (ident) |
michael@0 | 5809 | identifiers.push({ id: ident, token: state.tokens.curr }); |
michael@0 | 5810 | } |
michael@0 | 5811 | }; |
michael@0 | 5812 | if (state.tokens.next.value === "[") { |
michael@0 | 5813 | advance("["); |
michael@0 | 5814 | nextInnerDE(); |
michael@0 | 5815 | while (state.tokens.next.value !== "]") { |
michael@0 | 5816 | advance(","); |
michael@0 | 5817 | nextInnerDE(); |
michael@0 | 5818 | } |
michael@0 | 5819 | advance("]"); |
michael@0 | 5820 | } else if (state.tokens.next.value === "{") { |
michael@0 | 5821 | advance("{"); |
michael@0 | 5822 | id = identifier(); |
michael@0 | 5823 | if (state.tokens.next.value === ":") { |
michael@0 | 5824 | advance(":"); |
michael@0 | 5825 | nextInnerDE(); |
michael@0 | 5826 | } else { |
michael@0 | 5827 | identifiers.push({ id: id, token: state.tokens.curr }); |
michael@0 | 5828 | } |
michael@0 | 5829 | while (state.tokens.next.value !== "}") { |
michael@0 | 5830 | advance(","); |
michael@0 | 5831 | id = identifier(); |
michael@0 | 5832 | if (state.tokens.next.value === ":") { |
michael@0 | 5833 | advance(":"); |
michael@0 | 5834 | nextInnerDE(); |
michael@0 | 5835 | } else { |
michael@0 | 5836 | identifiers.push({ id: id, token: state.tokens.curr }); |
michael@0 | 5837 | } |
michael@0 | 5838 | } |
michael@0 | 5839 | advance("}"); |
michael@0 | 5840 | } |
michael@0 | 5841 | return identifiers; |
michael@0 | 5842 | } |
michael@0 | 5843 | function destructuringExpressionMatch(tokens, value) { |
michael@0 | 5844 | if (value.first) { |
michael@0 | 5845 | _.zip(tokens, value.first).forEach(function (val) { |
michael@0 | 5846 | var token = val[0]; |
michael@0 | 5847 | var value = val[1]; |
michael@0 | 5848 | if (token && value) { |
michael@0 | 5849 | token.first = value; |
michael@0 | 5850 | } else if (token && token.first && !value) { |
michael@0 | 5851 | warning("W080", token.first, token.first.value); |
michael@0 | 5852 | } /* else { |
michael@0 | 5853 | XXX value is discarded: wouldn't it need a warning ? |
michael@0 | 5854 | } */ |
michael@0 | 5855 | }); |
michael@0 | 5856 | } |
michael@0 | 5857 | } |
michael@0 | 5858 | |
michael@0 | 5859 | var conststatement = stmt("const", function (prefix) { |
michael@0 | 5860 | var tokens, value; |
michael@0 | 5861 | // state variable to know if it is a lone identifier, or a destructuring statement. |
michael@0 | 5862 | var lone; |
michael@0 | 5863 | |
michael@0 | 5864 | if (!state.option.inESNext()) { |
michael@0 | 5865 | warning("W104", state.tokens.curr, "const"); |
michael@0 | 5866 | } |
michael@0 | 5867 | |
michael@0 | 5868 | this.first = []; |
michael@0 | 5869 | for (;;) { |
michael@0 | 5870 | var names = []; |
michael@0 | 5871 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5872 | if (_.contains(["{", "["], state.tokens.next.value)) { |
michael@0 | 5873 | tokens = destructuringExpression(); |
michael@0 | 5874 | lone = false; |
michael@0 | 5875 | } else { |
michael@0 | 5876 | tokens = [ { id: identifier(), token: state.tokens.curr } ]; |
michael@0 | 5877 | lone = true; |
michael@0 | 5878 | } |
michael@0 | 5879 | for (var t in tokens) { |
michael@0 | 5880 | t = tokens[t]; |
michael@0 | 5881 | if (funct[t.id] === "const") { |
michael@0 | 5882 | warning("E011", null, t.id); |
michael@0 | 5883 | } |
michael@0 | 5884 | if (funct["(global)"] && predefined[t.id] === false) { |
michael@0 | 5885 | warning("W079", t.token, t.id); |
michael@0 | 5886 | } |
michael@0 | 5887 | if (t.id) { |
michael@0 | 5888 | addlabel(t.id, "const"); |
michael@0 | 5889 | names.push(t.token); |
michael@0 | 5890 | } |
michael@0 | 5891 | } |
michael@0 | 5892 | if (prefix) { |
michael@0 | 5893 | break; |
michael@0 | 5894 | } |
michael@0 | 5895 | |
michael@0 | 5896 | this.first = this.first.concat(names); |
michael@0 | 5897 | |
michael@0 | 5898 | if (state.tokens.next.id !== "=") { |
michael@0 | 5899 | warning("E012", state.tokens.curr, state.tokens.curr.value); |
michael@0 | 5900 | } |
michael@0 | 5901 | |
michael@0 | 5902 | if (state.tokens.next.id === "=") { |
michael@0 | 5903 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5904 | advance("="); |
michael@0 | 5905 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5906 | if (state.tokens.next.id === "undefined") { |
michael@0 | 5907 | warning("W080", state.tokens.prev, state.tokens.prev.value); |
michael@0 | 5908 | } |
michael@0 | 5909 | if (peek(0).id === "=" && state.tokens.next.identifier) { |
michael@0 | 5910 | error("E037", state.tokens.next, state.tokens.next.value); |
michael@0 | 5911 | } |
michael@0 | 5912 | value = expression(5); |
michael@0 | 5913 | if (lone) { |
michael@0 | 5914 | tokens[0].first = value; |
michael@0 | 5915 | } else { |
michael@0 | 5916 | destructuringExpressionMatch(names, value); |
michael@0 | 5917 | } |
michael@0 | 5918 | } |
michael@0 | 5919 | |
michael@0 | 5920 | if (state.tokens.next.id !== ",") { |
michael@0 | 5921 | break; |
michael@0 | 5922 | } |
michael@0 | 5923 | comma(); |
michael@0 | 5924 | } |
michael@0 | 5925 | return this; |
michael@0 | 5926 | }); |
michael@0 | 5927 | conststatement.exps = true; |
michael@0 | 5928 | var varstatement = stmt("var", function (prefix) { |
michael@0 | 5929 | // JavaScript does not have block scope. It only has function scope. So, |
michael@0 | 5930 | // declaring a variable in a block can have unexpected consequences. |
michael@0 | 5931 | var tokens, lone, value; |
michael@0 | 5932 | |
michael@0 | 5933 | if (funct["(onevar)"] && state.option.onevar) { |
michael@0 | 5934 | warning("W081"); |
michael@0 | 5935 | } else if (!funct["(global)"]) { |
michael@0 | 5936 | funct["(onevar)"] = true; |
michael@0 | 5937 | } |
michael@0 | 5938 | |
michael@0 | 5939 | this.first = []; |
michael@0 | 5940 | for (;;) { |
michael@0 | 5941 | var names = []; |
michael@0 | 5942 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5943 | if (_.contains(["{", "["], state.tokens.next.value)) { |
michael@0 | 5944 | tokens = destructuringExpression(); |
michael@0 | 5945 | lone = false; |
michael@0 | 5946 | } else { |
michael@0 | 5947 | tokens = [ { id: identifier(), token: state.tokens.curr } ]; |
michael@0 | 5948 | lone = true; |
michael@0 | 5949 | } |
michael@0 | 5950 | for (var t in tokens) { |
michael@0 | 5951 | t = tokens[t]; |
michael@0 | 5952 | if (state.option.inESNext() && funct[t.id] === "const") { |
michael@0 | 5953 | warning("E011", null, t.id); |
michael@0 | 5954 | } |
michael@0 | 5955 | if (funct["(global)"] && predefined[t.id] === false) { |
michael@0 | 5956 | warning("W079", t.token, t.id); |
michael@0 | 5957 | } |
michael@0 | 5958 | if (t.id) { |
michael@0 | 5959 | addlabel(t.id, "unused", t.token); |
michael@0 | 5960 | names.push(t.token); |
michael@0 | 5961 | } |
michael@0 | 5962 | } |
michael@0 | 5963 | if (prefix) { |
michael@0 | 5964 | break; |
michael@0 | 5965 | } |
michael@0 | 5966 | |
michael@0 | 5967 | this.first = this.first.concat(names); |
michael@0 | 5968 | |
michael@0 | 5969 | if (state.tokens.next.id === "=") { |
michael@0 | 5970 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5971 | advance("="); |
michael@0 | 5972 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 5973 | if (state.tokens.next.id === "undefined") { |
michael@0 | 5974 | warning("W080", state.tokens.prev, state.tokens.prev.value); |
michael@0 | 5975 | } |
michael@0 | 5976 | if (peek(0).id === "=" && state.tokens.next.identifier) { |
michael@0 | 5977 | error("E038", state.tokens.next, state.tokens.next.value); |
michael@0 | 5978 | } |
michael@0 | 5979 | value = expression(5); |
michael@0 | 5980 | if (lone) { |
michael@0 | 5981 | tokens[0].first = value; |
michael@0 | 5982 | } else { |
michael@0 | 5983 | destructuringExpressionMatch(names, value); |
michael@0 | 5984 | } |
michael@0 | 5985 | } |
michael@0 | 5986 | |
michael@0 | 5987 | if (state.tokens.next.id !== ",") { |
michael@0 | 5988 | break; |
michael@0 | 5989 | } |
michael@0 | 5990 | comma(); |
michael@0 | 5991 | } |
michael@0 | 5992 | return this; |
michael@0 | 5993 | }); |
michael@0 | 5994 | varstatement.exps = true; |
michael@0 | 5995 | var letstatement = stmt("let", function (prefix) { |
michael@0 | 5996 | var tokens, lone, value, letblock; |
michael@0 | 5997 | |
michael@0 | 5998 | if (!state.option.inESNext()) { |
michael@0 | 5999 | warning("W104", state.tokens.curr, "let"); |
michael@0 | 6000 | } |
michael@0 | 6001 | |
michael@0 | 6002 | if (state.tokens.next.value === "(") { |
michael@0 | 6003 | if (!state.option.inMoz(true)) { |
michael@0 | 6004 | warning("W118", state.tokens.next, "let block"); |
michael@0 | 6005 | } |
michael@0 | 6006 | advance("("); |
michael@0 | 6007 | funct["(blockscope)"].stack(); |
michael@0 | 6008 | letblock = true; |
michael@0 | 6009 | } else if (funct["(nolet)"]) { |
michael@0 | 6010 | error("E048", state.tokens.curr); |
michael@0 | 6011 | } |
michael@0 | 6012 | |
michael@0 | 6013 | if (funct["(onevar)"] && state.option.onevar) { |
michael@0 | 6014 | warning("W081"); |
michael@0 | 6015 | } else if (!funct["(global)"]) { |
michael@0 | 6016 | funct["(onevar)"] = true; |
michael@0 | 6017 | } |
michael@0 | 6018 | |
michael@0 | 6019 | this.first = []; |
michael@0 | 6020 | for (;;) { |
michael@0 | 6021 | var names = []; |
michael@0 | 6022 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6023 | if (_.contains(["{", "["], state.tokens.next.value)) { |
michael@0 | 6024 | tokens = destructuringExpression(); |
michael@0 | 6025 | lone = false; |
michael@0 | 6026 | } else { |
michael@0 | 6027 | tokens = [ { id: identifier(), token: state.tokens.curr.value } ]; |
michael@0 | 6028 | lone = true; |
michael@0 | 6029 | } |
michael@0 | 6030 | for (var t in tokens) { |
michael@0 | 6031 | t = tokens[t]; |
michael@0 | 6032 | if (state.option.inESNext() && funct[t.id] === "const") { |
michael@0 | 6033 | warning("E011", null, t.id); |
michael@0 | 6034 | } |
michael@0 | 6035 | if (funct["(global)"] && predefined[t.id] === false) { |
michael@0 | 6036 | warning("W079", t.token, t.id); |
michael@0 | 6037 | } |
michael@0 | 6038 | if (t.id && !funct["(nolet)"]) { |
michael@0 | 6039 | addlabel(t.id, "unused", t.token, true); |
michael@0 | 6040 | names.push(t.token); |
michael@0 | 6041 | } |
michael@0 | 6042 | } |
michael@0 | 6043 | if (prefix) { |
michael@0 | 6044 | break; |
michael@0 | 6045 | } |
michael@0 | 6046 | |
michael@0 | 6047 | this.first = this.first.concat(names); |
michael@0 | 6048 | |
michael@0 | 6049 | if (state.tokens.next.id === "=") { |
michael@0 | 6050 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6051 | advance("="); |
michael@0 | 6052 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6053 | if (state.tokens.next.id === "undefined") { |
michael@0 | 6054 | warning("W080", state.tokens.prev, state.tokens.prev.value); |
michael@0 | 6055 | } |
michael@0 | 6056 | if (peek(0).id === "=" && state.tokens.next.identifier) { |
michael@0 | 6057 | error("E037", state.tokens.next, state.tokens.next.value); |
michael@0 | 6058 | } |
michael@0 | 6059 | value = expression(5); |
michael@0 | 6060 | if (lone) { |
michael@0 | 6061 | tokens[0].first = value; |
michael@0 | 6062 | } else { |
michael@0 | 6063 | destructuringExpressionMatch(names, value); |
michael@0 | 6064 | } |
michael@0 | 6065 | } |
michael@0 | 6066 | |
michael@0 | 6067 | if (state.tokens.next.id !== ",") { |
michael@0 | 6068 | break; |
michael@0 | 6069 | } |
michael@0 | 6070 | comma(); |
michael@0 | 6071 | } |
michael@0 | 6072 | if (letblock) { |
michael@0 | 6073 | advance(")"); |
michael@0 | 6074 | block(true, true); |
michael@0 | 6075 | this.block = true; |
michael@0 | 6076 | funct["(blockscope)"].unstack(); |
michael@0 | 6077 | } |
michael@0 | 6078 | |
michael@0 | 6079 | return this; |
michael@0 | 6080 | }); |
michael@0 | 6081 | letstatement.exps = true; |
michael@0 | 6082 | |
michael@0 | 6083 | blockstmt("class", function () { |
michael@0 | 6084 | return classdef.call(this, true); |
michael@0 | 6085 | }); |
michael@0 | 6086 | |
michael@0 | 6087 | function classdef(stmt) { |
michael@0 | 6088 | /*jshint validthis:true */ |
michael@0 | 6089 | if (!state.option.inESNext()) { |
michael@0 | 6090 | warning("W104", state.tokens.curr, "class"); |
michael@0 | 6091 | } |
michael@0 | 6092 | if (stmt) { |
michael@0 | 6093 | // BindingIdentifier |
michael@0 | 6094 | this.name = identifier(); |
michael@0 | 6095 | addlabel(this.name, "unused", state.tokens.curr); |
michael@0 | 6096 | } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { |
michael@0 | 6097 | // BindingIdentifier(opt) |
michael@0 | 6098 | this.name = identifier(); |
michael@0 | 6099 | } |
michael@0 | 6100 | classtail(this); |
michael@0 | 6101 | return this; |
michael@0 | 6102 | } |
michael@0 | 6103 | |
michael@0 | 6104 | function classtail(c) { |
michael@0 | 6105 | var strictness = state.directive["use strict"]; |
michael@0 | 6106 | |
michael@0 | 6107 | // ClassHeritage(opt) |
michael@0 | 6108 | if (state.tokens.next.value === "extends") { |
michael@0 | 6109 | advance("extends"); |
michael@0 | 6110 | c.heritage = expression(10); |
michael@0 | 6111 | } |
michael@0 | 6112 | |
michael@0 | 6113 | // A ClassBody is always strict code. |
michael@0 | 6114 | state.directive["use strict"] = true; |
michael@0 | 6115 | advance("{"); |
michael@0 | 6116 | // ClassBody(opt) |
michael@0 | 6117 | c.body = state.syntax["{"].nud(true); |
michael@0 | 6118 | state.directive["use strict"] = strictness; |
michael@0 | 6119 | } |
michael@0 | 6120 | |
michael@0 | 6121 | blockstmt("function", function () { |
michael@0 | 6122 | var generator = false; |
michael@0 | 6123 | if (state.tokens.next.value === "*") { |
michael@0 | 6124 | advance("*"); |
michael@0 | 6125 | if (state.option.inESNext(true)) { |
michael@0 | 6126 | generator = true; |
michael@0 | 6127 | } else { |
michael@0 | 6128 | warning("W119", state.tokens.curr, "function*"); |
michael@0 | 6129 | } |
michael@0 | 6130 | } |
michael@0 | 6131 | if (inblock) { |
michael@0 | 6132 | warning("W082", state.tokens.curr); |
michael@0 | 6133 | |
michael@0 | 6134 | } |
michael@0 | 6135 | var i = identifier(); |
michael@0 | 6136 | if (funct[i] === "const") { |
michael@0 | 6137 | warning("E011", null, i); |
michael@0 | 6138 | } |
michael@0 | 6139 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6140 | addlabel(i, "unction", state.tokens.curr); |
michael@0 | 6141 | |
michael@0 | 6142 | doFunction(i, { statement: true }, generator); |
michael@0 | 6143 | if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { |
michael@0 | 6144 | error("E039"); |
michael@0 | 6145 | } |
michael@0 | 6146 | return this; |
michael@0 | 6147 | }); |
michael@0 | 6148 | |
michael@0 | 6149 | prefix("function", function () { |
michael@0 | 6150 | var generator = false; |
michael@0 | 6151 | if (state.tokens.next.value === "*") { |
michael@0 | 6152 | if (!state.option.inESNext()) { |
michael@0 | 6153 | warning("W119", state.tokens.curr, "function*"); |
michael@0 | 6154 | } |
michael@0 | 6155 | advance("*"); |
michael@0 | 6156 | generator = true; |
michael@0 | 6157 | } |
michael@0 | 6158 | var i = optionalidentifier(); |
michael@0 | 6159 | if (i || state.option.gcl) { |
michael@0 | 6160 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6161 | } else { |
michael@0 | 6162 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6163 | } |
michael@0 | 6164 | doFunction(i, undefined, generator); |
michael@0 | 6165 | if (!state.option.loopfunc && funct["(loopage)"]) { |
michael@0 | 6166 | warning("W083"); |
michael@0 | 6167 | } |
michael@0 | 6168 | return this; |
michael@0 | 6169 | }); |
michael@0 | 6170 | |
michael@0 | 6171 | blockstmt("if", function () { |
michael@0 | 6172 | var t = state.tokens.next; |
michael@0 | 6173 | increaseComplexityCount(); |
michael@0 | 6174 | state.condition = true; |
michael@0 | 6175 | advance("("); |
michael@0 | 6176 | nonadjacent(this, t); |
michael@0 | 6177 | nospace(); |
michael@0 | 6178 | checkCondAssignment(expression(0)); |
michael@0 | 6179 | advance(")", t); |
michael@0 | 6180 | state.condition = false; |
michael@0 | 6181 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 6182 | block(true, true); |
michael@0 | 6183 | if (state.tokens.next.id === "else") { |
michael@0 | 6184 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6185 | advance("else"); |
michael@0 | 6186 | if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") { |
michael@0 | 6187 | statement(true); |
michael@0 | 6188 | } else { |
michael@0 | 6189 | block(true, true); |
michael@0 | 6190 | } |
michael@0 | 6191 | } |
michael@0 | 6192 | return this; |
michael@0 | 6193 | }); |
michael@0 | 6194 | |
michael@0 | 6195 | blockstmt("try", function () { |
michael@0 | 6196 | var b; |
michael@0 | 6197 | |
michael@0 | 6198 | function doCatch() { |
michael@0 | 6199 | var oldScope = scope; |
michael@0 | 6200 | var e; |
michael@0 | 6201 | |
michael@0 | 6202 | advance("catch"); |
michael@0 | 6203 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6204 | advance("("); |
michael@0 | 6205 | |
michael@0 | 6206 | scope = Object.create(oldScope); |
michael@0 | 6207 | |
michael@0 | 6208 | e = state.tokens.next.value; |
michael@0 | 6209 | if (state.tokens.next.type !== "(identifier)") { |
michael@0 | 6210 | e = null; |
michael@0 | 6211 | warning("E030", state.tokens.next, e); |
michael@0 | 6212 | } |
michael@0 | 6213 | |
michael@0 | 6214 | advance(); |
michael@0 | 6215 | |
michael@0 | 6216 | funct = { |
michael@0 | 6217 | "(name)" : "(catch)", |
michael@0 | 6218 | "(line)" : state.tokens.next.line, |
michael@0 | 6219 | "(character)": state.tokens.next.character, |
michael@0 | 6220 | "(context)" : funct, |
michael@0 | 6221 | "(breakage)" : funct["(breakage)"], |
michael@0 | 6222 | "(loopage)" : funct["(loopage)"], |
michael@0 | 6223 | "(scope)" : scope, |
michael@0 | 6224 | "(statement)": false, |
michael@0 | 6225 | "(metrics)" : createMetrics(state.tokens.next), |
michael@0 | 6226 | "(catch)" : true, |
michael@0 | 6227 | "(tokens)" : {}, |
michael@0 | 6228 | "(blockscope)": funct["(blockscope)"], |
michael@0 | 6229 | "(comparray)": funct["(comparray)"] |
michael@0 | 6230 | }; |
michael@0 | 6231 | |
michael@0 | 6232 | if (e) { |
michael@0 | 6233 | addlabel(e, "exception"); |
michael@0 | 6234 | } |
michael@0 | 6235 | |
michael@0 | 6236 | if (state.tokens.next.value === "if") { |
michael@0 | 6237 | if (!state.option.inMoz(true)) { |
michael@0 | 6238 | warning("W118", state.tokens.curr, "catch filter"); |
michael@0 | 6239 | } |
michael@0 | 6240 | advance("if"); |
michael@0 | 6241 | expression(0); |
michael@0 | 6242 | } |
michael@0 | 6243 | |
michael@0 | 6244 | advance(")"); |
michael@0 | 6245 | |
michael@0 | 6246 | state.tokens.curr.funct = funct; |
michael@0 | 6247 | functions.push(funct); |
michael@0 | 6248 | |
michael@0 | 6249 | block(false); |
michael@0 | 6250 | |
michael@0 | 6251 | scope = oldScope; |
michael@0 | 6252 | |
michael@0 | 6253 | funct["(last)"] = state.tokens.curr.line; |
michael@0 | 6254 | funct["(lastcharacter)"] = state.tokens.curr.character; |
michael@0 | 6255 | funct = funct["(context)"]; |
michael@0 | 6256 | } |
michael@0 | 6257 | |
michael@0 | 6258 | block(false); |
michael@0 | 6259 | |
michael@0 | 6260 | while (state.tokens.next.id === "catch") { |
michael@0 | 6261 | increaseComplexityCount(); |
michael@0 | 6262 | if (b && (!state.option.inMoz(true))) { |
michael@0 | 6263 | warning("W118", state.tokens.next, "multiple catch blocks"); |
michael@0 | 6264 | } |
michael@0 | 6265 | doCatch(); |
michael@0 | 6266 | b = true; |
michael@0 | 6267 | } |
michael@0 | 6268 | |
michael@0 | 6269 | if (state.tokens.next.id === "finally") { |
michael@0 | 6270 | advance("finally"); |
michael@0 | 6271 | block(false); |
michael@0 | 6272 | return; |
michael@0 | 6273 | } |
michael@0 | 6274 | |
michael@0 | 6275 | if (!b) { |
michael@0 | 6276 | error("E021", state.tokens.next, "catch", state.tokens.next.value); |
michael@0 | 6277 | } |
michael@0 | 6278 | |
michael@0 | 6279 | return this; |
michael@0 | 6280 | }); |
michael@0 | 6281 | |
michael@0 | 6282 | blockstmt("while", function () { |
michael@0 | 6283 | var t = state.tokens.next; |
michael@0 | 6284 | funct["(breakage)"] += 1; |
michael@0 | 6285 | funct["(loopage)"] += 1; |
michael@0 | 6286 | increaseComplexityCount(); |
michael@0 | 6287 | advance("("); |
michael@0 | 6288 | nonadjacent(this, t); |
michael@0 | 6289 | nospace(); |
michael@0 | 6290 | checkCondAssignment(expression(0)); |
michael@0 | 6291 | advance(")", t); |
michael@0 | 6292 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 6293 | block(true, true); |
michael@0 | 6294 | funct["(breakage)"] -= 1; |
michael@0 | 6295 | funct["(loopage)"] -= 1; |
michael@0 | 6296 | return this; |
michael@0 | 6297 | }).labelled = true; |
michael@0 | 6298 | |
michael@0 | 6299 | blockstmt("with", function () { |
michael@0 | 6300 | var t = state.tokens.next; |
michael@0 | 6301 | if (state.directive["use strict"]) { |
michael@0 | 6302 | error("E010", state.tokens.curr); |
michael@0 | 6303 | } else if (!state.option.withstmt) { |
michael@0 | 6304 | warning("W085", state.tokens.curr); |
michael@0 | 6305 | } |
michael@0 | 6306 | |
michael@0 | 6307 | advance("("); |
michael@0 | 6308 | nonadjacent(this, t); |
michael@0 | 6309 | nospace(); |
michael@0 | 6310 | expression(0); |
michael@0 | 6311 | advance(")", t); |
michael@0 | 6312 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 6313 | block(true, true); |
michael@0 | 6314 | |
michael@0 | 6315 | return this; |
michael@0 | 6316 | }); |
michael@0 | 6317 | |
michael@0 | 6318 | blockstmt("switch", function () { |
michael@0 | 6319 | var t = state.tokens.next, |
michael@0 | 6320 | g = false; |
michael@0 | 6321 | funct["(breakage)"] += 1; |
michael@0 | 6322 | advance("("); |
michael@0 | 6323 | nonadjacent(this, t); |
michael@0 | 6324 | nospace(); |
michael@0 | 6325 | checkCondAssignment(expression(0)); |
michael@0 | 6326 | advance(")", t); |
michael@0 | 6327 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 6328 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6329 | t = state.tokens.next; |
michael@0 | 6330 | advance("{"); |
michael@0 | 6331 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6332 | indent += state.option.indent; |
michael@0 | 6333 | this.cases = []; |
michael@0 | 6334 | |
michael@0 | 6335 | for (;;) { |
michael@0 | 6336 | switch (state.tokens.next.id) { |
michael@0 | 6337 | case "case": |
michael@0 | 6338 | switch (funct["(verb)"]) { |
michael@0 | 6339 | case "yield": |
michael@0 | 6340 | case "break": |
michael@0 | 6341 | case "case": |
michael@0 | 6342 | case "continue": |
michael@0 | 6343 | case "return": |
michael@0 | 6344 | case "switch": |
michael@0 | 6345 | case "throw": |
michael@0 | 6346 | break; |
michael@0 | 6347 | default: |
michael@0 | 6348 | // You can tell JSHint that you don't use break intentionally by |
michael@0 | 6349 | // adding a comment /* falls through */ on a line just before |
michael@0 | 6350 | // the next `case`. |
michael@0 | 6351 | if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { |
michael@0 | 6352 | warning("W086", state.tokens.curr, "case"); |
michael@0 | 6353 | } |
michael@0 | 6354 | } |
michael@0 | 6355 | indentation(-state.option.indent); |
michael@0 | 6356 | advance("case"); |
michael@0 | 6357 | this.cases.push(expression(20)); |
michael@0 | 6358 | increaseComplexityCount(); |
michael@0 | 6359 | g = true; |
michael@0 | 6360 | advance(":"); |
michael@0 | 6361 | funct["(verb)"] = "case"; |
michael@0 | 6362 | break; |
michael@0 | 6363 | case "default": |
michael@0 | 6364 | switch (funct["(verb)"]) { |
michael@0 | 6365 | case "yield": |
michael@0 | 6366 | case "break": |
michael@0 | 6367 | case "continue": |
michael@0 | 6368 | case "return": |
michael@0 | 6369 | case "throw": |
michael@0 | 6370 | break; |
michael@0 | 6371 | default: |
michael@0 | 6372 | // Do not display a warning if 'default' is the first statement or if |
michael@0 | 6373 | // there is a special /* falls through */ comment. |
michael@0 | 6374 | if (this.cases.length) { |
michael@0 | 6375 | if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { |
michael@0 | 6376 | warning("W086", state.tokens.curr, "default"); |
michael@0 | 6377 | } |
michael@0 | 6378 | } |
michael@0 | 6379 | } |
michael@0 | 6380 | indentation(-state.option.indent); |
michael@0 | 6381 | advance("default"); |
michael@0 | 6382 | g = true; |
michael@0 | 6383 | advance(":"); |
michael@0 | 6384 | break; |
michael@0 | 6385 | case "}": |
michael@0 | 6386 | indent -= state.option.indent; |
michael@0 | 6387 | indentation(); |
michael@0 | 6388 | advance("}", t); |
michael@0 | 6389 | funct["(breakage)"] -= 1; |
michael@0 | 6390 | funct["(verb)"] = undefined; |
michael@0 | 6391 | return; |
michael@0 | 6392 | case "(end)": |
michael@0 | 6393 | error("E023", state.tokens.next, "}"); |
michael@0 | 6394 | return; |
michael@0 | 6395 | default: |
michael@0 | 6396 | if (g) { |
michael@0 | 6397 | switch (state.tokens.curr.id) { |
michael@0 | 6398 | case ",": |
michael@0 | 6399 | error("E040"); |
michael@0 | 6400 | return; |
michael@0 | 6401 | case ":": |
michael@0 | 6402 | g = false; |
michael@0 | 6403 | statements(); |
michael@0 | 6404 | break; |
michael@0 | 6405 | default: |
michael@0 | 6406 | error("E025", state.tokens.curr); |
michael@0 | 6407 | return; |
michael@0 | 6408 | } |
michael@0 | 6409 | } else { |
michael@0 | 6410 | if (state.tokens.curr.id === ":") { |
michael@0 | 6411 | advance(":"); |
michael@0 | 6412 | error("E024", state.tokens.curr, ":"); |
michael@0 | 6413 | statements(); |
michael@0 | 6414 | } else { |
michael@0 | 6415 | error("E021", state.tokens.next, "case", state.tokens.next.value); |
michael@0 | 6416 | return; |
michael@0 | 6417 | } |
michael@0 | 6418 | } |
michael@0 | 6419 | } |
michael@0 | 6420 | } |
michael@0 | 6421 | }).labelled = true; |
michael@0 | 6422 | |
michael@0 | 6423 | stmt("debugger", function () { |
michael@0 | 6424 | if (!state.option.debug) { |
michael@0 | 6425 | warning("W087"); |
michael@0 | 6426 | } |
michael@0 | 6427 | return this; |
michael@0 | 6428 | }).exps = true; |
michael@0 | 6429 | |
michael@0 | 6430 | (function () { |
michael@0 | 6431 | var x = stmt("do", function () { |
michael@0 | 6432 | funct["(breakage)"] += 1; |
michael@0 | 6433 | funct["(loopage)"] += 1; |
michael@0 | 6434 | increaseComplexityCount(); |
michael@0 | 6435 | |
michael@0 | 6436 | this.first = block(true, true); |
michael@0 | 6437 | advance("while"); |
michael@0 | 6438 | var t = state.tokens.next; |
michael@0 | 6439 | nonadjacent(state.tokens.curr, t); |
michael@0 | 6440 | advance("("); |
michael@0 | 6441 | nospace(); |
michael@0 | 6442 | checkCondAssignment(expression(0)); |
michael@0 | 6443 | advance(")", t); |
michael@0 | 6444 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 6445 | funct["(breakage)"] -= 1; |
michael@0 | 6446 | funct["(loopage)"] -= 1; |
michael@0 | 6447 | return this; |
michael@0 | 6448 | }); |
michael@0 | 6449 | x.labelled = true; |
michael@0 | 6450 | x.exps = true; |
michael@0 | 6451 | }()); |
michael@0 | 6452 | |
michael@0 | 6453 | blockstmt("for", function () { |
michael@0 | 6454 | var s, t = state.tokens.next; |
michael@0 | 6455 | var letscope = false; |
michael@0 | 6456 | var foreachtok = null; |
michael@0 | 6457 | |
michael@0 | 6458 | if (t.value === "each") { |
michael@0 | 6459 | foreachtok = t; |
michael@0 | 6460 | advance("each"); |
michael@0 | 6461 | if (!state.option.inMoz(true)) { |
michael@0 | 6462 | warning("W118", state.tokens.curr, "for each"); |
michael@0 | 6463 | } |
michael@0 | 6464 | } |
michael@0 | 6465 | |
michael@0 | 6466 | funct["(breakage)"] += 1; |
michael@0 | 6467 | funct["(loopage)"] += 1; |
michael@0 | 6468 | increaseComplexityCount(); |
michael@0 | 6469 | advance("("); |
michael@0 | 6470 | nonadjacent(this, t); |
michael@0 | 6471 | nospace(); |
michael@0 | 6472 | |
michael@0 | 6473 | // what kind of for(…) statement it is? for(…of…)? for(…in…)? for(…;…;…)? |
michael@0 | 6474 | var nextop; // contains the token of the "in" or "of" operator |
michael@0 | 6475 | var i = 0; |
michael@0 | 6476 | var inof = ["in", "of"]; |
michael@0 | 6477 | do { |
michael@0 | 6478 | nextop = peek(i); |
michael@0 | 6479 | ++i; |
michael@0 | 6480 | } while (!_.contains(inof, nextop.value) && nextop.value !== ";" && |
michael@0 | 6481 | nextop.type !== "(end)"); |
michael@0 | 6482 | |
michael@0 | 6483 | // if we're in a for (… in|of …) statement |
michael@0 | 6484 | if (_.contains(inof, nextop.value)) { |
michael@0 | 6485 | if (!state.option.inESNext() && nextop.value === "of") { |
michael@0 | 6486 | error("W104", nextop, "for of"); |
michael@0 | 6487 | } |
michael@0 | 6488 | if (state.tokens.next.id === "var") { |
michael@0 | 6489 | advance("var"); |
michael@0 | 6490 | state.syntax["var"].fud.call(state.syntax["var"].fud, true); |
michael@0 | 6491 | } else if (state.tokens.next.id === "let") { |
michael@0 | 6492 | advance("let"); |
michael@0 | 6493 | // create a new block scope |
michael@0 | 6494 | letscope = true; |
michael@0 | 6495 | funct["(blockscope)"].stack(); |
michael@0 | 6496 | state.syntax["let"].fud.call(state.syntax["let"].fud, true); |
michael@0 | 6497 | } else { |
michael@0 | 6498 | switch (funct[state.tokens.next.value]) { |
michael@0 | 6499 | case "unused": |
michael@0 | 6500 | funct[state.tokens.next.value] = "var"; |
michael@0 | 6501 | break; |
michael@0 | 6502 | case "var": |
michael@0 | 6503 | break; |
michael@0 | 6504 | default: |
michael@0 | 6505 | if (!funct["(blockscope)"].getlabel(state.tokens.next.value)) |
michael@0 | 6506 | warning("W088", state.tokens.next, state.tokens.next.value); |
michael@0 | 6507 | } |
michael@0 | 6508 | advance(); |
michael@0 | 6509 | } |
michael@0 | 6510 | advance(nextop.value); |
michael@0 | 6511 | expression(20); |
michael@0 | 6512 | advance(")", t); |
michael@0 | 6513 | s = block(true, true); |
michael@0 | 6514 | if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" || |
michael@0 | 6515 | s[0].value !== "if")) { |
michael@0 | 6516 | warning("W089", this); |
michael@0 | 6517 | } |
michael@0 | 6518 | funct["(breakage)"] -= 1; |
michael@0 | 6519 | funct["(loopage)"] -= 1; |
michael@0 | 6520 | } else { |
michael@0 | 6521 | if (foreachtok) { |
michael@0 | 6522 | error("E045", foreachtok); |
michael@0 | 6523 | } |
michael@0 | 6524 | if (state.tokens.next.id !== ";") { |
michael@0 | 6525 | if (state.tokens.next.id === "var") { |
michael@0 | 6526 | advance("var"); |
michael@0 | 6527 | state.syntax["var"].fud.call(state.syntax["var"].fud); |
michael@0 | 6528 | } else if (state.tokens.next.id === "let") { |
michael@0 | 6529 | advance("let"); |
michael@0 | 6530 | // create a new block scope |
michael@0 | 6531 | letscope = true; |
michael@0 | 6532 | funct["(blockscope)"].stack(); |
michael@0 | 6533 | state.syntax["let"].fud.call(state.syntax["let"].fud); |
michael@0 | 6534 | } else { |
michael@0 | 6535 | for (;;) { |
michael@0 | 6536 | expression(0, "for"); |
michael@0 | 6537 | if (state.tokens.next.id !== ",") { |
michael@0 | 6538 | break; |
michael@0 | 6539 | } |
michael@0 | 6540 | comma(); |
michael@0 | 6541 | } |
michael@0 | 6542 | } |
michael@0 | 6543 | } |
michael@0 | 6544 | nolinebreak(state.tokens.curr); |
michael@0 | 6545 | advance(";"); |
michael@0 | 6546 | if (state.tokens.next.id !== ";") { |
michael@0 | 6547 | checkCondAssignment(expression(0)); |
michael@0 | 6548 | } |
michael@0 | 6549 | nolinebreak(state.tokens.curr); |
michael@0 | 6550 | advance(";"); |
michael@0 | 6551 | if (state.tokens.next.id === ";") { |
michael@0 | 6552 | error("E021", state.tokens.next, ")", ";"); |
michael@0 | 6553 | } |
michael@0 | 6554 | if (state.tokens.next.id !== ")") { |
michael@0 | 6555 | for (;;) { |
michael@0 | 6556 | expression(0, "for"); |
michael@0 | 6557 | if (state.tokens.next.id !== ",") { |
michael@0 | 6558 | break; |
michael@0 | 6559 | } |
michael@0 | 6560 | comma(); |
michael@0 | 6561 | } |
michael@0 | 6562 | } |
michael@0 | 6563 | advance(")", t); |
michael@0 | 6564 | nospace(state.tokens.prev, state.tokens.curr); |
michael@0 | 6565 | block(true, true); |
michael@0 | 6566 | funct["(breakage)"] -= 1; |
michael@0 | 6567 | funct["(loopage)"] -= 1; |
michael@0 | 6568 | |
michael@0 | 6569 | } |
michael@0 | 6570 | // unstack loop blockscope |
michael@0 | 6571 | if (letscope) { |
michael@0 | 6572 | funct["(blockscope)"].unstack(); |
michael@0 | 6573 | } |
michael@0 | 6574 | return this; |
michael@0 | 6575 | }).labelled = true; |
michael@0 | 6576 | |
michael@0 | 6577 | |
michael@0 | 6578 | stmt("break", function () { |
michael@0 | 6579 | var v = state.tokens.next.value; |
michael@0 | 6580 | |
michael@0 | 6581 | if (funct["(breakage)"] === 0) |
michael@0 | 6582 | warning("W052", state.tokens.next, this.value); |
michael@0 | 6583 | |
michael@0 | 6584 | if (!state.option.asi) |
michael@0 | 6585 | nolinebreak(this); |
michael@0 | 6586 | |
michael@0 | 6587 | if (state.tokens.next.id !== ";") { |
michael@0 | 6588 | if (state.tokens.curr.line === state.tokens.next.line) { |
michael@0 | 6589 | if (funct[v] !== "label") { |
michael@0 | 6590 | warning("W090", state.tokens.next, v); |
michael@0 | 6591 | } else if (scope[v] !== funct) { |
michael@0 | 6592 | warning("W091", state.tokens.next, v); |
michael@0 | 6593 | } |
michael@0 | 6594 | this.first = state.tokens.next; |
michael@0 | 6595 | advance(); |
michael@0 | 6596 | } |
michael@0 | 6597 | } |
michael@0 | 6598 | reachable("break"); |
michael@0 | 6599 | return this; |
michael@0 | 6600 | }).exps = true; |
michael@0 | 6601 | |
michael@0 | 6602 | |
michael@0 | 6603 | stmt("continue", function () { |
michael@0 | 6604 | var v = state.tokens.next.value; |
michael@0 | 6605 | |
michael@0 | 6606 | if (funct["(breakage)"] === 0) |
michael@0 | 6607 | warning("W052", state.tokens.next, this.value); |
michael@0 | 6608 | |
michael@0 | 6609 | if (!state.option.asi) |
michael@0 | 6610 | nolinebreak(this); |
michael@0 | 6611 | |
michael@0 | 6612 | if (state.tokens.next.id !== ";") { |
michael@0 | 6613 | if (state.tokens.curr.line === state.tokens.next.line) { |
michael@0 | 6614 | if (funct[v] !== "label") { |
michael@0 | 6615 | warning("W090", state.tokens.next, v); |
michael@0 | 6616 | } else if (scope[v] !== funct) { |
michael@0 | 6617 | warning("W091", state.tokens.next, v); |
michael@0 | 6618 | } |
michael@0 | 6619 | this.first = state.tokens.next; |
michael@0 | 6620 | advance(); |
michael@0 | 6621 | } |
michael@0 | 6622 | } else if (!funct["(loopage)"]) { |
michael@0 | 6623 | warning("W052", state.tokens.next, this.value); |
michael@0 | 6624 | } |
michael@0 | 6625 | reachable("continue"); |
michael@0 | 6626 | return this; |
michael@0 | 6627 | }).exps = true; |
michael@0 | 6628 | |
michael@0 | 6629 | |
michael@0 | 6630 | stmt("return", function () { |
michael@0 | 6631 | if (this.line === state.tokens.next.line) { |
michael@0 | 6632 | if (state.tokens.next.id === "(regexp)") |
michael@0 | 6633 | warning("W092"); |
michael@0 | 6634 | |
michael@0 | 6635 | if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { |
michael@0 | 6636 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6637 | this.first = expression(0); |
michael@0 | 6638 | |
michael@0 | 6639 | if (this.first && |
michael@0 | 6640 | this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) { |
michael@0 | 6641 | warningAt("W093", this.first.line, this.first.character); |
michael@0 | 6642 | } |
michael@0 | 6643 | } |
michael@0 | 6644 | } else { |
michael@0 | 6645 | if (state.tokens.next.type === "(punctuator)" && |
michael@0 | 6646 | ["[", "{", "+", "-"].indexOf(state.tokens.next.value) > -1) { |
michael@0 | 6647 | nolinebreak(this); // always warn (Line breaking error) |
michael@0 | 6648 | } |
michael@0 | 6649 | } |
michael@0 | 6650 | reachable("return"); |
michael@0 | 6651 | return this; |
michael@0 | 6652 | }).exps = true; |
michael@0 | 6653 | |
michael@0 | 6654 | stmt("yield", function () { |
michael@0 | 6655 | if (state.option.inESNext(true) && funct["(generator)"] !== true) { |
michael@0 | 6656 | error("E046", state.tokens.curr, "yield"); |
michael@0 | 6657 | } else if (!state.option.inESNext()) { |
michael@0 | 6658 | warning("W104", state.tokens.curr, "yield"); |
michael@0 | 6659 | } |
michael@0 | 6660 | funct["(generator)"] = "yielded"; |
michael@0 | 6661 | if (this.line === state.tokens.next.line) { |
michael@0 | 6662 | if (state.tokens.next.id === "(regexp)") |
michael@0 | 6663 | warning("W092"); |
michael@0 | 6664 | |
michael@0 | 6665 | if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { |
michael@0 | 6666 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6667 | this.first = expression(0); |
michael@0 | 6668 | |
michael@0 | 6669 | if (this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) { |
michael@0 | 6670 | warningAt("W093", this.first.line, this.first.character); |
michael@0 | 6671 | } |
michael@0 | 6672 | } |
michael@0 | 6673 | } else if (!state.option.asi) { |
michael@0 | 6674 | nolinebreak(this); // always warn (Line breaking error) |
michael@0 | 6675 | } |
michael@0 | 6676 | return this; |
michael@0 | 6677 | }).exps = true; |
michael@0 | 6678 | |
michael@0 | 6679 | |
michael@0 | 6680 | stmt("throw", function () { |
michael@0 | 6681 | nolinebreak(this); |
michael@0 | 6682 | nonadjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6683 | this.first = expression(20); |
michael@0 | 6684 | reachable("throw"); |
michael@0 | 6685 | return this; |
michael@0 | 6686 | }).exps = true; |
michael@0 | 6687 | |
michael@0 | 6688 | // Future Reserved Words |
michael@0 | 6689 | |
michael@0 | 6690 | FutureReservedWord("abstract"); |
michael@0 | 6691 | FutureReservedWord("boolean"); |
michael@0 | 6692 | FutureReservedWord("byte"); |
michael@0 | 6693 | FutureReservedWord("char"); |
michael@0 | 6694 | FutureReservedWord("class", { es5: true, nud: classdef }); |
michael@0 | 6695 | FutureReservedWord("double"); |
michael@0 | 6696 | FutureReservedWord("enum", { es5: true }); |
michael@0 | 6697 | FutureReservedWord("export", { es5: true }); |
michael@0 | 6698 | FutureReservedWord("extends", { es5: true }); |
michael@0 | 6699 | FutureReservedWord("final"); |
michael@0 | 6700 | FutureReservedWord("float"); |
michael@0 | 6701 | FutureReservedWord("goto"); |
michael@0 | 6702 | FutureReservedWord("implements", { es5: true, strictOnly: true }); |
michael@0 | 6703 | FutureReservedWord("import", { es5: true }); |
michael@0 | 6704 | FutureReservedWord("int"); |
michael@0 | 6705 | FutureReservedWord("interface", { es5: true, strictOnly: true }); |
michael@0 | 6706 | FutureReservedWord("long"); |
michael@0 | 6707 | FutureReservedWord("native"); |
michael@0 | 6708 | FutureReservedWord("package", { es5: true, strictOnly: true }); |
michael@0 | 6709 | FutureReservedWord("private", { es5: true, strictOnly: true }); |
michael@0 | 6710 | FutureReservedWord("protected", { es5: true, strictOnly: true }); |
michael@0 | 6711 | FutureReservedWord("public", { es5: true, strictOnly: true }); |
michael@0 | 6712 | FutureReservedWord("short"); |
michael@0 | 6713 | FutureReservedWord("static", { es5: true, strictOnly: true }); |
michael@0 | 6714 | FutureReservedWord("super", { es5: true }); |
michael@0 | 6715 | FutureReservedWord("synchronized"); |
michael@0 | 6716 | FutureReservedWord("throws"); |
michael@0 | 6717 | FutureReservedWord("transient"); |
michael@0 | 6718 | FutureReservedWord("volatile"); |
michael@0 | 6719 | |
michael@0 | 6720 | // this function is used to determine wether a squarebracket or a curlybracket |
michael@0 | 6721 | // expression is a comprehension array, destructuring assignment or a json value. |
michael@0 | 6722 | |
michael@0 | 6723 | var lookupBlockType = function () { |
michael@0 | 6724 | var pn, pn1; |
michael@0 | 6725 | var i = 0; |
michael@0 | 6726 | var bracketStack = 0; |
michael@0 | 6727 | var ret = {}; |
michael@0 | 6728 | if (_.contains(["[", "{"], state.tokens.curr.value)) |
michael@0 | 6729 | bracketStack += 1; |
michael@0 | 6730 | if (_.contains(["[", "{"], state.tokens.next.value)) |
michael@0 | 6731 | bracketStack += 1; |
michael@0 | 6732 | if (_.contains(["]", "}"], state.tokens.next.value)) |
michael@0 | 6733 | bracketStack -= 1; |
michael@0 | 6734 | do { |
michael@0 | 6735 | pn = peek(i); |
michael@0 | 6736 | pn1 = peek(i + 1); |
michael@0 | 6737 | i = i + 1; |
michael@0 | 6738 | if (_.contains(["[", "{"], pn.value)) { |
michael@0 | 6739 | bracketStack += 1; |
michael@0 | 6740 | } else if (_.contains(["]", "}"], pn.value)) { |
michael@0 | 6741 | bracketStack -= 1; |
michael@0 | 6742 | } |
michael@0 | 6743 | if (pn.identifier && pn.value === "for" && bracketStack === 1) { |
michael@0 | 6744 | ret.isCompArray = true; |
michael@0 | 6745 | ret.notJson = true; |
michael@0 | 6746 | break; |
michael@0 | 6747 | } |
michael@0 | 6748 | if (_.contains(["}", "]"], pn.value) && pn1.value === "=") { |
michael@0 | 6749 | ret.isDestAssign = true; |
michael@0 | 6750 | ret.notJson = true; |
michael@0 | 6751 | break; |
michael@0 | 6752 | } |
michael@0 | 6753 | if (pn.value === ";") { |
michael@0 | 6754 | ret.isBlock = true; |
michael@0 | 6755 | ret.notJson = true; |
michael@0 | 6756 | } |
michael@0 | 6757 | } while (bracketStack > 0 && pn.id !== "(end)" && i < 15); |
michael@0 | 6758 | return ret; |
michael@0 | 6759 | }; |
michael@0 | 6760 | |
michael@0 | 6761 | // Check whether this function has been reached for a destructuring assign with undeclared values |
michael@0 | 6762 | function destructuringAssignOrJsonValue() { |
michael@0 | 6763 | // lookup for the assignment (esnext only) |
michael@0 | 6764 | // if it has semicolons, it is a block, so go parse it as a block |
michael@0 | 6765 | // or it's not a block, but there are assignments, check for undeclared variables |
michael@0 | 6766 | |
michael@0 | 6767 | var block = lookupBlockType(); |
michael@0 | 6768 | if (block.notJson) { |
michael@0 | 6769 | if (!state.option.inESNext() && block.isDestAssign) { |
michael@0 | 6770 | warning("W104", state.tokens.curr, "destructuring assignment"); |
michael@0 | 6771 | } |
michael@0 | 6772 | statements(); |
michael@0 | 6773 | // otherwise parse json value |
michael@0 | 6774 | } else { |
michael@0 | 6775 | state.option.laxbreak = true; |
michael@0 | 6776 | state.jsonMode = true; |
michael@0 | 6777 | jsonValue(); |
michael@0 | 6778 | } |
michael@0 | 6779 | } |
michael@0 | 6780 | |
michael@0 | 6781 | // array comprehension parsing function |
michael@0 | 6782 | // parses and defines the three states of the list comprehension in order |
michael@0 | 6783 | // to avoid defining global variables, but keeping them to the list comprehension scope |
michael@0 | 6784 | // only. The order of the states are as follows: |
michael@0 | 6785 | // * "use" which will be the returned iterative part of the list comprehension |
michael@0 | 6786 | // * "define" which will define the variables local to the list comprehension |
michael@0 | 6787 | // * "filter" which will help filter out values |
michael@0 | 6788 | |
michael@0 | 6789 | var arrayComprehension = function () { |
michael@0 | 6790 | var CompArray = function () { |
michael@0 | 6791 | this.mode = "use"; |
michael@0 | 6792 | this.variables = []; |
michael@0 | 6793 | }; |
michael@0 | 6794 | var _carrays = []; |
michael@0 | 6795 | var _current; |
michael@0 | 6796 | function declare(v) { |
michael@0 | 6797 | var l = _current.variables.filter(function (elt) { |
michael@0 | 6798 | // if it has, change its undef state |
michael@0 | 6799 | if (elt.value === v) { |
michael@0 | 6800 | elt.undef = false; |
michael@0 | 6801 | return v; |
michael@0 | 6802 | } |
michael@0 | 6803 | }).length; |
michael@0 | 6804 | return l !== 0; |
michael@0 | 6805 | } |
michael@0 | 6806 | function use(v) { |
michael@0 | 6807 | var l = _current.variables.filter(function (elt) { |
michael@0 | 6808 | // and if it has been defined |
michael@0 | 6809 | if (elt.value === v && !elt.undef) { |
michael@0 | 6810 | if (elt.unused === true) { |
michael@0 | 6811 | elt.unused = false; |
michael@0 | 6812 | } |
michael@0 | 6813 | return v; |
michael@0 | 6814 | } |
michael@0 | 6815 | }).length; |
michael@0 | 6816 | // otherwise we warn about it |
michael@0 | 6817 | return (l === 0); |
michael@0 | 6818 | } |
michael@0 | 6819 | return {stack: function () { |
michael@0 | 6820 | _current = new CompArray(); |
michael@0 | 6821 | _carrays.push(_current); |
michael@0 | 6822 | }, |
michael@0 | 6823 | unstack: function () { |
michael@0 | 6824 | _current.variables.filter(function (v) { |
michael@0 | 6825 | if (v.unused) |
michael@0 | 6826 | warning("W098", v.token, v.value); |
michael@0 | 6827 | if (v.undef) |
michael@0 | 6828 | isundef(v.funct, "W117", v.token, v.value); |
michael@0 | 6829 | }); |
michael@0 | 6830 | _carrays.splice(_carrays[_carrays.length - 1], 1); |
michael@0 | 6831 | _current = _carrays[_carrays.length - 1]; |
michael@0 | 6832 | }, |
michael@0 | 6833 | setState: function (s) { |
michael@0 | 6834 | if (_.contains(["use", "define", "filter"], s)) |
michael@0 | 6835 | _current.mode = s; |
michael@0 | 6836 | }, |
michael@0 | 6837 | check: function (v) { |
michael@0 | 6838 | // When we are in "use" state of the list comp, we enqueue that var |
michael@0 | 6839 | if (_current && _current.mode === "use") { |
michael@0 | 6840 | _current.variables.push({funct: funct, |
michael@0 | 6841 | token: state.tokens.curr, |
michael@0 | 6842 | value: v, |
michael@0 | 6843 | undef: true, |
michael@0 | 6844 | unused: false}); |
michael@0 | 6845 | return true; |
michael@0 | 6846 | // When we are in "define" state of the list comp, |
michael@0 | 6847 | } else if (_current && _current.mode === "define") { |
michael@0 | 6848 | // check if the variable has been used previously |
michael@0 | 6849 | if (!declare(v)) { |
michael@0 | 6850 | _current.variables.push({funct: funct, |
michael@0 | 6851 | token: state.tokens.curr, |
michael@0 | 6852 | value: v, |
michael@0 | 6853 | undef: false, |
michael@0 | 6854 | unused: true}); |
michael@0 | 6855 | } |
michael@0 | 6856 | return true; |
michael@0 | 6857 | // When we are in "filter" state, |
michael@0 | 6858 | } else if (_current && _current.mode === "filter") { |
michael@0 | 6859 | // we check whether current variable has been declared |
michael@0 | 6860 | if (use(v)) { |
michael@0 | 6861 | // if not we warn about it |
michael@0 | 6862 | isundef(funct, "W117", state.tokens.curr, v); |
michael@0 | 6863 | } |
michael@0 | 6864 | return true; |
michael@0 | 6865 | } |
michael@0 | 6866 | return false; |
michael@0 | 6867 | } |
michael@0 | 6868 | }; |
michael@0 | 6869 | }; |
michael@0 | 6870 | |
michael@0 | 6871 | |
michael@0 | 6872 | // Parse JSON |
michael@0 | 6873 | |
michael@0 | 6874 | function jsonValue() { |
michael@0 | 6875 | |
michael@0 | 6876 | function jsonObject() { |
michael@0 | 6877 | var o = {}, t = state.tokens.next; |
michael@0 | 6878 | advance("{"); |
michael@0 | 6879 | if (state.tokens.next.id !== "}") { |
michael@0 | 6880 | for (;;) { |
michael@0 | 6881 | if (state.tokens.next.id === "(end)") { |
michael@0 | 6882 | error("E026", state.tokens.next, t.line); |
michael@0 | 6883 | } else if (state.tokens.next.id === "}") { |
michael@0 | 6884 | warning("W094", state.tokens.curr); |
michael@0 | 6885 | break; |
michael@0 | 6886 | } else if (state.tokens.next.id === ",") { |
michael@0 | 6887 | error("E028", state.tokens.next); |
michael@0 | 6888 | } else if (state.tokens.next.id !== "(string)") { |
michael@0 | 6889 | warning("W095", state.tokens.next, state.tokens.next.value); |
michael@0 | 6890 | } |
michael@0 | 6891 | if (o[state.tokens.next.value] === true) { |
michael@0 | 6892 | warning("W075", state.tokens.next, state.tokens.next.value); |
michael@0 | 6893 | } else if ((state.tokens.next.value === "__proto__" && |
michael@0 | 6894 | !state.option.proto) || (state.tokens.next.value === "__iterator__" && |
michael@0 | 6895 | !state.option.iterator)) { |
michael@0 | 6896 | warning("W096", state.tokens.next, state.tokens.next.value); |
michael@0 | 6897 | } else { |
michael@0 | 6898 | o[state.tokens.next.value] = true; |
michael@0 | 6899 | } |
michael@0 | 6900 | advance(); |
michael@0 | 6901 | advance(":"); |
michael@0 | 6902 | jsonValue(); |
michael@0 | 6903 | if (state.tokens.next.id !== ",") { |
michael@0 | 6904 | break; |
michael@0 | 6905 | } |
michael@0 | 6906 | advance(","); |
michael@0 | 6907 | } |
michael@0 | 6908 | } |
michael@0 | 6909 | advance("}"); |
michael@0 | 6910 | } |
michael@0 | 6911 | |
michael@0 | 6912 | function jsonArray() { |
michael@0 | 6913 | var t = state.tokens.next; |
michael@0 | 6914 | advance("["); |
michael@0 | 6915 | if (state.tokens.next.id !== "]") { |
michael@0 | 6916 | for (;;) { |
michael@0 | 6917 | if (state.tokens.next.id === "(end)") { |
michael@0 | 6918 | error("E027", state.tokens.next, t.line); |
michael@0 | 6919 | } else if (state.tokens.next.id === "]") { |
michael@0 | 6920 | warning("W094", state.tokens.curr); |
michael@0 | 6921 | break; |
michael@0 | 6922 | } else if (state.tokens.next.id === ",") { |
michael@0 | 6923 | error("E028", state.tokens.next); |
michael@0 | 6924 | } |
michael@0 | 6925 | jsonValue(); |
michael@0 | 6926 | if (state.tokens.next.id !== ",") { |
michael@0 | 6927 | break; |
michael@0 | 6928 | } |
michael@0 | 6929 | advance(","); |
michael@0 | 6930 | } |
michael@0 | 6931 | } |
michael@0 | 6932 | advance("]"); |
michael@0 | 6933 | } |
michael@0 | 6934 | |
michael@0 | 6935 | switch (state.tokens.next.id) { |
michael@0 | 6936 | case "{": |
michael@0 | 6937 | jsonObject(); |
michael@0 | 6938 | break; |
michael@0 | 6939 | case "[": |
michael@0 | 6940 | jsonArray(); |
michael@0 | 6941 | break; |
michael@0 | 6942 | case "true": |
michael@0 | 6943 | case "false": |
michael@0 | 6944 | case "null": |
michael@0 | 6945 | case "(number)": |
michael@0 | 6946 | case "(string)": |
michael@0 | 6947 | advance(); |
michael@0 | 6948 | break; |
michael@0 | 6949 | case "-": |
michael@0 | 6950 | advance("-"); |
michael@0 | 6951 | if (state.tokens.curr.character !== state.tokens.next.from) { |
michael@0 | 6952 | warning("W011", state.tokens.curr); |
michael@0 | 6953 | } |
michael@0 | 6954 | adjacent(state.tokens.curr, state.tokens.next); |
michael@0 | 6955 | advance("(number)"); |
michael@0 | 6956 | break; |
michael@0 | 6957 | default: |
michael@0 | 6958 | error("E003", state.tokens.next); |
michael@0 | 6959 | } |
michael@0 | 6960 | } |
michael@0 | 6961 | |
michael@0 | 6962 | var blockScope = function () { |
michael@0 | 6963 | var _current = {}; |
michael@0 | 6964 | var _variables = [_current]; |
michael@0 | 6965 | |
michael@0 | 6966 | function _checkBlockLabels() { |
michael@0 | 6967 | for (var t in _current) { |
michael@0 | 6968 | if (_current[t]["(type)"] === "unused") { |
michael@0 | 6969 | if (state.option.unused) { |
michael@0 | 6970 | var tkn = _current[t]["(token)"]; |
michael@0 | 6971 | var line = tkn.line; |
michael@0 | 6972 | var chr = tkn.character; |
michael@0 | 6973 | warningAt("W098", line, chr, t); |
michael@0 | 6974 | } |
michael@0 | 6975 | } |
michael@0 | 6976 | } |
michael@0 | 6977 | } |
michael@0 | 6978 | |
michael@0 | 6979 | return { |
michael@0 | 6980 | stack: function () { |
michael@0 | 6981 | _current = {}; |
michael@0 | 6982 | _variables.push(_current); |
michael@0 | 6983 | }, |
michael@0 | 6984 | |
michael@0 | 6985 | unstack: function () { |
michael@0 | 6986 | _checkBlockLabels(); |
michael@0 | 6987 | _variables.splice(_variables.length - 1, 1); |
michael@0 | 6988 | _current = _.last(_variables); |
michael@0 | 6989 | }, |
michael@0 | 6990 | |
michael@0 | 6991 | getlabel: function (l) { |
michael@0 | 6992 | for (var i = _variables.length - 1 ; i >= 0; --i) { |
michael@0 | 6993 | if (_.has(_variables[i], l)) { |
michael@0 | 6994 | return _variables[i]; |
michael@0 | 6995 | } |
michael@0 | 6996 | } |
michael@0 | 6997 | }, |
michael@0 | 6998 | |
michael@0 | 6999 | current: { |
michael@0 | 7000 | has: function (t) { |
michael@0 | 7001 | return _.has(_current, t); |
michael@0 | 7002 | }, |
michael@0 | 7003 | add: function (t, type, tok) { |
michael@0 | 7004 | _current[t] = { "(type)" : type, |
michael@0 | 7005 | "(token)": tok }; |
michael@0 | 7006 | } |
michael@0 | 7007 | } |
michael@0 | 7008 | }; |
michael@0 | 7009 | }; |
michael@0 | 7010 | |
michael@0 | 7011 | // The actual JSHINT function itself. |
michael@0 | 7012 | var itself = function (s, o, g) { |
michael@0 | 7013 | var a, i, k, x; |
michael@0 | 7014 | var optionKeys; |
michael@0 | 7015 | var newOptionObj = {}; |
michael@0 | 7016 | var newIgnoredObj = {}; |
michael@0 | 7017 | |
michael@0 | 7018 | state.reset(); |
michael@0 | 7019 | |
michael@0 | 7020 | if (o && o.scope) { |
michael@0 | 7021 | JSHINT.scope = o.scope; |
michael@0 | 7022 | } else { |
michael@0 | 7023 | JSHINT.errors = []; |
michael@0 | 7024 | JSHINT.undefs = []; |
michael@0 | 7025 | JSHINT.internals = []; |
michael@0 | 7026 | JSHINT.blacklist = {}; |
michael@0 | 7027 | JSHINT.scope = "(main)"; |
michael@0 | 7028 | } |
michael@0 | 7029 | |
michael@0 | 7030 | predefined = Object.create(null); |
michael@0 | 7031 | combine(predefined, vars.ecmaIdentifiers); |
michael@0 | 7032 | combine(predefined, vars.reservedVars); |
michael@0 | 7033 | |
michael@0 | 7034 | combine(predefined, g || {}); |
michael@0 | 7035 | |
michael@0 | 7036 | declared = Object.create(null); |
michael@0 | 7037 | exported = Object.create(null); |
michael@0 | 7038 | |
michael@0 | 7039 | if (o) { |
michael@0 | 7040 | a = o.predef; |
michael@0 | 7041 | if (a) { |
michael@0 | 7042 | if (!Array.isArray(a) && typeof a === "object") { |
michael@0 | 7043 | a = Object.keys(a); |
michael@0 | 7044 | } |
michael@0 | 7045 | |
michael@0 | 7046 | a.forEach(function (item) { |
michael@0 | 7047 | var slice, prop; |
michael@0 | 7048 | |
michael@0 | 7049 | if (item[0] === "-") { |
michael@0 | 7050 | slice = item.slice(1); |
michael@0 | 7051 | JSHINT.blacklist[slice] = slice; |
michael@0 | 7052 | } else { |
michael@0 | 7053 | prop = Object.getOwnPropertyDescriptor(o.predef, item); |
michael@0 | 7054 | predefined[item] = prop ? prop.value : false; |
michael@0 | 7055 | } |
michael@0 | 7056 | }); |
michael@0 | 7057 | } |
michael@0 | 7058 | |
michael@0 | 7059 | optionKeys = Object.keys(o); |
michael@0 | 7060 | for (x = 0; x < optionKeys.length; x++) { |
michael@0 | 7061 | if (/^-W\d{3}$/g.test(optionKeys[x])) { |
michael@0 | 7062 | newIgnoredObj[optionKeys[x].slice(1)] = true; |
michael@0 | 7063 | } else { |
michael@0 | 7064 | newOptionObj[optionKeys[x]] = o[optionKeys[x]]; |
michael@0 | 7065 | |
michael@0 | 7066 | if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) |
michael@0 | 7067 | newOptionObj["(explicitNewcap)"] = true; |
michael@0 | 7068 | |
michael@0 | 7069 | if (optionKeys[x] === "indent") |
michael@0 | 7070 | newOptionObj["(explicitIndent)"] = o[optionKeys[x]] === false ? false : true; |
michael@0 | 7071 | } |
michael@0 | 7072 | } |
michael@0 | 7073 | } |
michael@0 | 7074 | |
michael@0 | 7075 | state.option = newOptionObj; |
michael@0 | 7076 | state.ignored = newIgnoredObj; |
michael@0 | 7077 | |
michael@0 | 7078 | state.option.indent = state.option.indent || 4; |
michael@0 | 7079 | state.option.maxerr = state.option.maxerr || 50; |
michael@0 | 7080 | |
michael@0 | 7081 | indent = 1; |
michael@0 | 7082 | global = Object.create(predefined); |
michael@0 | 7083 | scope = global; |
michael@0 | 7084 | funct = { |
michael@0 | 7085 | "(global)": true, |
michael@0 | 7086 | "(name)": "(global)", |
michael@0 | 7087 | "(scope)": scope, |
michael@0 | 7088 | "(breakage)": 0, |
michael@0 | 7089 | "(loopage)": 0, |
michael@0 | 7090 | "(tokens)": {}, |
michael@0 | 7091 | "(metrics)": createMetrics(state.tokens.next), |
michael@0 | 7092 | "(blockscope)": blockScope(), |
michael@0 | 7093 | "(comparray)": arrayComprehension() |
michael@0 | 7094 | }; |
michael@0 | 7095 | functions = [funct]; |
michael@0 | 7096 | urls = []; |
michael@0 | 7097 | stack = null; |
michael@0 | 7098 | member = {}; |
michael@0 | 7099 | membersOnly = null; |
michael@0 | 7100 | implied = {}; |
michael@0 | 7101 | inblock = false; |
michael@0 | 7102 | lookahead = []; |
michael@0 | 7103 | warnings = 0; |
michael@0 | 7104 | unuseds = []; |
michael@0 | 7105 | |
michael@0 | 7106 | if (!isString(s) && !Array.isArray(s)) { |
michael@0 | 7107 | errorAt("E004", 0); |
michael@0 | 7108 | return false; |
michael@0 | 7109 | } |
michael@0 | 7110 | |
michael@0 | 7111 | api = { |
michael@0 | 7112 | get isJSON() { |
michael@0 | 7113 | return state.jsonMode; |
michael@0 | 7114 | }, |
michael@0 | 7115 | |
michael@0 | 7116 | getOption: function (name) { |
michael@0 | 7117 | return state.option[name] || null; |
michael@0 | 7118 | }, |
michael@0 | 7119 | |
michael@0 | 7120 | getCache: function (name) { |
michael@0 | 7121 | return state.cache[name]; |
michael@0 | 7122 | }, |
michael@0 | 7123 | |
michael@0 | 7124 | setCache: function (name, value) { |
michael@0 | 7125 | state.cache[name] = value; |
michael@0 | 7126 | }, |
michael@0 | 7127 | |
michael@0 | 7128 | warn: function (code, data) { |
michael@0 | 7129 | warningAt.apply(null, [ code, data.line, data.char ].concat(data.data)); |
michael@0 | 7130 | }, |
michael@0 | 7131 | |
michael@0 | 7132 | on: function (names, listener) { |
michael@0 | 7133 | names.split(" ").forEach(function (name) { |
michael@0 | 7134 | emitter.on(name, listener); |
michael@0 | 7135 | }.bind(this)); |
michael@0 | 7136 | } |
michael@0 | 7137 | }; |
michael@0 | 7138 | |
michael@0 | 7139 | emitter.removeAllListeners(); |
michael@0 | 7140 | (extraModules || []).forEach(function (func) { |
michael@0 | 7141 | func(api); |
michael@0 | 7142 | }); |
michael@0 | 7143 | |
michael@0 | 7144 | state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"]; |
michael@0 | 7145 | |
michael@0 | 7146 | lex = new Lexer(s); |
michael@0 | 7147 | |
michael@0 | 7148 | lex.on("warning", function (ev) { |
michael@0 | 7149 | warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data)); |
michael@0 | 7150 | }); |
michael@0 | 7151 | |
michael@0 | 7152 | lex.on("error", function (ev) { |
michael@0 | 7153 | errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data)); |
michael@0 | 7154 | }); |
michael@0 | 7155 | |
michael@0 | 7156 | lex.on("fatal", function (ev) { |
michael@0 | 7157 | quit("E041", ev.line, ev.from); |
michael@0 | 7158 | }); |
michael@0 | 7159 | |
michael@0 | 7160 | lex.on("Identifier", function (ev) { |
michael@0 | 7161 | emitter.emit("Identifier", ev); |
michael@0 | 7162 | }); |
michael@0 | 7163 | |
michael@0 | 7164 | lex.on("String", function (ev) { |
michael@0 | 7165 | emitter.emit("String", ev); |
michael@0 | 7166 | }); |
michael@0 | 7167 | |
michael@0 | 7168 | lex.on("Number", function (ev) { |
michael@0 | 7169 | emitter.emit("Number", ev); |
michael@0 | 7170 | }); |
michael@0 | 7171 | |
michael@0 | 7172 | lex.start(); |
michael@0 | 7173 | |
michael@0 | 7174 | // Check options |
michael@0 | 7175 | for (var name in o) { |
michael@0 | 7176 | if (_.has(o, name)) { |
michael@0 | 7177 | checkOption(name, state.tokens.curr); |
michael@0 | 7178 | } |
michael@0 | 7179 | } |
michael@0 | 7180 | |
michael@0 | 7181 | assume(); |
michael@0 | 7182 | |
michael@0 | 7183 | // combine the passed globals after we've assumed all our options |
michael@0 | 7184 | combine(predefined, g || {}); |
michael@0 | 7185 | |
michael@0 | 7186 | //reset values |
michael@0 | 7187 | comma.first = true; |
michael@0 | 7188 | |
michael@0 | 7189 | try { |
michael@0 | 7190 | advance(); |
michael@0 | 7191 | switch (state.tokens.next.id) { |
michael@0 | 7192 | case "{": |
michael@0 | 7193 | case "[": |
michael@0 | 7194 | destructuringAssignOrJsonValue(); |
michael@0 | 7195 | break; |
michael@0 | 7196 | default: |
michael@0 | 7197 | directives(); |
michael@0 | 7198 | |
michael@0 | 7199 | if (state.directive["use strict"]) { |
michael@0 | 7200 | if (!state.option.globalstrict && !state.option.node) { |
michael@0 | 7201 | warning("W097", state.tokens.prev); |
michael@0 | 7202 | } |
michael@0 | 7203 | } |
michael@0 | 7204 | |
michael@0 | 7205 | statements(); |
michael@0 | 7206 | } |
michael@0 | 7207 | advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined); |
michael@0 | 7208 | funct["(blockscope)"].unstack(); |
michael@0 | 7209 | |
michael@0 | 7210 | var markDefined = function (name, context) { |
michael@0 | 7211 | do { |
michael@0 | 7212 | if (typeof context[name] === "string") { |
michael@0 | 7213 | // JSHINT marks unused variables as 'unused' and |
michael@0 | 7214 | // unused function declaration as 'unction'. This |
michael@0 | 7215 | // code changes such instances back 'var' and |
michael@0 | 7216 | // 'closure' so that the code in JSHINT.data() |
michael@0 | 7217 | // doesn't think they're unused. |
michael@0 | 7218 | |
michael@0 | 7219 | if (context[name] === "unused") |
michael@0 | 7220 | context[name] = "var"; |
michael@0 | 7221 | else if (context[name] === "unction") |
michael@0 | 7222 | context[name] = "closure"; |
michael@0 | 7223 | |
michael@0 | 7224 | return true; |
michael@0 | 7225 | } |
michael@0 | 7226 | |
michael@0 | 7227 | context = context["(context)"]; |
michael@0 | 7228 | } while (context); |
michael@0 | 7229 | |
michael@0 | 7230 | return false; |
michael@0 | 7231 | }; |
michael@0 | 7232 | |
michael@0 | 7233 | var clearImplied = function (name, line) { |
michael@0 | 7234 | if (!implied[name]) |
michael@0 | 7235 | return; |
michael@0 | 7236 | |
michael@0 | 7237 | var newImplied = []; |
michael@0 | 7238 | for (var i = 0; i < implied[name].length; i += 1) { |
michael@0 | 7239 | if (implied[name][i] !== line) |
michael@0 | 7240 | newImplied.push(implied[name][i]); |
michael@0 | 7241 | } |
michael@0 | 7242 | |
michael@0 | 7243 | if (newImplied.length === 0) |
michael@0 | 7244 | delete implied[name]; |
michael@0 | 7245 | else |
michael@0 | 7246 | implied[name] = newImplied; |
michael@0 | 7247 | }; |
michael@0 | 7248 | |
michael@0 | 7249 | var warnUnused = function (name, tkn, type, unused_opt) { |
michael@0 | 7250 | var line = tkn.line; |
michael@0 | 7251 | var chr = tkn.character; |
michael@0 | 7252 | |
michael@0 | 7253 | if (unused_opt === undefined) { |
michael@0 | 7254 | unused_opt = state.option.unused; |
michael@0 | 7255 | } |
michael@0 | 7256 | |
michael@0 | 7257 | if (unused_opt === true) { |
michael@0 | 7258 | unused_opt = "last-param"; |
michael@0 | 7259 | } |
michael@0 | 7260 | |
michael@0 | 7261 | var warnable_types = { |
michael@0 | 7262 | "vars": ["var"], |
michael@0 | 7263 | "last-param": ["var", "param"], |
michael@0 | 7264 | "strict": ["var", "param", "last-param"] |
michael@0 | 7265 | }; |
michael@0 | 7266 | |
michael@0 | 7267 | if (unused_opt) { |
michael@0 | 7268 | if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { |
michael@0 | 7269 | warningAt("W098", line, chr, name); |
michael@0 | 7270 | } |
michael@0 | 7271 | } |
michael@0 | 7272 | |
michael@0 | 7273 | unuseds.push({ |
michael@0 | 7274 | name: name, |
michael@0 | 7275 | line: line, |
michael@0 | 7276 | character: chr |
michael@0 | 7277 | }); |
michael@0 | 7278 | }; |
michael@0 | 7279 | |
michael@0 | 7280 | var checkUnused = function (func, key) { |
michael@0 | 7281 | var type = func[key]; |
michael@0 | 7282 | var tkn = func["(tokens)"][key]; |
michael@0 | 7283 | |
michael@0 | 7284 | if (key.charAt(0) === "(") |
michael@0 | 7285 | return; |
michael@0 | 7286 | |
michael@0 | 7287 | if (type !== "unused" && type !== "unction") |
michael@0 | 7288 | return; |
michael@0 | 7289 | |
michael@0 | 7290 | // Params are checked separately from other variables. |
michael@0 | 7291 | if (func["(params)"] && func["(params)"].indexOf(key) !== -1) |
michael@0 | 7292 | return; |
michael@0 | 7293 | |
michael@0 | 7294 | // Variable is in global scope and defined as exported. |
michael@0 | 7295 | if (func["(global)"] && _.has(exported, key)) { |
michael@0 | 7296 | return; |
michael@0 | 7297 | } |
michael@0 | 7298 | |
michael@0 | 7299 | warnUnused(key, tkn, "var"); |
michael@0 | 7300 | }; |
michael@0 | 7301 | |
michael@0 | 7302 | // Check queued 'x is not defined' instances to see if they're still undefined. |
michael@0 | 7303 | for (i = 0; i < JSHINT.undefs.length; i += 1) { |
michael@0 | 7304 | k = JSHINT.undefs[i].slice(0); |
michael@0 | 7305 | |
michael@0 | 7306 | if (markDefined(k[2].value, k[0])) { |
michael@0 | 7307 | clearImplied(k[2].value, k[2].line); |
michael@0 | 7308 | } else if (state.option.undef) { |
michael@0 | 7309 | warning.apply(warning, k.slice(1)); |
michael@0 | 7310 | } |
michael@0 | 7311 | } |
michael@0 | 7312 | |
michael@0 | 7313 | functions.forEach(function (func) { |
michael@0 | 7314 | if (func["(unusedOption)"] === false) { |
michael@0 | 7315 | return; |
michael@0 | 7316 | } |
michael@0 | 7317 | |
michael@0 | 7318 | for (var key in func) { |
michael@0 | 7319 | if (_.has(func, key)) { |
michael@0 | 7320 | checkUnused(func, key); |
michael@0 | 7321 | } |
michael@0 | 7322 | } |
michael@0 | 7323 | |
michael@0 | 7324 | if (!func["(params)"]) |
michael@0 | 7325 | return; |
michael@0 | 7326 | |
michael@0 | 7327 | var params = func["(params)"].slice(); |
michael@0 | 7328 | var param = params.pop(); |
michael@0 | 7329 | var type, unused_opt; |
michael@0 | 7330 | |
michael@0 | 7331 | while (param) { |
michael@0 | 7332 | type = func[param]; |
michael@0 | 7333 | unused_opt = func["(unusedOption)"] || state.option.unused; |
michael@0 | 7334 | unused_opt = unused_opt === true ? "last-param" : unused_opt; |
michael@0 | 7335 | |
michael@0 | 7336 | // 'undefined' is a special case for (function (window, undefined) { ... })(); |
michael@0 | 7337 | // patterns. |
michael@0 | 7338 | |
michael@0 | 7339 | if (param === "undefined") |
michael@0 | 7340 | return; |
michael@0 | 7341 | |
michael@0 | 7342 | if (type === "unused" || type === "unction") { |
michael@0 | 7343 | warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); |
michael@0 | 7344 | } else if (unused_opt === "last-param") { |
michael@0 | 7345 | return; |
michael@0 | 7346 | } |
michael@0 | 7347 | |
michael@0 | 7348 | param = params.pop(); |
michael@0 | 7349 | } |
michael@0 | 7350 | }); |
michael@0 | 7351 | |
michael@0 | 7352 | for (var key in declared) { |
michael@0 | 7353 | if (_.has(declared, key) && !_.has(global, key)) { |
michael@0 | 7354 | warnUnused(key, declared[key], "var"); |
michael@0 | 7355 | } |
michael@0 | 7356 | } |
michael@0 | 7357 | |
michael@0 | 7358 | } catch (err) { |
michael@0 | 7359 | if (err && err.name === "JSHintError") { |
michael@0 | 7360 | var nt = state.tokens.next || {}; |
michael@0 | 7361 | JSHINT.errors.push({ |
michael@0 | 7362 | scope : "(main)", |
michael@0 | 7363 | raw : err.raw, |
michael@0 | 7364 | reason : err.message, |
michael@0 | 7365 | line : err.line || nt.line, |
michael@0 | 7366 | character : err.character || nt.from |
michael@0 | 7367 | }, null); |
michael@0 | 7368 | } else { |
michael@0 | 7369 | throw err; |
michael@0 | 7370 | } |
michael@0 | 7371 | } |
michael@0 | 7372 | |
michael@0 | 7373 | // Loop over the listed "internals", and check them as well. |
michael@0 | 7374 | |
michael@0 | 7375 | if (JSHINT.scope === "(main)") { |
michael@0 | 7376 | o = o || {}; |
michael@0 | 7377 | |
michael@0 | 7378 | for (i = 0; i < JSHINT.internals.length; i += 1) { |
michael@0 | 7379 | k = JSHINT.internals[i]; |
michael@0 | 7380 | o.scope = k.elem; |
michael@0 | 7381 | itself(k.value, o, g); |
michael@0 | 7382 | } |
michael@0 | 7383 | } |
michael@0 | 7384 | |
michael@0 | 7385 | return JSHINT.errors.length === 0; |
michael@0 | 7386 | }; |
michael@0 | 7387 | |
michael@0 | 7388 | // Modules. |
michael@0 | 7389 | itself.addModule = function (func) { |
michael@0 | 7390 | extraModules.push(func); |
michael@0 | 7391 | }; |
michael@0 | 7392 | |
michael@0 | 7393 | itself.addModule(style.register); |
michael@0 | 7394 | |
michael@0 | 7395 | // Data summary. |
michael@0 | 7396 | itself.data = function () { |
michael@0 | 7397 | var data = { |
michael@0 | 7398 | functions: [], |
michael@0 | 7399 | options: state.option |
michael@0 | 7400 | }; |
michael@0 | 7401 | var implieds = []; |
michael@0 | 7402 | var members = []; |
michael@0 | 7403 | var fu, f, i, j, n, globals; |
michael@0 | 7404 | |
michael@0 | 7405 | if (itself.errors.length) { |
michael@0 | 7406 | data.errors = itself.errors; |
michael@0 | 7407 | } |
michael@0 | 7408 | |
michael@0 | 7409 | if (state.jsonMode) { |
michael@0 | 7410 | data.json = true; |
michael@0 | 7411 | } |
michael@0 | 7412 | |
michael@0 | 7413 | for (n in implied) { |
michael@0 | 7414 | if (_.has(implied, n)) { |
michael@0 | 7415 | implieds.push({ |
michael@0 | 7416 | name: n, |
michael@0 | 7417 | line: implied[n] |
michael@0 | 7418 | }); |
michael@0 | 7419 | } |
michael@0 | 7420 | } |
michael@0 | 7421 | |
michael@0 | 7422 | if (implieds.length > 0) { |
michael@0 | 7423 | data.implieds = implieds; |
michael@0 | 7424 | } |
michael@0 | 7425 | |
michael@0 | 7426 | if (urls.length > 0) { |
michael@0 | 7427 | data.urls = urls; |
michael@0 | 7428 | } |
michael@0 | 7429 | |
michael@0 | 7430 | globals = Object.keys(scope); |
michael@0 | 7431 | if (globals.length > 0) { |
michael@0 | 7432 | data.globals = globals; |
michael@0 | 7433 | } |
michael@0 | 7434 | |
michael@0 | 7435 | for (i = 1; i < functions.length; i += 1) { |
michael@0 | 7436 | f = functions[i]; |
michael@0 | 7437 | fu = {}; |
michael@0 | 7438 | |
michael@0 | 7439 | for (j = 0; j < functionicity.length; j += 1) { |
michael@0 | 7440 | fu[functionicity[j]] = []; |
michael@0 | 7441 | } |
michael@0 | 7442 | |
michael@0 | 7443 | for (j = 0; j < functionicity.length; j += 1) { |
michael@0 | 7444 | if (fu[functionicity[j]].length === 0) { |
michael@0 | 7445 | delete fu[functionicity[j]]; |
michael@0 | 7446 | } |
michael@0 | 7447 | } |
michael@0 | 7448 | |
michael@0 | 7449 | fu.name = f["(name)"]; |
michael@0 | 7450 | fu.param = f["(params)"]; |
michael@0 | 7451 | fu.line = f["(line)"]; |
michael@0 | 7452 | fu.character = f["(character)"]; |
michael@0 | 7453 | fu.last = f["(last)"]; |
michael@0 | 7454 | fu.lastcharacter = f["(lastcharacter)"]; |
michael@0 | 7455 | data.functions.push(fu); |
michael@0 | 7456 | } |
michael@0 | 7457 | |
michael@0 | 7458 | if (unuseds.length > 0) { |
michael@0 | 7459 | data.unused = unuseds; |
michael@0 | 7460 | } |
michael@0 | 7461 | |
michael@0 | 7462 | members = []; |
michael@0 | 7463 | for (n in member) { |
michael@0 | 7464 | if (typeof member[n] === "number") { |
michael@0 | 7465 | data.member = member; |
michael@0 | 7466 | break; |
michael@0 | 7467 | } |
michael@0 | 7468 | } |
michael@0 | 7469 | |
michael@0 | 7470 | return data; |
michael@0 | 7471 | }; |
michael@0 | 7472 | |
michael@0 | 7473 | itself.jshint = itself; |
michael@0 | 7474 | |
michael@0 | 7475 | return itself; |
michael@0 | 7476 | }()); |
michael@0 | 7477 | |
michael@0 | 7478 | // Make JSHINT a Node module, if possible. |
michael@0 | 7479 | if (typeof exports === "object" && exports) { |
michael@0 | 7480 | exports.JSHINT = JSHINT; |
michael@0 | 7481 | } |
michael@0 | 7482 | |
michael@0 | 7483 | })() |
michael@0 | 7484 | },{"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 | 7485 | (function(){"use strict"; |
michael@0 | 7486 | |
michael@0 | 7487 | var _ = require("underscore"); |
michael@0 | 7488 | |
michael@0 | 7489 | var errors = { |
michael@0 | 7490 | // JSHint options |
michael@0 | 7491 | E001: "Bad option: '{a}'.", |
michael@0 | 7492 | E002: "Bad option value.", |
michael@0 | 7493 | |
michael@0 | 7494 | // JSHint input |
michael@0 | 7495 | E003: "Expected a JSON value.", |
michael@0 | 7496 | E004: "Input is neither a string nor an array of strings.", |
michael@0 | 7497 | E005: "Input is empty.", |
michael@0 | 7498 | E006: "Unexpected early end of program.", |
michael@0 | 7499 | |
michael@0 | 7500 | // Strict mode |
michael@0 | 7501 | E007: "Missing \"use strict\" statement.", |
michael@0 | 7502 | E008: "Strict violation.", |
michael@0 | 7503 | E009: "Option 'validthis' can't be used in a global scope.", |
michael@0 | 7504 | E010: "'with' is not allowed in strict mode.", |
michael@0 | 7505 | |
michael@0 | 7506 | // Constants |
michael@0 | 7507 | E011: "const '{a}' has already been declared.", |
michael@0 | 7508 | E012: "const '{a}' is initialized to 'undefined'.", |
michael@0 | 7509 | E013: "Attempting to override '{a}' which is a constant.", |
michael@0 | 7510 | |
michael@0 | 7511 | // Regular expressions |
michael@0 | 7512 | E014: "A regular expression literal can be confused with '/='.", |
michael@0 | 7513 | E015: "Unclosed regular expression.", |
michael@0 | 7514 | E016: "Invalid regular expression.", |
michael@0 | 7515 | |
michael@0 | 7516 | // Tokens |
michael@0 | 7517 | E017: "Unclosed comment.", |
michael@0 | 7518 | E018: "Unbegun comment.", |
michael@0 | 7519 | E019: "Unmatched '{a}'.", |
michael@0 | 7520 | E020: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", |
michael@0 | 7521 | E021: "Expected '{a}' and instead saw '{b}'.", |
michael@0 | 7522 | E022: "Line breaking error '{a}'.", |
michael@0 | 7523 | E023: "Missing '{a}'.", |
michael@0 | 7524 | E024: "Unexpected '{a}'.", |
michael@0 | 7525 | E025: "Missing ':' on a case clause.", |
michael@0 | 7526 | E026: "Missing '}' to match '{' from line {a}.", |
michael@0 | 7527 | E027: "Missing ']' to match '[' form line {a}.", |
michael@0 | 7528 | E028: "Illegal comma.", |
michael@0 | 7529 | E029: "Unclosed string.", |
michael@0 | 7530 | |
michael@0 | 7531 | // Everything else |
michael@0 | 7532 | E030: "Expected an identifier and instead saw '{a}'.", |
michael@0 | 7533 | E031: "Bad assignment.", // FIXME: Rephrase |
michael@0 | 7534 | E032: "Expected a small integer or 'false' and instead saw '{a}'.", |
michael@0 | 7535 | E033: "Expected an operator and instead saw '{a}'.", |
michael@0 | 7536 | E034: "get/set are ES5 features.", |
michael@0 | 7537 | E035: "Missing property name.", |
michael@0 | 7538 | E036: "Expected to see a statement and instead saw a block.", |
michael@0 | 7539 | E037: "Constant {a} was not declared correctly.", |
michael@0 | 7540 | E038: "Variable {a} was not declared correctly.", |
michael@0 | 7541 | E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.", |
michael@0 | 7542 | E040: "Each value should have its own case label.", |
michael@0 | 7543 | E041: "Unrecoverable syntax error.", |
michael@0 | 7544 | E042: "Stopping.", |
michael@0 | 7545 | E043: "Too many errors.", |
michael@0 | 7546 | E044: "'{a}' is already defined and can't be redefined.", |
michael@0 | 7547 | E045: "Invalid for each loop.", |
michael@0 | 7548 | E046: "A yield statement shall be within a generator function (with syntax: `function*`)", |
michael@0 | 7549 | E047: "A generator function shall contain a yield statement.", |
michael@0 | 7550 | E048: "Let declaration not directly within block.", |
michael@0 | 7551 | E049: "A {a} cannot be named '{b}'." |
michael@0 | 7552 | }; |
michael@0 | 7553 | |
michael@0 | 7554 | var warnings = { |
michael@0 | 7555 | W001: "'hasOwnProperty' is a really bad name.", |
michael@0 | 7556 | W002: "Value of '{a}' may be overwritten in IE.", |
michael@0 | 7557 | W003: "'{a}' was used before it was defined.", |
michael@0 | 7558 | W004: "'{a}' is already defined.", |
michael@0 | 7559 | W005: "A dot following a number can be confused with a decimal point.", |
michael@0 | 7560 | W006: "Confusing minuses.", |
michael@0 | 7561 | W007: "Confusing pluses.", |
michael@0 | 7562 | W008: "A leading decimal point can be confused with a dot: '{a}'.", |
michael@0 | 7563 | W009: "The array literal notation [] is preferrable.", |
michael@0 | 7564 | W010: "The object literal notation {} is preferrable.", |
michael@0 | 7565 | W011: "Unexpected space after '{a}'.", |
michael@0 | 7566 | W012: "Unexpected space before '{a}'.", |
michael@0 | 7567 | W013: "Missing space after '{a}'.", |
michael@0 | 7568 | W014: "Bad line breaking before '{a}'.", |
michael@0 | 7569 | W015: "Expected '{a}' to have an indentation at {b} instead at {c}.", |
michael@0 | 7570 | W016: "Unexpected use of '{a}'.", |
michael@0 | 7571 | W017: "Bad operand.", |
michael@0 | 7572 | W018: "Confusing use of '{a}'.", |
michael@0 | 7573 | W019: "Use the isNaN function to compare with NaN.", |
michael@0 | 7574 | W020: "Read only.", |
michael@0 | 7575 | W021: "'{a}' is a function.", |
michael@0 | 7576 | W022: "Do not assign to the exception parameter.", |
michael@0 | 7577 | W023: "Expected an identifier in an assignment and instead saw a function invocation.", |
michael@0 | 7578 | W024: "Expected an identifier and instead saw '{a}' (a reserved word).", |
michael@0 | 7579 | W025: "Missing name in function declaration.", |
michael@0 | 7580 | W026: "Inner functions should be listed at the top of the outer function.", |
michael@0 | 7581 | W027: "Unreachable '{a}' after '{b}'.", |
michael@0 | 7582 | W028: "Label '{a}' on {b} statement.", |
michael@0 | 7583 | W030: "Expected an assignment or function call and instead saw an expression.", |
michael@0 | 7584 | W031: "Do not use 'new' for side effects.", |
michael@0 | 7585 | W032: "Unnecessary semicolon.", |
michael@0 | 7586 | W033: "Missing semicolon.", |
michael@0 | 7587 | W034: "Unnecessary directive \"{a}\".", |
michael@0 | 7588 | W035: "Empty block.", |
michael@0 | 7589 | W036: "Unexpected /*member '{a}'.", |
michael@0 | 7590 | W037: "'{a}' is a statement label.", |
michael@0 | 7591 | W038: "'{a}' used out of scope.", |
michael@0 | 7592 | W039: "'{a}' is not allowed.", |
michael@0 | 7593 | W040: "Possible strict violation.", |
michael@0 | 7594 | W041: "Use '{a}' to compare with '{b}'.", |
michael@0 | 7595 | W042: "Avoid EOL escaping.", |
michael@0 | 7596 | W043: "Bad escaping of EOL. Use option multistr if needed.", |
michael@0 | 7597 | W044: "Bad or unnecessary escaping.", |
michael@0 | 7598 | W045: "Bad number '{a}'.", |
michael@0 | 7599 | W046: "Don't use extra leading zeros '{a}'.", |
michael@0 | 7600 | W047: "A trailing decimal point can be confused with a dot: '{a}'.", |
michael@0 | 7601 | W048: "Unexpected control character in regular expression.", |
michael@0 | 7602 | W049: "Unexpected escaped character '{a}' in regular expression.", |
michael@0 | 7603 | W050: "JavaScript URL.", |
michael@0 | 7604 | W051: "Variables should not be deleted.", |
michael@0 | 7605 | W052: "Unexpected '{a}'.", |
michael@0 | 7606 | W053: "Do not use {a} as a constructor.", |
michael@0 | 7607 | W054: "The Function constructor is a form of eval.", |
michael@0 | 7608 | W055: "A constructor name should start with an uppercase letter.", |
michael@0 | 7609 | W056: "Bad constructor.", |
michael@0 | 7610 | W057: "Weird construction. Is 'new' unnecessary?", |
michael@0 | 7611 | W058: "Missing '()' invoking a constructor.", |
michael@0 | 7612 | W059: "Avoid arguments.{a}.", |
michael@0 | 7613 | W060: "document.write can be a form of eval.", |
michael@0 | 7614 | W061: "eval can be harmful.", |
michael@0 | 7615 | W062: "Wrap an immediate function invocation in parens " + |
michael@0 | 7616 | "to assist the reader in understanding that the expression " + |
michael@0 | 7617 | "is the result of a function, and not the function itself.", |
michael@0 | 7618 | W063: "Math is not a function.", |
michael@0 | 7619 | W064: "Missing 'new' prefix when invoking a constructor.", |
michael@0 | 7620 | W065: "Missing radix parameter.", |
michael@0 | 7621 | W066: "Implied eval. Consider passing a function instead of a string.", |
michael@0 | 7622 | W067: "Bad invocation.", |
michael@0 | 7623 | W068: "Wrapping non-IIFE function literals in parens is unnecessary.", |
michael@0 | 7624 | W069: "['{a}'] is better written in dot notation.", |
michael@0 | 7625 | W070: "Extra comma. (it breaks older versions of IE)", |
michael@0 | 7626 | W071: "This function has too many statements. ({a})", |
michael@0 | 7627 | W072: "This function has too many parameters. ({a})", |
michael@0 | 7628 | W073: "Blocks are nested too deeply. ({a})", |
michael@0 | 7629 | W074: "This function's cyclomatic complexity is too high. ({a})", |
michael@0 | 7630 | W075: "Duplicate key '{a}'.", |
michael@0 | 7631 | W076: "Unexpected parameter '{a}' in get {b} function.", |
michael@0 | 7632 | W077: "Expected a single parameter in set {a} function.", |
michael@0 | 7633 | W078: "Setter is defined without getter.", |
michael@0 | 7634 | W079: "Redefinition of '{a}'.", |
michael@0 | 7635 | W080: "It's not necessary to initialize '{a}' to 'undefined'.", |
michael@0 | 7636 | W081: "Too many var statements.", |
michael@0 | 7637 | W082: "Function declarations should not be placed in blocks. " + |
michael@0 | 7638 | "Use a function expression or move the statement to the top of " + |
michael@0 | 7639 | "the outer function.", |
michael@0 | 7640 | W083: "Don't make functions within a loop.", |
michael@0 | 7641 | W084: "Expected a conditional expression and instead saw an assignment.", |
michael@0 | 7642 | W085: "Don't use 'with'.", |
michael@0 | 7643 | W086: "Expected a 'break' statement before '{a}'.", |
michael@0 | 7644 | W087: "Forgotten 'debugger' statement?", |
michael@0 | 7645 | W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.", |
michael@0 | 7646 | W089: "The body of a for in should be wrapped in an if statement to filter " + |
michael@0 | 7647 | "unwanted properties from the prototype.", |
michael@0 | 7648 | W090: "'{a}' is not a statement label.", |
michael@0 | 7649 | W091: "'{a}' is out of scope.", |
michael@0 | 7650 | W092: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.", |
michael@0 | 7651 | W093: "Did you mean to return a conditional instead of an assignment?", |
michael@0 | 7652 | W094: "Unexpected comma.", |
michael@0 | 7653 | W095: "Expected a string and instead saw {a}.", |
michael@0 | 7654 | W096: "The '{a}' key may produce unexpected results.", |
michael@0 | 7655 | W097: "Use the function form of \"use strict\".", |
michael@0 | 7656 | W098: "'{a}' is defined but never used.", |
michael@0 | 7657 | W099: "Mixed spaces and tabs.", |
michael@0 | 7658 | W100: "This character may get silently deleted by one or more browsers.", |
michael@0 | 7659 | W101: "Line is too long.", |
michael@0 | 7660 | W102: "Trailing whitespace.", |
michael@0 | 7661 | W103: "The '{a}' property is deprecated.", |
michael@0 | 7662 | W104: "'{a}' is only available in JavaScript 1.7.", |
michael@0 | 7663 | W105: "Unexpected {a} in '{b}'.", |
michael@0 | 7664 | W106: "Identifier '{a}' is not in camel case.", |
michael@0 | 7665 | W107: "Script URL.", |
michael@0 | 7666 | W108: "Strings must use doublequote.", |
michael@0 | 7667 | W109: "Strings must use singlequote.", |
michael@0 | 7668 | W110: "Mixed double and single quotes.", |
michael@0 | 7669 | W112: "Unclosed string.", |
michael@0 | 7670 | W113: "Control character in string: {a}.", |
michael@0 | 7671 | W114: "Avoid {a}.", |
michael@0 | 7672 | W115: "Octal literals are not allowed in strict mode.", |
michael@0 | 7673 | W116: "Expected '{a}' and instead saw '{b}'.", |
michael@0 | 7674 | W117: "'{a}' is not defined.", |
michael@0 | 7675 | W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", |
michael@0 | 7676 | W119: "'{a}' is only available in ES6 (use esnext option)." |
michael@0 | 7677 | }; |
michael@0 | 7678 | |
michael@0 | 7679 | var info = { |
michael@0 | 7680 | I001: "Comma warnings can be turned off with 'laxcomma'.", |
michael@0 | 7681 | I002: "Reserved words as properties can be used under the 'es5' option.", |
michael@0 | 7682 | I003: "ES5 option is now set per default" |
michael@0 | 7683 | }; |
michael@0 | 7684 | |
michael@0 | 7685 | exports.errors = {}; |
michael@0 | 7686 | exports.warnings = {}; |
michael@0 | 7687 | exports.info = {}; |
michael@0 | 7688 | |
michael@0 | 7689 | _.each(errors, function (desc, code) { |
michael@0 | 7690 | exports.errors[code] = { code: code, desc: desc }; |
michael@0 | 7691 | }); |
michael@0 | 7692 | |
michael@0 | 7693 | _.each(warnings, function (desc, code) { |
michael@0 | 7694 | exports.warnings[code] = { code: code, desc: desc }; |
michael@0 | 7695 | }); |
michael@0 | 7696 | |
michael@0 | 7697 | _.each(info, function (desc, code) { |
michael@0 | 7698 | exports.info[code] = { code: code, desc: desc }; |
michael@0 | 7699 | }); |
michael@0 | 7700 | |
michael@0 | 7701 | })() |
michael@0 | 7702 | },{"underscore":11}],8:[function(require,module,exports){ |
michael@0 | 7703 | var events = require('events'); |
michael@0 | 7704 | |
michael@0 | 7705 | exports.isArray = isArray; |
michael@0 | 7706 | exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'}; |
michael@0 | 7707 | exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'}; |
michael@0 | 7708 | |
michael@0 | 7709 | |
michael@0 | 7710 | exports.print = function () {}; |
michael@0 | 7711 | exports.puts = function () {}; |
michael@0 | 7712 | exports.debug = function() {}; |
michael@0 | 7713 | |
michael@0 | 7714 | exports.inspect = function(obj, showHidden, depth, colors) { |
michael@0 | 7715 | var seen = []; |
michael@0 | 7716 | |
michael@0 | 7717 | var stylize = function(str, styleType) { |
michael@0 | 7718 | // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics |
michael@0 | 7719 | var styles = |
michael@0 | 7720 | { 'bold' : [1, 22], |
michael@0 | 7721 | 'italic' : [3, 23], |
michael@0 | 7722 | 'underline' : [4, 24], |
michael@0 | 7723 | 'inverse' : [7, 27], |
michael@0 | 7724 | 'white' : [37, 39], |
michael@0 | 7725 | 'grey' : [90, 39], |
michael@0 | 7726 | 'black' : [30, 39], |
michael@0 | 7727 | 'blue' : [34, 39], |
michael@0 | 7728 | 'cyan' : [36, 39], |
michael@0 | 7729 | 'green' : [32, 39], |
michael@0 | 7730 | 'magenta' : [35, 39], |
michael@0 | 7731 | 'red' : [31, 39], |
michael@0 | 7732 | 'yellow' : [33, 39] }; |
michael@0 | 7733 | |
michael@0 | 7734 | var style = |
michael@0 | 7735 | { 'special': 'cyan', |
michael@0 | 7736 | 'number': 'blue', |
michael@0 | 7737 | 'boolean': 'yellow', |
michael@0 | 7738 | 'undefined': 'grey', |
michael@0 | 7739 | 'null': 'bold', |
michael@0 | 7740 | 'string': 'green', |
michael@0 | 7741 | 'date': 'magenta', |
michael@0 | 7742 | // "name": intentionally not styling |
michael@0 | 7743 | 'regexp': 'red' }[styleType]; |
michael@0 | 7744 | |
michael@0 | 7745 | if (style) { |
michael@0 | 7746 | return '\033[' + styles[style][0] + 'm' + str + |
michael@0 | 7747 | '\033[' + styles[style][1] + 'm'; |
michael@0 | 7748 | } else { |
michael@0 | 7749 | return str; |
michael@0 | 7750 | } |
michael@0 | 7751 | }; |
michael@0 | 7752 | if (! colors) { |
michael@0 | 7753 | stylize = function(str, styleType) { return str; }; |
michael@0 | 7754 | } |
michael@0 | 7755 | |
michael@0 | 7756 | function format(value, recurseTimes) { |
michael@0 | 7757 | // Provide a hook for user-specified inspect functions. |
michael@0 | 7758 | // Check that value is an object with an inspect function on it |
michael@0 | 7759 | if (value && typeof value.inspect === 'function' && |
michael@0 | 7760 | // Filter out the util module, it's inspect function is special |
michael@0 | 7761 | value !== exports && |
michael@0 | 7762 | // Also filter out any prototype objects using the circular check. |
michael@0 | 7763 | !(value.constructor && value.constructor.prototype === value)) { |
michael@0 | 7764 | return value.inspect(recurseTimes); |
michael@0 | 7765 | } |
michael@0 | 7766 | |
michael@0 | 7767 | // Primitive types cannot have properties |
michael@0 | 7768 | switch (typeof value) { |
michael@0 | 7769 | case 'undefined': |
michael@0 | 7770 | return stylize('undefined', 'undefined'); |
michael@0 | 7771 | |
michael@0 | 7772 | case 'string': |
michael@0 | 7773 | var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') |
michael@0 | 7774 | .replace(/'/g, "\\'") |
michael@0 | 7775 | .replace(/\\"/g, '"') + '\''; |
michael@0 | 7776 | return stylize(simple, 'string'); |
michael@0 | 7777 | |
michael@0 | 7778 | case 'number': |
michael@0 | 7779 | return stylize('' + value, 'number'); |
michael@0 | 7780 | |
michael@0 | 7781 | case 'boolean': |
michael@0 | 7782 | return stylize('' + value, 'boolean'); |
michael@0 | 7783 | } |
michael@0 | 7784 | // For some reason typeof null is "object", so special case here. |
michael@0 | 7785 | if (value === null) { |
michael@0 | 7786 | return stylize('null', 'null'); |
michael@0 | 7787 | } |
michael@0 | 7788 | |
michael@0 | 7789 | // Look up the keys of the object. |
michael@0 | 7790 | var visible_keys = Object_keys(value); |
michael@0 | 7791 | var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys; |
michael@0 | 7792 | |
michael@0 | 7793 | // Functions without properties can be shortcutted. |
michael@0 | 7794 | if (typeof value === 'function' && keys.length === 0) { |
michael@0 | 7795 | if (isRegExp(value)) { |
michael@0 | 7796 | return stylize('' + value, 'regexp'); |
michael@0 | 7797 | } else { |
michael@0 | 7798 | var name = value.name ? ': ' + value.name : ''; |
michael@0 | 7799 | return stylize('[Function' + name + ']', 'special'); |
michael@0 | 7800 | } |
michael@0 | 7801 | } |
michael@0 | 7802 | |
michael@0 | 7803 | // Dates without properties can be shortcutted |
michael@0 | 7804 | if (isDate(value) && keys.length === 0) { |
michael@0 | 7805 | return stylize(value.toUTCString(), 'date'); |
michael@0 | 7806 | } |
michael@0 | 7807 | |
michael@0 | 7808 | var base, type, braces; |
michael@0 | 7809 | // Determine the object type |
michael@0 | 7810 | if (isArray(value)) { |
michael@0 | 7811 | type = 'Array'; |
michael@0 | 7812 | braces = ['[', ']']; |
michael@0 | 7813 | } else { |
michael@0 | 7814 | type = 'Object'; |
michael@0 | 7815 | braces = ['{', '}']; |
michael@0 | 7816 | } |
michael@0 | 7817 | |
michael@0 | 7818 | // Make functions say that they are functions |
michael@0 | 7819 | if (typeof value === 'function') { |
michael@0 | 7820 | var n = value.name ? ': ' + value.name : ''; |
michael@0 | 7821 | base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; |
michael@0 | 7822 | } else { |
michael@0 | 7823 | base = ''; |
michael@0 | 7824 | } |
michael@0 | 7825 | |
michael@0 | 7826 | // Make dates with properties first say the date |
michael@0 | 7827 | if (isDate(value)) { |
michael@0 | 7828 | base = ' ' + value.toUTCString(); |
michael@0 | 7829 | } |
michael@0 | 7830 | |
michael@0 | 7831 | if (keys.length === 0) { |
michael@0 | 7832 | return braces[0] + base + braces[1]; |
michael@0 | 7833 | } |
michael@0 | 7834 | |
michael@0 | 7835 | if (recurseTimes < 0) { |
michael@0 | 7836 | if (isRegExp(value)) { |
michael@0 | 7837 | return stylize('' + value, 'regexp'); |
michael@0 | 7838 | } else { |
michael@0 | 7839 | return stylize('[Object]', 'special'); |
michael@0 | 7840 | } |
michael@0 | 7841 | } |
michael@0 | 7842 | |
michael@0 | 7843 | seen.push(value); |
michael@0 | 7844 | |
michael@0 | 7845 | var output = keys.map(function(key) { |
michael@0 | 7846 | var name, str; |
michael@0 | 7847 | if (value.__lookupGetter__) { |
michael@0 | 7848 | if (value.__lookupGetter__(key)) { |
michael@0 | 7849 | if (value.__lookupSetter__(key)) { |
michael@0 | 7850 | str = stylize('[Getter/Setter]', 'special'); |
michael@0 | 7851 | } else { |
michael@0 | 7852 | str = stylize('[Getter]', 'special'); |
michael@0 | 7853 | } |
michael@0 | 7854 | } else { |
michael@0 | 7855 | if (value.__lookupSetter__(key)) { |
michael@0 | 7856 | str = stylize('[Setter]', 'special'); |
michael@0 | 7857 | } |
michael@0 | 7858 | } |
michael@0 | 7859 | } |
michael@0 | 7860 | if (visible_keys.indexOf(key) < 0) { |
michael@0 | 7861 | name = '[' + key + ']'; |
michael@0 | 7862 | } |
michael@0 | 7863 | if (!str) { |
michael@0 | 7864 | if (seen.indexOf(value[key]) < 0) { |
michael@0 | 7865 | if (recurseTimes === null) { |
michael@0 | 7866 | str = format(value[key]); |
michael@0 | 7867 | } else { |
michael@0 | 7868 | str = format(value[key], recurseTimes - 1); |
michael@0 | 7869 | } |
michael@0 | 7870 | if (str.indexOf('\n') > -1) { |
michael@0 | 7871 | if (isArray(value)) { |
michael@0 | 7872 | str = str.split('\n').map(function(line) { |
michael@0 | 7873 | return ' ' + line; |
michael@0 | 7874 | }).join('\n').substr(2); |
michael@0 | 7875 | } else { |
michael@0 | 7876 | str = '\n' + str.split('\n').map(function(line) { |
michael@0 | 7877 | return ' ' + line; |
michael@0 | 7878 | }).join('\n'); |
michael@0 | 7879 | } |
michael@0 | 7880 | } |
michael@0 | 7881 | } else { |
michael@0 | 7882 | str = stylize('[Circular]', 'special'); |
michael@0 | 7883 | } |
michael@0 | 7884 | } |
michael@0 | 7885 | if (typeof name === 'undefined') { |
michael@0 | 7886 | if (type === 'Array' && key.match(/^\d+$/)) { |
michael@0 | 7887 | return str; |
michael@0 | 7888 | } |
michael@0 | 7889 | name = JSON.stringify('' + key); |
michael@0 | 7890 | if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { |
michael@0 | 7891 | name = name.substr(1, name.length - 2); |
michael@0 | 7892 | name = stylize(name, 'name'); |
michael@0 | 7893 | } else { |
michael@0 | 7894 | name = name.replace(/'/g, "\\'") |
michael@0 | 7895 | .replace(/\\"/g, '"') |
michael@0 | 7896 | .replace(/(^"|"$)/g, "'"); |
michael@0 | 7897 | name = stylize(name, 'string'); |
michael@0 | 7898 | } |
michael@0 | 7899 | } |
michael@0 | 7900 | |
michael@0 | 7901 | return name + ': ' + str; |
michael@0 | 7902 | }); |
michael@0 | 7903 | |
michael@0 | 7904 | seen.pop(); |
michael@0 | 7905 | |
michael@0 | 7906 | var numLinesEst = 0; |
michael@0 | 7907 | var length = output.reduce(function(prev, cur) { |
michael@0 | 7908 | numLinesEst++; |
michael@0 | 7909 | if (cur.indexOf('\n') >= 0) numLinesEst++; |
michael@0 | 7910 | return prev + cur.length + 1; |
michael@0 | 7911 | }, 0); |
michael@0 | 7912 | |
michael@0 | 7913 | if (length > 50) { |
michael@0 | 7914 | output = braces[0] + |
michael@0 | 7915 | (base === '' ? '' : base + '\n ') + |
michael@0 | 7916 | ' ' + |
michael@0 | 7917 | output.join(',\n ') + |
michael@0 | 7918 | ' ' + |
michael@0 | 7919 | braces[1]; |
michael@0 | 7920 | |
michael@0 | 7921 | } else { |
michael@0 | 7922 | output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; |
michael@0 | 7923 | } |
michael@0 | 7924 | |
michael@0 | 7925 | return output; |
michael@0 | 7926 | } |
michael@0 | 7927 | return format(obj, (typeof depth === 'undefined' ? 2 : depth)); |
michael@0 | 7928 | }; |
michael@0 | 7929 | |
michael@0 | 7930 | |
michael@0 | 7931 | function isArray(ar) { |
michael@0 | 7932 | return ar instanceof Array || |
michael@0 | 7933 | Array.isArray(ar) || |
michael@0 | 7934 | (ar && ar !== Object.prototype && isArray(ar.__proto__)); |
michael@0 | 7935 | } |
michael@0 | 7936 | |
michael@0 | 7937 | |
michael@0 | 7938 | function isRegExp(re) { |
michael@0 | 7939 | return re instanceof RegExp || |
michael@0 | 7940 | (typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]'); |
michael@0 | 7941 | } |
michael@0 | 7942 | |
michael@0 | 7943 | |
michael@0 | 7944 | function isDate(d) { |
michael@0 | 7945 | if (d instanceof Date) return true; |
michael@0 | 7946 | if (typeof d !== 'object') return false; |
michael@0 | 7947 | var properties = Date.prototype && Object_getOwnPropertyNames(Date.prototype); |
michael@0 | 7948 | var proto = d.__proto__ && Object_getOwnPropertyNames(d.__proto__); |
michael@0 | 7949 | return JSON.stringify(proto) === JSON.stringify(properties); |
michael@0 | 7950 | } |
michael@0 | 7951 | |
michael@0 | 7952 | function pad(n) { |
michael@0 | 7953 | return n < 10 ? '0' + n.toString(10) : n.toString(10); |
michael@0 | 7954 | } |
michael@0 | 7955 | |
michael@0 | 7956 | var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', |
michael@0 | 7957 | 'Oct', 'Nov', 'Dec']; |
michael@0 | 7958 | |
michael@0 | 7959 | // 26 Feb 16:19:34 |
michael@0 | 7960 | function timestamp() { |
michael@0 | 7961 | var d = new Date(); |
michael@0 | 7962 | var time = [pad(d.getHours()), |
michael@0 | 7963 | pad(d.getMinutes()), |
michael@0 | 7964 | pad(d.getSeconds())].join(':'); |
michael@0 | 7965 | return [d.getDate(), months[d.getMonth()], time].join(' '); |
michael@0 | 7966 | } |
michael@0 | 7967 | |
michael@0 | 7968 | exports.log = function (msg) {}; |
michael@0 | 7969 | |
michael@0 | 7970 | exports.pump = null; |
michael@0 | 7971 | |
michael@0 | 7972 | var Object_keys = Object.keys || function (obj) { |
michael@0 | 7973 | var res = []; |
michael@0 | 7974 | for (var key in obj) res.push(key); |
michael@0 | 7975 | return res; |
michael@0 | 7976 | }; |
michael@0 | 7977 | |
michael@0 | 7978 | var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) { |
michael@0 | 7979 | var res = []; |
michael@0 | 7980 | for (var key in obj) { |
michael@0 | 7981 | if (Object.hasOwnProperty.call(obj, key)) res.push(key); |
michael@0 | 7982 | } |
michael@0 | 7983 | return res; |
michael@0 | 7984 | }; |
michael@0 | 7985 | |
michael@0 | 7986 | var Object_create = Object.create || function (prototype, properties) { |
michael@0 | 7987 | // from es5-shim |
michael@0 | 7988 | var object; |
michael@0 | 7989 | if (prototype === null) { |
michael@0 | 7990 | object = { '__proto__' : null }; |
michael@0 | 7991 | } |
michael@0 | 7992 | else { |
michael@0 | 7993 | if (typeof prototype !== 'object') { |
michael@0 | 7994 | throw new TypeError( |
michael@0 | 7995 | 'typeof prototype[' + (typeof prototype) + '] != \'object\'' |
michael@0 | 7996 | ); |
michael@0 | 7997 | } |
michael@0 | 7998 | var Type = function () {}; |
michael@0 | 7999 | Type.prototype = prototype; |
michael@0 | 8000 | object = new Type(); |
michael@0 | 8001 | object.__proto__ = prototype; |
michael@0 | 8002 | } |
michael@0 | 8003 | if (typeof properties !== 'undefined' && Object.defineProperties) { |
michael@0 | 8004 | Object.defineProperties(object, properties); |
michael@0 | 8005 | } |
michael@0 | 8006 | return object; |
michael@0 | 8007 | }; |
michael@0 | 8008 | |
michael@0 | 8009 | exports.inherits = function(ctor, superCtor) { |
michael@0 | 8010 | ctor.super_ = superCtor; |
michael@0 | 8011 | ctor.prototype = Object_create(superCtor.prototype, { |
michael@0 | 8012 | constructor: { |
michael@0 | 8013 | value: ctor, |
michael@0 | 8014 | enumerable: false, |
michael@0 | 8015 | writable: true, |
michael@0 | 8016 | configurable: true |
michael@0 | 8017 | } |
michael@0 | 8018 | }); |
michael@0 | 8019 | }; |
michael@0 | 8020 | |
michael@0 | 8021 | var formatRegExp = /%[sdj%]/g; |
michael@0 | 8022 | exports.format = function(f) { |
michael@0 | 8023 | if (typeof f !== 'string') { |
michael@0 | 8024 | var objects = []; |
michael@0 | 8025 | for (var i = 0; i < arguments.length; i++) { |
michael@0 | 8026 | objects.push(exports.inspect(arguments[i])); |
michael@0 | 8027 | } |
michael@0 | 8028 | return objects.join(' '); |
michael@0 | 8029 | } |
michael@0 | 8030 | |
michael@0 | 8031 | var i = 1; |
michael@0 | 8032 | var args = arguments; |
michael@0 | 8033 | var len = args.length; |
michael@0 | 8034 | var str = String(f).replace(formatRegExp, function(x) { |
michael@0 | 8035 | if (x === '%%') return '%'; |
michael@0 | 8036 | if (i >= len) return x; |
michael@0 | 8037 | switch (x) { |
michael@0 | 8038 | case '%s': return String(args[i++]); |
michael@0 | 8039 | case '%d': return Number(args[i++]); |
michael@0 | 8040 | case '%j': return JSON.stringify(args[i++]); |
michael@0 | 8041 | default: |
michael@0 | 8042 | return x; |
michael@0 | 8043 | } |
michael@0 | 8044 | }); |
michael@0 | 8045 | for(var x = args[i]; i < len; x = args[++i]){ |
michael@0 | 8046 | if (x === null || typeof x !== 'object') { |
michael@0 | 8047 | str += ' ' + x; |
michael@0 | 8048 | } else { |
michael@0 | 8049 | str += ' ' + exports.inspect(x); |
michael@0 | 8050 | } |
michael@0 | 8051 | } |
michael@0 | 8052 | return str; |
michael@0 | 8053 | }; |
michael@0 | 8054 | |
michael@0 | 8055 | },{"events":2}],9:[function(require,module,exports){ |
michael@0 | 8056 | (function(){// UTILITY |
michael@0 | 8057 | var util = require('util'); |
michael@0 | 8058 | var Buffer = require("buffer").Buffer; |
michael@0 | 8059 | var pSlice = Array.prototype.slice; |
michael@0 | 8060 | |
michael@0 | 8061 | function objectKeys(object) { |
michael@0 | 8062 | if (Object.keys) return Object.keys(object); |
michael@0 | 8063 | var result = []; |
michael@0 | 8064 | for (var name in object) { |
michael@0 | 8065 | if (Object.prototype.hasOwnProperty.call(object, name)) { |
michael@0 | 8066 | result.push(name); |
michael@0 | 8067 | } |
michael@0 | 8068 | } |
michael@0 | 8069 | return result; |
michael@0 | 8070 | } |
michael@0 | 8071 | |
michael@0 | 8072 | // 1. The assert module provides functions that throw |
michael@0 | 8073 | // AssertionError's when particular conditions are not met. The |
michael@0 | 8074 | // assert module must conform to the following interface. |
michael@0 | 8075 | |
michael@0 | 8076 | var assert = module.exports = ok; |
michael@0 | 8077 | |
michael@0 | 8078 | // 2. The AssertionError is defined in assert. |
michael@0 | 8079 | // new assert.AssertionError({ message: message, |
michael@0 | 8080 | // actual: actual, |
michael@0 | 8081 | // expected: expected }) |
michael@0 | 8082 | |
michael@0 | 8083 | assert.AssertionError = function AssertionError(options) { |
michael@0 | 8084 | this.name = 'AssertionError'; |
michael@0 | 8085 | this.message = options.message; |
michael@0 | 8086 | this.actual = options.actual; |
michael@0 | 8087 | this.expected = options.expected; |
michael@0 | 8088 | this.operator = options.operator; |
michael@0 | 8089 | var stackStartFunction = options.stackStartFunction || fail; |
michael@0 | 8090 | |
michael@0 | 8091 | if (Error.captureStackTrace) { |
michael@0 | 8092 | Error.captureStackTrace(this, stackStartFunction); |
michael@0 | 8093 | } |
michael@0 | 8094 | }; |
michael@0 | 8095 | util.inherits(assert.AssertionError, Error); |
michael@0 | 8096 | |
michael@0 | 8097 | function replacer(key, value) { |
michael@0 | 8098 | if (value === undefined) { |
michael@0 | 8099 | return '' + value; |
michael@0 | 8100 | } |
michael@0 | 8101 | if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) { |
michael@0 | 8102 | return value.toString(); |
michael@0 | 8103 | } |
michael@0 | 8104 | if (typeof value === 'function' || value instanceof RegExp) { |
michael@0 | 8105 | return value.toString(); |
michael@0 | 8106 | } |
michael@0 | 8107 | return value; |
michael@0 | 8108 | } |
michael@0 | 8109 | |
michael@0 | 8110 | function truncate(s, n) { |
michael@0 | 8111 | if (typeof s == 'string') { |
michael@0 | 8112 | return s.length < n ? s : s.slice(0, n); |
michael@0 | 8113 | } else { |
michael@0 | 8114 | return s; |
michael@0 | 8115 | } |
michael@0 | 8116 | } |
michael@0 | 8117 | |
michael@0 | 8118 | assert.AssertionError.prototype.toString = function() { |
michael@0 | 8119 | if (this.message) { |
michael@0 | 8120 | return [this.name + ':', this.message].join(' '); |
michael@0 | 8121 | } else { |
michael@0 | 8122 | return [ |
michael@0 | 8123 | this.name + ':', |
michael@0 | 8124 | truncate(JSON.stringify(this.actual, replacer), 128), |
michael@0 | 8125 | this.operator, |
michael@0 | 8126 | truncate(JSON.stringify(this.expected, replacer), 128) |
michael@0 | 8127 | ].join(' '); |
michael@0 | 8128 | } |
michael@0 | 8129 | }; |
michael@0 | 8130 | |
michael@0 | 8131 | // assert.AssertionError instanceof Error |
michael@0 | 8132 | |
michael@0 | 8133 | assert.AssertionError.__proto__ = Error.prototype; |
michael@0 | 8134 | |
michael@0 | 8135 | // At present only the three keys mentioned above are used and |
michael@0 | 8136 | // understood by the spec. Implementations or sub modules can pass |
michael@0 | 8137 | // other keys to the AssertionError's constructor - they will be |
michael@0 | 8138 | // ignored. |
michael@0 | 8139 | |
michael@0 | 8140 | // 3. All of the following functions must throw an AssertionError |
michael@0 | 8141 | // when a corresponding condition is not met, with a message that |
michael@0 | 8142 | // may be undefined if not provided. All assertion methods provide |
michael@0 | 8143 | // both the actual and expected values to the assertion error for |
michael@0 | 8144 | // display purposes. |
michael@0 | 8145 | |
michael@0 | 8146 | function fail(actual, expected, message, operator, stackStartFunction) { |
michael@0 | 8147 | throw new assert.AssertionError({ |
michael@0 | 8148 | message: message, |
michael@0 | 8149 | actual: actual, |
michael@0 | 8150 | expected: expected, |
michael@0 | 8151 | operator: operator, |
michael@0 | 8152 | stackStartFunction: stackStartFunction |
michael@0 | 8153 | }); |
michael@0 | 8154 | } |
michael@0 | 8155 | |
michael@0 | 8156 | // EXTENSION! allows for well behaved errors defined elsewhere. |
michael@0 | 8157 | assert.fail = fail; |
michael@0 | 8158 | |
michael@0 | 8159 | // 4. Pure assertion tests whether a value is truthy, as determined |
michael@0 | 8160 | // by !!guard. |
michael@0 | 8161 | // assert.ok(guard, message_opt); |
michael@0 | 8162 | // This statement is equivalent to assert.equal(true, guard, |
michael@0 | 8163 | // message_opt);. To test strictly for the value true, use |
michael@0 | 8164 | // assert.strictEqual(true, guard, message_opt);. |
michael@0 | 8165 | |
michael@0 | 8166 | function ok(value, message) { |
michael@0 | 8167 | if (!!!value) fail(value, true, message, '==', assert.ok); |
michael@0 | 8168 | } |
michael@0 | 8169 | assert.ok = ok; |
michael@0 | 8170 | |
michael@0 | 8171 | // 5. The equality assertion tests shallow, coercive equality with |
michael@0 | 8172 | // ==. |
michael@0 | 8173 | // assert.equal(actual, expected, message_opt); |
michael@0 | 8174 | |
michael@0 | 8175 | assert.equal = function equal(actual, expected, message) { |
michael@0 | 8176 | if (actual != expected) fail(actual, expected, message, '==', assert.equal); |
michael@0 | 8177 | }; |
michael@0 | 8178 | |
michael@0 | 8179 | // 6. The non-equality assertion tests for whether two objects are not equal |
michael@0 | 8180 | // with != assert.notEqual(actual, expected, message_opt); |
michael@0 | 8181 | |
michael@0 | 8182 | assert.notEqual = function notEqual(actual, expected, message) { |
michael@0 | 8183 | if (actual == expected) { |
michael@0 | 8184 | fail(actual, expected, message, '!=', assert.notEqual); |
michael@0 | 8185 | } |
michael@0 | 8186 | }; |
michael@0 | 8187 | |
michael@0 | 8188 | // 7. The equivalence assertion tests a deep equality relation. |
michael@0 | 8189 | // assert.deepEqual(actual, expected, message_opt); |
michael@0 | 8190 | |
michael@0 | 8191 | assert.deepEqual = function deepEqual(actual, expected, message) { |
michael@0 | 8192 | if (!_deepEqual(actual, expected)) { |
michael@0 | 8193 | fail(actual, expected, message, 'deepEqual', assert.deepEqual); |
michael@0 | 8194 | } |
michael@0 | 8195 | }; |
michael@0 | 8196 | |
michael@0 | 8197 | function _deepEqual(actual, expected) { |
michael@0 | 8198 | // 7.1. All identical values are equivalent, as determined by ===. |
michael@0 | 8199 | if (actual === expected) { |
michael@0 | 8200 | return true; |
michael@0 | 8201 | |
michael@0 | 8202 | } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { |
michael@0 | 8203 | if (actual.length != expected.length) return false; |
michael@0 | 8204 | |
michael@0 | 8205 | for (var i = 0; i < actual.length; i++) { |
michael@0 | 8206 | if (actual[i] !== expected[i]) return false; |
michael@0 | 8207 | } |
michael@0 | 8208 | |
michael@0 | 8209 | return true; |
michael@0 | 8210 | |
michael@0 | 8211 | // 7.2. If the expected value is a Date object, the actual value is |
michael@0 | 8212 | // equivalent if it is also a Date object that refers to the same time. |
michael@0 | 8213 | } else if (actual instanceof Date && expected instanceof Date) { |
michael@0 | 8214 | return actual.getTime() === expected.getTime(); |
michael@0 | 8215 | |
michael@0 | 8216 | // 7.3. Other pairs that do not both pass typeof value == 'object', |
michael@0 | 8217 | // equivalence is determined by ==. |
michael@0 | 8218 | } else if (typeof actual != 'object' && typeof expected != 'object') { |
michael@0 | 8219 | return actual == expected; |
michael@0 | 8220 | |
michael@0 | 8221 | // 7.4. For all other Object pairs, including Array objects, equivalence is |
michael@0 | 8222 | // determined by having the same number of owned properties (as verified |
michael@0 | 8223 | // with Object.prototype.hasOwnProperty.call), the same set of keys |
michael@0 | 8224 | // (although not necessarily the same order), equivalent values for every |
michael@0 | 8225 | // corresponding key, and an identical 'prototype' property. Note: this |
michael@0 | 8226 | // accounts for both named and indexed properties on Arrays. |
michael@0 | 8227 | } else { |
michael@0 | 8228 | return objEquiv(actual, expected); |
michael@0 | 8229 | } |
michael@0 | 8230 | } |
michael@0 | 8231 | |
michael@0 | 8232 | function isUndefinedOrNull(value) { |
michael@0 | 8233 | return value === null || value === undefined; |
michael@0 | 8234 | } |
michael@0 | 8235 | |
michael@0 | 8236 | function isArguments(object) { |
michael@0 | 8237 | return Object.prototype.toString.call(object) == '[object Arguments]'; |
michael@0 | 8238 | } |
michael@0 | 8239 | |
michael@0 | 8240 | function objEquiv(a, b) { |
michael@0 | 8241 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) |
michael@0 | 8242 | return false; |
michael@0 | 8243 | // an identical 'prototype' property. |
michael@0 | 8244 | if (a.prototype !== b.prototype) return false; |
michael@0 | 8245 | //~~~I've managed to break Object.keys through screwy arguments passing. |
michael@0 | 8246 | // Converting to array solves the problem. |
michael@0 | 8247 | if (isArguments(a)) { |
michael@0 | 8248 | if (!isArguments(b)) { |
michael@0 | 8249 | return false; |
michael@0 | 8250 | } |
michael@0 | 8251 | a = pSlice.call(a); |
michael@0 | 8252 | b = pSlice.call(b); |
michael@0 | 8253 | return _deepEqual(a, b); |
michael@0 | 8254 | } |
michael@0 | 8255 | try { |
michael@0 | 8256 | var ka = objectKeys(a), |
michael@0 | 8257 | kb = objectKeys(b), |
michael@0 | 8258 | key, i; |
michael@0 | 8259 | } catch (e) {//happens when one is a string literal and the other isn't |
michael@0 | 8260 | return false; |
michael@0 | 8261 | } |
michael@0 | 8262 | // having the same number of owned properties (keys incorporates |
michael@0 | 8263 | // hasOwnProperty) |
michael@0 | 8264 | if (ka.length != kb.length) |
michael@0 | 8265 | return false; |
michael@0 | 8266 | //the same set of keys (although not necessarily the same order), |
michael@0 | 8267 | ka.sort(); |
michael@0 | 8268 | kb.sort(); |
michael@0 | 8269 | //~~~cheap key test |
michael@0 | 8270 | for (i = ka.length - 1; i >= 0; i--) { |
michael@0 | 8271 | if (ka[i] != kb[i]) |
michael@0 | 8272 | return false; |
michael@0 | 8273 | } |
michael@0 | 8274 | //equivalent values for every corresponding key, and |
michael@0 | 8275 | //~~~possibly expensive deep test |
michael@0 | 8276 | for (i = ka.length - 1; i >= 0; i--) { |
michael@0 | 8277 | key = ka[i]; |
michael@0 | 8278 | if (!_deepEqual(a[key], b[key])) return false; |
michael@0 | 8279 | } |
michael@0 | 8280 | return true; |
michael@0 | 8281 | } |
michael@0 | 8282 | |
michael@0 | 8283 | // 8. The non-equivalence assertion tests for any deep inequality. |
michael@0 | 8284 | // assert.notDeepEqual(actual, expected, message_opt); |
michael@0 | 8285 | |
michael@0 | 8286 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) { |
michael@0 | 8287 | if (_deepEqual(actual, expected)) { |
michael@0 | 8288 | fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); |
michael@0 | 8289 | } |
michael@0 | 8290 | }; |
michael@0 | 8291 | |
michael@0 | 8292 | // 9. The strict equality assertion tests strict equality, as determined by ===. |
michael@0 | 8293 | // assert.strictEqual(actual, expected, message_opt); |
michael@0 | 8294 | |
michael@0 | 8295 | assert.strictEqual = function strictEqual(actual, expected, message) { |
michael@0 | 8296 | if (actual !== expected) { |
michael@0 | 8297 | fail(actual, expected, message, '===', assert.strictEqual); |
michael@0 | 8298 | } |
michael@0 | 8299 | }; |
michael@0 | 8300 | |
michael@0 | 8301 | // 10. The strict non-equality assertion tests for strict inequality, as |
michael@0 | 8302 | // determined by !==. assert.notStrictEqual(actual, expected, message_opt); |
michael@0 | 8303 | |
michael@0 | 8304 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) { |
michael@0 | 8305 | if (actual === expected) { |
michael@0 | 8306 | fail(actual, expected, message, '!==', assert.notStrictEqual); |
michael@0 | 8307 | } |
michael@0 | 8308 | }; |
michael@0 | 8309 | |
michael@0 | 8310 | function expectedException(actual, expected) { |
michael@0 | 8311 | if (!actual || !expected) { |
michael@0 | 8312 | return false; |
michael@0 | 8313 | } |
michael@0 | 8314 | |
michael@0 | 8315 | if (expected instanceof RegExp) { |
michael@0 | 8316 | return expected.test(actual); |
michael@0 | 8317 | } else if (actual instanceof expected) { |
michael@0 | 8318 | return true; |
michael@0 | 8319 | } else if (expected.call({}, actual) === true) { |
michael@0 | 8320 | return true; |
michael@0 | 8321 | } |
michael@0 | 8322 | |
michael@0 | 8323 | return false; |
michael@0 | 8324 | } |
michael@0 | 8325 | |
michael@0 | 8326 | function _throws(shouldThrow, block, expected, message) { |
michael@0 | 8327 | var actual; |
michael@0 | 8328 | |
michael@0 | 8329 | if (typeof expected === 'string') { |
michael@0 | 8330 | message = expected; |
michael@0 | 8331 | expected = null; |
michael@0 | 8332 | } |
michael@0 | 8333 | |
michael@0 | 8334 | try { |
michael@0 | 8335 | block(); |
michael@0 | 8336 | } catch (e) { |
michael@0 | 8337 | actual = e; |
michael@0 | 8338 | } |
michael@0 | 8339 | |
michael@0 | 8340 | message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + |
michael@0 | 8341 | (message ? ' ' + message : '.'); |
michael@0 | 8342 | |
michael@0 | 8343 | if (shouldThrow && !actual) { |
michael@0 | 8344 | fail('Missing expected exception' + message); |
michael@0 | 8345 | } |
michael@0 | 8346 | |
michael@0 | 8347 | if (!shouldThrow && expectedException(actual, expected)) { |
michael@0 | 8348 | fail('Got unwanted exception' + message); |
michael@0 | 8349 | } |
michael@0 | 8350 | |
michael@0 | 8351 | if ((shouldThrow && actual && expected && |
michael@0 | 8352 | !expectedException(actual, expected)) || (!shouldThrow && actual)) { |
michael@0 | 8353 | throw actual; |
michael@0 | 8354 | } |
michael@0 | 8355 | } |
michael@0 | 8356 | |
michael@0 | 8357 | // 11. Expected to throw an error: |
michael@0 | 8358 | // assert.throws(block, Error_opt, message_opt); |
michael@0 | 8359 | |
michael@0 | 8360 | assert.throws = function(block, /*optional*/error, /*optional*/message) { |
michael@0 | 8361 | _throws.apply(this, [true].concat(pSlice.call(arguments))); |
michael@0 | 8362 | }; |
michael@0 | 8363 | |
michael@0 | 8364 | // EXTENSION! This is annoying to write outside this module. |
michael@0 | 8365 | assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { |
michael@0 | 8366 | _throws.apply(this, [false].concat(pSlice.call(arguments))); |
michael@0 | 8367 | }; |
michael@0 | 8368 | |
michael@0 | 8369 | assert.ifError = function(err) { if (err) {throw err;}}; |
michael@0 | 8370 | |
michael@0 | 8371 | })() |
michael@0 | 8372 | },{"util":8,"buffer":13}],11:[function(require,module,exports){ |
michael@0 | 8373 | (function(){// Underscore.js 1.4.4 |
michael@0 | 8374 | // http://underscorejs.org |
michael@0 | 8375 | // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. |
michael@0 | 8376 | // Underscore may be freely distributed under the MIT license. |
michael@0 | 8377 | |
michael@0 | 8378 | (function() { |
michael@0 | 8379 | |
michael@0 | 8380 | // Baseline setup |
michael@0 | 8381 | // -------------- |
michael@0 | 8382 | |
michael@0 | 8383 | // Establish the root object, `window` in the browser, or `global` on the server. |
michael@0 | 8384 | var root = this; |
michael@0 | 8385 | |
michael@0 | 8386 | // Save the previous value of the `_` variable. |
michael@0 | 8387 | var previousUnderscore = root._; |
michael@0 | 8388 | |
michael@0 | 8389 | // Establish the object that gets returned to break out of a loop iteration. |
michael@0 | 8390 | var breaker = {}; |
michael@0 | 8391 | |
michael@0 | 8392 | // Save bytes in the minified (but not gzipped) version: |
michael@0 | 8393 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; |
michael@0 | 8394 | |
michael@0 | 8395 | // Create quick reference variables for speed access to core prototypes. |
michael@0 | 8396 | var push = ArrayProto.push, |
michael@0 | 8397 | slice = ArrayProto.slice, |
michael@0 | 8398 | concat = ArrayProto.concat, |
michael@0 | 8399 | toString = ObjProto.toString, |
michael@0 | 8400 | hasOwnProperty = ObjProto.hasOwnProperty; |
michael@0 | 8401 | |
michael@0 | 8402 | // All **ECMAScript 5** native function implementations that we hope to use |
michael@0 | 8403 | // are declared here. |
michael@0 | 8404 | var |
michael@0 | 8405 | nativeForEach = ArrayProto.forEach, |
michael@0 | 8406 | nativeMap = ArrayProto.map, |
michael@0 | 8407 | nativeReduce = ArrayProto.reduce, |
michael@0 | 8408 | nativeReduceRight = ArrayProto.reduceRight, |
michael@0 | 8409 | nativeFilter = ArrayProto.filter, |
michael@0 | 8410 | nativeEvery = ArrayProto.every, |
michael@0 | 8411 | nativeSome = ArrayProto.some, |
michael@0 | 8412 | nativeIndexOf = ArrayProto.indexOf, |
michael@0 | 8413 | nativeLastIndexOf = ArrayProto.lastIndexOf, |
michael@0 | 8414 | nativeIsArray = Array.isArray, |
michael@0 | 8415 | nativeKeys = Object.keys, |
michael@0 | 8416 | nativeBind = FuncProto.bind; |
michael@0 | 8417 | |
michael@0 | 8418 | // Create a safe reference to the Underscore object for use below. |
michael@0 | 8419 | var _ = function(obj) { |
michael@0 | 8420 | if (obj instanceof _) return obj; |
michael@0 | 8421 | if (!(this instanceof _)) return new _(obj); |
michael@0 | 8422 | this._wrapped = obj; |
michael@0 | 8423 | }; |
michael@0 | 8424 | |
michael@0 | 8425 | // Export the Underscore object for **Node.js**, with |
michael@0 | 8426 | // backwards-compatibility for the old `require()` API. If we're in |
michael@0 | 8427 | // the browser, add `_` as a global object via a string identifier, |
michael@0 | 8428 | // for Closure Compiler "advanced" mode. |
michael@0 | 8429 | if (typeof exports !== 'undefined') { |
michael@0 | 8430 | if (typeof module !== 'undefined' && module.exports) { |
michael@0 | 8431 | exports = module.exports = _; |
michael@0 | 8432 | } |
michael@0 | 8433 | exports._ = _; |
michael@0 | 8434 | } else { |
michael@0 | 8435 | root._ = _; |
michael@0 | 8436 | } |
michael@0 | 8437 | |
michael@0 | 8438 | // Current version. |
michael@0 | 8439 | _.VERSION = '1.4.4'; |
michael@0 | 8440 | |
michael@0 | 8441 | // Collection Functions |
michael@0 | 8442 | // -------------------- |
michael@0 | 8443 | |
michael@0 | 8444 | // The cornerstone, an `each` implementation, aka `forEach`. |
michael@0 | 8445 | // Handles objects with the built-in `forEach`, arrays, and raw objects. |
michael@0 | 8446 | // Delegates to **ECMAScript 5**'s native `forEach` if available. |
michael@0 | 8447 | var each = _.each = _.forEach = function(obj, iterator, context) { |
michael@0 | 8448 | if (obj == null) return; |
michael@0 | 8449 | if (nativeForEach && obj.forEach === nativeForEach) { |
michael@0 | 8450 | obj.forEach(iterator, context); |
michael@0 | 8451 | } else if (obj.length === +obj.length) { |
michael@0 | 8452 | for (var i = 0, l = obj.length; i < l; i++) { |
michael@0 | 8453 | if (iterator.call(context, obj[i], i, obj) === breaker) return; |
michael@0 | 8454 | } |
michael@0 | 8455 | } else { |
michael@0 | 8456 | for (var key in obj) { |
michael@0 | 8457 | if (_.has(obj, key)) { |
michael@0 | 8458 | if (iterator.call(context, obj[key], key, obj) === breaker) return; |
michael@0 | 8459 | } |
michael@0 | 8460 | } |
michael@0 | 8461 | } |
michael@0 | 8462 | }; |
michael@0 | 8463 | |
michael@0 | 8464 | // Return the results of applying the iterator to each element. |
michael@0 | 8465 | // Delegates to **ECMAScript 5**'s native `map` if available. |
michael@0 | 8466 | _.map = _.collect = function(obj, iterator, context) { |
michael@0 | 8467 | var results = []; |
michael@0 | 8468 | if (obj == null) return results; |
michael@0 | 8469 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); |
michael@0 | 8470 | each(obj, function(value, index, list) { |
michael@0 | 8471 | results[results.length] = iterator.call(context, value, index, list); |
michael@0 | 8472 | }); |
michael@0 | 8473 | return results; |
michael@0 | 8474 | }; |
michael@0 | 8475 | |
michael@0 | 8476 | var reduceError = 'Reduce of empty array with no initial value'; |
michael@0 | 8477 | |
michael@0 | 8478 | // **Reduce** builds up a single result from a list of values, aka `inject`, |
michael@0 | 8479 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. |
michael@0 | 8480 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { |
michael@0 | 8481 | var initial = arguments.length > 2; |
michael@0 | 8482 | if (obj == null) obj = []; |
michael@0 | 8483 | if (nativeReduce && obj.reduce === nativeReduce) { |
michael@0 | 8484 | if (context) iterator = _.bind(iterator, context); |
michael@0 | 8485 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); |
michael@0 | 8486 | } |
michael@0 | 8487 | each(obj, function(value, index, list) { |
michael@0 | 8488 | if (!initial) { |
michael@0 | 8489 | memo = value; |
michael@0 | 8490 | initial = true; |
michael@0 | 8491 | } else { |
michael@0 | 8492 | memo = iterator.call(context, memo, value, index, list); |
michael@0 | 8493 | } |
michael@0 | 8494 | }); |
michael@0 | 8495 | if (!initial) throw new TypeError(reduceError); |
michael@0 | 8496 | return memo; |
michael@0 | 8497 | }; |
michael@0 | 8498 | |
michael@0 | 8499 | // The right-associative version of reduce, also known as `foldr`. |
michael@0 | 8500 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. |
michael@0 | 8501 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { |
michael@0 | 8502 | var initial = arguments.length > 2; |
michael@0 | 8503 | if (obj == null) obj = []; |
michael@0 | 8504 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { |
michael@0 | 8505 | if (context) iterator = _.bind(iterator, context); |
michael@0 | 8506 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); |
michael@0 | 8507 | } |
michael@0 | 8508 | var length = obj.length; |
michael@0 | 8509 | if (length !== +length) { |
michael@0 | 8510 | var keys = _.keys(obj); |
michael@0 | 8511 | length = keys.length; |
michael@0 | 8512 | } |
michael@0 | 8513 | each(obj, function(value, index, list) { |
michael@0 | 8514 | index = keys ? keys[--length] : --length; |
michael@0 | 8515 | if (!initial) { |
michael@0 | 8516 | memo = obj[index]; |
michael@0 | 8517 | initial = true; |
michael@0 | 8518 | } else { |
michael@0 | 8519 | memo = iterator.call(context, memo, obj[index], index, list); |
michael@0 | 8520 | } |
michael@0 | 8521 | }); |
michael@0 | 8522 | if (!initial) throw new TypeError(reduceError); |
michael@0 | 8523 | return memo; |
michael@0 | 8524 | }; |
michael@0 | 8525 | |
michael@0 | 8526 | // Return the first value which passes a truth test. Aliased as `detect`. |
michael@0 | 8527 | _.find = _.detect = function(obj, iterator, context) { |
michael@0 | 8528 | var result; |
michael@0 | 8529 | any(obj, function(value, index, list) { |
michael@0 | 8530 | if (iterator.call(context, value, index, list)) { |
michael@0 | 8531 | result = value; |
michael@0 | 8532 | return true; |
michael@0 | 8533 | } |
michael@0 | 8534 | }); |
michael@0 | 8535 | return result; |
michael@0 | 8536 | }; |
michael@0 | 8537 | |
michael@0 | 8538 | // Return all the elements that pass a truth test. |
michael@0 | 8539 | // Delegates to **ECMAScript 5**'s native `filter` if available. |
michael@0 | 8540 | // Aliased as `select`. |
michael@0 | 8541 | _.filter = _.select = function(obj, iterator, context) { |
michael@0 | 8542 | var results = []; |
michael@0 | 8543 | if (obj == null) return results; |
michael@0 | 8544 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); |
michael@0 | 8545 | each(obj, function(value, index, list) { |
michael@0 | 8546 | if (iterator.call(context, value, index, list)) results[results.length] = value; |
michael@0 | 8547 | }); |
michael@0 | 8548 | return results; |
michael@0 | 8549 | }; |
michael@0 | 8550 | |
michael@0 | 8551 | // Return all the elements for which a truth test fails. |
michael@0 | 8552 | _.reject = function(obj, iterator, context) { |
michael@0 | 8553 | return _.filter(obj, function(value, index, list) { |
michael@0 | 8554 | return !iterator.call(context, value, index, list); |
michael@0 | 8555 | }, context); |
michael@0 | 8556 | }; |
michael@0 | 8557 | |
michael@0 | 8558 | // Determine whether all of the elements match a truth test. |
michael@0 | 8559 | // Delegates to **ECMAScript 5**'s native `every` if available. |
michael@0 | 8560 | // Aliased as `all`. |
michael@0 | 8561 | _.every = _.all = function(obj, iterator, context) { |
michael@0 | 8562 | iterator || (iterator = _.identity); |
michael@0 | 8563 | var result = true; |
michael@0 | 8564 | if (obj == null) return result; |
michael@0 | 8565 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); |
michael@0 | 8566 | each(obj, function(value, index, list) { |
michael@0 | 8567 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; |
michael@0 | 8568 | }); |
michael@0 | 8569 | return !!result; |
michael@0 | 8570 | }; |
michael@0 | 8571 | |
michael@0 | 8572 | // Determine if at least one element in the object matches a truth test. |
michael@0 | 8573 | // Delegates to **ECMAScript 5**'s native `some` if available. |
michael@0 | 8574 | // Aliased as `any`. |
michael@0 | 8575 | var any = _.some = _.any = function(obj, iterator, context) { |
michael@0 | 8576 | iterator || (iterator = _.identity); |
michael@0 | 8577 | var result = false; |
michael@0 | 8578 | if (obj == null) return result; |
michael@0 | 8579 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); |
michael@0 | 8580 | each(obj, function(value, index, list) { |
michael@0 | 8581 | if (result || (result = iterator.call(context, value, index, list))) return breaker; |
michael@0 | 8582 | }); |
michael@0 | 8583 | return !!result; |
michael@0 | 8584 | }; |
michael@0 | 8585 | |
michael@0 | 8586 | // Determine if the array or object contains a given value (using `===`). |
michael@0 | 8587 | // Aliased as `include`. |
michael@0 | 8588 | _.contains = _.include = function(obj, target) { |
michael@0 | 8589 | if (obj == null) return false; |
michael@0 | 8590 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; |
michael@0 | 8591 | return any(obj, function(value) { |
michael@0 | 8592 | return value === target; |
michael@0 | 8593 | }); |
michael@0 | 8594 | }; |
michael@0 | 8595 | |
michael@0 | 8596 | // Invoke a method (with arguments) on every item in a collection. |
michael@0 | 8597 | _.invoke = function(obj, method) { |
michael@0 | 8598 | var args = slice.call(arguments, 2); |
michael@0 | 8599 | var isFunc = _.isFunction(method); |
michael@0 | 8600 | return _.map(obj, function(value) { |
michael@0 | 8601 | return (isFunc ? method : value[method]).apply(value, args); |
michael@0 | 8602 | }); |
michael@0 | 8603 | }; |
michael@0 | 8604 | |
michael@0 | 8605 | // Convenience version of a common use case of `map`: fetching a property. |
michael@0 | 8606 | _.pluck = function(obj, key) { |
michael@0 | 8607 | return _.map(obj, function(value){ return value[key]; }); |
michael@0 | 8608 | }; |
michael@0 | 8609 | |
michael@0 | 8610 | // Convenience version of a common use case of `filter`: selecting only objects |
michael@0 | 8611 | // containing specific `key:value` pairs. |
michael@0 | 8612 | _.where = function(obj, attrs, first) { |
michael@0 | 8613 | if (_.isEmpty(attrs)) return first ? null : []; |
michael@0 | 8614 | return _[first ? 'find' : 'filter'](obj, function(value) { |
michael@0 | 8615 | for (var key in attrs) { |
michael@0 | 8616 | if (attrs[key] !== value[key]) return false; |
michael@0 | 8617 | } |
michael@0 | 8618 | return true; |
michael@0 | 8619 | }); |
michael@0 | 8620 | }; |
michael@0 | 8621 | |
michael@0 | 8622 | // Convenience version of a common use case of `find`: getting the first object |
michael@0 | 8623 | // containing specific `key:value` pairs. |
michael@0 | 8624 | _.findWhere = function(obj, attrs) { |
michael@0 | 8625 | return _.where(obj, attrs, true); |
michael@0 | 8626 | }; |
michael@0 | 8627 | |
michael@0 | 8628 | // Return the maximum element or (element-based computation). |
michael@0 | 8629 | // Can't optimize arrays of integers longer than 65,535 elements. |
michael@0 | 8630 | // See: https://bugs.webkit.org/show_bug.cgi?id=80797 |
michael@0 | 8631 | _.max = function(obj, iterator, context) { |
michael@0 | 8632 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { |
michael@0 | 8633 | return Math.max.apply(Math, obj); |
michael@0 | 8634 | } |
michael@0 | 8635 | if (!iterator && _.isEmpty(obj)) return -Infinity; |
michael@0 | 8636 | var result = {computed : -Infinity, value: -Infinity}; |
michael@0 | 8637 | each(obj, function(value, index, list) { |
michael@0 | 8638 | var computed = iterator ? iterator.call(context, value, index, list) : value; |
michael@0 | 8639 | computed >= result.computed && (result = {value : value, computed : computed}); |
michael@0 | 8640 | }); |
michael@0 | 8641 | return result.value; |
michael@0 | 8642 | }; |
michael@0 | 8643 | |
michael@0 | 8644 | // Return the minimum element (or element-based computation). |
michael@0 | 8645 | _.min = function(obj, iterator, context) { |
michael@0 | 8646 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { |
michael@0 | 8647 | return Math.min.apply(Math, obj); |
michael@0 | 8648 | } |
michael@0 | 8649 | if (!iterator && _.isEmpty(obj)) return Infinity; |
michael@0 | 8650 | var result = {computed : Infinity, value: Infinity}; |
michael@0 | 8651 | each(obj, function(value, index, list) { |
michael@0 | 8652 | var computed = iterator ? iterator.call(context, value, index, list) : value; |
michael@0 | 8653 | computed < result.computed && (result = {value : value, computed : computed}); |
michael@0 | 8654 | }); |
michael@0 | 8655 | return result.value; |
michael@0 | 8656 | }; |
michael@0 | 8657 | |
michael@0 | 8658 | // Shuffle an array. |
michael@0 | 8659 | _.shuffle = function(obj) { |
michael@0 | 8660 | var rand; |
michael@0 | 8661 | var index = 0; |
michael@0 | 8662 | var shuffled = []; |
michael@0 | 8663 | each(obj, function(value) { |
michael@0 | 8664 | rand = _.random(index++); |
michael@0 | 8665 | shuffled[index - 1] = shuffled[rand]; |
michael@0 | 8666 | shuffled[rand] = value; |
michael@0 | 8667 | }); |
michael@0 | 8668 | return shuffled; |
michael@0 | 8669 | }; |
michael@0 | 8670 | |
michael@0 | 8671 | // An internal function to generate lookup iterators. |
michael@0 | 8672 | var lookupIterator = function(value) { |
michael@0 | 8673 | return _.isFunction(value) ? value : function(obj){ return obj[value]; }; |
michael@0 | 8674 | }; |
michael@0 | 8675 | |
michael@0 | 8676 | // Sort the object's values by a criterion produced by an iterator. |
michael@0 | 8677 | _.sortBy = function(obj, value, context) { |
michael@0 | 8678 | var iterator = lookupIterator(value); |
michael@0 | 8679 | return _.pluck(_.map(obj, function(value, index, list) { |
michael@0 | 8680 | return { |
michael@0 | 8681 | value : value, |
michael@0 | 8682 | index : index, |
michael@0 | 8683 | criteria : iterator.call(context, value, index, list) |
michael@0 | 8684 | }; |
michael@0 | 8685 | }).sort(function(left, right) { |
michael@0 | 8686 | var a = left.criteria; |
michael@0 | 8687 | var b = right.criteria; |
michael@0 | 8688 | if (a !== b) { |
michael@0 | 8689 | if (a > b || a === void 0) return 1; |
michael@0 | 8690 | if (a < b || b === void 0) return -1; |
michael@0 | 8691 | } |
michael@0 | 8692 | return left.index < right.index ? -1 : 1; |
michael@0 | 8693 | }), 'value'); |
michael@0 | 8694 | }; |
michael@0 | 8695 | |
michael@0 | 8696 | // An internal function used for aggregate "group by" operations. |
michael@0 | 8697 | var group = function(obj, value, context, behavior) { |
michael@0 | 8698 | var result = {}; |
michael@0 | 8699 | var iterator = lookupIterator(value || _.identity); |
michael@0 | 8700 | each(obj, function(value, index) { |
michael@0 | 8701 | var key = iterator.call(context, value, index, obj); |
michael@0 | 8702 | behavior(result, key, value); |
michael@0 | 8703 | }); |
michael@0 | 8704 | return result; |
michael@0 | 8705 | }; |
michael@0 | 8706 | |
michael@0 | 8707 | // Groups the object's values by a criterion. Pass either a string attribute |
michael@0 | 8708 | // to group by, or a function that returns the criterion. |
michael@0 | 8709 | _.groupBy = function(obj, value, context) { |
michael@0 | 8710 | return group(obj, value, context, function(result, key, value) { |
michael@0 | 8711 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value); |
michael@0 | 8712 | }); |
michael@0 | 8713 | }; |
michael@0 | 8714 | |
michael@0 | 8715 | // Counts instances of an object that group by a certain criterion. Pass |
michael@0 | 8716 | // either a string attribute to count by, or a function that returns the |
michael@0 | 8717 | // criterion. |
michael@0 | 8718 | _.countBy = function(obj, value, context) { |
michael@0 | 8719 | return group(obj, value, context, function(result, key) { |
michael@0 | 8720 | if (!_.has(result, key)) result[key] = 0; |
michael@0 | 8721 | result[key]++; |
michael@0 | 8722 | }); |
michael@0 | 8723 | }; |
michael@0 | 8724 | |
michael@0 | 8725 | // Use a comparator function to figure out the smallest index at which |
michael@0 | 8726 | // an object should be inserted so as to maintain order. Uses binary search. |
michael@0 | 8727 | _.sortedIndex = function(array, obj, iterator, context) { |
michael@0 | 8728 | iterator = iterator == null ? _.identity : lookupIterator(iterator); |
michael@0 | 8729 | var value = iterator.call(context, obj); |
michael@0 | 8730 | var low = 0, high = array.length; |
michael@0 | 8731 | while (low < high) { |
michael@0 | 8732 | var mid = (low + high) >>> 1; |
michael@0 | 8733 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; |
michael@0 | 8734 | } |
michael@0 | 8735 | return low; |
michael@0 | 8736 | }; |
michael@0 | 8737 | |
michael@0 | 8738 | // Safely convert anything iterable into a real, live array. |
michael@0 | 8739 | _.toArray = function(obj) { |
michael@0 | 8740 | if (!obj) return []; |
michael@0 | 8741 | if (_.isArray(obj)) return slice.call(obj); |
michael@0 | 8742 | if (obj.length === +obj.length) return _.map(obj, _.identity); |
michael@0 | 8743 | return _.values(obj); |
michael@0 | 8744 | }; |
michael@0 | 8745 | |
michael@0 | 8746 | // Return the number of elements in an object. |
michael@0 | 8747 | _.size = function(obj) { |
michael@0 | 8748 | if (obj == null) return 0; |
michael@0 | 8749 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; |
michael@0 | 8750 | }; |
michael@0 | 8751 | |
michael@0 | 8752 | // Array Functions |
michael@0 | 8753 | // --------------- |
michael@0 | 8754 | |
michael@0 | 8755 | // Get the first element of an array. Passing **n** will return the first N |
michael@0 | 8756 | // values in the array. Aliased as `head` and `take`. The **guard** check |
michael@0 | 8757 | // allows it to work with `_.map`. |
michael@0 | 8758 | _.first = _.head = _.take = function(array, n, guard) { |
michael@0 | 8759 | if (array == null) return void 0; |
michael@0 | 8760 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; |
michael@0 | 8761 | }; |
michael@0 | 8762 | |
michael@0 | 8763 | // Returns everything but the last entry of the array. Especially useful on |
michael@0 | 8764 | // the arguments object. Passing **n** will return all the values in |
michael@0 | 8765 | // the array, excluding the last N. The **guard** check allows it to work with |
michael@0 | 8766 | // `_.map`. |
michael@0 | 8767 | _.initial = function(array, n, guard) { |
michael@0 | 8768 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); |
michael@0 | 8769 | }; |
michael@0 | 8770 | |
michael@0 | 8771 | // Get the last element of an array. Passing **n** will return the last N |
michael@0 | 8772 | // values in the array. The **guard** check allows it to work with `_.map`. |
michael@0 | 8773 | _.last = function(array, n, guard) { |
michael@0 | 8774 | if (array == null) return void 0; |
michael@0 | 8775 | if ((n != null) && !guard) { |
michael@0 | 8776 | return slice.call(array, Math.max(array.length - n, 0)); |
michael@0 | 8777 | } else { |
michael@0 | 8778 | return array[array.length - 1]; |
michael@0 | 8779 | } |
michael@0 | 8780 | }; |
michael@0 | 8781 | |
michael@0 | 8782 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. |
michael@0 | 8783 | // Especially useful on the arguments object. Passing an **n** will return |
michael@0 | 8784 | // the rest N values in the array. The **guard** |
michael@0 | 8785 | // check allows it to work with `_.map`. |
michael@0 | 8786 | _.rest = _.tail = _.drop = function(array, n, guard) { |
michael@0 | 8787 | return slice.call(array, (n == null) || guard ? 1 : n); |
michael@0 | 8788 | }; |
michael@0 | 8789 | |
michael@0 | 8790 | // Trim out all falsy values from an array. |
michael@0 | 8791 | _.compact = function(array) { |
michael@0 | 8792 | return _.filter(array, _.identity); |
michael@0 | 8793 | }; |
michael@0 | 8794 | |
michael@0 | 8795 | // Internal implementation of a recursive `flatten` function. |
michael@0 | 8796 | var flatten = function(input, shallow, output) { |
michael@0 | 8797 | each(input, function(value) { |
michael@0 | 8798 | if (_.isArray(value)) { |
michael@0 | 8799 | shallow ? push.apply(output, value) : flatten(value, shallow, output); |
michael@0 | 8800 | } else { |
michael@0 | 8801 | output.push(value); |
michael@0 | 8802 | } |
michael@0 | 8803 | }); |
michael@0 | 8804 | return output; |
michael@0 | 8805 | }; |
michael@0 | 8806 | |
michael@0 | 8807 | // Return a completely flattened version of an array. |
michael@0 | 8808 | _.flatten = function(array, shallow) { |
michael@0 | 8809 | return flatten(array, shallow, []); |
michael@0 | 8810 | }; |
michael@0 | 8811 | |
michael@0 | 8812 | // Return a version of the array that does not contain the specified value(s). |
michael@0 | 8813 | _.without = function(array) { |
michael@0 | 8814 | return _.difference(array, slice.call(arguments, 1)); |
michael@0 | 8815 | }; |
michael@0 | 8816 | |
michael@0 | 8817 | // Produce a duplicate-free version of the array. If the array has already |
michael@0 | 8818 | // been sorted, you have the option of using a faster algorithm. |
michael@0 | 8819 | // Aliased as `unique`. |
michael@0 | 8820 | _.uniq = _.unique = function(array, isSorted, iterator, context) { |
michael@0 | 8821 | if (_.isFunction(isSorted)) { |
michael@0 | 8822 | context = iterator; |
michael@0 | 8823 | iterator = isSorted; |
michael@0 | 8824 | isSorted = false; |
michael@0 | 8825 | } |
michael@0 | 8826 | var initial = iterator ? _.map(array, iterator, context) : array; |
michael@0 | 8827 | var results = []; |
michael@0 | 8828 | var seen = []; |
michael@0 | 8829 | each(initial, function(value, index) { |
michael@0 | 8830 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { |
michael@0 | 8831 | seen.push(value); |
michael@0 | 8832 | results.push(array[index]); |
michael@0 | 8833 | } |
michael@0 | 8834 | }); |
michael@0 | 8835 | return results; |
michael@0 | 8836 | }; |
michael@0 | 8837 | |
michael@0 | 8838 | // Produce an array that contains the union: each distinct element from all of |
michael@0 | 8839 | // the passed-in arrays. |
michael@0 | 8840 | _.union = function() { |
michael@0 | 8841 | return _.uniq(concat.apply(ArrayProto, arguments)); |
michael@0 | 8842 | }; |
michael@0 | 8843 | |
michael@0 | 8844 | // Produce an array that contains every item shared between all the |
michael@0 | 8845 | // passed-in arrays. |
michael@0 | 8846 | _.intersection = function(array) { |
michael@0 | 8847 | var rest = slice.call(arguments, 1); |
michael@0 | 8848 | return _.filter(_.uniq(array), function(item) { |
michael@0 | 8849 | return _.every(rest, function(other) { |
michael@0 | 8850 | return _.indexOf(other, item) >= 0; |
michael@0 | 8851 | }); |
michael@0 | 8852 | }); |
michael@0 | 8853 | }; |
michael@0 | 8854 | |
michael@0 | 8855 | // Take the difference between one array and a number of other arrays. |
michael@0 | 8856 | // Only the elements present in just the first array will remain. |
michael@0 | 8857 | _.difference = function(array) { |
michael@0 | 8858 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); |
michael@0 | 8859 | return _.filter(array, function(value){ return !_.contains(rest, value); }); |
michael@0 | 8860 | }; |
michael@0 | 8861 | |
michael@0 | 8862 | // Zip together multiple lists into a single array -- elements that share |
michael@0 | 8863 | // an index go together. |
michael@0 | 8864 | _.zip = function() { |
michael@0 | 8865 | var args = slice.call(arguments); |
michael@0 | 8866 | var length = _.max(_.pluck(args, 'length')); |
michael@0 | 8867 | var results = new Array(length); |
michael@0 | 8868 | for (var i = 0; i < length; i++) { |
michael@0 | 8869 | results[i] = _.pluck(args, "" + i); |
michael@0 | 8870 | } |
michael@0 | 8871 | return results; |
michael@0 | 8872 | }; |
michael@0 | 8873 | |
michael@0 | 8874 | // Converts lists into objects. Pass either a single array of `[key, value]` |
michael@0 | 8875 | // pairs, or two parallel arrays of the same length -- one of keys, and one of |
michael@0 | 8876 | // the corresponding values. |
michael@0 | 8877 | _.object = function(list, values) { |
michael@0 | 8878 | if (list == null) return {}; |
michael@0 | 8879 | var result = {}; |
michael@0 | 8880 | for (var i = 0, l = list.length; i < l; i++) { |
michael@0 | 8881 | if (values) { |
michael@0 | 8882 | result[list[i]] = values[i]; |
michael@0 | 8883 | } else { |
michael@0 | 8884 | result[list[i][0]] = list[i][1]; |
michael@0 | 8885 | } |
michael@0 | 8886 | } |
michael@0 | 8887 | return result; |
michael@0 | 8888 | }; |
michael@0 | 8889 | |
michael@0 | 8890 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), |
michael@0 | 8891 | // we need this function. Return the position of the first occurrence of an |
michael@0 | 8892 | // item in an array, or -1 if the item is not included in the array. |
michael@0 | 8893 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. |
michael@0 | 8894 | // If the array is large and already in sort order, pass `true` |
michael@0 | 8895 | // for **isSorted** to use binary search. |
michael@0 | 8896 | _.indexOf = function(array, item, isSorted) { |
michael@0 | 8897 | if (array == null) return -1; |
michael@0 | 8898 | var i = 0, l = array.length; |
michael@0 | 8899 | if (isSorted) { |
michael@0 | 8900 | if (typeof isSorted == 'number') { |
michael@0 | 8901 | i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); |
michael@0 | 8902 | } else { |
michael@0 | 8903 | i = _.sortedIndex(array, item); |
michael@0 | 8904 | return array[i] === item ? i : -1; |
michael@0 | 8905 | } |
michael@0 | 8906 | } |
michael@0 | 8907 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); |
michael@0 | 8908 | for (; i < l; i++) if (array[i] === item) return i; |
michael@0 | 8909 | return -1; |
michael@0 | 8910 | }; |
michael@0 | 8911 | |
michael@0 | 8912 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. |
michael@0 | 8913 | _.lastIndexOf = function(array, item, from) { |
michael@0 | 8914 | if (array == null) return -1; |
michael@0 | 8915 | var hasIndex = from != null; |
michael@0 | 8916 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { |
michael@0 | 8917 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); |
michael@0 | 8918 | } |
michael@0 | 8919 | var i = (hasIndex ? from : array.length); |
michael@0 | 8920 | while (i--) if (array[i] === item) return i; |
michael@0 | 8921 | return -1; |
michael@0 | 8922 | }; |
michael@0 | 8923 | |
michael@0 | 8924 | // Generate an integer Array containing an arithmetic progression. A port of |
michael@0 | 8925 | // the native Python `range()` function. See |
michael@0 | 8926 | // [the Python documentation](http://docs.python.org/library/functions.html#range). |
michael@0 | 8927 | _.range = function(start, stop, step) { |
michael@0 | 8928 | if (arguments.length <= 1) { |
michael@0 | 8929 | stop = start || 0; |
michael@0 | 8930 | start = 0; |
michael@0 | 8931 | } |
michael@0 | 8932 | step = arguments[2] || 1; |
michael@0 | 8933 | |
michael@0 | 8934 | var len = Math.max(Math.ceil((stop - start) / step), 0); |
michael@0 | 8935 | var idx = 0; |
michael@0 | 8936 | var range = new Array(len); |
michael@0 | 8937 | |
michael@0 | 8938 | while(idx < len) { |
michael@0 | 8939 | range[idx++] = start; |
michael@0 | 8940 | start += step; |
michael@0 | 8941 | } |
michael@0 | 8942 | |
michael@0 | 8943 | return range; |
michael@0 | 8944 | }; |
michael@0 | 8945 | |
michael@0 | 8946 | // Function (ahem) Functions |
michael@0 | 8947 | // ------------------ |
michael@0 | 8948 | |
michael@0 | 8949 | // Create a function bound to a given object (assigning `this`, and arguments, |
michael@0 | 8950 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if |
michael@0 | 8951 | // available. |
michael@0 | 8952 | _.bind = function(func, context) { |
michael@0 | 8953 | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); |
michael@0 | 8954 | var args = slice.call(arguments, 2); |
michael@0 | 8955 | return function() { |
michael@0 | 8956 | return func.apply(context, args.concat(slice.call(arguments))); |
michael@0 | 8957 | }; |
michael@0 | 8958 | }; |
michael@0 | 8959 | |
michael@0 | 8960 | // Partially apply a function by creating a version that has had some of its |
michael@0 | 8961 | // arguments pre-filled, without changing its dynamic `this` context. |
michael@0 | 8962 | _.partial = function(func) { |
michael@0 | 8963 | var args = slice.call(arguments, 1); |
michael@0 | 8964 | return function() { |
michael@0 | 8965 | return func.apply(this, args.concat(slice.call(arguments))); |
michael@0 | 8966 | }; |
michael@0 | 8967 | }; |
michael@0 | 8968 | |
michael@0 | 8969 | // Bind all of an object's methods to that object. Useful for ensuring that |
michael@0 | 8970 | // all callbacks defined on an object belong to it. |
michael@0 | 8971 | _.bindAll = function(obj) { |
michael@0 | 8972 | var funcs = slice.call(arguments, 1); |
michael@0 | 8973 | if (funcs.length === 0) funcs = _.functions(obj); |
michael@0 | 8974 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); |
michael@0 | 8975 | return obj; |
michael@0 | 8976 | }; |
michael@0 | 8977 | |
michael@0 | 8978 | // Memoize an expensive function by storing its results. |
michael@0 | 8979 | _.memoize = function(func, hasher) { |
michael@0 | 8980 | var memo = {}; |
michael@0 | 8981 | hasher || (hasher = _.identity); |
michael@0 | 8982 | return function() { |
michael@0 | 8983 | var key = hasher.apply(this, arguments); |
michael@0 | 8984 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); |
michael@0 | 8985 | }; |
michael@0 | 8986 | }; |
michael@0 | 8987 | |
michael@0 | 8988 | // Delays a function for the given number of milliseconds, and then calls |
michael@0 | 8989 | // it with the arguments supplied. |
michael@0 | 8990 | _.delay = function(func, wait) { |
michael@0 | 8991 | var args = slice.call(arguments, 2); |
michael@0 | 8992 | return setTimeout(function(){ return func.apply(null, args); }, wait); |
michael@0 | 8993 | }; |
michael@0 | 8994 | |
michael@0 | 8995 | // Defers a function, scheduling it to run after the current call stack has |
michael@0 | 8996 | // cleared. |
michael@0 | 8997 | _.defer = function(func) { |
michael@0 | 8998 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); |
michael@0 | 8999 | }; |
michael@0 | 9000 | |
michael@0 | 9001 | // Returns a function, that, when invoked, will only be triggered at most once |
michael@0 | 9002 | // during a given window of time. |
michael@0 | 9003 | _.throttle = function(func, wait) { |
michael@0 | 9004 | var context, args, timeout, result; |
michael@0 | 9005 | var previous = 0; |
michael@0 | 9006 | var later = function() { |
michael@0 | 9007 | previous = new Date; |
michael@0 | 9008 | timeout = null; |
michael@0 | 9009 | result = func.apply(context, args); |
michael@0 | 9010 | }; |
michael@0 | 9011 | return function() { |
michael@0 | 9012 | var now = new Date; |
michael@0 | 9013 | var remaining = wait - (now - previous); |
michael@0 | 9014 | context = this; |
michael@0 | 9015 | args = arguments; |
michael@0 | 9016 | if (remaining <= 0) { |
michael@0 | 9017 | clearTimeout(timeout); |
michael@0 | 9018 | timeout = null; |
michael@0 | 9019 | previous = now; |
michael@0 | 9020 | result = func.apply(context, args); |
michael@0 | 9021 | } else if (!timeout) { |
michael@0 | 9022 | timeout = setTimeout(later, remaining); |
michael@0 | 9023 | } |
michael@0 | 9024 | return result; |
michael@0 | 9025 | }; |
michael@0 | 9026 | }; |
michael@0 | 9027 | |
michael@0 | 9028 | // Returns a function, that, as long as it continues to be invoked, will not |
michael@0 | 9029 | // be triggered. The function will be called after it stops being called for |
michael@0 | 9030 | // N milliseconds. If `immediate` is passed, trigger the function on the |
michael@0 | 9031 | // leading edge, instead of the trailing. |
michael@0 | 9032 | _.debounce = function(func, wait, immediate) { |
michael@0 | 9033 | var timeout, result; |
michael@0 | 9034 | return function() { |
michael@0 | 9035 | var context = this, args = arguments; |
michael@0 | 9036 | var later = function() { |
michael@0 | 9037 | timeout = null; |
michael@0 | 9038 | if (!immediate) result = func.apply(context, args); |
michael@0 | 9039 | }; |
michael@0 | 9040 | var callNow = immediate && !timeout; |
michael@0 | 9041 | clearTimeout(timeout); |
michael@0 | 9042 | timeout = setTimeout(later, wait); |
michael@0 | 9043 | if (callNow) result = func.apply(context, args); |
michael@0 | 9044 | return result; |
michael@0 | 9045 | }; |
michael@0 | 9046 | }; |
michael@0 | 9047 | |
michael@0 | 9048 | // Returns a function that will be executed at most one time, no matter how |
michael@0 | 9049 | // often you call it. Useful for lazy initialization. |
michael@0 | 9050 | _.once = function(func) { |
michael@0 | 9051 | var ran = false, memo; |
michael@0 | 9052 | return function() { |
michael@0 | 9053 | if (ran) return memo; |
michael@0 | 9054 | ran = true; |
michael@0 | 9055 | memo = func.apply(this, arguments); |
michael@0 | 9056 | func = null; |
michael@0 | 9057 | return memo; |
michael@0 | 9058 | }; |
michael@0 | 9059 | }; |
michael@0 | 9060 | |
michael@0 | 9061 | // Returns the first function passed as an argument to the second, |
michael@0 | 9062 | // allowing you to adjust arguments, run code before and after, and |
michael@0 | 9063 | // conditionally execute the original function. |
michael@0 | 9064 | _.wrap = function(func, wrapper) { |
michael@0 | 9065 | return function() { |
michael@0 | 9066 | var args = [func]; |
michael@0 | 9067 | push.apply(args, arguments); |
michael@0 | 9068 | return wrapper.apply(this, args); |
michael@0 | 9069 | }; |
michael@0 | 9070 | }; |
michael@0 | 9071 | |
michael@0 | 9072 | // Returns a function that is the composition of a list of functions, each |
michael@0 | 9073 | // consuming the return value of the function that follows. |
michael@0 | 9074 | _.compose = function() { |
michael@0 | 9075 | var funcs = arguments; |
michael@0 | 9076 | return function() { |
michael@0 | 9077 | var args = arguments; |
michael@0 | 9078 | for (var i = funcs.length - 1; i >= 0; i--) { |
michael@0 | 9079 | args = [funcs[i].apply(this, args)]; |
michael@0 | 9080 | } |
michael@0 | 9081 | return args[0]; |
michael@0 | 9082 | }; |
michael@0 | 9083 | }; |
michael@0 | 9084 | |
michael@0 | 9085 | // Returns a function that will only be executed after being called N times. |
michael@0 | 9086 | _.after = function(times, func) { |
michael@0 | 9087 | if (times <= 0) return func(); |
michael@0 | 9088 | return function() { |
michael@0 | 9089 | if (--times < 1) { |
michael@0 | 9090 | return func.apply(this, arguments); |
michael@0 | 9091 | } |
michael@0 | 9092 | }; |
michael@0 | 9093 | }; |
michael@0 | 9094 | |
michael@0 | 9095 | // Object Functions |
michael@0 | 9096 | // ---------------- |
michael@0 | 9097 | |
michael@0 | 9098 | // Retrieve the names of an object's properties. |
michael@0 | 9099 | // Delegates to **ECMAScript 5**'s native `Object.keys` |
michael@0 | 9100 | _.keys = nativeKeys || function(obj) { |
michael@0 | 9101 | if (obj !== Object(obj)) throw new TypeError('Invalid object'); |
michael@0 | 9102 | var keys = []; |
michael@0 | 9103 | for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; |
michael@0 | 9104 | return keys; |
michael@0 | 9105 | }; |
michael@0 | 9106 | |
michael@0 | 9107 | // Retrieve the values of an object's properties. |
michael@0 | 9108 | _.values = function(obj) { |
michael@0 | 9109 | var values = []; |
michael@0 | 9110 | for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); |
michael@0 | 9111 | return values; |
michael@0 | 9112 | }; |
michael@0 | 9113 | |
michael@0 | 9114 | // Convert an object into a list of `[key, value]` pairs. |
michael@0 | 9115 | _.pairs = function(obj) { |
michael@0 | 9116 | var pairs = []; |
michael@0 | 9117 | for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); |
michael@0 | 9118 | return pairs; |
michael@0 | 9119 | }; |
michael@0 | 9120 | |
michael@0 | 9121 | // Invert the keys and values of an object. The values must be serializable. |
michael@0 | 9122 | _.invert = function(obj) { |
michael@0 | 9123 | var result = {}; |
michael@0 | 9124 | for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; |
michael@0 | 9125 | return result; |
michael@0 | 9126 | }; |
michael@0 | 9127 | |
michael@0 | 9128 | // Return a sorted list of the function names available on the object. |
michael@0 | 9129 | // Aliased as `methods` |
michael@0 | 9130 | _.functions = _.methods = function(obj) { |
michael@0 | 9131 | var names = []; |
michael@0 | 9132 | for (var key in obj) { |
michael@0 | 9133 | if (_.isFunction(obj[key])) names.push(key); |
michael@0 | 9134 | } |
michael@0 | 9135 | return names.sort(); |
michael@0 | 9136 | }; |
michael@0 | 9137 | |
michael@0 | 9138 | // Extend a given object with all the properties in passed-in object(s). |
michael@0 | 9139 | _.extend = function(obj) { |
michael@0 | 9140 | each(slice.call(arguments, 1), function(source) { |
michael@0 | 9141 | if (source) { |
michael@0 | 9142 | for (var prop in source) { |
michael@0 | 9143 | obj[prop] = source[prop]; |
michael@0 | 9144 | } |
michael@0 | 9145 | } |
michael@0 | 9146 | }); |
michael@0 | 9147 | return obj; |
michael@0 | 9148 | }; |
michael@0 | 9149 | |
michael@0 | 9150 | // Return a copy of the object only containing the whitelisted properties. |
michael@0 | 9151 | _.pick = function(obj) { |
michael@0 | 9152 | var copy = {}; |
michael@0 | 9153 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); |
michael@0 | 9154 | each(keys, function(key) { |
michael@0 | 9155 | if (key in obj) copy[key] = obj[key]; |
michael@0 | 9156 | }); |
michael@0 | 9157 | return copy; |
michael@0 | 9158 | }; |
michael@0 | 9159 | |
michael@0 | 9160 | // Return a copy of the object without the blacklisted properties. |
michael@0 | 9161 | _.omit = function(obj) { |
michael@0 | 9162 | var copy = {}; |
michael@0 | 9163 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); |
michael@0 | 9164 | for (var key in obj) { |
michael@0 | 9165 | if (!_.contains(keys, key)) copy[key] = obj[key]; |
michael@0 | 9166 | } |
michael@0 | 9167 | return copy; |
michael@0 | 9168 | }; |
michael@0 | 9169 | |
michael@0 | 9170 | // Fill in a given object with default properties. |
michael@0 | 9171 | _.defaults = function(obj) { |
michael@0 | 9172 | each(slice.call(arguments, 1), function(source) { |
michael@0 | 9173 | if (source) { |
michael@0 | 9174 | for (var prop in source) { |
michael@0 | 9175 | if (obj[prop] == null) obj[prop] = source[prop]; |
michael@0 | 9176 | } |
michael@0 | 9177 | } |
michael@0 | 9178 | }); |
michael@0 | 9179 | return obj; |
michael@0 | 9180 | }; |
michael@0 | 9181 | |
michael@0 | 9182 | // Create a (shallow-cloned) duplicate of an object. |
michael@0 | 9183 | _.clone = function(obj) { |
michael@0 | 9184 | if (!_.isObject(obj)) return obj; |
michael@0 | 9185 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); |
michael@0 | 9186 | }; |
michael@0 | 9187 | |
michael@0 | 9188 | // Invokes interceptor with the obj, and then returns obj. |
michael@0 | 9189 | // The primary purpose of this method is to "tap into" a method chain, in |
michael@0 | 9190 | // order to perform operations on intermediate results within the chain. |
michael@0 | 9191 | _.tap = function(obj, interceptor) { |
michael@0 | 9192 | interceptor(obj); |
michael@0 | 9193 | return obj; |
michael@0 | 9194 | }; |
michael@0 | 9195 | |
michael@0 | 9196 | // Internal recursive comparison function for `isEqual`. |
michael@0 | 9197 | var eq = function(a, b, aStack, bStack) { |
michael@0 | 9198 | // Identical objects are equal. `0 === -0`, but they aren't identical. |
michael@0 | 9199 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. |
michael@0 | 9200 | if (a === b) return a !== 0 || 1 / a == 1 / b; |
michael@0 | 9201 | // A strict comparison is necessary because `null == undefined`. |
michael@0 | 9202 | if (a == null || b == null) return a === b; |
michael@0 | 9203 | // Unwrap any wrapped objects. |
michael@0 | 9204 | if (a instanceof _) a = a._wrapped; |
michael@0 | 9205 | if (b instanceof _) b = b._wrapped; |
michael@0 | 9206 | // Compare `[[Class]]` names. |
michael@0 | 9207 | var className = toString.call(a); |
michael@0 | 9208 | if (className != toString.call(b)) return false; |
michael@0 | 9209 | switch (className) { |
michael@0 | 9210 | // Strings, numbers, dates, and booleans are compared by value. |
michael@0 | 9211 | case '[object String]': |
michael@0 | 9212 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is |
michael@0 | 9213 | // equivalent to `new String("5")`. |
michael@0 | 9214 | return a == String(b); |
michael@0 | 9215 | case '[object Number]': |
michael@0 | 9216 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for |
michael@0 | 9217 | // other numeric values. |
michael@0 | 9218 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); |
michael@0 | 9219 | case '[object Date]': |
michael@0 | 9220 | case '[object Boolean]': |
michael@0 | 9221 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their |
michael@0 | 9222 | // millisecond representations. Note that invalid dates with millisecond representations |
michael@0 | 9223 | // of `NaN` are not equivalent. |
michael@0 | 9224 | return +a == +b; |
michael@0 | 9225 | // RegExps are compared by their source patterns and flags. |
michael@0 | 9226 | case '[object RegExp]': |
michael@0 | 9227 | return a.source == b.source && |
michael@0 | 9228 | a.global == b.global && |
michael@0 | 9229 | a.multiline == b.multiline && |
michael@0 | 9230 | a.ignoreCase == b.ignoreCase; |
michael@0 | 9231 | } |
michael@0 | 9232 | if (typeof a != 'object' || typeof b != 'object') return false; |
michael@0 | 9233 | // Assume equality for cyclic structures. The algorithm for detecting cyclic |
michael@0 | 9234 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. |
michael@0 | 9235 | var length = aStack.length; |
michael@0 | 9236 | while (length--) { |
michael@0 | 9237 | // Linear search. Performance is inversely proportional to the number of |
michael@0 | 9238 | // unique nested structures. |
michael@0 | 9239 | if (aStack[length] == a) return bStack[length] == b; |
michael@0 | 9240 | } |
michael@0 | 9241 | // Add the first object to the stack of traversed objects. |
michael@0 | 9242 | aStack.push(a); |
michael@0 | 9243 | bStack.push(b); |
michael@0 | 9244 | var size = 0, result = true; |
michael@0 | 9245 | // Recursively compare objects and arrays. |
michael@0 | 9246 | if (className == '[object Array]') { |
michael@0 | 9247 | // Compare array lengths to determine if a deep comparison is necessary. |
michael@0 | 9248 | size = a.length; |
michael@0 | 9249 | result = size == b.length; |
michael@0 | 9250 | if (result) { |
michael@0 | 9251 | // Deep compare the contents, ignoring non-numeric properties. |
michael@0 | 9252 | while (size--) { |
michael@0 | 9253 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; |
michael@0 | 9254 | } |
michael@0 | 9255 | } |
michael@0 | 9256 | } else { |
michael@0 | 9257 | // Objects with different constructors are not equivalent, but `Object`s |
michael@0 | 9258 | // from different frames are. |
michael@0 | 9259 | var aCtor = a.constructor, bCtor = b.constructor; |
michael@0 | 9260 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && |
michael@0 | 9261 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) { |
michael@0 | 9262 | return false; |
michael@0 | 9263 | } |
michael@0 | 9264 | // Deep compare objects. |
michael@0 | 9265 | for (var key in a) { |
michael@0 | 9266 | if (_.has(a, key)) { |
michael@0 | 9267 | // Count the expected number of properties. |
michael@0 | 9268 | size++; |
michael@0 | 9269 | // Deep compare each member. |
michael@0 | 9270 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; |
michael@0 | 9271 | } |
michael@0 | 9272 | } |
michael@0 | 9273 | // Ensure that both objects contain the same number of properties. |
michael@0 | 9274 | if (result) { |
michael@0 | 9275 | for (key in b) { |
michael@0 | 9276 | if (_.has(b, key) && !(size--)) break; |
michael@0 | 9277 | } |
michael@0 | 9278 | result = !size; |
michael@0 | 9279 | } |
michael@0 | 9280 | } |
michael@0 | 9281 | // Remove the first object from the stack of traversed objects. |
michael@0 | 9282 | aStack.pop(); |
michael@0 | 9283 | bStack.pop(); |
michael@0 | 9284 | return result; |
michael@0 | 9285 | }; |
michael@0 | 9286 | |
michael@0 | 9287 | // Perform a deep comparison to check if two objects are equal. |
michael@0 | 9288 | _.isEqual = function(a, b) { |
michael@0 | 9289 | return eq(a, b, [], []); |
michael@0 | 9290 | }; |
michael@0 | 9291 | |
michael@0 | 9292 | // Is a given array, string, or object empty? |
michael@0 | 9293 | // An "empty" object has no enumerable own-properties. |
michael@0 | 9294 | _.isEmpty = function(obj) { |
michael@0 | 9295 | if (obj == null) return true; |
michael@0 | 9296 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; |
michael@0 | 9297 | for (var key in obj) if (_.has(obj, key)) return false; |
michael@0 | 9298 | return true; |
michael@0 | 9299 | }; |
michael@0 | 9300 | |
michael@0 | 9301 | // Is a given value a DOM element? |
michael@0 | 9302 | _.isElement = function(obj) { |
michael@0 | 9303 | return !!(obj && obj.nodeType === 1); |
michael@0 | 9304 | }; |
michael@0 | 9305 | |
michael@0 | 9306 | // Is a given value an array? |
michael@0 | 9307 | // Delegates to ECMA5's native Array.isArray |
michael@0 | 9308 | _.isArray = nativeIsArray || function(obj) { |
michael@0 | 9309 | return toString.call(obj) == '[object Array]'; |
michael@0 | 9310 | }; |
michael@0 | 9311 | |
michael@0 | 9312 | // Is a given variable an object? |
michael@0 | 9313 | _.isObject = function(obj) { |
michael@0 | 9314 | return obj === Object(obj); |
michael@0 | 9315 | }; |
michael@0 | 9316 | |
michael@0 | 9317 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. |
michael@0 | 9318 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { |
michael@0 | 9319 | _['is' + name] = function(obj) { |
michael@0 | 9320 | return toString.call(obj) == '[object ' + name + ']'; |
michael@0 | 9321 | }; |
michael@0 | 9322 | }); |
michael@0 | 9323 | |
michael@0 | 9324 | // Define a fallback version of the method in browsers (ahem, IE), where |
michael@0 | 9325 | // there isn't any inspectable "Arguments" type. |
michael@0 | 9326 | if (!_.isArguments(arguments)) { |
michael@0 | 9327 | _.isArguments = function(obj) { |
michael@0 | 9328 | return !!(obj && _.has(obj, 'callee')); |
michael@0 | 9329 | }; |
michael@0 | 9330 | } |
michael@0 | 9331 | |
michael@0 | 9332 | // Optimize `isFunction` if appropriate. |
michael@0 | 9333 | if (typeof (/./) !== 'function') { |
michael@0 | 9334 | _.isFunction = function(obj) { |
michael@0 | 9335 | return typeof obj === 'function'; |
michael@0 | 9336 | }; |
michael@0 | 9337 | } |
michael@0 | 9338 | |
michael@0 | 9339 | // Is a given object a finite number? |
michael@0 | 9340 | _.isFinite = function(obj) { |
michael@0 | 9341 | return isFinite(obj) && !isNaN(parseFloat(obj)); |
michael@0 | 9342 | }; |
michael@0 | 9343 | |
michael@0 | 9344 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). |
michael@0 | 9345 | _.isNaN = function(obj) { |
michael@0 | 9346 | return _.isNumber(obj) && obj != +obj; |
michael@0 | 9347 | }; |
michael@0 | 9348 | |
michael@0 | 9349 | // Is a given value a boolean? |
michael@0 | 9350 | _.isBoolean = function(obj) { |
michael@0 | 9351 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; |
michael@0 | 9352 | }; |
michael@0 | 9353 | |
michael@0 | 9354 | // Is a given value equal to null? |
michael@0 | 9355 | _.isNull = function(obj) { |
michael@0 | 9356 | return obj === null; |
michael@0 | 9357 | }; |
michael@0 | 9358 | |
michael@0 | 9359 | // Is a given variable undefined? |
michael@0 | 9360 | _.isUndefined = function(obj) { |
michael@0 | 9361 | return obj === void 0; |
michael@0 | 9362 | }; |
michael@0 | 9363 | |
michael@0 | 9364 | // Shortcut function for checking if an object has a given property directly |
michael@0 | 9365 | // on itself (in other words, not on a prototype). |
michael@0 | 9366 | _.has = function(obj, key) { |
michael@0 | 9367 | return hasOwnProperty.call(obj, key); |
michael@0 | 9368 | }; |
michael@0 | 9369 | |
michael@0 | 9370 | // Utility Functions |
michael@0 | 9371 | // ----------------- |
michael@0 | 9372 | |
michael@0 | 9373 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its |
michael@0 | 9374 | // previous owner. Returns a reference to the Underscore object. |
michael@0 | 9375 | _.noConflict = function() { |
michael@0 | 9376 | root._ = previousUnderscore; |
michael@0 | 9377 | return this; |
michael@0 | 9378 | }; |
michael@0 | 9379 | |
michael@0 | 9380 | // Keep the identity function around for default iterators. |
michael@0 | 9381 | _.identity = function(value) { |
michael@0 | 9382 | return value; |
michael@0 | 9383 | }; |
michael@0 | 9384 | |
michael@0 | 9385 | // Run a function **n** times. |
michael@0 | 9386 | _.times = function(n, iterator, context) { |
michael@0 | 9387 | var accum = Array(n); |
michael@0 | 9388 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); |
michael@0 | 9389 | return accum; |
michael@0 | 9390 | }; |
michael@0 | 9391 | |
michael@0 | 9392 | // Return a random integer between min and max (inclusive). |
michael@0 | 9393 | _.random = function(min, max) { |
michael@0 | 9394 | if (max == null) { |
michael@0 | 9395 | max = min; |
michael@0 | 9396 | min = 0; |
michael@0 | 9397 | } |
michael@0 | 9398 | return min + Math.floor(Math.random() * (max - min + 1)); |
michael@0 | 9399 | }; |
michael@0 | 9400 | |
michael@0 | 9401 | // List of HTML entities for escaping. |
michael@0 | 9402 | var entityMap = { |
michael@0 | 9403 | escape: { |
michael@0 | 9404 | '&': '&', |
michael@0 | 9405 | '<': '<', |
michael@0 | 9406 | '>': '>', |
michael@0 | 9407 | '"': '"', |
michael@0 | 9408 | "'": ''', |
michael@0 | 9409 | '/': '/' |
michael@0 | 9410 | } |
michael@0 | 9411 | }; |
michael@0 | 9412 | entityMap.unescape = _.invert(entityMap.escape); |
michael@0 | 9413 | |
michael@0 | 9414 | // Regexes containing the keys and values listed immediately above. |
michael@0 | 9415 | var entityRegexes = { |
michael@0 | 9416 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), |
michael@0 | 9417 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') |
michael@0 | 9418 | }; |
michael@0 | 9419 | |
michael@0 | 9420 | // Functions for escaping and unescaping strings to/from HTML interpolation. |
michael@0 | 9421 | _.each(['escape', 'unescape'], function(method) { |
michael@0 | 9422 | _[method] = function(string) { |
michael@0 | 9423 | if (string == null) return ''; |
michael@0 | 9424 | return ('' + string).replace(entityRegexes[method], function(match) { |
michael@0 | 9425 | return entityMap[method][match]; |
michael@0 | 9426 | }); |
michael@0 | 9427 | }; |
michael@0 | 9428 | }); |
michael@0 | 9429 | |
michael@0 | 9430 | // If the value of the named property is a function then invoke it; |
michael@0 | 9431 | // otherwise, return it. |
michael@0 | 9432 | _.result = function(object, property) { |
michael@0 | 9433 | if (object == null) return null; |
michael@0 | 9434 | var value = object[property]; |
michael@0 | 9435 | return _.isFunction(value) ? value.call(object) : value; |
michael@0 | 9436 | }; |
michael@0 | 9437 | |
michael@0 | 9438 | // Add your own custom functions to the Underscore object. |
michael@0 | 9439 | _.mixin = function(obj) { |
michael@0 | 9440 | each(_.functions(obj), function(name){ |
michael@0 | 9441 | var func = _[name] = obj[name]; |
michael@0 | 9442 | _.prototype[name] = function() { |
michael@0 | 9443 | var args = [this._wrapped]; |
michael@0 | 9444 | push.apply(args, arguments); |
michael@0 | 9445 | return result.call(this, func.apply(_, args)); |
michael@0 | 9446 | }; |
michael@0 | 9447 | }); |
michael@0 | 9448 | }; |
michael@0 | 9449 | |
michael@0 | 9450 | // Generate a unique integer id (unique within the entire client session). |
michael@0 | 9451 | // Useful for temporary DOM ids. |
michael@0 | 9452 | var idCounter = 0; |
michael@0 | 9453 | _.uniqueId = function(prefix) { |
michael@0 | 9454 | var id = ++idCounter + ''; |
michael@0 | 9455 | return prefix ? prefix + id : id; |
michael@0 | 9456 | }; |
michael@0 | 9457 | |
michael@0 | 9458 | // By default, Underscore uses ERB-style template delimiters, change the |
michael@0 | 9459 | // following template settings to use alternative delimiters. |
michael@0 | 9460 | _.templateSettings = { |
michael@0 | 9461 | evaluate : /<%([\s\S]+?)%>/g, |
michael@0 | 9462 | interpolate : /<%=([\s\S]+?)%>/g, |
michael@0 | 9463 | escape : /<%-([\s\S]+?)%>/g |
michael@0 | 9464 | }; |
michael@0 | 9465 | |
michael@0 | 9466 | // When customizing `templateSettings`, if you don't want to define an |
michael@0 | 9467 | // interpolation, evaluation or escaping regex, we need one that is |
michael@0 | 9468 | // guaranteed not to match. |
michael@0 | 9469 | var noMatch = /(.)^/; |
michael@0 | 9470 | |
michael@0 | 9471 | // Certain characters need to be escaped so that they can be put into a |
michael@0 | 9472 | // string literal. |
michael@0 | 9473 | var escapes = { |
michael@0 | 9474 | "'": "'", |
michael@0 | 9475 | '\\': '\\', |
michael@0 | 9476 | '\r': 'r', |
michael@0 | 9477 | '\n': 'n', |
michael@0 | 9478 | '\t': 't', |
michael@0 | 9479 | '\u2028': 'u2028', |
michael@0 | 9480 | '\u2029': 'u2029' |
michael@0 | 9481 | }; |
michael@0 | 9482 | |
michael@0 | 9483 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; |
michael@0 | 9484 | |
michael@0 | 9485 | // JavaScript micro-templating, similar to John Resig's implementation. |
michael@0 | 9486 | // Underscore templating handles arbitrary delimiters, preserves whitespace, |
michael@0 | 9487 | // and correctly escapes quotes within interpolated code. |
michael@0 | 9488 | _.template = function(text, data, settings) { |
michael@0 | 9489 | var render; |
michael@0 | 9490 | settings = _.defaults({}, settings, _.templateSettings); |
michael@0 | 9491 | |
michael@0 | 9492 | // Combine delimiters into one regular expression via alternation. |
michael@0 | 9493 | var matcher = new RegExp([ |
michael@0 | 9494 | (settings.escape || noMatch).source, |
michael@0 | 9495 | (settings.interpolate || noMatch).source, |
michael@0 | 9496 | (settings.evaluate || noMatch).source |
michael@0 | 9497 | ].join('|') + '|$', 'g'); |
michael@0 | 9498 | |
michael@0 | 9499 | // Compile the template source, escaping string literals appropriately. |
michael@0 | 9500 | var index = 0; |
michael@0 | 9501 | var source = "__p+='"; |
michael@0 | 9502 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { |
michael@0 | 9503 | source += text.slice(index, offset) |
michael@0 | 9504 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); |
michael@0 | 9505 | |
michael@0 | 9506 | if (escape) { |
michael@0 | 9507 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; |
michael@0 | 9508 | } |
michael@0 | 9509 | if (interpolate) { |
michael@0 | 9510 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; |
michael@0 | 9511 | } |
michael@0 | 9512 | if (evaluate) { |
michael@0 | 9513 | source += "';\n" + evaluate + "\n__p+='"; |
michael@0 | 9514 | } |
michael@0 | 9515 | index = offset + match.length; |
michael@0 | 9516 | return match; |
michael@0 | 9517 | }); |
michael@0 | 9518 | source += "';\n"; |
michael@0 | 9519 | |
michael@0 | 9520 | // If a variable is not specified, place data values in local scope. |
michael@0 | 9521 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; |
michael@0 | 9522 | |
michael@0 | 9523 | source = "var __t,__p='',__j=Array.prototype.join," + |
michael@0 | 9524 | "print=function(){__p+=__j.call(arguments,'');};\n" + |
michael@0 | 9525 | source + "return __p;\n"; |
michael@0 | 9526 | |
michael@0 | 9527 | try { |
michael@0 | 9528 | render = new Function(settings.variable || 'obj', '_', source); |
michael@0 | 9529 | } catch (e) { |
michael@0 | 9530 | e.source = source; |
michael@0 | 9531 | throw e; |
michael@0 | 9532 | } |
michael@0 | 9533 | |
michael@0 | 9534 | if (data) return render(data, _); |
michael@0 | 9535 | var template = function(data) { |
michael@0 | 9536 | return render.call(this, data, _); |
michael@0 | 9537 | }; |
michael@0 | 9538 | |
michael@0 | 9539 | // Provide the compiled function source as a convenience for precompilation. |
michael@0 | 9540 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; |
michael@0 | 9541 | |
michael@0 | 9542 | return template; |
michael@0 | 9543 | }; |
michael@0 | 9544 | |
michael@0 | 9545 | // Add a "chain" function, which will delegate to the wrapper. |
michael@0 | 9546 | _.chain = function(obj) { |
michael@0 | 9547 | return _(obj).chain(); |
michael@0 | 9548 | }; |
michael@0 | 9549 | |
michael@0 | 9550 | // OOP |
michael@0 | 9551 | // --------------- |
michael@0 | 9552 | // If Underscore is called as a function, it returns a wrapped object that |
michael@0 | 9553 | // can be used OO-style. This wrapper holds altered versions of all the |
michael@0 | 9554 | // underscore functions. Wrapped objects may be chained. |
michael@0 | 9555 | |
michael@0 | 9556 | // Helper function to continue chaining intermediate results. |
michael@0 | 9557 | var result = function(obj) { |
michael@0 | 9558 | return this._chain ? _(obj).chain() : obj; |
michael@0 | 9559 | }; |
michael@0 | 9560 | |
michael@0 | 9561 | // Add all of the Underscore functions to the wrapper object. |
michael@0 | 9562 | _.mixin(_); |
michael@0 | 9563 | |
michael@0 | 9564 | // Add all mutator Array functions to the wrapper. |
michael@0 | 9565 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { |
michael@0 | 9566 | var method = ArrayProto[name]; |
michael@0 | 9567 | _.prototype[name] = function() { |
michael@0 | 9568 | var obj = this._wrapped; |
michael@0 | 9569 | method.apply(obj, arguments); |
michael@0 | 9570 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; |
michael@0 | 9571 | return result.call(this, obj); |
michael@0 | 9572 | }; |
michael@0 | 9573 | }); |
michael@0 | 9574 | |
michael@0 | 9575 | // Add all accessor Array functions to the wrapper. |
michael@0 | 9576 | each(['concat', 'join', 'slice'], function(name) { |
michael@0 | 9577 | var method = ArrayProto[name]; |
michael@0 | 9578 | _.prototype[name] = function() { |
michael@0 | 9579 | return result.call(this, method.apply(this._wrapped, arguments)); |
michael@0 | 9580 | }; |
michael@0 | 9581 | }); |
michael@0 | 9582 | |
michael@0 | 9583 | _.extend(_.prototype, { |
michael@0 | 9584 | |
michael@0 | 9585 | // Start chaining a wrapped Underscore object. |
michael@0 | 9586 | chain: function() { |
michael@0 | 9587 | this._chain = true; |
michael@0 | 9588 | return this; |
michael@0 | 9589 | }, |
michael@0 | 9590 | |
michael@0 | 9591 | // Extracts the result from a wrapped and chained object. |
michael@0 | 9592 | value: function() { |
michael@0 | 9593 | return this._wrapped; |
michael@0 | 9594 | } |
michael@0 | 9595 | |
michael@0 | 9596 | }); |
michael@0 | 9597 | |
michael@0 | 9598 | }).call(this); |
michael@0 | 9599 | |
michael@0 | 9600 | })() |
michael@0 | 9601 | },{}],14:[function(require,module,exports){ |
michael@0 | 9602 | exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) { |
michael@0 | 9603 | var e, m, |
michael@0 | 9604 | eLen = nBytes * 8 - mLen - 1, |
michael@0 | 9605 | eMax = (1 << eLen) - 1, |
michael@0 | 9606 | eBias = eMax >> 1, |
michael@0 | 9607 | nBits = -7, |
michael@0 | 9608 | i = isBE ? 0 : (nBytes - 1), |
michael@0 | 9609 | d = isBE ? 1 : -1, |
michael@0 | 9610 | s = buffer[offset + i]; |
michael@0 | 9611 | |
michael@0 | 9612 | i += d; |
michael@0 | 9613 | |
michael@0 | 9614 | e = s & ((1 << (-nBits)) - 1); |
michael@0 | 9615 | s >>= (-nBits); |
michael@0 | 9616 | nBits += eLen; |
michael@0 | 9617 | for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); |
michael@0 | 9618 | |
michael@0 | 9619 | m = e & ((1 << (-nBits)) - 1); |
michael@0 | 9620 | e >>= (-nBits); |
michael@0 | 9621 | nBits += mLen; |
michael@0 | 9622 | for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); |
michael@0 | 9623 | |
michael@0 | 9624 | if (e === 0) { |
michael@0 | 9625 | e = 1 - eBias; |
michael@0 | 9626 | } else if (e === eMax) { |
michael@0 | 9627 | return m ? NaN : ((s ? -1 : 1) * Infinity); |
michael@0 | 9628 | } else { |
michael@0 | 9629 | m = m + Math.pow(2, mLen); |
michael@0 | 9630 | e = e - eBias; |
michael@0 | 9631 | } |
michael@0 | 9632 | return (s ? -1 : 1) * m * Math.pow(2, e - mLen); |
michael@0 | 9633 | }; |
michael@0 | 9634 | |
michael@0 | 9635 | exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { |
michael@0 | 9636 | var e, m, c, |
michael@0 | 9637 | eLen = nBytes * 8 - mLen - 1, |
michael@0 | 9638 | eMax = (1 << eLen) - 1, |
michael@0 | 9639 | eBias = eMax >> 1, |
michael@0 | 9640 | rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), |
michael@0 | 9641 | i = isBE ? (nBytes - 1) : 0, |
michael@0 | 9642 | d = isBE ? -1 : 1, |
michael@0 | 9643 | s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; |
michael@0 | 9644 | |
michael@0 | 9645 | value = Math.abs(value); |
michael@0 | 9646 | |
michael@0 | 9647 | if (isNaN(value) || value === Infinity) { |
michael@0 | 9648 | m = isNaN(value) ? 1 : 0; |
michael@0 | 9649 | e = eMax; |
michael@0 | 9650 | } else { |
michael@0 | 9651 | e = Math.floor(Math.log(value) / Math.LN2); |
michael@0 | 9652 | if (value * (c = Math.pow(2, -e)) < 1) { |
michael@0 | 9653 | e--; |
michael@0 | 9654 | c *= 2; |
michael@0 | 9655 | } |
michael@0 | 9656 | if (e + eBias >= 1) { |
michael@0 | 9657 | value += rt / c; |
michael@0 | 9658 | } else { |
michael@0 | 9659 | value += rt * Math.pow(2, 1 - eBias); |
michael@0 | 9660 | } |
michael@0 | 9661 | if (value * c >= 2) { |
michael@0 | 9662 | e++; |
michael@0 | 9663 | c /= 2; |
michael@0 | 9664 | } |
michael@0 | 9665 | |
michael@0 | 9666 | if (e + eBias >= eMax) { |
michael@0 | 9667 | m = 0; |
michael@0 | 9668 | e = eMax; |
michael@0 | 9669 | } else if (e + eBias >= 1) { |
michael@0 | 9670 | m = (value * c - 1) * Math.pow(2, mLen); |
michael@0 | 9671 | e = e + eBias; |
michael@0 | 9672 | } else { |
michael@0 | 9673 | m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); |
michael@0 | 9674 | e = 0; |
michael@0 | 9675 | } |
michael@0 | 9676 | } |
michael@0 | 9677 | |
michael@0 | 9678 | for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); |
michael@0 | 9679 | |
michael@0 | 9680 | e = (e << mLen) | m; |
michael@0 | 9681 | eLen += mLen; |
michael@0 | 9682 | for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); |
michael@0 | 9683 | |
michael@0 | 9684 | buffer[offset + i - d] |= s * 128; |
michael@0 | 9685 | }; |
michael@0 | 9686 | |
michael@0 | 9687 | },{}],13:[function(require,module,exports){ |
michael@0 | 9688 | (function(){function SlowBuffer (size) { |
michael@0 | 9689 | this.length = size; |
michael@0 | 9690 | }; |
michael@0 | 9691 | |
michael@0 | 9692 | var assert = require('assert'); |
michael@0 | 9693 | |
michael@0 | 9694 | exports.INSPECT_MAX_BYTES = 50; |
michael@0 | 9695 | |
michael@0 | 9696 | |
michael@0 | 9697 | function toHex(n) { |
michael@0 | 9698 | if (n < 16) return '0' + n.toString(16); |
michael@0 | 9699 | return n.toString(16); |
michael@0 | 9700 | } |
michael@0 | 9701 | |
michael@0 | 9702 | function utf8ToBytes(str) { |
michael@0 | 9703 | var byteArray = []; |
michael@0 | 9704 | for (var i = 0; i < str.length; i++) |
michael@0 | 9705 | if (str.charCodeAt(i) <= 0x7F) |
michael@0 | 9706 | byteArray.push(str.charCodeAt(i)); |
michael@0 | 9707 | else { |
michael@0 | 9708 | var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); |
michael@0 | 9709 | for (var j = 0; j < h.length; j++) |
michael@0 | 9710 | byteArray.push(parseInt(h[j], 16)); |
michael@0 | 9711 | } |
michael@0 | 9712 | |
michael@0 | 9713 | return byteArray; |
michael@0 | 9714 | } |
michael@0 | 9715 | |
michael@0 | 9716 | function asciiToBytes(str) { |
michael@0 | 9717 | var byteArray = [] |
michael@0 | 9718 | for (var i = 0; i < str.length; i++ ) |
michael@0 | 9719 | // Node's code seems to be doing this and not & 0x7F.. |
michael@0 | 9720 | byteArray.push( str.charCodeAt(i) & 0xFF ); |
michael@0 | 9721 | |
michael@0 | 9722 | return byteArray; |
michael@0 | 9723 | } |
michael@0 | 9724 | |
michael@0 | 9725 | function base64ToBytes(str) { |
michael@0 | 9726 | return require("base64-js").toByteArray(str); |
michael@0 | 9727 | } |
michael@0 | 9728 | |
michael@0 | 9729 | SlowBuffer.byteLength = function (str, encoding) { |
michael@0 | 9730 | switch (encoding || "utf8") { |
michael@0 | 9731 | case 'hex': |
michael@0 | 9732 | return str.length / 2; |
michael@0 | 9733 | |
michael@0 | 9734 | case 'utf8': |
michael@0 | 9735 | case 'utf-8': |
michael@0 | 9736 | return utf8ToBytes(str).length; |
michael@0 | 9737 | |
michael@0 | 9738 | case 'ascii': |
michael@0 | 9739 | case 'binary': |
michael@0 | 9740 | return str.length; |
michael@0 | 9741 | |
michael@0 | 9742 | case 'base64': |
michael@0 | 9743 | return base64ToBytes(str).length; |
michael@0 | 9744 | |
michael@0 | 9745 | default: |
michael@0 | 9746 | throw new Error('Unknown encoding'); |
michael@0 | 9747 | } |
michael@0 | 9748 | }; |
michael@0 | 9749 | |
michael@0 | 9750 | function blitBuffer(src, dst, offset, length) { |
michael@0 | 9751 | var pos, i = 0; |
michael@0 | 9752 | while (i < length) { |
michael@0 | 9753 | if ((i+offset >= dst.length) || (i >= src.length)) |
michael@0 | 9754 | break; |
michael@0 | 9755 | |
michael@0 | 9756 | dst[i + offset] = src[i]; |
michael@0 | 9757 | i++; |
michael@0 | 9758 | } |
michael@0 | 9759 | return i; |
michael@0 | 9760 | } |
michael@0 | 9761 | |
michael@0 | 9762 | SlowBuffer.prototype.utf8Write = function (string, offset, length) { |
michael@0 | 9763 | var bytes, pos; |
michael@0 | 9764 | return SlowBuffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); |
michael@0 | 9765 | }; |
michael@0 | 9766 | |
michael@0 | 9767 | SlowBuffer.prototype.asciiWrite = function (string, offset, length) { |
michael@0 | 9768 | var bytes, pos; |
michael@0 | 9769 | return SlowBuffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); |
michael@0 | 9770 | }; |
michael@0 | 9771 | |
michael@0 | 9772 | SlowBuffer.prototype.binaryWrite = SlowBuffer.prototype.asciiWrite; |
michael@0 | 9773 | |
michael@0 | 9774 | SlowBuffer.prototype.base64Write = function (string, offset, length) { |
michael@0 | 9775 | var bytes, pos; |
michael@0 | 9776 | return SlowBuffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); |
michael@0 | 9777 | }; |
michael@0 | 9778 | |
michael@0 | 9779 | SlowBuffer.prototype.base64Slice = function (start, end) { |
michael@0 | 9780 | var bytes = Array.prototype.slice.apply(this, arguments) |
michael@0 | 9781 | return require("base64-js").fromByteArray(bytes); |
michael@0 | 9782 | } |
michael@0 | 9783 | |
michael@0 | 9784 | function decodeUtf8Char(str) { |
michael@0 | 9785 | try { |
michael@0 | 9786 | return decodeURIComponent(str); |
michael@0 | 9787 | } catch (err) { |
michael@0 | 9788 | return String.fromCharCode(0xFFFD); // UTF 8 invalid char |
michael@0 | 9789 | } |
michael@0 | 9790 | } |
michael@0 | 9791 | |
michael@0 | 9792 | SlowBuffer.prototype.utf8Slice = function () { |
michael@0 | 9793 | var bytes = Array.prototype.slice.apply(this, arguments); |
michael@0 | 9794 | var res = ""; |
michael@0 | 9795 | var tmp = ""; |
michael@0 | 9796 | var i = 0; |
michael@0 | 9797 | while (i < bytes.length) { |
michael@0 | 9798 | if (bytes[i] <= 0x7F) { |
michael@0 | 9799 | res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); |
michael@0 | 9800 | tmp = ""; |
michael@0 | 9801 | } else |
michael@0 | 9802 | tmp += "%" + bytes[i].toString(16); |
michael@0 | 9803 | |
michael@0 | 9804 | i++; |
michael@0 | 9805 | } |
michael@0 | 9806 | |
michael@0 | 9807 | return res + decodeUtf8Char(tmp); |
michael@0 | 9808 | } |
michael@0 | 9809 | |
michael@0 | 9810 | SlowBuffer.prototype.asciiSlice = function () { |
michael@0 | 9811 | var bytes = Array.prototype.slice.apply(this, arguments); |
michael@0 | 9812 | var ret = ""; |
michael@0 | 9813 | for (var i = 0; i < bytes.length; i++) |
michael@0 | 9814 | ret += String.fromCharCode(bytes[i]); |
michael@0 | 9815 | return ret; |
michael@0 | 9816 | } |
michael@0 | 9817 | |
michael@0 | 9818 | SlowBuffer.prototype.binarySlice = SlowBuffer.prototype.asciiSlice; |
michael@0 | 9819 | |
michael@0 | 9820 | SlowBuffer.prototype.inspect = function() { |
michael@0 | 9821 | var out = [], |
michael@0 | 9822 | len = this.length; |
michael@0 | 9823 | for (var i = 0; i < len; i++) { |
michael@0 | 9824 | out[i] = toHex(this[i]); |
michael@0 | 9825 | if (i == exports.INSPECT_MAX_BYTES) { |
michael@0 | 9826 | out[i + 1] = '...'; |
michael@0 | 9827 | break; |
michael@0 | 9828 | } |
michael@0 | 9829 | } |
michael@0 | 9830 | return '<SlowBuffer ' + out.join(' ') + '>'; |
michael@0 | 9831 | }; |
michael@0 | 9832 | |
michael@0 | 9833 | |
michael@0 | 9834 | SlowBuffer.prototype.hexSlice = function(start, end) { |
michael@0 | 9835 | var len = this.length; |
michael@0 | 9836 | |
michael@0 | 9837 | if (!start || start < 0) start = 0; |
michael@0 | 9838 | if (!end || end < 0 || end > len) end = len; |
michael@0 | 9839 | |
michael@0 | 9840 | var out = ''; |
michael@0 | 9841 | for (var i = start; i < end; i++) { |
michael@0 | 9842 | out += toHex(this[i]); |
michael@0 | 9843 | } |
michael@0 | 9844 | return out; |
michael@0 | 9845 | }; |
michael@0 | 9846 | |
michael@0 | 9847 | |
michael@0 | 9848 | SlowBuffer.prototype.toString = function(encoding, start, end) { |
michael@0 | 9849 | encoding = String(encoding || 'utf8').toLowerCase(); |
michael@0 | 9850 | start = +start || 0; |
michael@0 | 9851 | if (typeof end == 'undefined') end = this.length; |
michael@0 | 9852 | |
michael@0 | 9853 | // Fastpath empty strings |
michael@0 | 9854 | if (+end == start) { |
michael@0 | 9855 | return ''; |
michael@0 | 9856 | } |
michael@0 | 9857 | |
michael@0 | 9858 | switch (encoding) { |
michael@0 | 9859 | case 'hex': |
michael@0 | 9860 | return this.hexSlice(start, end); |
michael@0 | 9861 | |
michael@0 | 9862 | case 'utf8': |
michael@0 | 9863 | case 'utf-8': |
michael@0 | 9864 | return this.utf8Slice(start, end); |
michael@0 | 9865 | |
michael@0 | 9866 | case 'ascii': |
michael@0 | 9867 | return this.asciiSlice(start, end); |
michael@0 | 9868 | |
michael@0 | 9869 | case 'binary': |
michael@0 | 9870 | return this.binarySlice(start, end); |
michael@0 | 9871 | |
michael@0 | 9872 | case 'base64': |
michael@0 | 9873 | return this.base64Slice(start, end); |
michael@0 | 9874 | |
michael@0 | 9875 | case 'ucs2': |
michael@0 | 9876 | case 'ucs-2': |
michael@0 | 9877 | return this.ucs2Slice(start, end); |
michael@0 | 9878 | |
michael@0 | 9879 | default: |
michael@0 | 9880 | throw new Error('Unknown encoding'); |
michael@0 | 9881 | } |
michael@0 | 9882 | }; |
michael@0 | 9883 | |
michael@0 | 9884 | |
michael@0 | 9885 | SlowBuffer.prototype.hexWrite = function(string, offset, length) { |
michael@0 | 9886 | offset = +offset || 0; |
michael@0 | 9887 | var remaining = this.length - offset; |
michael@0 | 9888 | if (!length) { |
michael@0 | 9889 | length = remaining; |
michael@0 | 9890 | } else { |
michael@0 | 9891 | length = +length; |
michael@0 | 9892 | if (length > remaining) { |
michael@0 | 9893 | length = remaining; |
michael@0 | 9894 | } |
michael@0 | 9895 | } |
michael@0 | 9896 | |
michael@0 | 9897 | // must be an even number of digits |
michael@0 | 9898 | var strLen = string.length; |
michael@0 | 9899 | if (strLen % 2) { |
michael@0 | 9900 | throw new Error('Invalid hex string'); |
michael@0 | 9901 | } |
michael@0 | 9902 | if (length > strLen / 2) { |
michael@0 | 9903 | length = strLen / 2; |
michael@0 | 9904 | } |
michael@0 | 9905 | for (var i = 0; i < length; i++) { |
michael@0 | 9906 | var byte = parseInt(string.substr(i * 2, 2), 16); |
michael@0 | 9907 | if (isNaN(byte)) throw new Error('Invalid hex string'); |
michael@0 | 9908 | this[offset + i] = byte; |
michael@0 | 9909 | } |
michael@0 | 9910 | SlowBuffer._charsWritten = i * 2; |
michael@0 | 9911 | return i; |
michael@0 | 9912 | }; |
michael@0 | 9913 | |
michael@0 | 9914 | |
michael@0 | 9915 | SlowBuffer.prototype.write = function(string, offset, length, encoding) { |
michael@0 | 9916 | // Support both (string, offset, length, encoding) |
michael@0 | 9917 | // and the legacy (string, encoding, offset, length) |
michael@0 | 9918 | if (isFinite(offset)) { |
michael@0 | 9919 | if (!isFinite(length)) { |
michael@0 | 9920 | encoding = length; |
michael@0 | 9921 | length = undefined; |
michael@0 | 9922 | } |
michael@0 | 9923 | } else { // legacy |
michael@0 | 9924 | var swap = encoding; |
michael@0 | 9925 | encoding = offset; |
michael@0 | 9926 | offset = length; |
michael@0 | 9927 | length = swap; |
michael@0 | 9928 | } |
michael@0 | 9929 | |
michael@0 | 9930 | offset = +offset || 0; |
michael@0 | 9931 | var remaining = this.length - offset; |
michael@0 | 9932 | if (!length) { |
michael@0 | 9933 | length = remaining; |
michael@0 | 9934 | } else { |
michael@0 | 9935 | length = +length; |
michael@0 | 9936 | if (length > remaining) { |
michael@0 | 9937 | length = remaining; |
michael@0 | 9938 | } |
michael@0 | 9939 | } |
michael@0 | 9940 | encoding = String(encoding || 'utf8').toLowerCase(); |
michael@0 | 9941 | |
michael@0 | 9942 | switch (encoding) { |
michael@0 | 9943 | case 'hex': |
michael@0 | 9944 | return this.hexWrite(string, offset, length); |
michael@0 | 9945 | |
michael@0 | 9946 | case 'utf8': |
michael@0 | 9947 | case 'utf-8': |
michael@0 | 9948 | return this.utf8Write(string, offset, length); |
michael@0 | 9949 | |
michael@0 | 9950 | case 'ascii': |
michael@0 | 9951 | return this.asciiWrite(string, offset, length); |
michael@0 | 9952 | |
michael@0 | 9953 | case 'binary': |
michael@0 | 9954 | return this.binaryWrite(string, offset, length); |
michael@0 | 9955 | |
michael@0 | 9956 | case 'base64': |
michael@0 | 9957 | return this.base64Write(string, offset, length); |
michael@0 | 9958 | |
michael@0 | 9959 | case 'ucs2': |
michael@0 | 9960 | case 'ucs-2': |
michael@0 | 9961 | return this.ucs2Write(string, offset, length); |
michael@0 | 9962 | |
michael@0 | 9963 | default: |
michael@0 | 9964 | throw new Error('Unknown encoding'); |
michael@0 | 9965 | } |
michael@0 | 9966 | }; |
michael@0 | 9967 | |
michael@0 | 9968 | |
michael@0 | 9969 | // slice(start, end) |
michael@0 | 9970 | SlowBuffer.prototype.slice = function(start, end) { |
michael@0 | 9971 | if (end === undefined) end = this.length; |
michael@0 | 9972 | |
michael@0 | 9973 | if (end > this.length) { |
michael@0 | 9974 | throw new Error('oob'); |
michael@0 | 9975 | } |
michael@0 | 9976 | if (start > end) { |
michael@0 | 9977 | throw new Error('oob'); |
michael@0 | 9978 | } |
michael@0 | 9979 | |
michael@0 | 9980 | return new Buffer(this, end - start, +start); |
michael@0 | 9981 | }; |
michael@0 | 9982 | |
michael@0 | 9983 | SlowBuffer.prototype.copy = function(target, targetstart, sourcestart, sourceend) { |
michael@0 | 9984 | var temp = []; |
michael@0 | 9985 | for (var i=sourcestart; i<sourceend; i++) { |
michael@0 | 9986 | assert.ok(typeof this[i] !== 'undefined', "copying undefined buffer bytes!"); |
michael@0 | 9987 | temp.push(this[i]); |
michael@0 | 9988 | } |
michael@0 | 9989 | |
michael@0 | 9990 | for (var i=targetstart; i<targetstart+temp.length; i++) { |
michael@0 | 9991 | target[i] = temp[i-targetstart]; |
michael@0 | 9992 | } |
michael@0 | 9993 | }; |
michael@0 | 9994 | |
michael@0 | 9995 | SlowBuffer.prototype.fill = function(value, start, end) { |
michael@0 | 9996 | if (end > this.length) { |
michael@0 | 9997 | throw new Error('oob'); |
michael@0 | 9998 | } |
michael@0 | 9999 | if (start > end) { |
michael@0 | 10000 | throw new Error('oob'); |
michael@0 | 10001 | } |
michael@0 | 10002 | |
michael@0 | 10003 | for (var i = start; i < end; i++) { |
michael@0 | 10004 | this[i] = value; |
michael@0 | 10005 | } |
michael@0 | 10006 | } |
michael@0 | 10007 | |
michael@0 | 10008 | function coerce(length) { |
michael@0 | 10009 | // Coerce length to a number (possibly NaN), round up |
michael@0 | 10010 | // in case it's fractional (e.g. 123.456) then do a |
michael@0 | 10011 | // double negate to coerce a NaN to 0. Easy, right? |
michael@0 | 10012 | length = ~~Math.ceil(+length); |
michael@0 | 10013 | return length < 0 ? 0 : length; |
michael@0 | 10014 | } |
michael@0 | 10015 | |
michael@0 | 10016 | |
michael@0 | 10017 | // Buffer |
michael@0 | 10018 | |
michael@0 | 10019 | function Buffer(subject, encoding, offset) { |
michael@0 | 10020 | if (!(this instanceof Buffer)) { |
michael@0 | 10021 | return new Buffer(subject, encoding, offset); |
michael@0 | 10022 | } |
michael@0 | 10023 | |
michael@0 | 10024 | var type; |
michael@0 | 10025 | |
michael@0 | 10026 | // Are we slicing? |
michael@0 | 10027 | if (typeof offset === 'number') { |
michael@0 | 10028 | this.length = coerce(encoding); |
michael@0 | 10029 | this.parent = subject; |
michael@0 | 10030 | this.offset = offset; |
michael@0 | 10031 | } else { |
michael@0 | 10032 | // Find the length |
michael@0 | 10033 | switch (type = typeof subject) { |
michael@0 | 10034 | case 'number': |
michael@0 | 10035 | this.length = coerce(subject); |
michael@0 | 10036 | break; |
michael@0 | 10037 | |
michael@0 | 10038 | case 'string': |
michael@0 | 10039 | this.length = Buffer.byteLength(subject, encoding); |
michael@0 | 10040 | break; |
michael@0 | 10041 | |
michael@0 | 10042 | case 'object': // Assume object is an array |
michael@0 | 10043 | this.length = coerce(subject.length); |
michael@0 | 10044 | break; |
michael@0 | 10045 | |
michael@0 | 10046 | default: |
michael@0 | 10047 | throw new Error('First argument needs to be a number, ' + |
michael@0 | 10048 | 'array or string.'); |
michael@0 | 10049 | } |
michael@0 | 10050 | |
michael@0 | 10051 | if (this.length > Buffer.poolSize) { |
michael@0 | 10052 | // Big buffer, just alloc one. |
michael@0 | 10053 | this.parent = new SlowBuffer(this.length); |
michael@0 | 10054 | this.offset = 0; |
michael@0 | 10055 | |
michael@0 | 10056 | } else { |
michael@0 | 10057 | // Small buffer. |
michael@0 | 10058 | if (!pool || pool.length - pool.used < this.length) allocPool(); |
michael@0 | 10059 | this.parent = pool; |
michael@0 | 10060 | this.offset = pool.used; |
michael@0 | 10061 | pool.used += this.length; |
michael@0 | 10062 | } |
michael@0 | 10063 | |
michael@0 | 10064 | // Treat array-ish objects as a byte array. |
michael@0 | 10065 | if (isArrayIsh(subject)) { |
michael@0 | 10066 | for (var i = 0; i < this.length; i++) { |
michael@0 | 10067 | if (subject instanceof Buffer) { |
michael@0 | 10068 | this.parent[i + this.offset] = subject.readUInt8(i); |
michael@0 | 10069 | } |
michael@0 | 10070 | else { |
michael@0 | 10071 | this.parent[i + this.offset] = subject[i]; |
michael@0 | 10072 | } |
michael@0 | 10073 | } |
michael@0 | 10074 | } else if (type == 'string') { |
michael@0 | 10075 | // We are a string |
michael@0 | 10076 | this.length = this.write(subject, 0, encoding); |
michael@0 | 10077 | } |
michael@0 | 10078 | } |
michael@0 | 10079 | |
michael@0 | 10080 | } |
michael@0 | 10081 | |
michael@0 | 10082 | function isArrayIsh(subject) { |
michael@0 | 10083 | return Array.isArray(subject) || Buffer.isBuffer(subject) || |
michael@0 | 10084 | subject && typeof subject === 'object' && |
michael@0 | 10085 | typeof subject.length === 'number'; |
michael@0 | 10086 | } |
michael@0 | 10087 | |
michael@0 | 10088 | exports.SlowBuffer = SlowBuffer; |
michael@0 | 10089 | exports.Buffer = Buffer; |
michael@0 | 10090 | |
michael@0 | 10091 | Buffer.poolSize = 8 * 1024; |
michael@0 | 10092 | var pool; |
michael@0 | 10093 | |
michael@0 | 10094 | function allocPool() { |
michael@0 | 10095 | pool = new SlowBuffer(Buffer.poolSize); |
michael@0 | 10096 | pool.used = 0; |
michael@0 | 10097 | } |
michael@0 | 10098 | |
michael@0 | 10099 | |
michael@0 | 10100 | // Static methods |
michael@0 | 10101 | Buffer.isBuffer = function isBuffer(b) { |
michael@0 | 10102 | return b instanceof Buffer || b instanceof SlowBuffer; |
michael@0 | 10103 | }; |
michael@0 | 10104 | |
michael@0 | 10105 | Buffer.concat = function (list, totalLength) { |
michael@0 | 10106 | if (!Array.isArray(list)) { |
michael@0 | 10107 | throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ |
michael@0 | 10108 | list should be an Array."); |
michael@0 | 10109 | } |
michael@0 | 10110 | |
michael@0 | 10111 | if (list.length === 0) { |
michael@0 | 10112 | return new Buffer(0); |
michael@0 | 10113 | } else if (list.length === 1) { |
michael@0 | 10114 | return list[0]; |
michael@0 | 10115 | } |
michael@0 | 10116 | |
michael@0 | 10117 | if (typeof totalLength !== 'number') { |
michael@0 | 10118 | totalLength = 0; |
michael@0 | 10119 | for (var i = 0; i < list.length; i++) { |
michael@0 | 10120 | var buf = list[i]; |
michael@0 | 10121 | totalLength += buf.length; |
michael@0 | 10122 | } |
michael@0 | 10123 | } |
michael@0 | 10124 | |
michael@0 | 10125 | var buffer = new Buffer(totalLength); |
michael@0 | 10126 | var pos = 0; |
michael@0 | 10127 | for (var i = 0; i < list.length; i++) { |
michael@0 | 10128 | var buf = list[i]; |
michael@0 | 10129 | buf.copy(buffer, pos); |
michael@0 | 10130 | pos += buf.length; |
michael@0 | 10131 | } |
michael@0 | 10132 | return buffer; |
michael@0 | 10133 | }; |
michael@0 | 10134 | |
michael@0 | 10135 | // Inspect |
michael@0 | 10136 | Buffer.prototype.inspect = function inspect() { |
michael@0 | 10137 | var out = [], |
michael@0 | 10138 | len = this.length; |
michael@0 | 10139 | |
michael@0 | 10140 | for (var i = 0; i < len; i++) { |
michael@0 | 10141 | out[i] = toHex(this.parent[i + this.offset]); |
michael@0 | 10142 | if (i == exports.INSPECT_MAX_BYTES) { |
michael@0 | 10143 | out[i + 1] = '...'; |
michael@0 | 10144 | break; |
michael@0 | 10145 | } |
michael@0 | 10146 | } |
michael@0 | 10147 | |
michael@0 | 10148 | return '<Buffer ' + out.join(' ') + '>'; |
michael@0 | 10149 | }; |
michael@0 | 10150 | |
michael@0 | 10151 | |
michael@0 | 10152 | Buffer.prototype.get = function get(i) { |
michael@0 | 10153 | if (i < 0 || i >= this.length) throw new Error('oob'); |
michael@0 | 10154 | return this.parent[this.offset + i]; |
michael@0 | 10155 | }; |
michael@0 | 10156 | |
michael@0 | 10157 | |
michael@0 | 10158 | Buffer.prototype.set = function set(i, v) { |
michael@0 | 10159 | if (i < 0 || i >= this.length) throw new Error('oob'); |
michael@0 | 10160 | return this.parent[this.offset + i] = v; |
michael@0 | 10161 | }; |
michael@0 | 10162 | |
michael@0 | 10163 | |
michael@0 | 10164 | // write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8') |
michael@0 | 10165 | Buffer.prototype.write = function(string, offset, length, encoding) { |
michael@0 | 10166 | // Support both (string, offset, length, encoding) |
michael@0 | 10167 | // and the legacy (string, encoding, offset, length) |
michael@0 | 10168 | if (isFinite(offset)) { |
michael@0 | 10169 | if (!isFinite(length)) { |
michael@0 | 10170 | encoding = length; |
michael@0 | 10171 | length = undefined; |
michael@0 | 10172 | } |
michael@0 | 10173 | } else { // legacy |
michael@0 | 10174 | var swap = encoding; |
michael@0 | 10175 | encoding = offset; |
michael@0 | 10176 | offset = length; |
michael@0 | 10177 | length = swap; |
michael@0 | 10178 | } |
michael@0 | 10179 | |
michael@0 | 10180 | offset = +offset || 0; |
michael@0 | 10181 | var remaining = this.length - offset; |
michael@0 | 10182 | if (!length) { |
michael@0 | 10183 | length = remaining; |
michael@0 | 10184 | } else { |
michael@0 | 10185 | length = +length; |
michael@0 | 10186 | if (length > remaining) { |
michael@0 | 10187 | length = remaining; |
michael@0 | 10188 | } |
michael@0 | 10189 | } |
michael@0 | 10190 | encoding = String(encoding || 'utf8').toLowerCase(); |
michael@0 | 10191 | |
michael@0 | 10192 | var ret; |
michael@0 | 10193 | switch (encoding) { |
michael@0 | 10194 | case 'hex': |
michael@0 | 10195 | ret = this.parent.hexWrite(string, this.offset + offset, length); |
michael@0 | 10196 | break; |
michael@0 | 10197 | |
michael@0 | 10198 | case 'utf8': |
michael@0 | 10199 | case 'utf-8': |
michael@0 | 10200 | ret = this.parent.utf8Write(string, this.offset + offset, length); |
michael@0 | 10201 | break; |
michael@0 | 10202 | |
michael@0 | 10203 | case 'ascii': |
michael@0 | 10204 | ret = this.parent.asciiWrite(string, this.offset + offset, length); |
michael@0 | 10205 | break; |
michael@0 | 10206 | |
michael@0 | 10207 | case 'binary': |
michael@0 | 10208 | ret = this.parent.binaryWrite(string, this.offset + offset, length); |
michael@0 | 10209 | break; |
michael@0 | 10210 | |
michael@0 | 10211 | case 'base64': |
michael@0 | 10212 | // Warning: maxLength not taken into account in base64Write |
michael@0 | 10213 | ret = this.parent.base64Write(string, this.offset + offset, length); |
michael@0 | 10214 | break; |
michael@0 | 10215 | |
michael@0 | 10216 | case 'ucs2': |
michael@0 | 10217 | case 'ucs-2': |
michael@0 | 10218 | ret = this.parent.ucs2Write(string, this.offset + offset, length); |
michael@0 | 10219 | break; |
michael@0 | 10220 | |
michael@0 | 10221 | default: |
michael@0 | 10222 | throw new Error('Unknown encoding'); |
michael@0 | 10223 | } |
michael@0 | 10224 | |
michael@0 | 10225 | Buffer._charsWritten = SlowBuffer._charsWritten; |
michael@0 | 10226 | |
michael@0 | 10227 | return ret; |
michael@0 | 10228 | }; |
michael@0 | 10229 | |
michael@0 | 10230 | |
michael@0 | 10231 | // toString(encoding, start=0, end=buffer.length) |
michael@0 | 10232 | Buffer.prototype.toString = function(encoding, start, end) { |
michael@0 | 10233 | encoding = String(encoding || 'utf8').toLowerCase(); |
michael@0 | 10234 | |
michael@0 | 10235 | if (typeof start == 'undefined' || start < 0) { |
michael@0 | 10236 | start = 0; |
michael@0 | 10237 | } else if (start > this.length) { |
michael@0 | 10238 | start = this.length; |
michael@0 | 10239 | } |
michael@0 | 10240 | |
michael@0 | 10241 | if (typeof end == 'undefined' || end > this.length) { |
michael@0 | 10242 | end = this.length; |
michael@0 | 10243 | } else if (end < 0) { |
michael@0 | 10244 | end = 0; |
michael@0 | 10245 | } |
michael@0 | 10246 | |
michael@0 | 10247 | start = start + this.offset; |
michael@0 | 10248 | end = end + this.offset; |
michael@0 | 10249 | |
michael@0 | 10250 | switch (encoding) { |
michael@0 | 10251 | case 'hex': |
michael@0 | 10252 | return this.parent.hexSlice(start, end); |
michael@0 | 10253 | |
michael@0 | 10254 | case 'utf8': |
michael@0 | 10255 | case 'utf-8': |
michael@0 | 10256 | return this.parent.utf8Slice(start, end); |
michael@0 | 10257 | |
michael@0 | 10258 | case 'ascii': |
michael@0 | 10259 | return this.parent.asciiSlice(start, end); |
michael@0 | 10260 | |
michael@0 | 10261 | case 'binary': |
michael@0 | 10262 | return this.parent.binarySlice(start, end); |
michael@0 | 10263 | |
michael@0 | 10264 | case 'base64': |
michael@0 | 10265 | return this.parent.base64Slice(start, end); |
michael@0 | 10266 | |
michael@0 | 10267 | case 'ucs2': |
michael@0 | 10268 | case 'ucs-2': |
michael@0 | 10269 | return this.parent.ucs2Slice(start, end); |
michael@0 | 10270 | |
michael@0 | 10271 | default: |
michael@0 | 10272 | throw new Error('Unknown encoding'); |
michael@0 | 10273 | } |
michael@0 | 10274 | }; |
michael@0 | 10275 | |
michael@0 | 10276 | |
michael@0 | 10277 | // byteLength |
michael@0 | 10278 | Buffer.byteLength = SlowBuffer.byteLength; |
michael@0 | 10279 | |
michael@0 | 10280 | |
michael@0 | 10281 | // fill(value, start=0, end=buffer.length) |
michael@0 | 10282 | Buffer.prototype.fill = function fill(value, start, end) { |
michael@0 | 10283 | value || (value = 0); |
michael@0 | 10284 | start || (start = 0); |
michael@0 | 10285 | end || (end = this.length); |
michael@0 | 10286 | |
michael@0 | 10287 | if (typeof value === 'string') { |
michael@0 | 10288 | value = value.charCodeAt(0); |
michael@0 | 10289 | } |
michael@0 | 10290 | if (!(typeof value === 'number') || isNaN(value)) { |
michael@0 | 10291 | throw new Error('value is not a number'); |
michael@0 | 10292 | } |
michael@0 | 10293 | |
michael@0 | 10294 | if (end < start) throw new Error('end < start'); |
michael@0 | 10295 | |
michael@0 | 10296 | // Fill 0 bytes; we're done |
michael@0 | 10297 | if (end === start) return 0; |
michael@0 | 10298 | if (this.length == 0) return 0; |
michael@0 | 10299 | |
michael@0 | 10300 | if (start < 0 || start >= this.length) { |
michael@0 | 10301 | throw new Error('start out of bounds'); |
michael@0 | 10302 | } |
michael@0 | 10303 | |
michael@0 | 10304 | if (end < 0 || end > this.length) { |
michael@0 | 10305 | throw new Error('end out of bounds'); |
michael@0 | 10306 | } |
michael@0 | 10307 | |
michael@0 | 10308 | return this.parent.fill(value, |
michael@0 | 10309 | start + this.offset, |
michael@0 | 10310 | end + this.offset); |
michael@0 | 10311 | }; |
michael@0 | 10312 | |
michael@0 | 10313 | |
michael@0 | 10314 | // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) |
michael@0 | 10315 | Buffer.prototype.copy = function(target, target_start, start, end) { |
michael@0 | 10316 | var source = this; |
michael@0 | 10317 | start || (start = 0); |
michael@0 | 10318 | end || (end = this.length); |
michael@0 | 10319 | target_start || (target_start = 0); |
michael@0 | 10320 | |
michael@0 | 10321 | if (end < start) throw new Error('sourceEnd < sourceStart'); |
michael@0 | 10322 | |
michael@0 | 10323 | // Copy 0 bytes; we're done |
michael@0 | 10324 | if (end === start) return 0; |
michael@0 | 10325 | if (target.length == 0 || source.length == 0) return 0; |
michael@0 | 10326 | |
michael@0 | 10327 | if (target_start < 0 || target_start >= target.length) { |
michael@0 | 10328 | throw new Error('targetStart out of bounds'); |
michael@0 | 10329 | } |
michael@0 | 10330 | |
michael@0 | 10331 | if (start < 0 || start >= source.length) { |
michael@0 | 10332 | throw new Error('sourceStart out of bounds'); |
michael@0 | 10333 | } |
michael@0 | 10334 | |
michael@0 | 10335 | if (end < 0 || end > source.length) { |
michael@0 | 10336 | throw new Error('sourceEnd out of bounds'); |
michael@0 | 10337 | } |
michael@0 | 10338 | |
michael@0 | 10339 | // Are we oob? |
michael@0 | 10340 | if (end > this.length) { |
michael@0 | 10341 | end = this.length; |
michael@0 | 10342 | } |
michael@0 | 10343 | |
michael@0 | 10344 | if (target.length - target_start < end - start) { |
michael@0 | 10345 | end = target.length - target_start + start; |
michael@0 | 10346 | } |
michael@0 | 10347 | |
michael@0 | 10348 | return this.parent.copy(target.parent, |
michael@0 | 10349 | target_start + target.offset, |
michael@0 | 10350 | start + this.offset, |
michael@0 | 10351 | end + this.offset); |
michael@0 | 10352 | }; |
michael@0 | 10353 | |
michael@0 | 10354 | |
michael@0 | 10355 | // slice(start, end) |
michael@0 | 10356 | Buffer.prototype.slice = function(start, end) { |
michael@0 | 10357 | if (end === undefined) end = this.length; |
michael@0 | 10358 | if (end > this.length) throw new Error('oob'); |
michael@0 | 10359 | if (start > end) throw new Error('oob'); |
michael@0 | 10360 | |
michael@0 | 10361 | return new Buffer(this.parent, end - start, +start + this.offset); |
michael@0 | 10362 | }; |
michael@0 | 10363 | |
michael@0 | 10364 | |
michael@0 | 10365 | // Legacy methods for backwards compatibility. |
michael@0 | 10366 | |
michael@0 | 10367 | Buffer.prototype.utf8Slice = function(start, end) { |
michael@0 | 10368 | return this.toString('utf8', start, end); |
michael@0 | 10369 | }; |
michael@0 | 10370 | |
michael@0 | 10371 | Buffer.prototype.binarySlice = function(start, end) { |
michael@0 | 10372 | return this.toString('binary', start, end); |
michael@0 | 10373 | }; |
michael@0 | 10374 | |
michael@0 | 10375 | Buffer.prototype.asciiSlice = function(start, end) { |
michael@0 | 10376 | return this.toString('ascii', start, end); |
michael@0 | 10377 | }; |
michael@0 | 10378 | |
michael@0 | 10379 | Buffer.prototype.utf8Write = function(string, offset) { |
michael@0 | 10380 | return this.write(string, offset, 'utf8'); |
michael@0 | 10381 | }; |
michael@0 | 10382 | |
michael@0 | 10383 | Buffer.prototype.binaryWrite = function(string, offset) { |
michael@0 | 10384 | return this.write(string, offset, 'binary'); |
michael@0 | 10385 | }; |
michael@0 | 10386 | |
michael@0 | 10387 | Buffer.prototype.asciiWrite = function(string, offset) { |
michael@0 | 10388 | return this.write(string, offset, 'ascii'); |
michael@0 | 10389 | }; |
michael@0 | 10390 | |
michael@0 | 10391 | Buffer.prototype.readUInt8 = function(offset, noAssert) { |
michael@0 | 10392 | var buffer = this; |
michael@0 | 10393 | |
michael@0 | 10394 | if (!noAssert) { |
michael@0 | 10395 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10396 | 'missing offset'); |
michael@0 | 10397 | |
michael@0 | 10398 | assert.ok(offset < buffer.length, |
michael@0 | 10399 | 'Trying to read beyond buffer length'); |
michael@0 | 10400 | } |
michael@0 | 10401 | |
michael@0 | 10402 | if (offset >= buffer.length) return; |
michael@0 | 10403 | |
michael@0 | 10404 | return buffer.parent[buffer.offset + offset]; |
michael@0 | 10405 | }; |
michael@0 | 10406 | |
michael@0 | 10407 | function readUInt16(buffer, offset, isBigEndian, noAssert) { |
michael@0 | 10408 | var val = 0; |
michael@0 | 10409 | |
michael@0 | 10410 | |
michael@0 | 10411 | if (!noAssert) { |
michael@0 | 10412 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10413 | 'missing or invalid endian'); |
michael@0 | 10414 | |
michael@0 | 10415 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10416 | 'missing offset'); |
michael@0 | 10417 | |
michael@0 | 10418 | assert.ok(offset + 1 < buffer.length, |
michael@0 | 10419 | 'Trying to read beyond buffer length'); |
michael@0 | 10420 | } |
michael@0 | 10421 | |
michael@0 | 10422 | if (offset >= buffer.length) return 0; |
michael@0 | 10423 | |
michael@0 | 10424 | if (isBigEndian) { |
michael@0 | 10425 | val = buffer.parent[buffer.offset + offset] << 8; |
michael@0 | 10426 | if (offset + 1 < buffer.length) { |
michael@0 | 10427 | val |= buffer.parent[buffer.offset + offset + 1]; |
michael@0 | 10428 | } |
michael@0 | 10429 | } else { |
michael@0 | 10430 | val = buffer.parent[buffer.offset + offset]; |
michael@0 | 10431 | if (offset + 1 < buffer.length) { |
michael@0 | 10432 | val |= buffer.parent[buffer.offset + offset + 1] << 8; |
michael@0 | 10433 | } |
michael@0 | 10434 | } |
michael@0 | 10435 | |
michael@0 | 10436 | return val; |
michael@0 | 10437 | } |
michael@0 | 10438 | |
michael@0 | 10439 | Buffer.prototype.readUInt16LE = function(offset, noAssert) { |
michael@0 | 10440 | return readUInt16(this, offset, false, noAssert); |
michael@0 | 10441 | }; |
michael@0 | 10442 | |
michael@0 | 10443 | Buffer.prototype.readUInt16BE = function(offset, noAssert) { |
michael@0 | 10444 | return readUInt16(this, offset, true, noAssert); |
michael@0 | 10445 | }; |
michael@0 | 10446 | |
michael@0 | 10447 | function readUInt32(buffer, offset, isBigEndian, noAssert) { |
michael@0 | 10448 | var val = 0; |
michael@0 | 10449 | |
michael@0 | 10450 | if (!noAssert) { |
michael@0 | 10451 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10452 | 'missing or invalid endian'); |
michael@0 | 10453 | |
michael@0 | 10454 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10455 | 'missing offset'); |
michael@0 | 10456 | |
michael@0 | 10457 | assert.ok(offset + 3 < buffer.length, |
michael@0 | 10458 | 'Trying to read beyond buffer length'); |
michael@0 | 10459 | } |
michael@0 | 10460 | |
michael@0 | 10461 | if (offset >= buffer.length) return 0; |
michael@0 | 10462 | |
michael@0 | 10463 | if (isBigEndian) { |
michael@0 | 10464 | if (offset + 1 < buffer.length) |
michael@0 | 10465 | val = buffer.parent[buffer.offset + offset + 1] << 16; |
michael@0 | 10466 | if (offset + 2 < buffer.length) |
michael@0 | 10467 | val |= buffer.parent[buffer.offset + offset + 2] << 8; |
michael@0 | 10468 | if (offset + 3 < buffer.length) |
michael@0 | 10469 | val |= buffer.parent[buffer.offset + offset + 3]; |
michael@0 | 10470 | val = val + (buffer.parent[buffer.offset + offset] << 24 >>> 0); |
michael@0 | 10471 | } else { |
michael@0 | 10472 | if (offset + 2 < buffer.length) |
michael@0 | 10473 | val = buffer.parent[buffer.offset + offset + 2] << 16; |
michael@0 | 10474 | if (offset + 1 < buffer.length) |
michael@0 | 10475 | val |= buffer.parent[buffer.offset + offset + 1] << 8; |
michael@0 | 10476 | val |= buffer.parent[buffer.offset + offset]; |
michael@0 | 10477 | if (offset + 3 < buffer.length) |
michael@0 | 10478 | val = val + (buffer.parent[buffer.offset + offset + 3] << 24 >>> 0); |
michael@0 | 10479 | } |
michael@0 | 10480 | |
michael@0 | 10481 | return val; |
michael@0 | 10482 | } |
michael@0 | 10483 | |
michael@0 | 10484 | Buffer.prototype.readUInt32LE = function(offset, noAssert) { |
michael@0 | 10485 | return readUInt32(this, offset, false, noAssert); |
michael@0 | 10486 | }; |
michael@0 | 10487 | |
michael@0 | 10488 | Buffer.prototype.readUInt32BE = function(offset, noAssert) { |
michael@0 | 10489 | return readUInt32(this, offset, true, noAssert); |
michael@0 | 10490 | }; |
michael@0 | 10491 | |
michael@0 | 10492 | |
michael@0 | 10493 | /* |
michael@0 | 10494 | * Signed integer types, yay team! A reminder on how two's complement actually |
michael@0 | 10495 | * works. The first bit is the signed bit, i.e. tells us whether or not the |
michael@0 | 10496 | * number should be positive or negative. If the two's complement value is |
michael@0 | 10497 | * positive, then we're done, as it's equivalent to the unsigned representation. |
michael@0 | 10498 | * |
michael@0 | 10499 | * Now if the number is positive, you're pretty much done, you can just leverage |
michael@0 | 10500 | * the unsigned translations and return those. Unfortunately, negative numbers |
michael@0 | 10501 | * aren't quite that straightforward. |
michael@0 | 10502 | * |
michael@0 | 10503 | * At first glance, one might be inclined to use the traditional formula to |
michael@0 | 10504 | * translate binary numbers between the positive and negative values in two's |
michael@0 | 10505 | * complement. (Though it doesn't quite work for the most negative value) |
michael@0 | 10506 | * Mainly: |
michael@0 | 10507 | * - invert all the bits |
michael@0 | 10508 | * - add one to the result |
michael@0 | 10509 | * |
michael@0 | 10510 | * Of course, this doesn't quite work in Javascript. Take for example the value |
michael@0 | 10511 | * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of |
michael@0 | 10512 | * course, Javascript will do the following: |
michael@0 | 10513 | * |
michael@0 | 10514 | * > ~0xff80 |
michael@0 | 10515 | * -65409 |
michael@0 | 10516 | * |
michael@0 | 10517 | * Whoh there, Javascript, that's not quite right. But wait, according to |
michael@0 | 10518 | * Javascript that's perfectly correct. When Javascript ends up seeing the |
michael@0 | 10519 | * constant 0xff80, it has no notion that it is actually a signed number. It |
michael@0 | 10520 | * assumes that we've input the unsigned value 0xff80. Thus, when it does the |
michael@0 | 10521 | * binary negation, it casts it into a signed value, (positive 0xff80). Then |
michael@0 | 10522 | * when you perform binary negation on that, it turns it into a negative number. |
michael@0 | 10523 | * |
michael@0 | 10524 | * Instead, we're going to have to use the following general formula, that works |
michael@0 | 10525 | * in a rather Javascript friendly way. I'm glad we don't support this kind of |
michael@0 | 10526 | * weird numbering scheme in the kernel. |
michael@0 | 10527 | * |
michael@0 | 10528 | * (BIT-MAX - (unsigned)val + 1) * -1 |
michael@0 | 10529 | * |
michael@0 | 10530 | * The astute observer, may think that this doesn't make sense for 8-bit numbers |
michael@0 | 10531 | * (really it isn't necessary for them). However, when you get 16-bit numbers, |
michael@0 | 10532 | * you do. Let's go back to our prior example and see how this will look: |
michael@0 | 10533 | * |
michael@0 | 10534 | * (0xffff - 0xff80 + 1) * -1 |
michael@0 | 10535 | * (0x007f + 1) * -1 |
michael@0 | 10536 | * (0x0080) * -1 |
michael@0 | 10537 | */ |
michael@0 | 10538 | Buffer.prototype.readInt8 = function(offset, noAssert) { |
michael@0 | 10539 | var buffer = this; |
michael@0 | 10540 | var neg; |
michael@0 | 10541 | |
michael@0 | 10542 | if (!noAssert) { |
michael@0 | 10543 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10544 | 'missing offset'); |
michael@0 | 10545 | |
michael@0 | 10546 | assert.ok(offset < buffer.length, |
michael@0 | 10547 | 'Trying to read beyond buffer length'); |
michael@0 | 10548 | } |
michael@0 | 10549 | |
michael@0 | 10550 | if (offset >= buffer.length) return; |
michael@0 | 10551 | |
michael@0 | 10552 | neg = buffer.parent[buffer.offset + offset] & 0x80; |
michael@0 | 10553 | if (!neg) { |
michael@0 | 10554 | return (buffer.parent[buffer.offset + offset]); |
michael@0 | 10555 | } |
michael@0 | 10556 | |
michael@0 | 10557 | return ((0xff - buffer.parent[buffer.offset + offset] + 1) * -1); |
michael@0 | 10558 | }; |
michael@0 | 10559 | |
michael@0 | 10560 | function readInt16(buffer, offset, isBigEndian, noAssert) { |
michael@0 | 10561 | var neg, val; |
michael@0 | 10562 | |
michael@0 | 10563 | if (!noAssert) { |
michael@0 | 10564 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10565 | 'missing or invalid endian'); |
michael@0 | 10566 | |
michael@0 | 10567 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10568 | 'missing offset'); |
michael@0 | 10569 | |
michael@0 | 10570 | assert.ok(offset + 1 < buffer.length, |
michael@0 | 10571 | 'Trying to read beyond buffer length'); |
michael@0 | 10572 | } |
michael@0 | 10573 | |
michael@0 | 10574 | val = readUInt16(buffer, offset, isBigEndian, noAssert); |
michael@0 | 10575 | neg = val & 0x8000; |
michael@0 | 10576 | if (!neg) { |
michael@0 | 10577 | return val; |
michael@0 | 10578 | } |
michael@0 | 10579 | |
michael@0 | 10580 | return (0xffff - val + 1) * -1; |
michael@0 | 10581 | } |
michael@0 | 10582 | |
michael@0 | 10583 | Buffer.prototype.readInt16LE = function(offset, noAssert) { |
michael@0 | 10584 | return readInt16(this, offset, false, noAssert); |
michael@0 | 10585 | }; |
michael@0 | 10586 | |
michael@0 | 10587 | Buffer.prototype.readInt16BE = function(offset, noAssert) { |
michael@0 | 10588 | return readInt16(this, offset, true, noAssert); |
michael@0 | 10589 | }; |
michael@0 | 10590 | |
michael@0 | 10591 | function readInt32(buffer, offset, isBigEndian, noAssert) { |
michael@0 | 10592 | var neg, val; |
michael@0 | 10593 | |
michael@0 | 10594 | if (!noAssert) { |
michael@0 | 10595 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10596 | 'missing or invalid endian'); |
michael@0 | 10597 | |
michael@0 | 10598 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10599 | 'missing offset'); |
michael@0 | 10600 | |
michael@0 | 10601 | assert.ok(offset + 3 < buffer.length, |
michael@0 | 10602 | 'Trying to read beyond buffer length'); |
michael@0 | 10603 | } |
michael@0 | 10604 | |
michael@0 | 10605 | val = readUInt32(buffer, offset, isBigEndian, noAssert); |
michael@0 | 10606 | neg = val & 0x80000000; |
michael@0 | 10607 | if (!neg) { |
michael@0 | 10608 | return (val); |
michael@0 | 10609 | } |
michael@0 | 10610 | |
michael@0 | 10611 | return (0xffffffff - val + 1) * -1; |
michael@0 | 10612 | } |
michael@0 | 10613 | |
michael@0 | 10614 | Buffer.prototype.readInt32LE = function(offset, noAssert) { |
michael@0 | 10615 | return readInt32(this, offset, false, noAssert); |
michael@0 | 10616 | }; |
michael@0 | 10617 | |
michael@0 | 10618 | Buffer.prototype.readInt32BE = function(offset, noAssert) { |
michael@0 | 10619 | return readInt32(this, offset, true, noAssert); |
michael@0 | 10620 | }; |
michael@0 | 10621 | |
michael@0 | 10622 | function readFloat(buffer, offset, isBigEndian, noAssert) { |
michael@0 | 10623 | if (!noAssert) { |
michael@0 | 10624 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10625 | 'missing or invalid endian'); |
michael@0 | 10626 | |
michael@0 | 10627 | assert.ok(offset + 3 < buffer.length, |
michael@0 | 10628 | 'Trying to read beyond buffer length'); |
michael@0 | 10629 | } |
michael@0 | 10630 | |
michael@0 | 10631 | return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, |
michael@0 | 10632 | 23, 4); |
michael@0 | 10633 | } |
michael@0 | 10634 | |
michael@0 | 10635 | Buffer.prototype.readFloatLE = function(offset, noAssert) { |
michael@0 | 10636 | return readFloat(this, offset, false, noAssert); |
michael@0 | 10637 | }; |
michael@0 | 10638 | |
michael@0 | 10639 | Buffer.prototype.readFloatBE = function(offset, noAssert) { |
michael@0 | 10640 | return readFloat(this, offset, true, noAssert); |
michael@0 | 10641 | }; |
michael@0 | 10642 | |
michael@0 | 10643 | function readDouble(buffer, offset, isBigEndian, noAssert) { |
michael@0 | 10644 | if (!noAssert) { |
michael@0 | 10645 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10646 | 'missing or invalid endian'); |
michael@0 | 10647 | |
michael@0 | 10648 | assert.ok(offset + 7 < buffer.length, |
michael@0 | 10649 | 'Trying to read beyond buffer length'); |
michael@0 | 10650 | } |
michael@0 | 10651 | |
michael@0 | 10652 | return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, |
michael@0 | 10653 | 52, 8); |
michael@0 | 10654 | } |
michael@0 | 10655 | |
michael@0 | 10656 | Buffer.prototype.readDoubleLE = function(offset, noAssert) { |
michael@0 | 10657 | return readDouble(this, offset, false, noAssert); |
michael@0 | 10658 | }; |
michael@0 | 10659 | |
michael@0 | 10660 | Buffer.prototype.readDoubleBE = function(offset, noAssert) { |
michael@0 | 10661 | return readDouble(this, offset, true, noAssert); |
michael@0 | 10662 | }; |
michael@0 | 10663 | |
michael@0 | 10664 | |
michael@0 | 10665 | /* |
michael@0 | 10666 | * We have to make sure that the value is a valid integer. This means that it is |
michael@0 | 10667 | * non-negative. It has no fractional component and that it does not exceed the |
michael@0 | 10668 | * maximum allowed value. |
michael@0 | 10669 | * |
michael@0 | 10670 | * value The number to check for validity |
michael@0 | 10671 | * |
michael@0 | 10672 | * max The maximum value |
michael@0 | 10673 | */ |
michael@0 | 10674 | function verifuint(value, max) { |
michael@0 | 10675 | assert.ok(typeof (value) == 'number', |
michael@0 | 10676 | 'cannot write a non-number as a number'); |
michael@0 | 10677 | |
michael@0 | 10678 | assert.ok(value >= 0, |
michael@0 | 10679 | 'specified a negative value for writing an unsigned value'); |
michael@0 | 10680 | |
michael@0 | 10681 | assert.ok(value <= max, 'value is larger than maximum value for type'); |
michael@0 | 10682 | |
michael@0 | 10683 | assert.ok(Math.floor(value) === value, 'value has a fractional component'); |
michael@0 | 10684 | } |
michael@0 | 10685 | |
michael@0 | 10686 | Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { |
michael@0 | 10687 | var buffer = this; |
michael@0 | 10688 | |
michael@0 | 10689 | if (!noAssert) { |
michael@0 | 10690 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10691 | 'missing value'); |
michael@0 | 10692 | |
michael@0 | 10693 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10694 | 'missing offset'); |
michael@0 | 10695 | |
michael@0 | 10696 | assert.ok(offset < buffer.length, |
michael@0 | 10697 | 'trying to write beyond buffer length'); |
michael@0 | 10698 | |
michael@0 | 10699 | verifuint(value, 0xff); |
michael@0 | 10700 | } |
michael@0 | 10701 | |
michael@0 | 10702 | if (offset < buffer.length) { |
michael@0 | 10703 | buffer.parent[buffer.offset + offset] = value; |
michael@0 | 10704 | } |
michael@0 | 10705 | }; |
michael@0 | 10706 | |
michael@0 | 10707 | function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { |
michael@0 | 10708 | if (!noAssert) { |
michael@0 | 10709 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10710 | 'missing value'); |
michael@0 | 10711 | |
michael@0 | 10712 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10713 | 'missing or invalid endian'); |
michael@0 | 10714 | |
michael@0 | 10715 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10716 | 'missing offset'); |
michael@0 | 10717 | |
michael@0 | 10718 | assert.ok(offset + 1 < buffer.length, |
michael@0 | 10719 | 'trying to write beyond buffer length'); |
michael@0 | 10720 | |
michael@0 | 10721 | verifuint(value, 0xffff); |
michael@0 | 10722 | } |
michael@0 | 10723 | |
michael@0 | 10724 | for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) { |
michael@0 | 10725 | buffer.parent[buffer.offset + offset + i] = |
michael@0 | 10726 | (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>> |
michael@0 | 10727 | (isBigEndian ? 1 - i : i) * 8; |
michael@0 | 10728 | } |
michael@0 | 10729 | |
michael@0 | 10730 | } |
michael@0 | 10731 | |
michael@0 | 10732 | Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { |
michael@0 | 10733 | writeUInt16(this, value, offset, false, noAssert); |
michael@0 | 10734 | }; |
michael@0 | 10735 | |
michael@0 | 10736 | Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { |
michael@0 | 10737 | writeUInt16(this, value, offset, true, noAssert); |
michael@0 | 10738 | }; |
michael@0 | 10739 | |
michael@0 | 10740 | function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { |
michael@0 | 10741 | if (!noAssert) { |
michael@0 | 10742 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10743 | 'missing value'); |
michael@0 | 10744 | |
michael@0 | 10745 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10746 | 'missing or invalid endian'); |
michael@0 | 10747 | |
michael@0 | 10748 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10749 | 'missing offset'); |
michael@0 | 10750 | |
michael@0 | 10751 | assert.ok(offset + 3 < buffer.length, |
michael@0 | 10752 | 'trying to write beyond buffer length'); |
michael@0 | 10753 | |
michael@0 | 10754 | verifuint(value, 0xffffffff); |
michael@0 | 10755 | } |
michael@0 | 10756 | |
michael@0 | 10757 | for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { |
michael@0 | 10758 | buffer.parent[buffer.offset + offset + i] = |
michael@0 | 10759 | (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; |
michael@0 | 10760 | } |
michael@0 | 10761 | } |
michael@0 | 10762 | |
michael@0 | 10763 | Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { |
michael@0 | 10764 | writeUInt32(this, value, offset, false, noAssert); |
michael@0 | 10765 | }; |
michael@0 | 10766 | |
michael@0 | 10767 | Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { |
michael@0 | 10768 | writeUInt32(this, value, offset, true, noAssert); |
michael@0 | 10769 | }; |
michael@0 | 10770 | |
michael@0 | 10771 | |
michael@0 | 10772 | /* |
michael@0 | 10773 | * We now move onto our friends in the signed number category. Unlike unsigned |
michael@0 | 10774 | * numbers, we're going to have to worry a bit more about how we put values into |
michael@0 | 10775 | * arrays. Since we are only worrying about signed 32-bit values, we're in |
michael@0 | 10776 | * slightly better shape. Unfortunately, we really can't do our favorite binary |
michael@0 | 10777 | * & in this system. It really seems to do the wrong thing. For example: |
michael@0 | 10778 | * |
michael@0 | 10779 | * > -32 & 0xff |
michael@0 | 10780 | * 224 |
michael@0 | 10781 | * |
michael@0 | 10782 | * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of |
michael@0 | 10783 | * this aren't treated as a signed number. Ultimately a bad thing. |
michael@0 | 10784 | * |
michael@0 | 10785 | * What we're going to want to do is basically create the unsigned equivalent of |
michael@0 | 10786 | * our representation and pass that off to the wuint* functions. To do that |
michael@0 | 10787 | * we're going to do the following: |
michael@0 | 10788 | * |
michael@0 | 10789 | * - if the value is positive |
michael@0 | 10790 | * we can pass it directly off to the equivalent wuint |
michael@0 | 10791 | * - if the value is negative |
michael@0 | 10792 | * we do the following computation: |
michael@0 | 10793 | * mb + val + 1, where |
michael@0 | 10794 | * mb is the maximum unsigned value in that byte size |
michael@0 | 10795 | * val is the Javascript negative integer |
michael@0 | 10796 | * |
michael@0 | 10797 | * |
michael@0 | 10798 | * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If |
michael@0 | 10799 | * you do out the computations: |
michael@0 | 10800 | * |
michael@0 | 10801 | * 0xffff - 128 + 1 |
michael@0 | 10802 | * 0xffff - 127 |
michael@0 | 10803 | * 0xff80 |
michael@0 | 10804 | * |
michael@0 | 10805 | * You can then encode this value as the signed version. This is really rather |
michael@0 | 10806 | * hacky, but it should work and get the job done which is our goal here. |
michael@0 | 10807 | */ |
michael@0 | 10808 | |
michael@0 | 10809 | /* |
michael@0 | 10810 | * A series of checks to make sure we actually have a signed 32-bit number |
michael@0 | 10811 | */ |
michael@0 | 10812 | function verifsint(value, max, min) { |
michael@0 | 10813 | assert.ok(typeof (value) == 'number', |
michael@0 | 10814 | 'cannot write a non-number as a number'); |
michael@0 | 10815 | |
michael@0 | 10816 | assert.ok(value <= max, 'value larger than maximum allowed value'); |
michael@0 | 10817 | |
michael@0 | 10818 | assert.ok(value >= min, 'value smaller than minimum allowed value'); |
michael@0 | 10819 | |
michael@0 | 10820 | assert.ok(Math.floor(value) === value, 'value has a fractional component'); |
michael@0 | 10821 | } |
michael@0 | 10822 | |
michael@0 | 10823 | function verifIEEE754(value, max, min) { |
michael@0 | 10824 | assert.ok(typeof (value) == 'number', |
michael@0 | 10825 | 'cannot write a non-number as a number'); |
michael@0 | 10826 | |
michael@0 | 10827 | assert.ok(value <= max, 'value larger than maximum allowed value'); |
michael@0 | 10828 | |
michael@0 | 10829 | assert.ok(value >= min, 'value smaller than minimum allowed value'); |
michael@0 | 10830 | } |
michael@0 | 10831 | |
michael@0 | 10832 | Buffer.prototype.writeInt8 = function(value, offset, noAssert) { |
michael@0 | 10833 | var buffer = this; |
michael@0 | 10834 | |
michael@0 | 10835 | if (!noAssert) { |
michael@0 | 10836 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10837 | 'missing value'); |
michael@0 | 10838 | |
michael@0 | 10839 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10840 | 'missing offset'); |
michael@0 | 10841 | |
michael@0 | 10842 | assert.ok(offset < buffer.length, |
michael@0 | 10843 | 'Trying to write beyond buffer length'); |
michael@0 | 10844 | |
michael@0 | 10845 | verifsint(value, 0x7f, -0x80); |
michael@0 | 10846 | } |
michael@0 | 10847 | |
michael@0 | 10848 | if (value >= 0) { |
michael@0 | 10849 | buffer.writeUInt8(value, offset, noAssert); |
michael@0 | 10850 | } else { |
michael@0 | 10851 | buffer.writeUInt8(0xff + value + 1, offset, noAssert); |
michael@0 | 10852 | } |
michael@0 | 10853 | }; |
michael@0 | 10854 | |
michael@0 | 10855 | function writeInt16(buffer, value, offset, isBigEndian, noAssert) { |
michael@0 | 10856 | if (!noAssert) { |
michael@0 | 10857 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10858 | 'missing value'); |
michael@0 | 10859 | |
michael@0 | 10860 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10861 | 'missing or invalid endian'); |
michael@0 | 10862 | |
michael@0 | 10863 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10864 | 'missing offset'); |
michael@0 | 10865 | |
michael@0 | 10866 | assert.ok(offset + 1 < buffer.length, |
michael@0 | 10867 | 'Trying to write beyond buffer length'); |
michael@0 | 10868 | |
michael@0 | 10869 | verifsint(value, 0x7fff, -0x8000); |
michael@0 | 10870 | } |
michael@0 | 10871 | |
michael@0 | 10872 | if (value >= 0) { |
michael@0 | 10873 | writeUInt16(buffer, value, offset, isBigEndian, noAssert); |
michael@0 | 10874 | } else { |
michael@0 | 10875 | writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); |
michael@0 | 10876 | } |
michael@0 | 10877 | } |
michael@0 | 10878 | |
michael@0 | 10879 | Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { |
michael@0 | 10880 | writeInt16(this, value, offset, false, noAssert); |
michael@0 | 10881 | }; |
michael@0 | 10882 | |
michael@0 | 10883 | Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { |
michael@0 | 10884 | writeInt16(this, value, offset, true, noAssert); |
michael@0 | 10885 | }; |
michael@0 | 10886 | |
michael@0 | 10887 | function writeInt32(buffer, value, offset, isBigEndian, noAssert) { |
michael@0 | 10888 | if (!noAssert) { |
michael@0 | 10889 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10890 | 'missing value'); |
michael@0 | 10891 | |
michael@0 | 10892 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10893 | 'missing or invalid endian'); |
michael@0 | 10894 | |
michael@0 | 10895 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10896 | 'missing offset'); |
michael@0 | 10897 | |
michael@0 | 10898 | assert.ok(offset + 3 < buffer.length, |
michael@0 | 10899 | 'Trying to write beyond buffer length'); |
michael@0 | 10900 | |
michael@0 | 10901 | verifsint(value, 0x7fffffff, -0x80000000); |
michael@0 | 10902 | } |
michael@0 | 10903 | |
michael@0 | 10904 | if (value >= 0) { |
michael@0 | 10905 | writeUInt32(buffer, value, offset, isBigEndian, noAssert); |
michael@0 | 10906 | } else { |
michael@0 | 10907 | writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); |
michael@0 | 10908 | } |
michael@0 | 10909 | } |
michael@0 | 10910 | |
michael@0 | 10911 | Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { |
michael@0 | 10912 | writeInt32(this, value, offset, false, noAssert); |
michael@0 | 10913 | }; |
michael@0 | 10914 | |
michael@0 | 10915 | Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { |
michael@0 | 10916 | writeInt32(this, value, offset, true, noAssert); |
michael@0 | 10917 | }; |
michael@0 | 10918 | |
michael@0 | 10919 | function writeFloat(buffer, value, offset, isBigEndian, noAssert) { |
michael@0 | 10920 | if (!noAssert) { |
michael@0 | 10921 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10922 | 'missing value'); |
michael@0 | 10923 | |
michael@0 | 10924 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10925 | 'missing or invalid endian'); |
michael@0 | 10926 | |
michael@0 | 10927 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10928 | 'missing offset'); |
michael@0 | 10929 | |
michael@0 | 10930 | assert.ok(offset + 3 < buffer.length, |
michael@0 | 10931 | 'Trying to write beyond buffer length'); |
michael@0 | 10932 | |
michael@0 | 10933 | verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); |
michael@0 | 10934 | } |
michael@0 | 10935 | |
michael@0 | 10936 | require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, |
michael@0 | 10937 | 23, 4); |
michael@0 | 10938 | } |
michael@0 | 10939 | |
michael@0 | 10940 | Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { |
michael@0 | 10941 | writeFloat(this, value, offset, false, noAssert); |
michael@0 | 10942 | }; |
michael@0 | 10943 | |
michael@0 | 10944 | Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { |
michael@0 | 10945 | writeFloat(this, value, offset, true, noAssert); |
michael@0 | 10946 | }; |
michael@0 | 10947 | |
michael@0 | 10948 | function writeDouble(buffer, value, offset, isBigEndian, noAssert) { |
michael@0 | 10949 | if (!noAssert) { |
michael@0 | 10950 | assert.ok(value !== undefined && value !== null, |
michael@0 | 10951 | 'missing value'); |
michael@0 | 10952 | |
michael@0 | 10953 | assert.ok(typeof (isBigEndian) === 'boolean', |
michael@0 | 10954 | 'missing or invalid endian'); |
michael@0 | 10955 | |
michael@0 | 10956 | assert.ok(offset !== undefined && offset !== null, |
michael@0 | 10957 | 'missing offset'); |
michael@0 | 10958 | |
michael@0 | 10959 | assert.ok(offset + 7 < buffer.length, |
michael@0 | 10960 | 'Trying to write beyond buffer length'); |
michael@0 | 10961 | |
michael@0 | 10962 | verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); |
michael@0 | 10963 | } |
michael@0 | 10964 | |
michael@0 | 10965 | require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, |
michael@0 | 10966 | 52, 8); |
michael@0 | 10967 | } |
michael@0 | 10968 | |
michael@0 | 10969 | Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { |
michael@0 | 10970 | writeDouble(this, value, offset, false, noAssert); |
michael@0 | 10971 | }; |
michael@0 | 10972 | |
michael@0 | 10973 | Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { |
michael@0 | 10974 | writeDouble(this, value, offset, true, noAssert); |
michael@0 | 10975 | }; |
michael@0 | 10976 | |
michael@0 | 10977 | SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8; |
michael@0 | 10978 | SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE; |
michael@0 | 10979 | SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE; |
michael@0 | 10980 | SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE; |
michael@0 | 10981 | SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE; |
michael@0 | 10982 | SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8; |
michael@0 | 10983 | SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE; |
michael@0 | 10984 | SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE; |
michael@0 | 10985 | SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE; |
michael@0 | 10986 | SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE; |
michael@0 | 10987 | SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE; |
michael@0 | 10988 | SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE; |
michael@0 | 10989 | SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE; |
michael@0 | 10990 | SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE; |
michael@0 | 10991 | SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8; |
michael@0 | 10992 | SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE; |
michael@0 | 10993 | SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE; |
michael@0 | 10994 | SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE; |
michael@0 | 10995 | SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE; |
michael@0 | 10996 | SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8; |
michael@0 | 10997 | SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE; |
michael@0 | 10998 | SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE; |
michael@0 | 10999 | SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE; |
michael@0 | 11000 | SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE; |
michael@0 | 11001 | SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE; |
michael@0 | 11002 | SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE; |
michael@0 | 11003 | SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE; |
michael@0 | 11004 | SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE; |
michael@0 | 11005 | |
michael@0 | 11006 | })() |
michael@0 | 11007 | },{"assert":9,"./buffer_ieee754":14,"base64-js":15}],15:[function(require,module,exports){ |
michael@0 | 11008 | (function (exports) { |
michael@0 | 11009 | 'use strict'; |
michael@0 | 11010 | |
michael@0 | 11011 | var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; |
michael@0 | 11012 | |
michael@0 | 11013 | function b64ToByteArray(b64) { |
michael@0 | 11014 | var i, j, l, tmp, placeHolders, arr; |
michael@0 | 11015 | |
michael@0 | 11016 | if (b64.length % 4 > 0) { |
michael@0 | 11017 | throw 'Invalid string. Length must be a multiple of 4'; |
michael@0 | 11018 | } |
michael@0 | 11019 | |
michael@0 | 11020 | // the number of equal signs (place holders) |
michael@0 | 11021 | // if there are two placeholders, than the two characters before it |
michael@0 | 11022 | // represent one byte |
michael@0 | 11023 | // if there is only one, then the three characters before it represent 2 bytes |
michael@0 | 11024 | // this is just a cheap hack to not do indexOf twice |
michael@0 | 11025 | placeHolders = b64.indexOf('='); |
michael@0 | 11026 | placeHolders = placeHolders > 0 ? b64.length - placeHolders : 0; |
michael@0 | 11027 | |
michael@0 | 11028 | // base64 is 4/3 + up to two characters of the original data |
michael@0 | 11029 | arr = [];//new Uint8Array(b64.length * 3 / 4 - placeHolders); |
michael@0 | 11030 | |
michael@0 | 11031 | // if there are placeholders, only get up to the last complete 4 chars |
michael@0 | 11032 | l = placeHolders > 0 ? b64.length - 4 : b64.length; |
michael@0 | 11033 | |
michael@0 | 11034 | for (i = 0, j = 0; i < l; i += 4, j += 3) { |
michael@0 | 11035 | 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 | 11036 | arr.push((tmp & 0xFF0000) >> 16); |
michael@0 | 11037 | arr.push((tmp & 0xFF00) >> 8); |
michael@0 | 11038 | arr.push(tmp & 0xFF); |
michael@0 | 11039 | } |
michael@0 | 11040 | |
michael@0 | 11041 | if (placeHolders === 2) { |
michael@0 | 11042 | tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4); |
michael@0 | 11043 | arr.push(tmp & 0xFF); |
michael@0 | 11044 | } else if (placeHolders === 1) { |
michael@0 | 11045 | tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4) | (lookup.indexOf(b64[i + 2]) >> 2); |
michael@0 | 11046 | arr.push((tmp >> 8) & 0xFF); |
michael@0 | 11047 | arr.push(tmp & 0xFF); |
michael@0 | 11048 | } |
michael@0 | 11049 | |
michael@0 | 11050 | return arr; |
michael@0 | 11051 | } |
michael@0 | 11052 | |
michael@0 | 11053 | function uint8ToBase64(uint8) { |
michael@0 | 11054 | var i, |
michael@0 | 11055 | extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes |
michael@0 | 11056 | output = "", |
michael@0 | 11057 | temp, length; |
michael@0 | 11058 | |
michael@0 | 11059 | function tripletToBase64 (num) { |
michael@0 | 11060 | return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]; |
michael@0 | 11061 | }; |
michael@0 | 11062 | |
michael@0 | 11063 | // go through the array every three bytes, we'll deal with trailing stuff later |
michael@0 | 11064 | for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { |
michael@0 | 11065 | temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]); |
michael@0 | 11066 | output += tripletToBase64(temp); |
michael@0 | 11067 | } |
michael@0 | 11068 | |
michael@0 | 11069 | // pad the end with zeros, but make sure to not forget the extra bytes |
michael@0 | 11070 | switch (extraBytes) { |
michael@0 | 11071 | case 1: |
michael@0 | 11072 | temp = uint8[uint8.length - 1]; |
michael@0 | 11073 | output += lookup[temp >> 2]; |
michael@0 | 11074 | output += lookup[(temp << 4) & 0x3F]; |
michael@0 | 11075 | output += '=='; |
michael@0 | 11076 | break; |
michael@0 | 11077 | case 2: |
michael@0 | 11078 | temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]); |
michael@0 | 11079 | output += lookup[temp >> 10]; |
michael@0 | 11080 | output += lookup[(temp >> 4) & 0x3F]; |
michael@0 | 11081 | output += lookup[(temp << 2) & 0x3F]; |
michael@0 | 11082 | output += '='; |
michael@0 | 11083 | break; |
michael@0 | 11084 | } |
michael@0 | 11085 | |
michael@0 | 11086 | return output; |
michael@0 | 11087 | } |
michael@0 | 11088 | |
michael@0 | 11089 | module.exports.toByteArray = b64ToByteArray; |
michael@0 | 11090 | module.exports.fromByteArray = uint8ToBase64; |
michael@0 | 11091 | }()); |
michael@0 | 11092 | |
michael@0 | 11093 | },{}]},{},["E/GbHF"]) |
michael@0 | 11094 | ; |
michael@0 | 11095 | JSHINT = require('jshint').JSHINT; |
michael@0 | 11096 | }()); |