url-parse.js 19 KB

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