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
7 if (typeof define !== 'function') {
8 var define = require('amdefine')(module, require);
10 define(function (require, exports, module) {
13 * This is a helper function for getting values from parameter/options
16 * @param args The object we are extracting values from
17 * @param name The name of the property we are getting.
18 * @param defaultValue An optional value to return if the property is missing
19 * from the object. If this is not specified and the property is missing, an
20 * error will be thrown.
22 function getArg(aArgs, aName, aDefaultValue) {
25 } else if (arguments.length === 3) {
28 throw new Error('"' + aName + '" is a required argument.');
31 exports.getArg = getArg;
33 var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
34 var dataUrlRegexp = /^data:.+\,.+$/;
36 function urlParse(aUrl) {
37 var match = aUrl.match(urlRegexp);
49 exports.urlParse = urlParse;
51 function urlGenerate(aParsedUrl) {
53 if (aParsedUrl.scheme) {
54 url += aParsedUrl.scheme + ':';
57 if (aParsedUrl.auth) {
58 url += aParsedUrl.auth + '@';
60 if (aParsedUrl.host) {
61 url += aParsedUrl.host;
63 if (aParsedUrl.port) {
64 url += ":" + aParsedUrl.port
66 if (aParsedUrl.path) {
67 url += aParsedUrl.path;
71 exports.urlGenerate = urlGenerate;
74 * Normalizes a path, or the path portion of a URL:
76 * - Replaces consequtive slashes with one slash.
77 * - Removes unnecessary '.' parts.
78 * - Removes unnecessary '<dir>/..' parts.
80 * Based on code in the Node.js 'path' core module.
82 * @param aPath The path or url to normalize.
84 function normalize(aPath) {
86 var url = urlParse(aPath);
93 var isAbsolute = (path.charAt(0) === '/');
95 var parts = path.split(/\/+/);
96 for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
100 } else if (part === '..') {
104 // The first part is blank if the path is absolute. Trying to go
105 // above the root is a no-op. Therefore we can remove all '..' parts
106 // directly after the root.
107 parts.splice(i + 1, up);
115 path = parts.join('/');
118 path = isAbsolute ? '/' : '.';
123 return urlGenerate(url);
127 exports.normalize = normalize;
130 * Joins two paths/URLs.
132 * @param aRoot The root path or URL.
133 * @param aPath The path or URL to be joined with the root.
135 * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
136 * scheme-relative URL: Then the scheme of aRoot, if any, is prepended
138 * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
139 * is updated with the result and aRoot is returned. Otherwise the result
141 * - If aPath is absolute, the result is aPath.
142 * - Otherwise the two paths are joined with a slash.
143 * - Joining for example 'http://' and 'www.example.com' is also supported.
145 function join(aRoot, aPath) {
152 var aPathUrl = urlParse(aPath);
153 var aRootUrl = urlParse(aRoot);
155 aRoot = aRootUrl.path || '/';
158 // `join(foo, '//www.example.org')`
159 if (aPathUrl && !aPathUrl.scheme) {
161 aPathUrl.scheme = aRootUrl.scheme;
163 return urlGenerate(aPathUrl);
166 if (aPathUrl || aPath.match(dataUrlRegexp)) {
170 // `join('http://', 'www.example.com')`
171 if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
172 aRootUrl.host = aPath;
173 return urlGenerate(aRootUrl);
176 var joined = aPath.charAt(0) === '/'
178 : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
181 aRootUrl.path = joined;
182 return urlGenerate(aRootUrl);
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;
229 * Because behavior goes wacky when you set `__proto__` on objects, we
230 * have to prefix all the strings in our set with an arbitrary character.
232 * See https://github.com/mozilla/source-map/pull/31 and
233 * https://github.com/mozilla/source-map/issues/30
237 function toSetString(aStr) {
240 exports.toSetString = toSetString;
242 function fromSetString(aStr) {
243 return aStr.substr(1);
245 exports.fromSetString = fromSetString;
248 * Comparator between two mappings where the original positions are compared.
250 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
251 * mappings with the same original source/line/column, but different generated
252 * line and column the same. Useful when searching for a mapping with a
253 * stubbed out mapping.
255 function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
256 var cmp = mappingA.source - mappingB.source;
261 cmp = mappingA.originalLine - mappingB.originalLine;
266 cmp = mappingA.originalColumn - mappingB.originalColumn;
267 if (cmp !== 0 || onlyCompareOriginal) {
271 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
276 cmp = mappingA.generatedLine - mappingB.generatedLine;
281 return mappingA.name - mappingB.name;
283 exports.compareByOriginalPositions = compareByOriginalPositions;
286 * Comparator between two mappings with deflated source and name indices where
287 * the generated positions are compared.
289 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
290 * mappings with the same generated line and column, but different
291 * source/name/original line and column the same. Useful when searching for a
292 * mapping with a stubbed out mapping.
294 function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
295 var cmp = mappingA.generatedLine - mappingB.generatedLine;
300 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
301 if (cmp !== 0 || onlyCompareGenerated) {
305 cmp = mappingA.source - mappingB.source;
310 cmp = mappingA.originalLine - mappingB.originalLine;
315 cmp = mappingA.originalColumn - mappingB.originalColumn;
320 return mappingA.name - mappingB.name;
322 exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
324 function strcmp(aStr1, aStr2) {
325 if (aStr1 === aStr2) {
337 * Comparator between two mappings with inflated source and name strings where
338 * the generated positions are compared.
340 function compareByGeneratedPositionsInflated(mappingA, mappingB) {
341 var cmp = mappingA.generatedLine - mappingB.generatedLine;
346 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
351 cmp = strcmp(mappingA.source, mappingB.source);
356 cmp = mappingA.originalLine - mappingB.originalLine;
361 cmp = mappingA.originalColumn - mappingB.originalColumn;
366 return strcmp(mappingA.name, mappingB.name);
368 exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;