Pinsave/counterstrike
1
1/* -*- Mode: js; js-indent-level: 2; -*- */2/*3 * Copyright 2011 Mozilla Foundation and contributors4 * Licensed under the New BSD license. See LICENSE or:5 * http://opensource.org/licenses/BSD-3-Clause6 */7 8/**9 * This is a helper function for getting values from parameter/options10 * objects.11 *12 * @param args The object we are extracting values from13 * @param name The name of the property we are getting.14 * @param defaultValue An optional value to return if the property is missing15 * from the object. If this is not specified and the property is missing, an16 * error will be thrown.17 */18function getArg(aArgs, aName, aDefaultValue) {19 if (aName in aArgs) {20 return aArgs[aName];21 } else if (arguments.length === 3) {22 return aDefaultValue;23 } else {24 throw new Error('"' + aName + '" is a required argument.');25 }26}27exports.getArg = getArg;28 29var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;30var dataUrlRegexp = /^data:.+\,.+$/;31 32function urlParse(aUrl) {33 var match = aUrl.match(urlRegexp);34 if (!match) {35 return null;36 }37 return {38 scheme: match[1],39 auth: match[2],40 host: match[3],41 port: match[4],42 path: match[5]43 };44}45exports.urlParse = urlParse;46 47function urlGenerate(aParsedUrl) {48 var url = '';49 if (aParsedUrl.scheme) {50 url += aParsedUrl.scheme + ':';51 }52 url += '//';53 if (aParsedUrl.auth) {54 url += aParsedUrl.auth + '@';55 }56 if (aParsedUrl.host) {57 url += aParsedUrl.host;58 }59 if (aParsedUrl.port) {60 url += ":" + aParsedUrl.port61 }62 if (aParsedUrl.path) {63 url += aParsedUrl.path;64 }65 return url;66}67exports.urlGenerate = urlGenerate;68 69var MAX_CACHED_INPUTS = 32;70 71/**72 * Takes some function `f(input) -> result` and returns a memoized version of73 * `f`.74 *75 * We keep at most `MAX_CACHED_INPUTS` memoized results of `f` alive. The76 * memoization is a dumb-simple, linear least-recently-used cache.77 */78function lruMemoize(f) {79 var cache = [];80 81 return function(input) {82 for (var i = 0; i < cache.length; i++) {83 if (cache[i].input === input) {84 var temp = cache[0];85 cache[0] = cache[i];86 cache[i] = temp;87 return cache[0].result;88 }89 }90 91 var result = f(input);92 93 cache.unshift({94 input,95 result,96 });97 98 if (cache.length > MAX_CACHED_INPUTS) {99 cache.pop();100 }101 102 return result;103 };104}105 106/**107 * Normalizes a path, or the path portion of a URL:108 *109 * - Replaces consecutive slashes with one slash.110 * - Removes unnecessary '.' parts.111 * - Removes unnecessary '<dir>/..' parts.112 *113 * Based on code in the Node.js 'path' core module.114 *115 * @param aPath The path or url to normalize.116 */117var normalize = lruMemoize(function normalize(aPath) {118 var path = aPath;119 var url = urlParse(aPath);120 if (url) {121 if (!url.path) {122 return aPath;123 }124 path = url.path;125 }126 var isAbsolute = exports.isAbsolute(path);127 // Split the path into parts between `/` characters. This is much faster than128 // using `.split(/\/+/g)`.129 var parts = [];130 var start = 0;131 var i = 0;132 while (true) {133 start = i;134 i = path.indexOf("/", start);135 if (i === -1) {136 parts.push(path.slice(start));137 break;138 } else {139 parts.push(path.slice(start, i));140 while (i < path.length && path[i] === "/") {141 i++;142 }143 }144 }145 146 for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {147 part = parts[i];148 if (part === '.') {149 parts.splice(i, 1);150 } else if (part === '..') {151 up++;152 } else if (up > 0) {153 if (part === '') {154 // The first part is blank if the path is absolute. Trying to go155 // above the root is a no-op. Therefore we can remove all '..' parts156 // directly after the root.157 parts.splice(i + 1, up);158 up = 0;159 } else {160 parts.splice(i, 2);161 up--;162 }163 }164 }165 path = parts.join('/');166 167 if (path === '') {168 path = isAbsolute ? '/' : '.';169 }170 171 if (url) {172 url.path = path;173 return urlGenerate(url);174 }175 return path;176});177exports.normalize = normalize;178 179/**180 * Joins two paths/URLs.181 *182 * @param aRoot The root path or URL.183 * @param aPath The path or URL to be joined with the root.184 *185 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a186 * scheme-relative URL: Then the scheme of aRoot, if any, is prepended187 * first.188 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion189 * is updated with the result and aRoot is returned. Otherwise the result190 * is returned.191 * - If aPath is absolute, the result is aPath.192 * - Otherwise the two paths are joined with a slash.193 * - Joining for example 'http://' and 'www.example.com' is also supported.194 */195function join(aRoot, aPath) {196 if (aRoot === "") {197 aRoot = ".";198 }199 if (aPath === "") {200 aPath = ".";201 }202 var aPathUrl = urlParse(aPath);203 var aRootUrl = urlParse(aRoot);204 if (aRootUrl) {205 aRoot = aRootUrl.path || '/';206 }207 208 // `join(foo, '//www.example.org')`209 if (aPathUrl && !aPathUrl.scheme) {210 if (aRootUrl) {211 aPathUrl.scheme = aRootUrl.scheme;212 }213 return urlGenerate(aPathUrl);214 }215 216 if (aPathUrl || aPath.match(dataUrlRegexp)) {217 return aPath;218 }219 220 // `join('http://', 'www.example.com')`221 if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {222 aRootUrl.host = aPath;223 return urlGenerate(aRootUrl);224 }225 226 var joined = aPath.charAt(0) === '/'227 ? aPath228 : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);229 230 if (aRootUrl) {231 aRootUrl.path = joined;232 return urlGenerate(aRootUrl);233 }234 return joined;235}236exports.join = join;237 238exports.isAbsolute = function (aPath) {239 return aPath.charAt(0) === '/' || urlRegexp.test(aPath);240};241 242/**243 * Make a path relative to a URL or another path.244 *245 * @param aRoot The root path or URL.246 * @param aPath The path or URL to be made relative to aRoot.247 */248function relative(aRoot, aPath) {249 if (aRoot === "") {250 aRoot = ".";251 }252 253 aRoot = aRoot.replace(/\/$/, '');254 255 // It is possible for the path to be above the root. In this case, simply256 // checking whether the root is a prefix of the path won't work. Instead, we257 // need to remove components from the root one by one, until either we find258 // a prefix that fits, or we run out of components to remove.259 var level = 0;260 while (aPath.indexOf(aRoot + '/') !== 0) {261 var index = aRoot.lastIndexOf("/");262 if (index < 0) {263 return aPath;264 }265 266 // If the only part of the root that is left is the scheme (i.e. http://,267 // file:///, etc.), one or more slashes (/), or simply nothing at all, we268 // have exhausted all components, so the path is not relative to the root.269 aRoot = aRoot.slice(0, index);270 if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {271 return aPath;272 }273 274 ++level;275 }276 277 // Make sure we add a "../" for each component we removed from the root.278 return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);279}280exports.relative = relative;281 282var supportsNullProto = (function () {283 var obj = Object.create(null);284 return !('__proto__' in obj);285}());286 287function identity (s) {288 return s;289}290 291/**292 * Because behavior goes wacky when you set `__proto__` on objects, we293 * have to prefix all the strings in our set with an arbitrary character.294 *295 * See https://github.com/mozilla/source-map/pull/31 and296 * https://github.com/mozilla/source-map/issues/30297 *298 * @param String aStr299 */300function toSetString(aStr) {301 if (isProtoString(aStr)) {302 return '$' + aStr;303 }304 305 return aStr;306}307exports.toSetString = supportsNullProto ? identity : toSetString;308 309function fromSetString(aStr) {310 if (isProtoString(aStr)) {311 return aStr.slice(1);312 }313 314 return aStr;315}316exports.fromSetString = supportsNullProto ? identity : fromSetString;317 318function isProtoString(s) {319 if (!s) {320 return false;321 }322 323 var length = s.length;324 325 if (length < 9 /* "__proto__".length */) {326 return false;327 }328 329 if (s.charCodeAt(length - 1) !== 95 /* '_' */ ||330 s.charCodeAt(length - 2) !== 95 /* '_' */ ||331 s.charCodeAt(length - 3) !== 111 /* 'o' */ ||332 s.charCodeAt(length - 4) !== 116 /* 't' */ ||333 s.charCodeAt(length - 5) !== 111 /* 'o' */ ||334 s.charCodeAt(length - 6) !== 114 /* 'r' */ ||335 s.charCodeAt(length - 7) !== 112 /* 'p' */ ||336 s.charCodeAt(length - 8) !== 95 /* '_' */ ||337 s.charCodeAt(length - 9) !== 95 /* '_' */) {338 return false;339 }340 341 for (var i = length - 10; i >= 0; i--) {342 if (s.charCodeAt(i) !== 36 /* '$' */) {343 return false;344 }345 }346 347 return true;348}349 350/**351 * Comparator between two mappings where the original positions are compared.352 *353 * Optionally pass in `true` as `onlyCompareGenerated` to consider two354 * mappings with the same original source/line/column, but different generated355 * line and column the same. Useful when searching for a mapping with a356 * stubbed out mapping.357 */358function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {359 var cmp = strcmp(mappingA.source, mappingB.source);360 if (cmp !== 0) {361 return cmp;362 }363 364 cmp = mappingA.originalLine - mappingB.originalLine;365 if (cmp !== 0) {366 return cmp;367 }368 369 cmp = mappingA.originalColumn - mappingB.originalColumn;370 if (cmp !== 0 || onlyCompareOriginal) {371 return cmp;372 }373 374 cmp = mappingA.generatedColumn - mappingB.generatedColumn;375 if (cmp !== 0) {376 return cmp;377 }378 379 cmp = mappingA.generatedLine - mappingB.generatedLine;380 if (cmp !== 0) {381 return cmp;382 }383 384 return strcmp(mappingA.name, mappingB.name);385}386exports.compareByOriginalPositions = compareByOriginalPositions;387 388function compareByOriginalPositionsNoSource(mappingA, mappingB, onlyCompareOriginal) {389 var cmp390 391 cmp = mappingA.originalLine - mappingB.originalLine;392 if (cmp !== 0) {393 return cmp;394 }395 396 cmp = mappingA.originalColumn - mappingB.originalColumn;397 if (cmp !== 0 || onlyCompareOriginal) {398 return cmp;399 }400 401 cmp = mappingA.generatedColumn - mappingB.generatedColumn;402 if (cmp !== 0) {403 return cmp;404 }405 406 cmp = mappingA.generatedLine - mappingB.generatedLine;407 if (cmp !== 0) {408 return cmp;409 }410 411 return strcmp(mappingA.name, mappingB.name);412}413exports.compareByOriginalPositionsNoSource = compareByOriginalPositionsNoSource;414 415/**416 * Comparator between two mappings with deflated source and name indices where417 * the generated positions are compared.418 *419 * Optionally pass in `true` as `onlyCompareGenerated` to consider two420 * mappings with the same generated line and column, but different421 * source/name/original line and column the same. Useful when searching for a422 * mapping with a stubbed out mapping.423 */424function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {425 var cmp = mappingA.generatedLine - mappingB.generatedLine;426 if (cmp !== 0) {427 return cmp;428 }429 430 cmp = mappingA.generatedColumn - mappingB.generatedColumn;431 if (cmp !== 0 || onlyCompareGenerated) {432 return cmp;433 }434 435 cmp = strcmp(mappingA.source, mappingB.source);436 if (cmp !== 0) {437 return cmp;438 }439 440 cmp = mappingA.originalLine - mappingB.originalLine;441 if (cmp !== 0) {442 return cmp;443 }444 445 cmp = mappingA.originalColumn - mappingB.originalColumn;446 if (cmp !== 0) {447 return cmp;448 }449 450 return strcmp(mappingA.name, mappingB.name);451}452exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;453 454function compareByGeneratedPositionsDeflatedNoLine(mappingA, mappingB, onlyCompareGenerated) {455 var cmp = mappingA.generatedColumn - mappingB.generatedColumn;456 if (cmp !== 0 || onlyCompareGenerated) {457 return cmp;458 }459 460 cmp = strcmp(mappingA.source, mappingB.source);461 if (cmp !== 0) {462 return cmp;463 }464 465 cmp = mappingA.originalLine - mappingB.originalLine;466 if (cmp !== 0) {467 return cmp;468 }469 470 cmp = mappingA.originalColumn - mappingB.originalColumn;471 if (cmp !== 0) {472 return cmp;473 }474 475 return strcmp(mappingA.name, mappingB.name);476}477exports.compareByGeneratedPositionsDeflatedNoLine = compareByGeneratedPositionsDeflatedNoLine;478 479function strcmp(aStr1, aStr2) {480 if (aStr1 === aStr2) {481 return 0;482 }483 484 if (aStr1 === null) {485 return 1; // aStr2 !== null486 }487 488 if (aStr2 === null) {489 return -1; // aStr1 !== null490 }491 492 if (aStr1 > aStr2) {493 return 1;494 }495 496 return -1;497}498 499/**500 * Comparator between two mappings with inflated source and name strings where501 * the generated positions are compared.502 */503function compareByGeneratedPositionsInflated(mappingA, mappingB) {504 var cmp = mappingA.generatedLine - mappingB.generatedLine;505 if (cmp !== 0) {506 return cmp;507 }508 509 cmp = mappingA.generatedColumn - mappingB.generatedColumn;510 if (cmp !== 0) {511 return cmp;512 }513 514 cmp = strcmp(mappingA.source, mappingB.source);515 if (cmp !== 0) {516 return cmp;517 }518 519 cmp = mappingA.originalLine - mappingB.originalLine;520 if (cmp !== 0) {521 return cmp;522 }523 524 cmp = mappingA.originalColumn - mappingB.originalColumn;525 if (cmp !== 0) {526 return cmp;527 }528 529 return strcmp(mappingA.name, mappingB.name);530}531exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;532 533/**534 * Strip any JSON XSSI avoidance prefix from the string (as documented535 * in the source maps specification), and then parse the string as536 * JSON.537 */538function parseSourceMapInput(str) {539 return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));540}541exports.parseSourceMapInput = parseSourceMapInput;542 543/**544 * Compute the URL of a source given the the source root, the source's545 * URL, and the source map's URL.546 */547function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {548 sourceURL = sourceURL || '';549 550 if (sourceRoot) {551 // This follows what Chrome does.552 if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {553 sourceRoot += '/';554 }555 // The spec says:556 // Line 4: An optional source root, useful for relocating source557 // files on a server or removing repeated values in the558 // “sources” entry. This value is prepended to the individual559 // entries in the “source” field.560 sourceURL = sourceRoot + sourceURL;561 }562 563 // Historically, SourceMapConsumer did not take the sourceMapURL as564 // a parameter. This mode is still somewhat supported, which is why565 // this code block is conditional. However, it's preferable to pass566 // the source map URL to SourceMapConsumer, so that this function567 // can implement the source URL resolution algorithm as outlined in568 // the spec. This block is basically the equivalent of:569 // new URL(sourceURL, sourceMapURL).toString()570 // ... except it avoids using URL, which wasn't available in the571 // older releases of node still supported by this library.572 //573 // The spec says:574 // If the sources are not absolute URLs after prepending of the575 // “sourceRoot”, the sources are resolved relative to the576 // SourceMap (like resolving script src in a html document).577 if (sourceMapURL) {578 var parsed = urlParse(sourceMapURL);579 if (!parsed) {580 throw new Error("sourceMapURL could not be parsed");581 }582 if (parsed.path) {583 // Strip the last path component, but keep the "/".584 var index = parsed.path.lastIndexOf('/');585 if (index >= 0) {586 parsed.path = parsed.path.substring(0, index + 1);587 }588 }589 sourceURL = join(urlGenerate(parsed), sourceURL);590 }591 592 return normalize(sourceURL);593}594exports.computeSourceURL = computeSourceURL;595 