1 /* -*- Mode: js; js-indent-level: 2; -*- */
3 * Copyright 2011 Mozilla Foundation and contributors
4 * Licensed under the New BSD license. See LICENSE or:
5 * http://opensource.org/licenses/BSD-3-Clause
9 * This is a helper function for getting values from parameter/options
12 * @param args The object we are extracting values from
13 * @param name The name of the property we are getting.
14 * @param defaultValue An optional value to return if the property is missing
15 * from the object. If this is not specified and the property is missing, an
16 * error will be thrown.
18 function getArg(aArgs, aName, aDefaultValue) {
21 } else if (arguments.length === 3) {
24 throw new Error('"' + aName + '" is a required argument.');
27 exports.getArg = getArg;
29 var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
30 var dataUrlRegexp = /^data:.+\,.+$/;
32 function urlParse(aUrl) {
33 var match = aUrl.match(urlRegexp);
45 exports.urlParse = urlParse;
47 function urlGenerate(aParsedUrl) {
49 if (aParsedUrl.scheme) {
50 url += aParsedUrl.scheme + ':';
53 if (aParsedUrl.auth) {
54 url += aParsedUrl.auth + '@';
56 if (aParsedUrl.host) {
57 url += aParsedUrl.host;
59 if (aParsedUrl.port) {
60 url += ":" + aParsedUrl.port
62 if (aParsedUrl.path) {
63 url += aParsedUrl.path;
67 exports.urlGenerate = urlGenerate;
70 * Normalizes a path, or the path portion of a URL:
72 * - Replaces consecutive slashes with one slash.
73 * - Removes unnecessary '.' parts.
74 * - Removes unnecessary '<dir>/..' parts.
76 * Based on code in the Node.js 'path' core module.
78 * @param aPath The path or url to normalize.
80 function normalize(aPath) {
82 var url = urlParse(aPath);
89 var isAbsolute = exports.isAbsolute(path);
91 var parts = path.split(/\/+/);
92 for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
96 } else if (part === '..') {
100 // The first part is blank if the path is absolute. Trying to go
101 // above the root is a no-op. Therefore we can remove all '..' parts
102 // directly after the root.
103 parts.splice(i + 1, up);
111 path = parts.join('/');
114 path = isAbsolute ? '/' : '.';
119 return urlGenerate(url);
123 exports.normalize = normalize;
126 * Joins two paths/URLs.
128 * @param aRoot The root path or URL.
129 * @param aPath The path or URL to be joined with the root.
131 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
132 * scheme-relative URL: Then the scheme of aRoot, if any, is prepended
134 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
135 * is updated with the result and aRoot is returned. Otherwise the result
137 * - If aPath is absolute, the result is aPath.
138 * - Otherwise the two paths are joined with a slash.
139 * - Joining for example 'http://' and 'www.example.com' is also supported.
141 function join(aRoot, aPath) {
148 var aPathUrl = urlParse(aPath);
149 var aRootUrl = urlParse(aRoot);
151 aRoot = aRootUrl.path || '/';
154 // `join(foo, '//www.example.org')`
155 if (aPathUrl && !aPathUrl.scheme) {
157 aPathUrl.scheme = aRootUrl.scheme;
159 return urlGenerate(aPathUrl);
162 if (aPathUrl || aPath.match(dataUrlRegexp)) {
166 // `join('http://', 'www.example.com')`
167 if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
168 aRootUrl.host = aPath;
169 return urlGenerate(aRootUrl);
172 var joined = aPath.charAt(0) === '/'
174 : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
177 aRootUrl.path = joined;
178 return urlGenerate(aRootUrl);
184 exports.isAbsolute = function (aPath) {
185 return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
189 * Make a path relative to a URL or another path.
191 * @param aRoot The root path or URL.
192 * @param aPath The path or URL to be made relative to aRoot.
194 function relative(aRoot, aPath) {
199 aRoot = aRoot.replace(/\/$/, '');
201 // It is possible for the path to be above the root. In this case, simply
202 // checking whether the root is a prefix of the path won't work. Instead, we
203 // need to remove components from the root one by one, until either we find
204 // a prefix that fits, or we run out of components to remove.
206 while (aPath.indexOf(aRoot + '/') !== 0) {
207 var index = aRoot.lastIndexOf("/");
212 // If the only part of the root that is left is the scheme (i.e. http://,
213 // file:///, etc.), one or more slashes (/), or simply nothing at all, we
214 // have exhausted all components, so the path is not relative to the root.
215 aRoot = aRoot.slice(0, index);
216 if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
223 // Make sure we add a "../" for each component we removed from the root.
224 return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
226 exports.relative = relative;
228 var supportsNullProto = (function () {
229 var obj = Object.create(null);
230 return !('__proto__' in obj);
233 function identity (s) {
238 * Because behavior goes wacky when you set `__proto__` on objects, we
239 * have to prefix all the strings in our set with an arbitrary character.
241 * See https://github.com/mozilla/source-map/pull/31 and
242 * https://github.com/mozilla/source-map/issues/30
246 function toSetString(aStr) {
247 if (isProtoString(aStr)) {
253 exports.toSetString = supportsNullProto ? identity : toSetString;
255 function fromSetString(aStr) {
256 if (isProtoString(aStr)) {
257 return aStr.slice(1);
262 exports.fromSetString = supportsNullProto ? identity : fromSetString;
264 function isProtoString(s) {
269 var length = s.length;
271 if (length < 9 /* "__proto__".length */) {
275 if (s.charCodeAt(length - 1) !== 95 /* '_' */ ||
276 s.charCodeAt(length - 2) !== 95 /* '_' */ ||
277 s.charCodeAt(length - 3) !== 111 /* 'o' */ ||
278 s.charCodeAt(length - 4) !== 116 /* 't' */ ||
279 s.charCodeAt(length - 5) !== 111 /* 'o' */ ||
280 s.charCodeAt(length - 6) !== 114 /* 'r' */ ||
281 s.charCodeAt(length - 7) !== 112 /* 'p' */ ||
282 s.charCodeAt(length - 8) !== 95 /* '_' */ ||
283 s.charCodeAt(length - 9) !== 95 /* '_' */) {
287 for (var i = length - 10; i >= 0; i--) {
288 if (s.charCodeAt(i) !== 36 /* '$' */) {
297 * Comparator between two mappings where the original positions are compared.
299 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
300 * mappings with the same original source/line/column, but different generated
301 * line and column the same. Useful when searching for a mapping with a
302 * stubbed out mapping.
304 function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
305 var cmp = mappingA.source - mappingB.source;
310 cmp = mappingA.originalLine - mappingB.originalLine;
315 cmp = mappingA.originalColumn - mappingB.originalColumn;
316 if (cmp !== 0 || onlyCompareOriginal) {
320 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
325 cmp = mappingA.generatedLine - mappingB.generatedLine;
330 return mappingA.name - mappingB.name;
332 exports.compareByOriginalPositions = compareByOriginalPositions;
335 * Comparator between two mappings with deflated source and name indices where
336 * the generated positions are compared.
338 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
339 * mappings with the same generated line and column, but different
340 * source/name/original line and column the same. Useful when searching for a
341 * mapping with a stubbed out mapping.
343 function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
344 var cmp = mappingA.generatedLine - mappingB.generatedLine;
349 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
350 if (cmp !== 0 || onlyCompareGenerated) {
354 cmp = mappingA.source - mappingB.source;
359 cmp = mappingA.originalLine - mappingB.originalLine;
364 cmp = mappingA.originalColumn - mappingB.originalColumn;
369 return mappingA.name - mappingB.name;
371 exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
373 function strcmp(aStr1, aStr2) {
374 if (aStr1 === aStr2) {
386 * Comparator between two mappings with inflated source and name strings where
387 * the generated positions are compared.
389 function compareByGeneratedPositionsInflated(mappingA, mappingB) {
390 var cmp = mappingA.generatedLine - mappingB.generatedLine;
395 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
400 cmp = strcmp(mappingA.source, mappingB.source);
405 cmp = mappingA.originalLine - mappingB.originalLine;
410 cmp = mappingA.originalColumn - mappingB.originalColumn;
415 return strcmp(mappingA.name, mappingB.name);
417 exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;