url-parse.js 15 KB

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