You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					527 lines
				
				14 KiB
			
		
		
			
		
	
	
					527 lines
				
				14 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var required = require('requires-port')
							 | 
						||
| 
								 | 
							
								  , qs = require('querystringify')
							 | 
						||
| 
								 | 
							
								  , slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//
							 | 
						||
| 
								 | 
							
								  , protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\\/]+)?([\S\s]*)/i
							 | 
						||
| 
								 | 
							
								  , windowsDriveLetter = /^[a-zA-Z]:/
							 | 
						||
| 
								 | 
							
								  , 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]'
							 | 
						||
| 
								 | 
							
								  , left = new RegExp('^'+ whitespace +'+');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Trim a given string.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} str String to trim.
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function trimLeft(str) {
							 | 
						||
| 
								 | 
							
								  return (str ? str : '').toString().replace(left, '');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * These are the parse rules for the URL parser, it informs the parser
							 | 
						||
| 
								 | 
							
								 * about:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 0. The char it Needs to parse, if it's a string it should be done using
							 | 
						||
| 
								 | 
							
								 *    indexOf, RegExp using exec and NaN means set as current value.
							 | 
						||
| 
								 | 
							
								 * 1. The property we should set when parsing this value.
							 | 
						||
| 
								 | 
							
								 * 2. Indication if it's backwards or forward parsing, when set as number it's
							 | 
						||
| 
								 | 
							
								 *    the value of extra chars that should be split off.
							 | 
						||
| 
								 | 
							
								 * 3. Inherit from location if non existing in the parser.
							 | 
						||
| 
								 | 
							
								 * 4. `toLowerCase` the resulting value.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var rules = [
							 | 
						||
| 
								 | 
							
								  ['#', 'hash'],                        // Extract from the back.
							 | 
						||
| 
								 | 
							
								  ['?', 'query'],                       // Extract from the back.
							 | 
						||
| 
								 | 
							
								  function sanitize(address, url) {     // Sanitize what is left of the address
							 | 
						||
| 
								 | 
							
								    return isSpecial(url.protocol) ? address.replace(/\\/g, '/') : address;
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  ['/', 'pathname'],                    // Extract from the back.
							 | 
						||
| 
								 | 
							
								  ['@', 'auth', 1],                     // Extract from the front.
							 | 
						||
| 
								 | 
							
								  [NaN, 'host', undefined, 1, 1],       // Set left over value.
							 | 
						||
| 
								 | 
							
								  [/:(\d+)$/, 'port', undefined, 1],    // RegExp the back.
							 | 
						||
| 
								 | 
							
								  [NaN, 'hostname', undefined, 1, 1]    // Set left over.
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * These properties should not be copied or inherited from. This is only needed
							 | 
						||
| 
								 | 
							
								 * for all non blob URL's as a blob URL does not include a hash, only the
							 | 
						||
| 
								 | 
							
								 * origin.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @type {Object}
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var ignore = { hash: 1, query: 1 };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * The location object differs when your code is loaded through a normal page,
							 | 
						||
| 
								 | 
							
								 * Worker or through a worker using a blob. And with the blobble begins the
							 | 
						||
| 
								 | 
							
								 * trouble as the location object will contain the URL of the blob, not the
							 | 
						||
| 
								 | 
							
								 * location of the page where our code is loaded in. The actual origin is
							 | 
						||
| 
								 | 
							
								 * encoded in the `pathname` so we can thankfully generate a good "default"
							 | 
						||
| 
								 | 
							
								 * location from it so we can generate proper relative URL's again.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Object|String} loc Optional default location object.
							 | 
						||
| 
								 | 
							
								 * @returns {Object} lolcation object.
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function lolcation(loc) {
							 | 
						||
| 
								 | 
							
								  var globalVar;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof window !== 'undefined') globalVar = window;
							 | 
						||
| 
								 | 
							
								  else if (typeof global !== 'undefined') globalVar = global;
							 | 
						||
| 
								 | 
							
								  else if (typeof self !== 'undefined') globalVar = self;
							 | 
						||
| 
								 | 
							
								  else globalVar = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var location = globalVar.location || {};
							 | 
						||
| 
								 | 
							
								  loc = loc || location;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var finaldestination = {}
							 | 
						||
| 
								 | 
							
								    , type = typeof loc
							 | 
						||
| 
								 | 
							
								    , key;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ('blob:' === loc.protocol) {
							 | 
						||
| 
								 | 
							
								    finaldestination = new Url(unescape(loc.pathname), {});
							 | 
						||
| 
								 | 
							
								  } else if ('string' === type) {
							 | 
						||
| 
								 | 
							
								    finaldestination = new Url(loc, {});
							 | 
						||
| 
								 | 
							
								    for (key in ignore) delete finaldestination[key];
							 | 
						||
| 
								 | 
							
								  } else if ('object' === type) {
							 | 
						||
| 
								 | 
							
								    for (key in loc) {
							 | 
						||
| 
								 | 
							
								      if (key in ignore) continue;
							 | 
						||
| 
								 | 
							
								      finaldestination[key] = loc[key];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (finaldestination.slashes === undefined) {
							 | 
						||
| 
								 | 
							
								      finaldestination.slashes = slashes.test(loc.href);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return finaldestination;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Check whether a protocol scheme is special.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} The protocol scheme of the URL
							 | 
						||
| 
								 | 
							
								 * @return {Boolean} `true` if the protocol scheme is special, else `false`
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function isSpecial(scheme) {
							 | 
						||
| 
								 | 
							
								  return (
							 | 
						||
| 
								 | 
							
								    scheme === 'file:' ||
							 | 
						||
| 
								 | 
							
								    scheme === 'ftp:' ||
							 | 
						||
| 
								 | 
							
								    scheme === 'http:' ||
							 | 
						||
| 
								 | 
							
								    scheme === 'https:' ||
							 | 
						||
| 
								 | 
							
								    scheme === 'ws:' ||
							 | 
						||
| 
								 | 
							
								    scheme === 'wss:'
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef ProtocolExtract
							 | 
						||
| 
								 | 
							
								 * @type Object
							 | 
						||
| 
								 | 
							
								 * @property {String} protocol Protocol matched in the URL, in lowercase.
							 | 
						||
| 
								 | 
							
								 * @property {Boolean} slashes `true` if protocol is followed by "//", else `false`.
							 | 
						||
| 
								 | 
							
								 * @property {String} rest Rest of the URL that is not part of the protocol.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Extract protocol information from a URL with/without double slash ("//").
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} address URL we want to extract from.
							 | 
						||
| 
								 | 
							
								 * @param {Object} location
							 | 
						||
| 
								 | 
							
								 * @return {ProtocolExtract} Extracted information.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function extractProtocol(address, location) {
							 | 
						||
| 
								 | 
							
								  address = trimLeft(address);
							 | 
						||
| 
								 | 
							
								  location = location || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var match = protocolre.exec(address);
							 | 
						||
| 
								 | 
							
								  var protocol = match[1] ? match[1].toLowerCase() : '';
							 | 
						||
| 
								 | 
							
								  var forwardSlashes = !!match[2];
							 | 
						||
| 
								 | 
							
								  var otherSlashes = !!match[3];
							 | 
						||
| 
								 | 
							
								  var slashesCount = 0;
							 | 
						||
| 
								 | 
							
								  var rest;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (forwardSlashes) {
							 | 
						||
| 
								 | 
							
								    if (otherSlashes) {
							 | 
						||
| 
								 | 
							
								      rest = match[2] + match[3] + match[4];
							 | 
						||
| 
								 | 
							
								      slashesCount = match[2].length + match[3].length;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      rest = match[2] + match[4];
							 | 
						||
| 
								 | 
							
								      slashesCount = match[2].length;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    if (otherSlashes) {
							 | 
						||
| 
								 | 
							
								      rest = match[3] + match[4];
							 | 
						||
| 
								 | 
							
								      slashesCount = match[3].length;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      rest = match[4]
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (protocol === 'file:') {
							 | 
						||
| 
								 | 
							
								    if (slashesCount >= 2) {
							 | 
						||
| 
								 | 
							
								      rest = rest.slice(2);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if (isSpecial(protocol)) {
							 | 
						||
| 
								 | 
							
								    rest = match[4];
							 | 
						||
| 
								 | 
							
								  } else if (protocol) {
							 | 
						||
| 
								 | 
							
								    if (forwardSlashes) {
							 | 
						||
| 
								 | 
							
								      rest = rest.slice(2);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if (slashesCount >= 2 && isSpecial(location.protocol)) {
							 | 
						||
| 
								 | 
							
								    rest = match[4];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    protocol: protocol,
							 | 
						||
| 
								 | 
							
								    slashes: forwardSlashes || isSpecial(protocol),
							 | 
						||
| 
								 | 
							
								    slashesCount: slashesCount,
							 | 
						||
| 
								 | 
							
								    rest: rest
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Resolve a relative URL pathname against a base URL pathname.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} relative Pathname of the relative URL.
							 | 
						||
| 
								 | 
							
								 * @param {String} base Pathname of the base URL.
							 | 
						||
| 
								 | 
							
								 * @return {String} Resolved pathname.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function resolve(relative, base) {
							 | 
						||
| 
								 | 
							
								  if (relative === '') return base;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/'))
							 | 
						||
| 
								 | 
							
								    , i = path.length
							 | 
						||
| 
								 | 
							
								    , last = path[i - 1]
							 | 
						||
| 
								 | 
							
								    , unshift = false
							 | 
						||
| 
								 | 
							
								    , up = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (i--) {
							 | 
						||
| 
								 | 
							
								    if (path[i] === '.') {
							 | 
						||
| 
								 | 
							
								      path.splice(i, 1);
							 | 
						||
| 
								 | 
							
								    } else if (path[i] === '..') {
							 | 
						||
| 
								 | 
							
								      path.splice(i, 1);
							 | 
						||
| 
								 | 
							
								      up++;
							 | 
						||
| 
								 | 
							
								    } else if (up) {
							 | 
						||
| 
								 | 
							
								      if (i === 0) unshift = true;
							 | 
						||
| 
								 | 
							
								      path.splice(i, 1);
							 | 
						||
| 
								 | 
							
								      up--;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (unshift) path.unshift('');
							 | 
						||
| 
								 | 
							
								  if (last === '.' || last === '..') path.push('');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return path.join('/');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * The actual URL instance. Instead of returning an object we've opted-in to
							 | 
						||
| 
								 | 
							
								 * create an actual constructor as it's much more memory efficient and
							 | 
						||
| 
								 | 
							
								 * faster and it pleases my OCD.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * It is worth noting that we should not use `URL` as class name to prevent
							 | 
						||
| 
								 | 
							
								 * clashes with the global URL instance that got introduced in browsers.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @constructor
							 | 
						||
| 
								 | 
							
								 * @param {String} address URL we want to parse.
							 | 
						||
| 
								 | 
							
								 * @param {Object|String} [location] Location defaults for relative paths.
							 | 
						||
| 
								 | 
							
								 * @param {Boolean|Function} [parser] Parser for the query string.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function Url(address, location, parser) {
							 | 
						||
| 
								 | 
							
								  address = trimLeft(address);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!(this instanceof Url)) {
							 | 
						||
| 
								 | 
							
								    return new Url(address, location, parser);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var relative, extracted, parse, instruction, index, key
							 | 
						||
| 
								 | 
							
								    , instructions = rules.slice()
							 | 
						||
| 
								 | 
							
								    , type = typeof location
							 | 
						||
| 
								 | 
							
								    , url = this
							 | 
						||
| 
								 | 
							
								    , i = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // The following if statements allows this module two have compatibility with
							 | 
						||
| 
								 | 
							
								  // 2 different API:
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments
							 | 
						||
| 
								 | 
							
								  //    where the boolean indicates that the query string should also be parsed.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // 2. The `URL` interface of the browser which accepts a URL, object as
							 | 
						||
| 
								 | 
							
								  //    arguments. The supplied object will be used as default values / fall-back
							 | 
						||
| 
								 | 
							
								  //    for relative paths.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  if ('object' !== type && 'string' !== type) {
							 | 
						||
| 
								 | 
							
								    parser = location;
							 | 
						||
| 
								 | 
							
								    location = null;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (parser && 'function' !== typeof parser) parser = qs.parse;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  location = lolcation(location);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // Extract protocol information before running the instructions.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  extracted = extractProtocol(address || '', location);
							 | 
						||
| 
								 | 
							
								  relative = !extracted.protocol && !extracted.slashes;
							 | 
						||
| 
								 | 
							
								  url.slashes = extracted.slashes || relative && location.slashes;
							 | 
						||
| 
								 | 
							
								  url.protocol = extracted.protocol || location.protocol || '';
							 | 
						||
| 
								 | 
							
								  address = extracted.rest;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // When the authority component is absent the URL starts with a path
							 | 
						||
| 
								 | 
							
								  // component.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  if (
							 | 
						||
| 
								 | 
							
								    extracted.protocol === 'file:' && (
							 | 
						||
| 
								 | 
							
								      extracted.slashesCount !== 2 || windowsDriveLetter.test(address)) ||
							 | 
						||
| 
								 | 
							
								    (!extracted.slashes &&
							 | 
						||
| 
								 | 
							
								      (extracted.protocol ||
							 | 
						||
| 
								 | 
							
								        extracted.slashesCount < 2 ||
							 | 
						||
| 
								 | 
							
								        !isSpecial(url.protocol)))
							 | 
						||
| 
								 | 
							
								  ) {
							 | 
						||
| 
								 | 
							
								    instructions[3] = [/(.*)/, 'pathname'];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (; i < instructions.length; i++) {
							 | 
						||
| 
								 | 
							
								    instruction = instructions[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof instruction === 'function') {
							 | 
						||
| 
								 | 
							
								      address = instruction(address, url);
							 | 
						||
| 
								 | 
							
								      continue;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    parse = instruction[0];
							 | 
						||
| 
								 | 
							
								    key = instruction[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (parse !== parse) {
							 | 
						||
| 
								 | 
							
								      url[key] = address;
							 | 
						||
| 
								 | 
							
								    } else if ('string' === typeof parse) {
							 | 
						||
| 
								 | 
							
								      if (~(index = address.indexOf(parse))) {
							 | 
						||
| 
								 | 
							
								        if ('number' === typeof instruction[2]) {
							 | 
						||
| 
								 | 
							
								          url[key] = address.slice(0, index);
							 | 
						||
| 
								 | 
							
								          address = address.slice(index + instruction[2]);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          url[key] = address.slice(index);
							 | 
						||
| 
								 | 
							
								          address = address.slice(0, index);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else if ((index = parse.exec(address))) {
							 | 
						||
| 
								 | 
							
								      url[key] = index[1];
							 | 
						||
| 
								 | 
							
								      address = address.slice(0, index.index);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    url[key] = url[key] || (
							 | 
						||
| 
								 | 
							
								      relative && instruction[3] ? location[key] || '' : ''
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // Hostname, host and protocol should be lowercased so they can be used to
							 | 
						||
| 
								 | 
							
								    // create a proper `origin`.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    if (instruction[4]) url[key] = url[key].toLowerCase();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // Also parse the supplied query string in to an object. If we're supplied
							 | 
						||
| 
								 | 
							
								  // with a custom parser as function use that instead of the default build-in
							 | 
						||
| 
								 | 
							
								  // parser.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  if (parser) url.query = parser(url.query);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // If the URL is relative, resolve the pathname against the base URL.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  if (
							 | 
						||
| 
								 | 
							
								      relative
							 | 
						||
| 
								 | 
							
								    && location.slashes
							 | 
						||
| 
								 | 
							
								    && url.pathname.charAt(0) !== '/'
							 | 
						||
| 
								 | 
							
								    && (url.pathname !== '' || location.pathname !== '')
							 | 
						||
| 
								 | 
							
								  ) {
							 | 
						||
| 
								 | 
							
								    url.pathname = resolve(url.pathname, location.pathname);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // Default to a / for pathname if none exists. This normalizes the URL
							 | 
						||
| 
								 | 
							
								  // to always have a /
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  if (url.pathname.charAt(0) !== '/' && isSpecial(url.protocol)) {
							 | 
						||
| 
								 | 
							
								    url.pathname = '/' + url.pathname;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // We should not add port numbers if they are already the default port number
							 | 
						||
| 
								 | 
							
								  // for a given protocol. As the host also contains the port number we're going
							 | 
						||
| 
								 | 
							
								  // override it with the hostname which contains no port number.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  if (!required(url.port, url.protocol)) {
							 | 
						||
| 
								 | 
							
								    url.host = url.hostname;
							 | 
						||
| 
								 | 
							
								    url.port = '';
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // Parse down the `auth` for the username and password.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  url.username = url.password = '';
							 | 
						||
| 
								 | 
							
								  if (url.auth) {
							 | 
						||
| 
								 | 
							
								    instruction = url.auth.split(':');
							 | 
						||
| 
								 | 
							
								    url.username = instruction[0] || '';
							 | 
						||
| 
								 | 
							
								    url.password = instruction[1] || '';
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host
							 | 
						||
| 
								 | 
							
								    ? url.protocol +'//'+ url.host
							 | 
						||
| 
								 | 
							
								    : 'null';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  // The href is just the compiled result.
							 | 
						||
| 
								 | 
							
								  //
							 | 
						||
| 
								 | 
							
								  url.href = url.toString();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * This is convenience method for changing properties in the URL instance to
							 | 
						||
| 
								 | 
							
								 * insure that they all propagate correctly.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} part          Property we need to adjust.
							 | 
						||
| 
								 | 
							
								 * @param {Mixed} value          The newly assigned value.
							 | 
						||
| 
								 | 
							
								 * @param {Boolean|Function} fn  When setting the query, it will be the function
							 | 
						||
| 
								 | 
							
								 *                               used to parse the query.
							 | 
						||
| 
								 | 
							
								 *                               When setting the protocol, double slash will be
							 | 
						||
| 
								 | 
							
								 *                               removed from the final url if it is true.
							 | 
						||
| 
								 | 
							
								 * @returns {URL} URL instance for chaining.
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function set(part, value, fn) {
							 | 
						||
| 
								 | 
							
								  var url = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch (part) {
							 | 
						||
| 
								 | 
							
								    case 'query':
							 | 
						||
| 
								 | 
							
								      if ('string' === typeof value && value.length) {
							 | 
						||
| 
								 | 
							
								        value = (fn || qs.parse)(value);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      url[part] = value;
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 'port':
							 | 
						||
| 
								 | 
							
								      url[part] = value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!required(value, url.protocol)) {
							 | 
						||
| 
								 | 
							
								        url.host = url.hostname;
							 | 
						||
| 
								 | 
							
								        url[part] = '';
							 | 
						||
| 
								 | 
							
								      } else if (value) {
							 | 
						||
| 
								 | 
							
								        url.host = url.hostname +':'+ value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 'hostname':
							 | 
						||
| 
								 | 
							
								      url[part] = value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (url.port) value += ':'+ url.port;
							 | 
						||
| 
								 | 
							
								      url.host = value;
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 'host':
							 | 
						||
| 
								 | 
							
								      url[part] = value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (/:\d+$/.test(value)) {
							 | 
						||
| 
								 | 
							
								        value = value.split(':');
							 | 
						||
| 
								 | 
							
								        url.port = value.pop();
							 | 
						||
| 
								 | 
							
								        url.hostname = value.join(':');
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        url.hostname = value;
							 | 
						||
| 
								 | 
							
								        url.port = '';
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 'protocol':
							 | 
						||
| 
								 | 
							
								      url.protocol = value.toLowerCase();
							 | 
						||
| 
								 | 
							
								      url.slashes = !fn;
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case 'pathname':
							 | 
						||
| 
								 | 
							
								    case 'hash':
							 | 
						||
| 
								 | 
							
								      if (value) {
							 | 
						||
| 
								 | 
							
								        var char = part === 'pathname' ? '/' : '#';
							 | 
						||
| 
								 | 
							
								        url[part] = value.charAt(0) !== char ? char + value : value;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        url[part] = value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      url[part] = value;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (var i = 0; i < rules.length; i++) {
							 | 
						||
| 
								 | 
							
								    var ins = rules[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (ins[4]) url[ins[1]] = url[ins[1]].toLowerCase();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host
							 | 
						||
| 
								 | 
							
								    ? url.protocol +'//'+ url.host
							 | 
						||
| 
								 | 
							
								    : 'null';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  url.href = url.toString();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return url;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Transform the properties back in to a valid and full URL string.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Function} stringify Optional query stringify function.
							 | 
						||
| 
								 | 
							
								 * @returns {String} Compiled version of the URL.
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function toString(stringify) {
							 | 
						||
| 
								 | 
							
								  if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var query
							 | 
						||
| 
								 | 
							
								    , url = this
							 | 
						||
| 
								 | 
							
								    , protocol = url.protocol;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (protocol && protocol.charAt(protocol.length - 1) !== ':') protocol += ':';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var result = protocol + (url.slashes || isSpecial(url.protocol) ? '//' : '');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (url.username) {
							 | 
						||
| 
								 | 
							
								    result += url.username;
							 | 
						||
| 
								 | 
							
								    if (url.password) result += ':'+ url.password;
							 | 
						||
| 
								 | 
							
								    result += '@';
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  result += url.host + url.pathname;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  query = 'object' === typeof url.query ? stringify(url.query) : url.query;
							 | 
						||
| 
								 | 
							
								  if (query) result += '?' !== query.charAt(0) ? '?'+ query : query;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (url.hash) result += url.hash;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Url.prototype = { set: set, toString: toString };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Expose the URL parser and some additional properties that might be useful for
							 | 
						||
| 
								 | 
							
								// others or testing.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								Url.extractProtocol = extractProtocol;
							 | 
						||
| 
								 | 
							
								Url.location = lolcation;
							 | 
						||
| 
								 | 
							
								Url.trimLeft = trimLeft;
							 | 
						||
| 
								 | 
							
								Url.qs = qs;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Url;
							 |