source: trip-planner-front/node_modules/url-parse/index.js@ 188ee53

Last change on this file since 188ee53 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 14.3 KB
Line 
1'use strict';
2
3var required = require('requires-port')
4 , qs = require('querystringify')
5 , slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//
6 , protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\\/]+)?([\S\s]*)/i
7 , windowsDriveLetter = /^[a-zA-Z]:/
8 , whitespace = '[\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\xA0\\u1680\\u180E\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200A\\u202F\\u205F\\u3000\\u2028\\u2029\\uFEFF]'
9 , left = new RegExp('^'+ whitespace +'+');
10
11/**
12 * Trim a given string.
13 *
14 * @param {String} str String to trim.
15 * @public
16 */
17function trimLeft(str) {
18 return (str ? str : '').toString().replace(left, '');
19}
20
21/**
22 * These are the parse rules for the URL parser, it informs the parser
23 * about:
24 *
25 * 0. The char it Needs to parse, if it's a string it should be done using
26 * indexOf, RegExp using exec and NaN means set as current value.
27 * 1. The property we should set when parsing this value.
28 * 2. Indication if it's backwards or forward parsing, when set as number it's
29 * the value of extra chars that should be split off.
30 * 3. Inherit from location if non existing in the parser.
31 * 4. `toLowerCase` the resulting value.
32 */
33var rules = [
34 ['#', 'hash'], // Extract from the back.
35 ['?', 'query'], // Extract from the back.
36 function sanitize(address, url) { // Sanitize what is left of the address
37 return isSpecial(url.protocol) ? address.replace(/\\/g, '/') : address;
38 },
39 ['/', 'pathname'], // Extract from the back.
40 ['@', 'auth', 1], // Extract from the front.
41 [NaN, 'host', undefined, 1, 1], // Set left over value.
42 [/:(\d+)$/, 'port', undefined, 1], // RegExp the back.
43 [NaN, 'hostname', undefined, 1, 1] // Set left over.
44];
45
46/**
47 * These properties should not be copied or inherited from. This is only needed
48 * for all non blob URL's as a blob URL does not include a hash, only the
49 * origin.
50 *
51 * @type {Object}
52 * @private
53 */
54var ignore = { hash: 1, query: 1 };
55
56/**
57 * The location object differs when your code is loaded through a normal page,
58 * Worker or through a worker using a blob. And with the blobble begins the
59 * trouble as the location object will contain the URL of the blob, not the
60 * location of the page where our code is loaded in. The actual origin is
61 * encoded in the `pathname` so we can thankfully generate a good "default"
62 * location from it so we can generate proper relative URL's again.
63 *
64 * @param {Object|String} loc Optional default location object.
65 * @returns {Object} lolcation object.
66 * @public
67 */
68function lolcation(loc) {
69 var globalVar;
70
71 if (typeof window !== 'undefined') globalVar = window;
72 else if (typeof global !== 'undefined') globalVar = global;
73 else if (typeof self !== 'undefined') globalVar = self;
74 else globalVar = {};
75
76 var location = globalVar.location || {};
77 loc = loc || location;
78
79 var finaldestination = {}
80 , type = typeof loc
81 , key;
82
83 if ('blob:' === loc.protocol) {
84 finaldestination = new Url(unescape(loc.pathname), {});
85 } else if ('string' === type) {
86 finaldestination = new Url(loc, {});
87 for (key in ignore) delete finaldestination[key];
88 } else if ('object' === type) {
89 for (key in loc) {
90 if (key in ignore) continue;
91 finaldestination[key] = loc[key];
92 }
93
94 if (finaldestination.slashes === undefined) {
95 finaldestination.slashes = slashes.test(loc.href);
96 }
97 }
98
99 return finaldestination;
100}
101
102/**
103 * Check whether a protocol scheme is special.
104 *
105 * @param {String} The protocol scheme of the URL
106 * @return {Boolean} `true` if the protocol scheme is special, else `false`
107 * @private
108 */
109function isSpecial(scheme) {
110 return (
111 scheme === 'file:' ||
112 scheme === 'ftp:' ||
113 scheme === 'http:' ||
114 scheme === 'https:' ||
115 scheme === 'ws:' ||
116 scheme === 'wss:'
117 );
118}
119
120/**
121 * @typedef ProtocolExtract
122 * @type Object
123 * @property {String} protocol Protocol matched in the URL, in lowercase.
124 * @property {Boolean} slashes `true` if protocol is followed by "//", else `false`.
125 * @property {String} rest Rest of the URL that is not part of the protocol.
126 */
127
128/**
129 * Extract protocol information from a URL with/without double slash ("//").
130 *
131 * @param {String} address URL we want to extract from.
132 * @param {Object} location
133 * @return {ProtocolExtract} Extracted information.
134 * @private
135 */
136function extractProtocol(address, location) {
137 address = trimLeft(address);
138 location = location || {};
139
140 var match = protocolre.exec(address);
141 var protocol = match[1] ? match[1].toLowerCase() : '';
142 var forwardSlashes = !!match[2];
143 var otherSlashes = !!match[3];
144 var slashesCount = 0;
145 var rest;
146
147 if (forwardSlashes) {
148 if (otherSlashes) {
149 rest = match[2] + match[3] + match[4];
150 slashesCount = match[2].length + match[3].length;
151 } else {
152 rest = match[2] + match[4];
153 slashesCount = match[2].length;
154 }
155 } else {
156 if (otherSlashes) {
157 rest = match[3] + match[4];
158 slashesCount = match[3].length;
159 } else {
160 rest = match[4]
161 }
162 }
163
164 if (protocol === 'file:') {
165 if (slashesCount >= 2) {
166 rest = rest.slice(2);
167 }
168 } else if (isSpecial(protocol)) {
169 rest = match[4];
170 } else if (protocol) {
171 if (forwardSlashes) {
172 rest = rest.slice(2);
173 }
174 } else if (slashesCount >= 2 && isSpecial(location.protocol)) {
175 rest = match[4];
176 }
177
178 return {
179 protocol: protocol,
180 slashes: forwardSlashes || isSpecial(protocol),
181 slashesCount: slashesCount,
182 rest: rest
183 };
184}
185
186/**
187 * Resolve a relative URL pathname against a base URL pathname.
188 *
189 * @param {String} relative Pathname of the relative URL.
190 * @param {String} base Pathname of the base URL.
191 * @return {String} Resolved pathname.
192 * @private
193 */
194function resolve(relative, base) {
195 if (relative === '') return base;
196
197 var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/'))
198 , i = path.length
199 , last = path[i - 1]
200 , unshift = false
201 , up = 0;
202
203 while (i--) {
204 if (path[i] === '.') {
205 path.splice(i, 1);
206 } else if (path[i] === '..') {
207 path.splice(i, 1);
208 up++;
209 } else if (up) {
210 if (i === 0) unshift = true;
211 path.splice(i, 1);
212 up--;
213 }
214 }
215
216 if (unshift) path.unshift('');
217 if (last === '.' || last === '..') path.push('');
218
219 return path.join('/');
220}
221
222/**
223 * The actual URL instance. Instead of returning an object we've opted-in to
224 * create an actual constructor as it's much more memory efficient and
225 * faster and it pleases my OCD.
226 *
227 * It is worth noting that we should not use `URL` as class name to prevent
228 * clashes with the global URL instance that got introduced in browsers.
229 *
230 * @constructor
231 * @param {String} address URL we want to parse.
232 * @param {Object|String} [location] Location defaults for relative paths.
233 * @param {Boolean|Function} [parser] Parser for the query string.
234 * @private
235 */
236function Url(address, location, parser) {
237 address = trimLeft(address);
238
239 if (!(this instanceof Url)) {
240 return new Url(address, location, parser);
241 }
242
243 var relative, extracted, parse, instruction, index, key
244 , instructions = rules.slice()
245 , type = typeof location
246 , url = this
247 , i = 0;
248
249 //
250 // The following if statements allows this module two have compatibility with
251 // 2 different API:
252 //
253 // 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments
254 // where the boolean indicates that the query string should also be parsed.
255 //
256 // 2. The `URL` interface of the browser which accepts a URL, object as
257 // arguments. The supplied object will be used as default values / fall-back
258 // for relative paths.
259 //
260 if ('object' !== type && 'string' !== type) {
261 parser = location;
262 location = null;
263 }
264
265 if (parser && 'function' !== typeof parser) parser = qs.parse;
266
267 location = lolcation(location);
268
269 //
270 // Extract protocol information before running the instructions.
271 //
272 extracted = extractProtocol(address || '', location);
273 relative = !extracted.protocol && !extracted.slashes;
274 url.slashes = extracted.slashes || relative && location.slashes;
275 url.protocol = extracted.protocol || location.protocol || '';
276 address = extracted.rest;
277
278 //
279 // When the authority component is absent the URL starts with a path
280 // component.
281 //
282 if (
283 extracted.protocol === 'file:' && (
284 extracted.slashesCount !== 2 || windowsDriveLetter.test(address)) ||
285 (!extracted.slashes &&
286 (extracted.protocol ||
287 extracted.slashesCount < 2 ||
288 !isSpecial(url.protocol)))
289 ) {
290 instructions[3] = [/(.*)/, 'pathname'];
291 }
292
293 for (; i < instructions.length; i++) {
294 instruction = instructions[i];
295
296 if (typeof instruction === 'function') {
297 address = instruction(address, url);
298 continue;
299 }
300
301 parse = instruction[0];
302 key = instruction[1];
303
304 if (parse !== parse) {
305 url[key] = address;
306 } else if ('string' === typeof parse) {
307 if (~(index = address.indexOf(parse))) {
308 if ('number' === typeof instruction[2]) {
309 url[key] = address.slice(0, index);
310 address = address.slice(index + instruction[2]);
311 } else {
312 url[key] = address.slice(index);
313 address = address.slice(0, index);
314 }
315 }
316 } else if ((index = parse.exec(address))) {
317 url[key] = index[1];
318 address = address.slice(0, index.index);
319 }
320
321 url[key] = url[key] || (
322 relative && instruction[3] ? location[key] || '' : ''
323 );
324
325 //
326 // Hostname, host and protocol should be lowercased so they can be used to
327 // create a proper `origin`.
328 //
329 if (instruction[4]) url[key] = url[key].toLowerCase();
330 }
331
332 //
333 // Also parse the supplied query string in to an object. If we're supplied
334 // with a custom parser as function use that instead of the default build-in
335 // parser.
336 //
337 if (parser) url.query = parser(url.query);
338
339 //
340 // If the URL is relative, resolve the pathname against the base URL.
341 //
342 if (
343 relative
344 && location.slashes
345 && url.pathname.charAt(0) !== '/'
346 && (url.pathname !== '' || location.pathname !== '')
347 ) {
348 url.pathname = resolve(url.pathname, location.pathname);
349 }
350
351 //
352 // Default to a / for pathname if none exists. This normalizes the URL
353 // to always have a /
354 //
355 if (url.pathname.charAt(0) !== '/' && isSpecial(url.protocol)) {
356 url.pathname = '/' + url.pathname;
357 }
358
359 //
360 // We should not add port numbers if they are already the default port number
361 // for a given protocol. As the host also contains the port number we're going
362 // override it with the hostname which contains no port number.
363 //
364 if (!required(url.port, url.protocol)) {
365 url.host = url.hostname;
366 url.port = '';
367 }
368
369 //
370 // Parse down the `auth` for the username and password.
371 //
372 url.username = url.password = '';
373 if (url.auth) {
374 instruction = url.auth.split(':');
375 url.username = instruction[0] || '';
376 url.password = instruction[1] || '';
377 }
378
379 url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host
380 ? url.protocol +'//'+ url.host
381 : 'null';
382
383 //
384 // The href is just the compiled result.
385 //
386 url.href = url.toString();
387}
388
389/**
390 * This is convenience method for changing properties in the URL instance to
391 * insure that they all propagate correctly.
392 *
393 * @param {String} part Property we need to adjust.
394 * @param {Mixed} value The newly assigned value.
395 * @param {Boolean|Function} fn When setting the query, it will be the function
396 * used to parse the query.
397 * When setting the protocol, double slash will be
398 * removed from the final url if it is true.
399 * @returns {URL} URL instance for chaining.
400 * @public
401 */
402function set(part, value, fn) {
403 var url = this;
404
405 switch (part) {
406 case 'query':
407 if ('string' === typeof value && value.length) {
408 value = (fn || qs.parse)(value);
409 }
410
411 url[part] = value;
412 break;
413
414 case 'port':
415 url[part] = value;
416
417 if (!required(value, url.protocol)) {
418 url.host = url.hostname;
419 url[part] = '';
420 } else if (value) {
421 url.host = url.hostname +':'+ value;
422 }
423
424 break;
425
426 case 'hostname':
427 url[part] = value;
428
429 if (url.port) value += ':'+ url.port;
430 url.host = value;
431 break;
432
433 case 'host':
434 url[part] = value;
435
436 if (/:\d+$/.test(value)) {
437 value = value.split(':');
438 url.port = value.pop();
439 url.hostname = value.join(':');
440 } else {
441 url.hostname = value;
442 url.port = '';
443 }
444
445 break;
446
447 case 'protocol':
448 url.protocol = value.toLowerCase();
449 url.slashes = !fn;
450 break;
451
452 case 'pathname':
453 case 'hash':
454 if (value) {
455 var char = part === 'pathname' ? '/' : '#';
456 url[part] = value.charAt(0) !== char ? char + value : value;
457 } else {
458 url[part] = value;
459 }
460 break;
461
462 default:
463 url[part] = value;
464 }
465
466 for (var i = 0; i < rules.length; i++) {
467 var ins = rules[i];
468
469 if (ins[4]) url[ins[1]] = url[ins[1]].toLowerCase();
470 }
471
472 url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host
473 ? url.protocol +'//'+ url.host
474 : 'null';
475
476 url.href = url.toString();
477
478 return url;
479}
480
481/**
482 * Transform the properties back in to a valid and full URL string.
483 *
484 * @param {Function} stringify Optional query stringify function.
485 * @returns {String} Compiled version of the URL.
486 * @public
487 */
488function toString(stringify) {
489 if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;
490
491 var query
492 , url = this
493 , protocol = url.protocol;
494
495 if (protocol && protocol.charAt(protocol.length - 1) !== ':') protocol += ':';
496
497 var result = protocol + (url.slashes || isSpecial(url.protocol) ? '//' : '');
498
499 if (url.username) {
500 result += url.username;
501 if (url.password) result += ':'+ url.password;
502 result += '@';
503 }
504
505 result += url.host + url.pathname;
506
507 query = 'object' === typeof url.query ? stringify(url.query) : url.query;
508 if (query) result += '?' !== query.charAt(0) ? '?'+ query : query;
509
510 if (url.hash) result += url.hash;
511
512 return result;
513}
514
515Url.prototype = { set: set, toString: toString };
516
517//
518// Expose the URL parser and some additional properties that might be useful for
519// others or testing.
520//
521Url.extractProtocol = extractProtocol;
522Url.location = lolcation;
523Url.trimLeft = trimLeft;
524Url.qs = qs;
525
526module.exports = Url;
Note: See TracBrowser for help on using the repository browser.