dom/system/gonk/tests/marionette/ril_jshint/jshint.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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*!|&lt/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 '&': '&amp;',
michael@0 9405 '<': '&lt;',
michael@0 9406 '>': '&gt;',
michael@0 9407 '"': '&quot;',
michael@0 9408 "'": '&#x27;',
michael@0 9409 '/': '&#x2F;'
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 }());

mercurial