Initial commit
[yaffs-website] / node_modules / source-map / lib / util.js
1 /* -*- Mode: js; js-indent-level: 2; -*- */
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
6  */
7
8 /**
9  * This is a helper function for getting values from parameter/options
10  * objects.
11  *
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.
17  */
18 function 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 }
27 exports.getArg = getArg;
28
29 var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
30 var dataUrlRegexp = /^data:.+\,.+$/;
31
32 function 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 }
45 exports.urlParse = urlParse;
46
47 function 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.port
61   }
62   if (aParsedUrl.path) {
63     url += aParsedUrl.path;
64   }
65   return url;
66 }
67 exports.urlGenerate = urlGenerate;
68
69 /**
70  * Normalizes a path, or the path portion of a URL:
71  *
72  * - Replaces consecutive slashes with one slash.
73  * - Removes unnecessary '.' parts.
74  * - Removes unnecessary '<dir>/..' parts.
75  *
76  * Based on code in the Node.js 'path' core module.
77  *
78  * @param aPath The path or url to normalize.
79  */
80 function normalize(aPath) {
81   var path = aPath;
82   var url = urlParse(aPath);
83   if (url) {
84     if (!url.path) {
85       return aPath;
86     }
87     path = url.path;
88   }
89   var isAbsolute = exports.isAbsolute(path);
90
91   var parts = path.split(/\/+/);
92   for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
93     part = parts[i];
94     if (part === '.') {
95       parts.splice(i, 1);
96     } else if (part === '..') {
97       up++;
98     } else if (up > 0) {
99       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);
104         up = 0;
105       } else {
106         parts.splice(i, 2);
107         up--;
108       }
109     }
110   }
111   path = parts.join('/');
112
113   if (path === '') {
114     path = isAbsolute ? '/' : '.';
115   }
116
117   if (url) {
118     url.path = path;
119     return urlGenerate(url);
120   }
121   return path;
122 }
123 exports.normalize = normalize;
124
125 /**
126  * Joins two paths/URLs.
127  *
128  * @param aRoot The root path or URL.
129  * @param aPath The path or URL to be joined with the root.
130  *
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
133  *   first.
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
136  *   is returned.
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.
140  */
141 function join(aRoot, aPath) {
142   if (aRoot === "") {
143     aRoot = ".";
144   }
145   if (aPath === "") {
146     aPath = ".";
147   }
148   var aPathUrl = urlParse(aPath);
149   var aRootUrl = urlParse(aRoot);
150   if (aRootUrl) {
151     aRoot = aRootUrl.path || '/';
152   }
153
154   // `join(foo, '//www.example.org')`
155   if (aPathUrl && !aPathUrl.scheme) {
156     if (aRootUrl) {
157       aPathUrl.scheme = aRootUrl.scheme;
158     }
159     return urlGenerate(aPathUrl);
160   }
161
162   if (aPathUrl || aPath.match(dataUrlRegexp)) {
163     return aPath;
164   }
165
166   // `join('http://', 'www.example.com')`
167   if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
168     aRootUrl.host = aPath;
169     return urlGenerate(aRootUrl);
170   }
171
172   var joined = aPath.charAt(0) === '/'
173     ? aPath
174     : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
175
176   if (aRootUrl) {
177     aRootUrl.path = joined;
178     return urlGenerate(aRootUrl);
179   }
180   return joined;
181 }
182 exports.join = join;
183
184 exports.isAbsolute = function (aPath) {
185   return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
186 };
187
188 /**
189  * Make a path relative to a URL or another path.
190  *
191  * @param aRoot The root path or URL.
192  * @param aPath The path or URL to be made relative to aRoot.
193  */
194 function relative(aRoot, aPath) {
195   if (aRoot === "") {
196     aRoot = ".";
197   }
198
199   aRoot = aRoot.replace(/\/$/, '');
200
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.
205   var level = 0;
206   while (aPath.indexOf(aRoot + '/') !== 0) {
207     var index = aRoot.lastIndexOf("/");
208     if (index < 0) {
209       return aPath;
210     }
211
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(/^([^\/]+:\/)?\/*$/)) {
217       return aPath;
218     }
219
220     ++level;
221   }
222
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);
225 }
226 exports.relative = relative;
227
228 var supportsNullProto = (function () {
229   var obj = Object.create(null);
230   return !('__proto__' in obj);
231 }());
232
233 function identity (s) {
234   return s;
235 }
236
237 /**
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.
240  *
241  * See https://github.com/mozilla/source-map/pull/31 and
242  * https://github.com/mozilla/source-map/issues/30
243  *
244  * @param String aStr
245  */
246 function toSetString(aStr) {
247   if (isProtoString(aStr)) {
248     return '$' + aStr;
249   }
250
251   return aStr;
252 }
253 exports.toSetString = supportsNullProto ? identity : toSetString;
254
255 function fromSetString(aStr) {
256   if (isProtoString(aStr)) {
257     return aStr.slice(1);
258   }
259
260   return aStr;
261 }
262 exports.fromSetString = supportsNullProto ? identity : fromSetString;
263
264 function isProtoString(s) {
265   if (!s) {
266     return false;
267   }
268
269   var length = s.length;
270
271   if (length < 9 /* "__proto__".length */) {
272     return false;
273   }
274
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  /* '_' */) {
284     return false;
285   }
286
287   for (var i = length - 10; i >= 0; i--) {
288     if (s.charCodeAt(i) !== 36 /* '$' */) {
289       return false;
290     }
291   }
292
293   return true;
294 }
295
296 /**
297  * Comparator between two mappings where the original positions are compared.
298  *
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.
303  */
304 function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
305   var cmp = mappingA.source - mappingB.source;
306   if (cmp !== 0) {
307     return cmp;
308   }
309
310   cmp = mappingA.originalLine - mappingB.originalLine;
311   if (cmp !== 0) {
312     return cmp;
313   }
314
315   cmp = mappingA.originalColumn - mappingB.originalColumn;
316   if (cmp !== 0 || onlyCompareOriginal) {
317     return cmp;
318   }
319
320   cmp = mappingA.generatedColumn - mappingB.generatedColumn;
321   if (cmp !== 0) {
322     return cmp;
323   }
324
325   cmp = mappingA.generatedLine - mappingB.generatedLine;
326   if (cmp !== 0) {
327     return cmp;
328   }
329
330   return mappingA.name - mappingB.name;
331 }
332 exports.compareByOriginalPositions = compareByOriginalPositions;
333
334 /**
335  * Comparator between two mappings with deflated source and name indices where
336  * the generated positions are compared.
337  *
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.
342  */
343 function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
344   var cmp = mappingA.generatedLine - mappingB.generatedLine;
345   if (cmp !== 0) {
346     return cmp;
347   }
348
349   cmp = mappingA.generatedColumn - mappingB.generatedColumn;
350   if (cmp !== 0 || onlyCompareGenerated) {
351     return cmp;
352   }
353
354   cmp = mappingA.source - mappingB.source;
355   if (cmp !== 0) {
356     return cmp;
357   }
358
359   cmp = mappingA.originalLine - mappingB.originalLine;
360   if (cmp !== 0) {
361     return cmp;
362   }
363
364   cmp = mappingA.originalColumn - mappingB.originalColumn;
365   if (cmp !== 0) {
366     return cmp;
367   }
368
369   return mappingA.name - mappingB.name;
370 }
371 exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
372
373 function strcmp(aStr1, aStr2) {
374   if (aStr1 === aStr2) {
375     return 0;
376   }
377
378   if (aStr1 > aStr2) {
379     return 1;
380   }
381
382   return -1;
383 }
384
385 /**
386  * Comparator between two mappings with inflated source and name strings where
387  * the generated positions are compared.
388  */
389 function compareByGeneratedPositionsInflated(mappingA, mappingB) {
390   var cmp = mappingA.generatedLine - mappingB.generatedLine;
391   if (cmp !== 0) {
392     return cmp;
393   }
394
395   cmp = mappingA.generatedColumn - mappingB.generatedColumn;
396   if (cmp !== 0) {
397     return cmp;
398   }
399
400   cmp = strcmp(mappingA.source, mappingB.source);
401   if (cmp !== 0) {
402     return cmp;
403   }
404
405   cmp = mappingA.originalLine - mappingB.originalLine;
406   if (cmp !== 0) {
407     return cmp;
408   }
409
410   cmp = mappingA.originalColumn - mappingB.originalColumn;
411   if (cmp !== 0) {
412     return cmp;
413   }
414
415   return strcmp(mappingA.name, mappingB.name);
416 }
417 exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;