[Pkg-javascript-commits] [node-url-parse] 01/11: New upstream version 1.2.0

Praveen Arimbrathodiyil praveen at moszumanska.debian.org
Tue Jan 2 17:20:53 UTC 2018


This is an automated email from the git hooks/post-receive script.

praveen pushed a commit to branch master
in repository node-url-parse.

commit 4fd547d7271e6f70433cd9101a756ce9ba1ab8a5
Author: Pirate Praveen <praveen at debian.org>
Date:   Tue Jan 2 21:11:22 2018 +0530

    New upstream version 1.2.0
---
 .npmignore            |   6 -
 .travis.yml           |  29 ----
 .zuul.yml             |  21 ---
 README.md             |  75 ++++++----
 dist/url-parse.js     | 367 +++++++++++++++++++++++++++++++++++++++++++++
 dist/url-parse.min.js |   1 +
 fuzzy.js              | 124 ---------------
 index.js              | 306 +++++++++++++++++++++++++++++--------
 lolcation.js          |  45 ------
 package.json          |  36 +++--
 test.js               | 408 --------------------------------------------------
 11 files changed, 683 insertions(+), 735 deletions(-)

diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 63c1316..0000000
--- a/.npmignore
+++ /dev/null
@@ -1,6 +0,0 @@
-node_modules
-coverage
-.tern-port
-browserified.js
-npm-debug.log
-dist
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d958dcd..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-sudo: false
-language: node_js
-matrix:
-  fast_finish: true
-  include:
-    - node_js: "0.10"
-      env: TASK=test-node
-    - node_js: "0.12"
-      env: TASK=test-node
-    - node_js: "iojs"
-      env: TASK=test-node
-    - node_js: "4"
-      env: TASK=test-node
-    - node_js: "4"
-      env: TASK=test-browser
-env:
-  global:
-    - secure: edovUXd/s/VAVXTor0CT1XcGqAtBwGgLYN1zYq0JcNiJUgWFiZ5VGKYbKVpb6nKlwm0Fdi1xmByguSa6xLZllcXjtDgfXrkI1cQYmoVncJ63JpXNG+UKyy43BwnF2OqgUrAAOt/ic1YJr9kBe+IaRwDUsMxxIkuJ6Z8c4diX0HE=
-    - secure: IF01oyIKSs0C5dARdYRTilKnU1TG4zenjjEPClkQxAWIpUOxl9xcNJWDVEOPxJ/4pVt+pozyT80Rp7efh6ZiREJIQI1tUboBKSqZzSbnD5uViQNSbQ90PaDP0FIUc0IQ5o07W36rijBB0DTmtU1VofzN9PKkJO7XiSSXevI8RcM=
-script:
-  - "npm run ${TASK}"
-after_script:
-  - 'if [ "${TASK}" == "test-node" ]; then npm i coveralls at 2 && cat coverage/lcov.info | coveralls; fi'
-notifications:
-  irc:
-    channels:
-      - "irc.freenode.org#unshift"
-    on_success: change
-    on_failure: change
diff --git a/.zuul.yml b/.zuul.yml
deleted file mode 100644
index cdafdf9..0000000
--- a/.zuul.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-ui: mocha-bdd
-browsers:
-  - name: android
-    version: [oldest, latest]
-  - name: chrome
-    version: [oldest, latest]
-  - name: firefox
-    version: [oldest, latest]
-  - name: ie
-    version: oldest..latest
-  - name: iphone
-    version: [oldest, latest]
-  - name: opera
-    version: oldest..latest
-  - name: safari
-    version: oldest..latest
-  - name: microsoftedge
-    version: oldest..latest
-capabilities:
-  record-screenshots: false
-  record-video: false
diff --git a/README.md b/README.md
index b5fdd6d..d9dfd7a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 # url-parse
-[![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](https://img.shields.io/npm/v/url-parse.svg?style=flat-square)](http://browsenpm.org/package/url-parse)[![Build Status](https://img.shields.io/travis/unshiftio/url-parse/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/url-parse)[![Dependencies](https://img.shields.io/david/unshiftio/url-parse.svg?style=flat-square)](https://david-dm.org/unshift [...]
+
+[![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](https://img.shields.io/npm/v/url-parse.svg?style=flat-square)](https://www.npmjs.com/package/url-parse)[![Build Status](https://img.shields.io/travis/unshiftio/url-parse/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/url-parse)[![Dependencies](https://img.shields.io/david/unshiftio/url-parse.svg?style=flat-square)](https://david-dm.org/unshif [...]
 
 [![Sauce Test Status](https://saucelabs.com/browser-matrix/url-parse.svg)](https://saucelabs.com/u/url-parse)
 
@@ -8,21 +9,22 @@ The `url-parse` method exposes two different API interfaces. The
 and the new [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL)
 interface that is available in the latest browsers.
 
-Since `0.1` we've moved away from using the DOM's `<a>` element for URL parsing
-and moving to a full Regular Expression solution. The main reason for this
-change is to make the URL parser available in different JavaScript environments
-as you don't always have access to the DOM like `Worker` environments. This
-module still have a really small foot print as this module's main intention is
-to be bundled with client-side code. The only problem however with a RegExp
-based solution is that it required a lot of lookups causing major problems in
-FireFox. So the last and the current solution was a pure string parsing
-solution which chops up the URL in smaller pieces.
+In version `0.1` we moved from a DOM based parsing solution, using the `<a>`
+element, to a full Regular Expression solution. The main reason for this was
+to make the URL parser available in different JavaScript environments as you
+don't always have access to the DOM. An example of such environment is the
+[`Worker`](https://developer.mozilla.org/en/docs/Web/API/Worker) interface.
+The RegExp based solution didn't work well as it required a lot of lookups
+causing major problems in FireFox. In version `1.0.0` we ditched the RegExp
+based solution in favor of a pure string parsing solution which chops up the
+URL into smaller pieces. This module still has a really small footprint as it
+has been designed to be used on the client side.
 
 In addition to URL parsing we also expose the bundled `querystringify` module.
 
 ## Installation
 
-This module is designed to be used using either browserify or node.js it's
+This module is designed to be used using either browserify or Node.js it's
 released in the public npm registry and can be installed using:
 
 ```
@@ -40,16 +42,28 @@ var URL = require('url-parse');
 ```
 
 To parse an URL simply call the `URL` method with the URL that needs to be
-transformed in to an object.
+transformed into an object.
 
 ```js
 var url = new URL('https://github.com/foo/bar');
 ```
 
 The `new` keyword is optional but it will save you an extra function invocation.
-In the example above we've demonstrated the URL interface, but as said in the
-module description we also support the node.js interface. So you could also use
-the library in this way:
+The constructor takes the following arguments:
+
+- `url` (`String`): A string representing an absolute or relative URL.
+- `baseURL` (`Object` | `String`): An object or string representing
+  the base URL to use in case `url` is a relative URL. This argument is
+  optional and defaults to [`location`](https://developer.mozilla.org/en-US/docs/Web/API/Location)
+  in the browser.
+- `parser` (`Boolean` | `Function`): This argument is optional and specifies
+  how to parse the query string. By default it is `false` so the query string
+  is not parsed. If you pass `true` the query string is parsed using the
+  embedded `querystringify` module. If you pass a function the query string
+  will be parsed using this function.
+
+As said above we also support the Node.js interface so you can also use the
+library in this way:
 
 ```js
 'use strict';
@@ -60,10 +74,12 @@ var parse = require('url-parse')
 
 The returned `url` instance contains the following properties:
 
-- `protocol`: Requested protocol without slashes (e.g. `http:`).
+- `protocol`: The protocol scheme of the URL (e.g. `http:`).
+- `slashes`: A boolean which indicates whether the `protocol` is followed by two
+  forward slashes (`//`).
+- `auth`: Authentication information portion (e.g. `username:password`).
 - `username`: Username of basic authentication.
 - `password`: Password of basic authentication.
-- `auth`: Authentication information portion (e.g. `username:password`).
 - `host`: Host name with port number.
 - `hostname`: Host name without port number.
 - `port`: Optional port number.
@@ -71,6 +87,18 @@ The returned `url` instance contains the following properties:
 - `query`: Parsed object containing query string, unless parsing is set to false.
 - `hash`: The "fragment" portion of the URL including the pound-sign (`#`).
 - `href`: The full URL.
+- `origin`: The origin of the URL.
+
+Note that when `url-parse` is used in a browser environment, it will default to
+using the browser's current window location as the base URL when parsing all
+inputs. To parse an input independently of the browser's current URL (e.g. for
+functionality parity with the library in a Node environment), pass an empty
+location object as the second parameter:
+
+```js
+var parse = require('url-parse');
+parse('hostname', {});
+```
 
 ### URL.set(key, value)
 
@@ -108,14 +136,11 @@ will automatically update.
 
 The testing of this module is done in 3 different ways:
 
-1. We have unit tests setup which run under Node.js using the normal `npm test`
-   command.
-2. Code coverage can be run manually using `npm run coverage`
-3. For browser testing we use `testling` to startup a test server. We do assume
-   that you `testling` installed globally, if not please run `npm install -g
-   testling` and after that `testling -u` in the root of this repository. When
-   you visit the outputted URL all unit tests that were written from the Node
-   can now be ran inside browsers.
+1. We have unit tests that run under Node.js. You can run these tests with the
+  `npm test` command.
+2. Code coverage can be run manually using `npm run coverage`.
+3. For browser testing we use Sauce Labs and `zuul`. You can run browser tests
+  using the `npm run test-browser` command.
 
 ## License
 
diff --git a/dist/url-parse.js b/dist/url-parse.js
new file mode 100644
index 0000000..5cca0a4
--- /dev/null
+++ b/dist/url-parse.js
@@ -0,0 +1,367 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.URLParse=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot f [...]
+'use strict';
+
+var required = require('requires-port')
+  , lolcation = require('./lolcation')
+  , qs = require('querystringify');
+
+var keys = ',,protocol,username,password,host,hostname,port,pathname,query,hash'.split(',')
+  , inherit = { protocol: 1, host: 1, hostname: 1 }
+  , parts = keys.length;
+
+//
+// Story time children:
+//
+// FireFox 34 has some problems with their Regular Expression engine and
+// executing a RegExp can cause a `too much recursion` error. We initially fixed
+// this by moving the Regular Expression in the URL constructor so it's created
+// every single time. This fixed it for some URL's but the more complex the
+// URL's get the easier it is to trigger. Complexer URL like:
+//
+//   https://www.mozilla.org/en-US/firefox/34.0/whatsnew/?oldversion=33.1
+//
+// Still triggered the recursion error. After talking with Chrome and FireFox
+// engineers it seemed to be caused by:
+//
+//   https://code.google.com/p/v8/issues/detail?id=430
+//
+// As FireFox started using Chrome's RegExp engine. After testing various of
+// workarounds I finally stumbled upon this gem, use new RegExp as it sometimes
+// behaves different then a RegExp literal.
+//
+// Steps for compiling the new RegExp:
+//
+// 1. Take the regular RegExp as seen below.
+// 2. Escape the RegExp using XRegExp.escape from http://xregexp.com/tests/
+// 3. ??
+// 4. Profit.
+//
+// RegExp source: /^(?:(?:(([^:\/#\?]+:)?(?:(?:\/\/)(?:(?:(?:([^:@\/#\?]+)(?:\:([^:@\/#\?]*))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((?:\/?(?:[^\/\?#]+\/+)*)(?:[^\?#]*)))?(\?[^#]+)?)(#.*)?/
+//
+var regexp = new RegExp('\^\(\?:\(\?:\(\(\[\^:\\/\#\\\?\]\+:\)\?\(\?:\(\?:\\/\\/\)\(\?:\(\?:\(\?:\(\[\^:@\\/\#\\\?\]\+\)\(\?:\\:\(\[\^:@\\/\#\\\?\]\*\)\)\?\)@\)\?\(\(\[\^:\\/\#\\\?\\\]\\\[\]\+\|\\\[\[\^\\/\\\]@\#\?\]\+\\\]\)\(\?:\\:\(\[0\-9\]\+\)\)\?\)\)\?\)\?\)\?\(\(\?:\\/\?\(\?:\[\^\\/\\\?\#\]\+\\/\+\)\*\)\(\?:\[\^\\\?\#\]\*\)\)\)\?\(\\\?\[\^\#\]\+\)\?\)\(\#\.\*\)\?');
+
+function parse(url) {
+  try { return regexp.exec(url); }
+  catch (e) { return url.match(regexp); }
+}
+
+/**
+ * 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 CDO.
+ *
+ * @constructor
+ * @param {String} address URL we want to parse.
+ * @param {Boolean|function} parser Parser for the query string.
+ * @param {Object} location Location defaults for relative paths.
+ * @api public
+ */
+function URL(address, location, parser) {
+  if (!(this instanceof URL)) {
+    return new URL(address, location, parser);
+  }
+
+  var type = typeof location
+    , bits = parse(address)
+    , url = this
+    , i = 0
+    , key;
+
+  //
+  // 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);
+
+  for (; i < parts; key = keys[++i]) {
+    if (!key) continue;
+
+    url[key] = bits[i] || (key in inherit ? location[key] || '' : '');
+
+    //
+    // The protocol, host, host name should always be lower cased even if they
+    // are supplied in uppercase. This way, when people generate an `origin`
+    // it be correct.
+    //
+    if (i === 2 || i === 5 || i === 6) 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);
+
+  //
+  // 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 = '';
+  }
+
+  //
+  // 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} prop Property we need to adjust.
+ * @param {Mixed} value The newly assigned value.
+ * @returns {URL}
+ * @api public
+ */
+URL.prototype.set = function set(part, value, fn) {
+  var url = this;
+
+  if ('query' === part) {
+    if ('string' === typeof value) value = (fn || qs.parse)(value);
+    url[part] = value;
+  } else if ('port' === part) {
+    url[part] = value;
+
+    if (!required(value, url.protocol)) {
+      url.host = url.hostname;
+      url[part] = '';
+    } else if (value) {
+      url.host = url.hostname +':'+ value;
+    }
+  } else if ('hostname' === part) {
+    url[part] = value;
+
+    if (url.port) value += ':'+ url.port;
+    url.host = value;
+  } else if ('host' === part) {
+    url[part] = value;
+
+    if (/\:\d+/.test(value)) {
+      value = value.split(':');
+      url.hostname = value[0];
+      url.port = value[1];
+    }
+  } else {
+    url[part] = value;
+  }
+
+  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}
+ * @api public
+ */
+URL.prototype.toString = function toString(stringify) {
+  if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;
+
+  var query
+    , url = this
+    , result = url.protocol +'//';
+
+  if (url.username) result += url.username +':'+ url.password +'@';
+
+  result += url.hostname;
+  if (url.port) result += ':'+ url.port;
+
+  result += url.pathname;
+
+  if (url.query) {
+    if ('object' === typeof url.query) query = stringify(url.query);
+    else query = url.query;
+
+    result += (query.charAt(0) === '?' ? '' : '?') + query;
+  }
+
+  if (url.hash) result += url.hash;
+
+  return result;
+};
+
+//
+// Expose the URL parser and some additional properties that might be useful for
+// others.
+//
+URL.qs = qs;
+URL.location = lolcation;
+module.exports = URL;
+
+},{"./lolcation":2,"querystringify":3,"requires-port":4}],2:[function(require,module,exports){
+(function (global){
+'use strict';
+
+/**
+ * These properties should not be copied or inherited from. This is only needed
+ * for all non blob URL's as the a blob URL does not include a hash, only the
+ * origin.
+ *
+ * @type {Object}
+ * @private
+ */
+var ignore = { hash: 1, query: 1 }
+  , URL;
+
+/**
+ * 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} loc Optional default location object.
+ * @returns {Object} lolcation object.
+ * @api public
+ */
+module.exports = function lolcation(loc) {
+  loc = loc || global.location || {};
+  URL = URL || require('./');
+
+  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];
+  }
+
+  return finaldestination;
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./":1}],3:[function(require,module,exports){
+'use strict';
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * Simple query string parser.
+ *
+ * @param {String} query The query string that needs to be parsed.
+ * @returns {Object}
+ * @api public
+ */
+function querystring(query) {
+  var parser = /([^=?&]+)=([^&]*)/g
+    , result = {}
+    , part;
+
+  //
+  // Little nifty parsing hack, leverage the fact that RegExp.exec increments
+  // the lastIndex property so we can continue executing this loop until we've
+  // parsed all results.
+  //
+  for (;
+    part = parser.exec(query);
+    result[decodeURIComponent(part[1])] = decodeURIComponent(part[2])
+  );
+
+  return result;
+}
+
+/**
+ * Transform a query string to an object.
+ *
+ * @param {Object} obj Object that should be transformed.
+ * @param {String} prefix Optional prefix.
+ * @returns {String}
+ * @api public
+ */
+function querystringify(obj, prefix) {
+  prefix = prefix || '';
+
+  var pairs = [];
+
+  //
+  // Optionally prefix with a '?' if needed
+  //
+  if ('string' !== typeof prefix) prefix = '?';
+
+  for (var key in obj) {
+    if (has.call(obj, key)) {
+      pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key]));
+    }
+  }
+
+  return prefix + pairs.join('&');
+}
+
+//
+// Expose the module.
+//
+exports.stringify = querystringify;
+exports.parse = querystring;
+
+},{}],4:[function(require,module,exports){
+'use strict';
+
+/**
+ * Check if we're required to add a port number.
+ *
+ * @see https://url.spec.whatwg.org/#default-port
+ * @param {Number|String} port Port number we need to check
+ * @param {String} protocol Protocol we need to check against.
+ * @returns {Boolean} Is it a default port for the given protocol
+ * @api private
+ */
+module.exports = function required(port, protocol) {
+  protocol = protocol.split(':')[0];
+  port = +port;
+
+  if (!port) return false;
+
+  switch (protocol) {
+    case 'http':
+    case 'ws':
+    return port !== 80;
+
+    case 'https':
+    case 'wss':
+    return port !== 443;
+
+    case 'ftp':
+    return port !== 22;
+
+    case 'gopher':
+    return port !== 70;
+
+    case 'file':
+    return false;
+  }
+
+  return port !== 0;
+};
+
+},{}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/dist/url-parse.min.js b/dist/url-parse.min.js
new file mode 100644
index 0000000..f471b12
--- /dev/null
+++ b/dist/url-parse.min.js
@@ -0,0 +1 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).URLParse=e()}}(function(){return function e(t,o,r){function n(a,i){if(!o[a]){if(!t[a]){var p="function"==typeof require&&require;if(!i&&p)return p(a,!0);if(s)return s(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MO [...]
\ No newline at end of file
diff --git a/fuzzy.js b/fuzzy.js
deleted file mode 100644
index c0abe3c..0000000
--- a/fuzzy.js
+++ /dev/null
@@ -1,124 +0,0 @@
-'use strict';
-
-var URL = require('./')
-  , url = new URL('');
-
-/**
- * A dictionary with all kind of different options that should generate a valid
- * and parse-able URL.
- *
- * @type {Object}
- * @api private
- */
-var combinations = {};
-
-combinations.protocol = [
-  'http:',
-  'https:',
-  'ws:',
-  'wss:',
-  'blob:'/*,
-  ''*/
-];
-combinations.username = ['foo', 'bar'];
-combinations.password = combinations.username;
-combinations.hostname = [
-  'example.com',
-  'www.example.com',
-  'travel.travel',
-  'sub.sub.sub.domain.nl',
-  'xn--n3h.com',
-  'localhost',
-  '127.0.0.1',
-  '255.255.255.255'/*,
-  '3ffe:6a88:85a3:08d3:1319:8a2e:0370:7344',
-  '2001:2353::1428:57ab',
-  '2001:2353:0::0:1428:57ab',
-  '2001:2353:0:0:0:0:1428:57ab',
-  '2001:2353:0000:0000:0000::1428:57ab',
-  '2001:2353:0000:0000:0000:0000:1428:57ab',
-  '2001:2353:02de::0e13',
-  '2001:2353:2de::e13'*/
-];
-combinations.port = ['8080', '844', '3340'];
-combinations.pathname = [
-  '/',
-  '/bar',
-  '/bar/',
-  '/foo/bar',
-  '/foo.bar/foo',
-  '/fav.ico',
-  '/@3rd-Eden',
-  '/a/b/c/d/e/f/g/j/1/d/4/'
-];
-combinations.query = ['foo=bar',
-  'foo[]=bar&foo[]=foo',
-  'email=foo at bar.travel',
-  'q='
-];
-combinations.hash = [
-  'name',
-  'moo-with-longer-name',
-  '/what/about/slashes?querystring',
-  '?querystring',
-  '!/google/urls',
-  'use:foo@',
-  'http://'
-];
-
-/**
- * Get a random item from the given array.
- *
- * @param {String} name Name of the array we want to have a random item returned.
- * @returns {Mixed}
- * @api private
- */
-function get(name) {
-  var data = combinations[name];
-
-  return data[Math.floor(Math.random() * data.length)];
-}
-
-/**
- * Return a random boolean.
- *
- * @returns {Boolean}
- * @api private
- */
-function yep() {
-  return !!Math.round(Math.random() * 1);
-}
-
-/**
- * Generate the actual URL.
- *
- * @returns {Object} specification
- * @api public
- */
-module.exports = function generate() {
-  var spec = {}
-    , key;
-
-  spec.protocol = get('protocol');
-  spec.hostname = get('hostname');
-  spec.pathname = get('pathname');
-
-  if (yep()) spec.port = get('port');
-  if (yep()) spec.query = '?'+ get('query');
-  if (yep()) spec.hash = '#'+ get('hash');
-  if (yep()) {
-    spec.username = get('username');
-    spec.password = get('password');
-  }
-
-  for (key in combinations) {
-    url[key] = '';
-  }
-
-  for (key in spec) {
-    url[key] = spec[key];
-  }
-
-  spec.href = url.toString();
-  return spec;
-};
diff --git a/index.js b/index.js
index b7c822d..cb0f6e7 100644
--- a/index.js
+++ b/index.js
@@ -1,12 +1,12 @@
 'use strict';
 
 var required = require('requires-port')
-  , lolcation = require('./lolcation')
   , qs = require('querystringify')
-  , relativere = /^\/(?!\/)/;
+  , protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i
+  , slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//;
 
 /**
- * These are the parse instructions for the URL parsers, it informs the parser
+ * 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
@@ -17,26 +17,132 @@ var required = require('requires-port')
  * 3. Inherit from location if non existing in the parser.
  * 4. `toLowerCase` the resulting value.
  */
-var instructions = [
+var rules = [
   ['#', 'hash'],                        // Extract from the back.
   ['?', 'query'],                       // Extract from the back.
-  ['//', 'protocol', 2, 1, 1],          // Extract from the front.
   ['/', 'pathname'],                    // Extract from the back.
   ['@', 'auth', 1],                     // Extract from the front.
   [NaN, 'host', undefined, 1, 1],       // Set left over value.
-  [/\:(\d+)$/, 'port'],                 // RegExp the back.
+  [/:(\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.
+ * @api public
+ */
+function lolcation(loc) {
+  loc = loc || global.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;
+}
+
+/**
+ * @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.
+ * @return {ProtocolExtract} Extracted information.
+ * @api private
+ */
+function extractProtocol(address) {
+  var match = protocolre.exec(address);
+
+  return {
+    protocol: match[1] ? match[1].toLowerCase() : '',
+    slashes: !!match[2],
+    rest: match[3]
+  };
+}
+
+/**
+ * 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.
+ * @api private
+ */
+function resolve(relative, 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 CDO.
+ * faster and it pleases my OCD.
  *
  * @constructor
  * @param {String} address URL we want to parse.
- * @param {Boolean|function} parser Parser for the query string.
- * @param {Object} location Location defaults for relative paths.
+ * @param {Object|String} location Location defaults for relative paths.
+ * @param {Boolean|Function} parser Parser for the query string.
  * @api public
  */
 function URL(address, location, parser) {
@@ -44,8 +150,8 @@ function URL(address, location, parser) {
     return new URL(address, location, parser);
   }
 
-  var relative = relativere.test(address)
-    , parse, instruction, index, key
+  var relative, extracted, parse, instruction, index, key
+    , instructions = rules.slice()
     , type = typeof location
     , url = this
     , i = 0;
@@ -66,12 +172,25 @@ function URL(address, location, parser) {
     location = null;
   }
 
-  if (parser && 'function' !== typeof parser) {
-    parser = qs.parse;
-  }
+  if (parser && 'function' !== typeof parser) parser = qs.parse;
 
   location = lolcation(location);
 
+  //
+  // Extract protocol information before running the instructions.
+  //
+  extracted = extractProtocol(address || '');
+  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.slashes) instructions[2] = [/(.*)/, 'pathname'];
+
   for (; i < instructions.length; i++) {
     instruction = instructions[i];
     parse = instruction[0];
@@ -89,20 +208,20 @@ function URL(address, location, parser) {
           address = address.slice(0, index);
         }
       }
-    } else if (index = parse.exec(address)) {
+    } else if ((index = parse.exec(address))) {
       url[key] = index[1];
-      address = address.slice(0, address.length - index[0].length);
+      address = address.slice(0, index.index);
     }
 
-    url[key] = url[key] || (instruction[3] || ('port' === key && relative) ? location[key] || '' : '');
+    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();
-    }
+    if (instruction[4]) url[key] = url[key].toLowerCase();
   }
 
   //
@@ -113,6 +232,18 @@ function URL(address, location, 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);
+  }
+
+  //
   // 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.
@@ -132,6 +263,10 @@ function URL(address, location, parser) {
     url.password = instruction[1] || '';
   }
 
+  url.origin = url.protocol && url.host && url.protocol !== 'file:'
+    ? url.protocol +'//'+ url.host
+    : 'null';
+
   //
   // The href is just the compiled result.
   //
@@ -142,49 +277,93 @@ function URL(address, location, parser) {
  * This is convenience method for changing properties in the URL instance to
  * insure that they all propagate correctly.
  *
- * @param {String} prop Property we need to adjust.
- * @param {Mixed} value The newly assigned value.
+ * @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}
  * @api public
  */
-URL.prototype.set = function set(part, value, fn) {
+function set(part, value, fn) {
   var url = this;
 
-  if ('query' === part) {
-    if ('string' === typeof value && value.length) {
-      value = (fn || qs.parse)(value);
-    }
+  switch (part) {
+    case 'query':
+      if ('string' === typeof value && value.length) {
+        value = (fn || qs.parse)(value);
+      }
 
-    url[part] = value;
-  } else if ('port' === part) {
-    url[part] = value;
+      url[part] = value;
+      break;
 
-    if (!required(value, url.protocol)) {
-      url.host = url.hostname;
-      url[part] = '';
-    } else if (value) {
-      url.host = url.hostname +':'+ value;
-    }
-  } else if ('hostname' === part) {
-    url[part] = value;
-
-    if (url.port) value += ':'+ url.port;
-    url.host = value;
-  } else if ('host' === part) {
-    url[part] = value;
-
-    if (/\:\d+/.test(value)) {
-      value = value.split(':');
-      url.hostname = value[0];
-      url.port = value[1];
-    }
-  } else {
-    url[part] = value;
+    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 && url.host && url.protocol !== 'file:'
+    ? url.protocol +'//'+ url.host
+    : 'null';
+
   url.href = url.toString();
+
   return url;
-};
+}
 
 /**
  * Transform the properties back in to a valid and full URL string.
@@ -193,12 +372,16 @@ URL.prototype.set = function set(part, value, fn) {
  * @returns {String}
  * @api public
  */
-URL.prototype.toString = function toString(stringify) {
+function toString(stringify) {
   if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;
 
   var query
     , url = this
-    , result = url.protocol +'//';
+    , protocol = url.protocol;
+
+  if (protocol && protocol.charAt(protocol.length - 1) !== ':') protocol += ':';
+
+  var result = protocol + (url.slashes ? '//' : '');
 
   if (url.username) {
     result += url.username;
@@ -206,10 +389,7 @@ URL.prototype.toString = function toString(stringify) {
     result += '@';
   }
 
-  result += url.hostname;
-  if (url.port) result += ':'+ url.port;
-
-  result += url.pathname;
+  result += url.host + url.pathname;
 
   query = 'object' === typeof url.query ? stringify(url.query) : url.query;
   if (query) result += '?' !== query.charAt(0) ? '?'+ query : query;
@@ -217,12 +397,16 @@ URL.prototype.toString = function toString(stringify) {
   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.
+// others or testing.
 //
-URL.qs = qs;
+URL.extractProtocol = extractProtocol;
 URL.location = lolcation;
+URL.qs = qs;
+
 module.exports = URL;
diff --git a/lolcation.js b/lolcation.js
deleted file mode 100644
index d251856..0000000
--- a/lolcation.js
+++ /dev/null
@@ -1,45 +0,0 @@
-'use strict';
-
-/**
- * These properties should not be copied or inherited from. This is only needed
- * for all non blob URL's as the a blob URL does not include a hash, only the
- * origin.
- *
- * @type {Object}
- * @private
- */
-var ignore = { hash: 1, query: 1 }
-  , URL;
-
-/**
- * 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} loc Optional default location object.
- * @returns {Object} lolcation object.
- * @api public
- */
-module.exports = function lolcation(loc) {
-  loc = loc || global.location || {};
-  URL = URL || require('./');
-
-  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];
-  }
-
-  return finaldestination;
-};
diff --git a/package.json b/package.json
index 718b31e..0f2dd24 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,19 @@
 {
   "name": "url-parse",
-  "version": "1.0.5",
+  "version": "1.2.0",
   "description": "Small footprint URL parser that works seamlessly across Node.js and browser environments",
   "main": "index.js",
   "scripts": {
-    "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100",
-    "browserify": "mkdir -p dist && browserify index.js -s URLParse -o dist/url-parse.js",
-    "test-node": "istanbul cover _mocha --report lcovonly -- test.js",
-    "coverage": "istanbul cover _mocha -- test.js",
-    "test-browser": "zuul -- test.js",
-    "watch": "mocha --watch test.js",
-    "test": "mocha test.js"
+    "browserify": "mkdir -p dist && browserify index.js -s URLParse | uglifyjs -cm -o dist/url-parse.min.js",
+    "test": "nyc --reporter=html --reporter=text mocha test/test.js",
+    "test-browser": "node test/browser.js",
+    "prepublishOnly": "npm run browserify",
+    "watch": "mocha --watch test/test.js"
   },
+  "files": [
+    "index.js",
+    "dist"
+  ],
   "repository": {
     "type": "git",
     "url": "https://github.com/unshiftio/url-parse.git"
@@ -30,15 +32,17 @@
   "author": "Arnout Kazemier",
   "license": "MIT",
   "dependencies": {
-    "querystringify": "0.0.x",
-    "requires-port": "1.0.x"
+    "querystringify": "~1.0.0",
+    "requires-port": "~1.0.0"
   },
   "devDependencies": {
-    "assume": "1.3.x",
-    "browserify": "12.0.x",
-    "istanbul": "0.4.x",
-    "mocha": "2.3.x",
-    "pre-commit": "1.1.x",
-    "zuul": "3.7.x"
+    "assume": "~1.5.0",
+    "browserify": "~14.5.0",
+    "mocha": "~4.0.0",
+    "nyc": "~11.3.0",
+    "pre-commit": "~1.2.0",
+    "sauce-browsers": "~1.0.0",
+    "sauce-test": "~1.3.3",
+    "uglify-js": "~3.1.0"
   }
 }
diff --git a/test.js b/test.js
deleted file mode 100644
index 9770025..0000000
--- a/test.js
+++ /dev/null
@@ -1,408 +0,0 @@
-describe('url-parse', function () {
-  'use strict';
-
-  var assume = require('assume')
-    , parse = require('./');
-
-  it('exposes parse as a function', function () {
-    assume(parse).is.a('function');
-  });
-
-  it('exposes the querystring module', function () {
-    assume(parse.qs).equals(require('querystringify'));
-  });
-
-  it('exposes the location function', function () {
-    assume(parse.location).equals(require('./lolcation'));
-  });
-
-  it('parses the query string into an object', function () {
-    var url = 'http://google.com/?foo=bar'
-      , data = parse(url, true);
-
-    assume(data.query).is.a('object');
-    assume(data.query.foo).equals('bar');
-
-    url = 'http://google.com/';
-    data = parse(url, true);
-
-    assume(data.query).is.a('object');
-    assume(data.query).is.empty();
-  });
-
-  it('does not add question mark to href if query string is empty', function () {
-    var url = 'http://google.com/'
-      , data = parse(url, true);
-
-    assume(data.href).equals(url);
-  });
-
-  it('allows a custom function as parser', function () {
-    var url = 'http://google.com/?foo=bar'
-      , data = parse(url, function () { return '1337'; });
-
-    assume(data.query).equals('1337');
-  });
-
-  it('allows a custom stringify function', function () {
-    var url = 'http://google.com/?foo=bar'
-      , data = parse(url, true)
-      , str;
-
-    str = data.toString(function () { return 'lolcakes'; });
-    assume(str).equals('http://google.com/?lolcakes');
-  });
-
-  it('allows a custom location object', function () {
-    var url = '/foo?foo=bar'
-      , data = parse(url, parse('http://google.com'));
-
-    assume(data.href).equals('http://google.com/foo?foo=bar');
-  });
-
-  it('is blob: location aware', function () {
-    var blob = {
-      'href': 'blob:https%3A//gist.github.com/3f272586-6dac-4e29-92d0-f674f2dde618',
-      'pathname': 'https%3A//gist.github.com/3f272586-6dac-4e29-92d0-f674f2dde618',
-      'origin': 'https://gist.github.com',
-      'protocol': 'blob:',
-      'hostname': '',
-      'search': '',
-      'hash': '',
-      'host': '',
-      'port': ''
-    };
-
-    var url = '/unshiftio/url-parse'
-      , data = parse(url, blob);
-
-    assume(data.href).equals('https://gist.github.com/unshiftio/url-parse');
-  });
-
-  it('converts protocol to lowercase', function () {
-    var url = 'HTTP://example.com';
-
-    assume(parse(url).protocol).equals('http:');
-  });
-
-  it('can parse complex urls multiple times without errors', function () {
-    var url = 'https://www.mozilla.org/en-US/firefox/34.0/whatsnew/?oldversion=33.1';
-
-    for (var i = 0; i < 100; i++) {
-      parse(url);
-    }
-  });
-
-  it('converts hostname to lowercase', function () {
-    var url = 'HTTP://fOo.eXaMPle.com';
-
-    assume(parse(url).hostname).equals('foo.example.com');
-  });
-
-  it('does not lowercase the path', function () {
-    var url = 'HTTP://X.COM/Y/Z';
-
-    assume(parse(url).pathname).equals('/Y/Z');
-  });
-
-  it('removes default port numbers', function () {
-    var url = 'http://example.com:80'
-      , parsed = parse(url);
-
-    assume(parsed.port).equals('');
-    assume(parsed.host).equals('example.com');
-    assume(parsed.hostname).equals('example.com');
-    assume(parsed.href).equals('http://example.com');
-  });
-
-  it('understands an / as pathname', function () {
-    var url = 'http://example.com:80/'
-      , parsed = parse(url);
-
-    assume(parsed.port).equals('');
-    assume(parsed.username).equals('');
-    assume(parsed.password).equals('');
-    assume(parsed.pathname).equals('/');
-    assume(parsed.host).equals('example.com');
-    assume(parsed.hostname).equals('example.com');
-    assume(parsed.href).equals('http://example.com/');
-  });
-
-  it('does not care about spaces', function () {
-    var url = 'http://x.com/path?that\'s#all, folks'
-      , parsed = parse(url);
-
-    assume(parsed.port).equals('');
-    assume(parsed.username).equals('');
-    assume(parsed.password).equals('');
-    assume(parsed.pathname).equals('/path');
-    assume(parsed.hash).equal('#all, folks');
-    assume(parsed.query).equal('?that\'s');
-    assume(parsed.host).equals('x.com');
-    assume(parsed.hostname).equals('x.com');
-  });
-
-  it('accepts + in the url', function () {
-    var url = 'http://x.y.com+a/b/c'
-      , parsed = parse(url);
-
-    assume(parsed.protocol).equals('http:');
-    assume(parsed.host).equals('x.y.com+a');
-    assume(parsed.hostname).equals('x.y.com+a');
-    assume(parsed.pathname).equals('/b/c');
-  });
-
-  describe('ip', function () {
-    // coap://
-    //
-    it('parses ipv6', function () {
-      var url = 'http://[1080:0:0:0:8:800:200C:417A]:61616/foo/bar?q=z'
-        , parsed = parse(url);
-
-      assume(parsed.port).equals('61616');
-      assume(parsed.query).equals('?q=z');
-      assume(parsed.protocol).equals('http:');
-      assume(parsed.hostname).equals('[1080:0:0:0:8:800:200c:417a]');
-      assume(parsed.pathname).equals('/foo/bar');
-      assume(parsed.href).equals('http://[1080:0:0:0:8:800:200c:417a]:61616/foo/bar?q=z');
-    });
-
-    it('parses ipv6 with auth', function () {
-      var url = 'http://user:password@[3ffe:2a00:100:7031::1]:8080'
-        , parsed = parse(url);
-
-      assume(parsed.username).equals('user');
-      assume(parsed.password).equals('password');
-      assume(parsed.host).equals('[3ffe:2a00:100:7031::1]:8080');
-      assume(parsed.hostname).equals('[3ffe:2a00:100:7031::1]');
-      assume(parsed.href).equals(url);
-    });
-
-    it('parses ipv4', function () {
-      var url = 'http://222.148.142.13:61616/foo/bar?q=z'
-        , parsed = parse(url);
-
-      assume(parsed.port).equals('61616');
-      assume(parsed.query).equals('?q=z');
-      assume(parsed.protocol).equals('http:');
-      assume(parsed.hostname).equals('222.148.142.13');
-      assume(parsed.pathname).equals('/foo/bar');
-      assume(parsed.href).equals(url);
-    });
-  });
-
-  describe('auth', function () {
-    it('does not lowercase the USER:PASS', function () {
-      var url = 'HTTP://USER:PASS@EXAMPLE.COM'
-        , parsed = parse(url);
-
-      assume(parsed.username).equals('USER');
-      assume(parsed.password).equals('PASS');
-      assume(parsed.protocol).equals('http:');
-      assume(parsed.host).equals('example.com');
-      assume(parsed.hostname).equals('example.com');
-    });
-
-    it('accepts @ in pathnames', function () {
-      var url = 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s='
-        , parsed = parse(url);
-
-      assume(parsed.pathname).equals('/vt/lyrs=m at 114&hl=en&src=api&x=2&y=2&z=3&s=');
-      assume(parsed.username).equals('');
-      assume(parsed.password).equals('');
-    });
-
-    it('does not require passwords for auth', function () {
-      var url = 'http://user@www.example.com/'
-        , parsed = parse(url);
-
-      assume(parsed.password).equals('');
-      assume(parsed.pathname).equals('/');
-      assume(parsed.username).equals('user');
-      assume(parsed.protocol).equals('http:');
-      assume(parsed.hostname).equals('www.example.com');
-      assume(parsed.href).equals(url);
-    });
-  });
-
-  it('accepts multiple ???', function () {
-    var url = 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=';
-    assume(parse(url).query).equals('???&hl=en&src=api&x=2&y=2&z=3&s=');
-  });
-
-  it('accepts a string as source argument', function () {
-    var data = parse('/foo', 'http://sub.example.com/bar?foo=bar#hash');
-
-    assume(data.port).equals('');
-    assume(data.host).equals('sub.example.com');
-    assume(data.href).equals('http://sub.example.com/foo');
-  });
-
-  describe('inheritance', function () {
-    it('does not inherit port numbers for non relative urls', function () {
-      var data = parse('http://localhost', parse('http://sub.example.com:808/'));
-
-      assume(data.port).equals('');
-      assume(data.host).equals('localhost');
-      assume(data.href).equals('http://localhost');
-    });
-
-    it('does inherit port numbers from relative urls', function () {
-      var data = parse('/foo', parse('http://sub.example.com:808/'));
-
-      assume(data.port).equals('808');
-      assume(data.hostname).equals('sub.example.com');
-      assume(data.host).equals('sub.example.com:808');
-      assume(data.href).equals('http://sub.example.com:808/foo');
-    });
-
-    it('inherits protocol for relative protocols', function () {
-      var data = parse('//foo.com/foo', parse('http://sub.example.com:808/'));
-
-      assume(data.port).equals('');
-      assume(data.host).equals('foo.com');
-      assume(data.protocol).equals('http:');
-      assume(data.href).equals('http://foo.com/foo');
-    });
-
-    it('does not inherit pathnames from the source', function () {
-      var data = parse('http://localhost', parse('http://foo:bar@sub.example.com/bar?foo=bar#hash'));
-
-      assume(data.port).equals('');
-      assume(data.host).equals('localhost');
-      assume(data.href).equals('http://localhost');
-    });
-
-    it('does not inherit hashes and query strings from source object', function () {
-      var data = parse('/foo', parse('http://sub.example.com/bar?foo=bar#hash'));
-
-      assume(data.port).equals('');
-      assume(data.host).equals('sub.example.com');
-      assume(data.href).equals('http://sub.example.com/foo');
-    });
-
-    it('does not inherit auth from source object', function () {
-      var from = parse('http://foo:bar@sub.example.com')
-        , data = parse('/foo', from);
-
-      assume(data.port).equals('');
-      assume(data.username).equals('');
-      assume(data.password).equals('');
-      assume(data.host).equals('sub.example.com');
-      assume(data.href).equals('http://sub.example.com/foo');
-    });
-  });
-
-  describe('#set', function () {
-    it('correctly updates the host when setting port', function () {
-      var data = parse('http://google.com/foo');
-
-      assume(data.set('port', 8080)).equals(data);
-
-      assume(data.host).equals('google.com:8080');
-      assume(data.href).equals('http://google.com:8080/foo');
-    });
-
-    it('removes querystring and hash', function () {
-      var data = parse('https://thisanurl.com/?swag=yolo#representing');
-
-      data.set('query', '');
-      data.set('hash', '');
-
-      assume(data.href).equals('https://thisanurl.com/');
-    });
-
-    it('only sets port when its not default', function () {
-      var data = parse('http://google.com/foo');
-
-      assume(data.set('port', 80)).equals(data);
-
-      assume(data.host).equals('google.com');
-      assume(data.href).equals('http://google.com/foo');
-
-      assume(data.set('port', 443)).equals(data);
-      assume(data.host).equals('google.com:443');
-      assume(data.href).equals('http://google.com:443/foo');
-    });
-
-    it('updates query with object', function () {
-      var data = parse('http://google.com/?foo=bar');
-
-      assume(data.set('query', { bar: 'foo' })).equals(data);
-
-      assume(data.query.foo).equals(undefined);
-      assume(data.query.bar).equals('foo');
-
-      assume(data.href).equals('http://google.com/?bar=foo');
-    });
-
-    it('updates query with a string', function () {
-      var data = parse('http://google.com/?foo=bar');
-
-      assume(data.set('query', 'bar=foo')).equals(data);
-
-      assume(data.query.foo).equals(undefined);
-      assume(data.query.bar).equals('foo');
-
-      assume(data.href).equals('http://google.com/?bar=foo');
-
-      assume(data.set('query', '?baz=foo')).equals(data);
-
-      assume(data.query.bar).equals(undefined);
-      assume(data.query.baz).equals('foo');
-
-      assume(data.href).equals('http://google.com/?baz=foo');
-    });
-
-    it('updates the port when updating host', function () {
-      var data = parse('http://google.com/?foo=bar');
-
-      assume(data.set('host', 'yahoo.com:808')).equals(data);
-
-      assume(data.hostname).equals('yahoo.com');
-      assume(data.host).equals('yahoo.com:808');
-      assume(data.port).equals('808');
-
-      assume(data.href).equals('http://yahoo.com:808/?foo=bar');
-    });
-
-    it('updates the host when updating hostname', function () {
-      var data = parse('http://google.com:808/?foo=bar');
-
-      assume(data.set('hostname', 'yahoo.com')).equals(data);
-
-      assume(data.hostname).equals('yahoo.com');
-      assume(data.host).equals('yahoo.com:808');
-      assume(data.port).equals('808');
-
-      assume(data.href).equals('http://yahoo.com:808/?foo=bar');
-    });
-
-    it('updates other values', function () {
-      var data = parse('http://google.com/?foo=bar');
-
-      assume(data.set('protocol', 'https:')).equals(data);
-      assume(data.protocol).equals('https:');
-
-      assume(data.href).equals('https://google.com/?foo=bar');
-    });
-  });
-
-  describe('fuzzy', function () {
-    var fuzz = require('./fuzzy')
-      , times = 10;
-
-    for (var i = 0; i < times; i++) {
-      (function (spec) {
-        it('parses: '+ spec.href, function () {
-          var url = parse(spec.href)
-            , prop;
-
-          for (prop in spec) {
-            assume(url[prop]).equals(spec[prop]);
-          }
-        });
-      })(fuzz());
-    }
-  });
-});

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-url-parse.git



More information about the Pkg-javascript-commits mailing list