aboutsummaryrefslogtreecommitdiff
path: root/node_modules/url
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2017-05-03 15:35:00 +0200
committerFlorian Dold <florian.dold@gmail.com>2017-05-03 15:35:00 +0200
commitde98e0b232509d5f40c135d540a70e415272ff85 (patch)
treea79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/url
parente0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff)
downloadwallet-core-de98e0b232509d5f40c135d540a70e415272ff85.tar.xz
node_modules
Diffstat (limited to 'node_modules/url')
-rw-r--r--node_modules/url/.npmignore1
-rw-r--r--node_modules/url/.travis.yml7
-rw-r--r--node_modules/url/.zuul.yml16
-rw-r--r--node_modules/url/LICENSE21
-rw-r--r--node_modules/url/README.md108
-rw-r--r--node_modules/url/node_modules/punycode/LICENSE-MIT.txt20
-rw-r--r--node_modules/url/node_modules/punycode/README.md176
-rw-r--r--node_modules/url/node_modules/punycode/package.json53
-rw-r--r--node_modules/url/node_modules/punycode/punycode.js530
-rw-r--r--node_modules/url/package.json24
-rw-r--r--node_modules/url/test.js1599
-rw-r--r--node_modules/url/url.js732
-rw-r--r--node_modules/url/util.js16
13 files changed, 3303 insertions, 0 deletions
diff --git a/node_modules/url/.npmignore b/node_modules/url/.npmignore
new file mode 100644
index 000000000..ba114713b
--- /dev/null
+++ b/node_modules/url/.npmignore
@@ -0,0 +1 @@
+test-url.js
diff --git a/node_modules/url/.travis.yml b/node_modules/url/.travis.yml
new file mode 100644
index 000000000..16ed30155
--- /dev/null
+++ b/node_modules/url/.travis.yml
@@ -0,0 +1,7 @@
+language: node_js
+node_js:
+ - "0.10"
+env:
+ global:
+ - secure: OgPRLCzHFh5WbjHEKlghHFW1oOreSF2JVUr3CMaFDi03ngTS2WONSw8mRn8SA6FTldiGGBx1n8orDzUw6cdkB7+tkU3G5B0M0V3vl823NaUFKgxsCM3UGDYfJb3yfAG5cj72rVZoX/ABd1fVuG4vBIlDLxsSlKQFMzUCFoyttr8=
+ - secure: AiZP8GHbyx83ZBhOvOxxtpNcgNHoP+vo5G1a1OYU78EHCgHg8NRyHKyCdrBnPvw6mV2BI/8frZaXAEicsHMtHMofBYn7nibNlaajBPI8AkHtYfNSc+zO+71Kwv7VOTOKKnkMEIkqhHlc6njFoH3QaBNHsgNlzzplPxaIt8vdUVk=
diff --git a/node_modules/url/.zuul.yml b/node_modules/url/.zuul.yml
new file mode 100644
index 000000000..feea8b60d
--- /dev/null
+++ b/node_modules/url/.zuul.yml
@@ -0,0 +1,16 @@
+ui: mocha-tdd
+browsers:
+ - name: chrome
+ version: latest
+ - name: firefox
+ version: 24..latest
+ - name: safari
+ version: latest
+ - name: ie
+ version: 9..latest
+ - name: iphone
+ version: oldest..latest
+ - name: ipad
+ version: oldest..latest
+ - name: android
+ version: oldest..latest
diff --git a/node_modules/url/LICENSE b/node_modules/url/LICENSE
new file mode 100644
index 000000000..f45bc1186
--- /dev/null
+++ b/node_modules/url/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright Joyent, Inc. and other Node contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/url/README.md b/node_modules/url/README.md
new file mode 100644
index 000000000..8b3546028
--- /dev/null
+++ b/node_modules/url/README.md
@@ -0,0 +1,108 @@
+# node-url
+
+[![Build Status](https://travis-ci.org/defunctzombie/node-url.svg?branch=master)](https://travis-ci.org/defunctzombie/node-url)
+
+This module has utilities for URL resolution and parsing meant to have feature parity with node.js core [url](http://nodejs.org/api/url.html) module.
+
+```js
+var url = require('url');
+```
+
+## api
+
+Parsed URL objects have some or all of the following fields, depending on
+whether or not they exist in the URL string. Any parts that are not in the URL
+string will not be in the parsed object. Examples are shown for the URL
+
+`'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'`
+
+* `href`: The full URL that was originally parsed. Both the protocol and host are lowercased.
+
+ Example: `'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'`
+
+* `protocol`: The request protocol, lowercased.
+
+ Example: `'http:'`
+
+* `host`: The full lowercased host portion of the URL, including port
+ information.
+
+ Example: `'host.com:8080'`
+
+* `auth`: The authentication information portion of a URL.
+
+ Example: `'user:pass'`
+
+* `hostname`: Just the lowercased hostname portion of the host.
+
+ Example: `'host.com'`
+
+* `port`: The port number portion of the host.
+
+ Example: `'8080'`
+
+* `pathname`: The path section of the URL, that comes after the host and
+ before the query, including the initial slash if present.
+
+ Example: `'/p/a/t/h'`
+
+* `search`: The 'query string' portion of the URL, including the leading
+ question mark.
+
+ Example: `'?query=string'`
+
+* `path`: Concatenation of `pathname` and `search`.
+
+ Example: `'/p/a/t/h?query=string'`
+
+* `query`: Either the 'params' portion of the query string, or a
+ querystring-parsed object.
+
+ Example: `'query=string'` or `{'query':'string'}`
+
+* `hash`: The 'fragment' portion of the URL including the pound-sign.
+
+ Example: `'#hash'`
+
+The following methods are provided by the URL module:
+
+### url.parse(urlStr, [parseQueryString], [slashesDenoteHost])
+
+Take a URL string, and return an object.
+
+Pass `true` as the second argument to also parse
+the query string using the `querystring` module.
+Defaults to `false`.
+
+Pass `true` as the third argument to treat `//foo/bar` as
+`{ host: 'foo', pathname: '/bar' }` rather than
+`{ pathname: '//foo/bar' }`. Defaults to `false`.
+
+### url.format(urlObj)
+
+Take a parsed URL object, and return a formatted URL string.
+
+* `href` will be ignored.
+* `protocol` is treated the same with or without the trailing `:` (colon).
+ * The protocols `http`, `https`, `ftp`, `gopher`, `file` will be
+ postfixed with `://` (colon-slash-slash).
+ * All other protocols `mailto`, `xmpp`, `aim`, `sftp`, `foo`, etc will
+ be postfixed with `:` (colon)
+* `auth` will be used if present.
+* `hostname` will only be used if `host` is absent.
+* `port` will only be used if `host` is absent.
+* `host` will be used in place of `hostname` and `port`
+* `pathname` is treated the same with or without the leading `/` (slash)
+* `search` will be used in place of `query`
+* `query` (object; see `querystring`) will only be used if `search` is absent.
+* `search` is treated the same with or without the leading `?` (question mark)
+* `hash` is treated the same with or without the leading `#` (pound sign, anchor)
+
+### url.resolve(from, to)
+
+Take a base URL, and a href URL, and resolve them as a browser would for
+an anchor tag. Examples:
+
+ url.resolve('/one/two/three', 'four') // '/one/two/four'
+ url.resolve('http://example.com/', '/one') // 'http://example.com/one'
+ url.resolve('http://example.com/one', '/two') // 'http://example.com/two'
diff --git a/node_modules/url/node_modules/punycode/LICENSE-MIT.txt b/node_modules/url/node_modules/punycode/LICENSE-MIT.txt
new file mode 100644
index 000000000..a41e0a7ef
--- /dev/null
+++ b/node_modules/url/node_modules/punycode/LICENSE-MIT.txt
@@ -0,0 +1,20 @@
+Copyright Mathias Bynens <https://mathiasbynens.be/>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/url/node_modules/punycode/README.md b/node_modules/url/node_modules/punycode/README.md
new file mode 100644
index 000000000..831e6379b
--- /dev/null
+++ b/node_modules/url/node_modules/punycode/README.md
@@ -0,0 +1,176 @@
+# Punycode.js [![Build status](https://travis-ci.org/bestiejs/punycode.js.svg?branch=master)](https://travis-ci.org/bestiejs/punycode.js) [![Code coverage status](http://img.shields.io/coveralls/bestiejs/punycode.js/master.svg)](https://coveralls.io/r/bestiejs/punycode.js) [![Dependency status](https://gemnasium.com/bestiejs/punycode.js.svg)](https://gemnasium.com/bestiejs/punycode.js)
+
+A robust Punycode converter that fully complies to [RFC 3492](http://tools.ietf.org/html/rfc3492) and [RFC 5891](http://tools.ietf.org/html/rfc5891), and works on nearly all JavaScript platforms.
+
+This JavaScript library is the result of comparing, optimizing and documenting different open-source implementations of the Punycode algorithm:
+
+* [The C example code from RFC 3492](http://tools.ietf.org/html/rfc3492#appendix-C)
+* [`punycode.c` by _Markus W. Scherer_ (IBM)](http://opensource.apple.com/source/ICU/ICU-400.42/icuSources/common/punycode.c)
+* [`punycode.c` by _Ben Noordhuis_](https://github.com/bnoordhuis/punycode/blob/master/punycode.c)
+* [JavaScript implementation by _some_](http://stackoverflow.com/questions/183485/can-anyone-recommend-a-good-free-javascript-for-punycode-to-unicode-conversion/301287#301287)
+* [`punycode.js` by _Ben Noordhuis_](https://github.com/joyent/node/blob/426298c8c1c0d5b5224ac3658c41e7c2a3fe9377/lib/punycode.js) (note: [not fully compliant](https://github.com/joyent/node/issues/2072))
+
+This project is [bundled](https://github.com/joyent/node/blob/master/lib/punycode.js) with [Node.js v0.6.2+](https://github.com/joyent/node/compare/975f1930b1...61e796decc).
+
+## Installation
+
+Via [npm](http://npmjs.org/) (only required for Node.js releases older than v0.6.2):
+
+```bash
+npm install punycode
+```
+
+Via [Bower](http://bower.io/):
+
+```bash
+bower install punycode
+```
+
+Via [Component](https://github.com/component/component):
+
+```bash
+component install bestiejs/punycode.js
+```
+
+In a browser:
+
+```html
+<script src="punycode.js"></script>
+```
+
+In [Narwhal](http://narwhaljs.org/), [Node.js](http://nodejs.org/), and [RingoJS](http://ringojs.org/):
+
+```js
+var punycode = require('punycode');
+```
+
+In [Rhino](http://www.mozilla.org/rhino/):
+
+```js
+load('punycode.js');
+```
+
+Using an AMD loader like [RequireJS](http://requirejs.org/):
+
+```js
+require(
+ {
+ 'paths': {
+ 'punycode': 'path/to/punycode'
+ }
+ },
+ ['punycode'],
+ function(punycode) {
+ console.log(punycode);
+ }
+);
+```
+
+## API
+
+### `punycode.decode(string)`
+
+Converts a Punycode string of ASCII symbols to a string of Unicode symbols.
+
+```js
+// decode domain name parts
+punycode.decode('maana-pta'); // 'mañana'
+punycode.decode('--dqo34k'); // '☃-⌘'
+```
+
+### `punycode.encode(string)`
+
+Converts a string of Unicode symbols to a Punycode string of ASCII symbols.
+
+```js
+// encode domain name parts
+punycode.encode('mañana'); // 'maana-pta'
+punycode.encode('☃-⌘'); // '--dqo34k'
+```
+
+### `punycode.toUnicode(input)`
+
+Converts a Punycode string representing a domain name or an email address to Unicode. Only the Punycoded parts of the input will be converted, i.e. it doesn’t matter if you call it on a string that has already been converted to Unicode.
+
+```js
+// decode domain names
+punycode.toUnicode('xn--maana-pta.com');
+// → 'mañana.com'
+punycode.toUnicode('xn----dqo34k.com');
+// → '☃-⌘.com'
+
+// decode email addresses
+punycode.toUnicode('джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq');
+// → 'джумла@джpумлатест.bрфa'
+```
+
+### `punycode.toASCII(input)`
+
+Converts a Unicode string representing a domain name or an email address to Punycode. Only the non-ASCII parts of the input will be converted, i.e. it doesn’t matter if you call it with a domain that's already in ASCII.
+
+```js
+// encode domain names
+punycode.toASCII('mañana.com');
+// → 'xn--maana-pta.com'
+punycode.toASCII('☃-⌘.com');
+// → 'xn----dqo34k.com'
+
+// encode email addresses
+punycode.toASCII('джумла@джpумлатест.bрфa');
+// → 'джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq'
+```
+
+### `punycode.ucs2`
+
+#### `punycode.ucs2.decode(string)`
+
+Creates an array containing the numeric code point values of each Unicode symbol in the string. While [JavaScript uses UCS-2 internally](https://mathiasbynens.be/notes/javascript-encoding), this function will convert a pair of surrogate halves (each of which UCS-2 exposes as separate characters) into a single code point, matching UTF-16.
+
+```js
+punycode.ucs2.decode('abc');
+// → [0x61, 0x62, 0x63]
+// surrogate pair for U+1D306 TETRAGRAM FOR CENTRE:
+punycode.ucs2.decode('\uD834\uDF06');
+// → [0x1D306]
+```
+
+#### `punycode.ucs2.encode(codePoints)`
+
+Creates a string based on an array of numeric code point values.
+
+```js
+punycode.ucs2.encode([0x61, 0x62, 0x63]);
+// → 'abc'
+punycode.ucs2.encode([0x1D306]);
+// → '\uD834\uDF06'
+```
+
+### `punycode.version`
+
+A string representing the current Punycode.js version number.
+
+## Unit tests & code coverage
+
+After cloning this repository, run `npm install --dev` to install the dependencies needed for Punycode.js development and testing. You may want to install Istanbul _globally_ using `npm install istanbul -g`.
+
+Once that’s done, you can run the unit tests in Node using `npm test` or `node tests/tests.js`. To run the tests in Rhino, Ringo, Narwhal, PhantomJS, and web browsers as well, use `grunt test`.
+
+To generate the code coverage report, use `grunt cover`.
+
+Feel free to fork if you see possible improvements!
+
+## Author
+
+| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") |
+|---|
+| [Mathias Bynens](https://mathiasbynens.be/) |
+
+## Contributors
+
+| [![twitter/jdalton](https://gravatar.com/avatar/299a3d891ff1920b69c364d061007043?s=70)](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
+|---|
+| [John-David Dalton](http://allyoucanleet.com/) |
+
+## License
+
+Punycode.js is available under the [MIT](https://mths.be/mit) license.
diff --git a/node_modules/url/node_modules/punycode/package.json b/node_modules/url/node_modules/punycode/package.json
new file mode 100644
index 000000000..35f046aec
--- /dev/null
+++ b/node_modules/url/node_modules/punycode/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "punycode",
+ "version": "1.3.2",
+ "description": "A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.",
+ "homepage": "https://mths.be/punycode",
+ "main": "punycode.js",
+ "keywords": [
+ "punycode",
+ "unicode",
+ "idn",
+ "idna",
+ "dns",
+ "url",
+ "domain"
+ ],
+ "license": "MIT",
+ "author": {
+ "name": "Mathias Bynens",
+ "url": "https://mathiasbynens.be/"
+ },
+ "contributors": [
+ {
+ "name": "Mathias Bynens",
+ "url": "https://mathiasbynens.be/"
+ },
+ {
+ "name": "John-David Dalton",
+ "url": "http://allyoucanleet.com/"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/bestiejs/punycode.js.git"
+ },
+ "bugs": "https://github.com/bestiejs/punycode.js/issues",
+ "files": [
+ "LICENSE-MIT.txt",
+ "punycode.js"
+ ],
+ "scripts": {
+ "test": "node tests/tests.js"
+ },
+ "devDependencies": {
+ "coveralls": "^2.10.1",
+ "grunt": "^0.4.5",
+ "grunt-contrib-uglify": "^0.5.0",
+ "grunt-shell": "^0.7.0",
+ "istanbul": "^0.2.13",
+ "qunit-extras": "^1.2.0",
+ "qunitjs": "~1.11.0",
+ "requirejs": "^2.1.14"
+ }
+}
diff --git a/node_modules/url/node_modules/punycode/punycode.js b/node_modules/url/node_modules/punycode/punycode.js
new file mode 100644
index 000000000..ac6859738
--- /dev/null
+++ b/node_modules/url/node_modules/punycode/punycode.js
@@ -0,0 +1,530 @@
+/*! https://mths.be/punycode v1.3.2 by @mathias */
+;(function(root) {
+
+ /** Detect free variables */
+ var freeExports = typeof exports == 'object' && exports &&
+ !exports.nodeType && exports;
+ var freeModule = typeof module == 'object' && module &&
+ !module.nodeType && module;
+ var freeGlobal = typeof global == 'object' && global;
+ if (
+ freeGlobal.global === freeGlobal ||
+ freeGlobal.window === freeGlobal ||
+ freeGlobal.self === freeGlobal
+ ) {
+ root = freeGlobal;
+ }
+
+ /**
+ * The `punycode` object.
+ * @name punycode
+ * @type Object
+ */
+ var punycode,
+
+ /** Highest positive signed 32-bit float value */
+ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+ /** Bootstring parameters */
+ base = 36,
+ tMin = 1,
+ tMax = 26,
+ skew = 38,
+ damp = 700,
+ initialBias = 72,
+ initialN = 128, // 0x80
+ delimiter = '-', // '\x2D'
+
+ /** Regular expressions */
+ regexPunycode = /^xn--/,
+ regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+ regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+ /** Error messages */
+ errors = {
+ 'overflow': 'Overflow: input needs wider integers to process',
+ 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+ 'invalid-input': 'Invalid input'
+ },
+
+ /** Convenience shortcuts */
+ baseMinusTMin = base - tMin,
+ floor = Math.floor,
+ stringFromCharCode = String.fromCharCode,
+
+ /** Temporary variable */
+ key;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic error utility function.
+ * @private
+ * @param {String} type The error type.
+ * @returns {Error} Throws a `RangeError` with the applicable error message.
+ */
+ function error(type) {
+ throw RangeError(errors[type]);
+ }
+
+ /**
+ * A generic `Array#map` utility function.
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function that gets called for every array
+ * item.
+ * @returns {Array} A new array of values returned by the callback function.
+ */
+ function map(array, fn) {
+ var length = array.length;
+ var result = [];
+ while (length--) {
+ result[length] = fn(array[length]);
+ }
+ return result;
+ }
+
+ /**
+ * A simple `Array#map`-like wrapper to work with domain name strings or email
+ * addresses.
+ * @private
+ * @param {String} domain The domain name or email address.
+ * @param {Function} callback The function that gets called for every
+ * character.
+ * @returns {Array} A new string of characters returned by the callback
+ * function.
+ */
+ function mapDomain(string, fn) {
+ var parts = string.split('@');
+ var result = '';
+ if (parts.length > 1) {
+ // In email addresses, only the domain name should be punycoded. Leave
+ // the local part (i.e. everything up to `@`) intact.
+ result = parts[0] + '@';
+ string = parts[1];
+ }
+ // Avoid `split(regex)` for IE8 compatibility. See #17.
+ string = string.replace(regexSeparators, '\x2E');
+ var labels = string.split('.');
+ var encoded = map(labels, fn).join('.');
+ return result + encoded;
+ }
+
+ /**
+ * Creates an array containing the numeric code points of each Unicode
+ * character in the string. While JavaScript uses UCS-2 internally,
+ * this function will convert a pair of surrogate halves (each of which
+ * UCS-2 exposes as separate characters) into a single code point,
+ * matching UTF-16.
+ * @see `punycode.ucs2.encode`
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode.ucs2
+ * @name decode
+ * @param {String} string The Unicode input string (UCS-2).
+ * @returns {Array} The new array of code points.
+ */
+ function ucs2decode(string) {
+ var output = [],
+ counter = 0,
+ length = string.length,
+ value,
+ extra;
+ while (counter < length) {
+ value = string.charCodeAt(counter++);
+ if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+ // high surrogate, and there is a next character
+ extra = string.charCodeAt(counter++);
+ if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+ output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ // unmatched surrogate; only append this code unit, in case the next
+ // code unit is the high surrogate of a surrogate pair
+ output.push(value);
+ counter--;
+ }
+ } else {
+ output.push(value);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Creates a string based on an array of numeric code points.
+ * @see `punycode.ucs2.decode`
+ * @memberOf punycode.ucs2
+ * @name encode
+ * @param {Array} codePoints The array of numeric code points.
+ * @returns {String} The new Unicode string (UCS-2).
+ */
+ function ucs2encode(array) {
+ return map(array, function(value) {
+ var output = '';
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+ value = 0xDC00 | value & 0x3FF;
+ }
+ output += stringFromCharCode(value);
+ return output;
+ }).join('');
+ }
+
+ /**
+ * Converts a basic code point into a digit/integer.
+ * @see `digitToBasic()`
+ * @private
+ * @param {Number} codePoint The basic numeric code point value.
+ * @returns {Number} The numeric value of a basic code point (for use in
+ * representing integers) in the range `0` to `base - 1`, or `base` if
+ * the code point does not represent a value.
+ */
+ function basicToDigit(codePoint) {
+ if (codePoint - 48 < 10) {
+ return codePoint - 22;
+ }
+ if (codePoint - 65 < 26) {
+ return codePoint - 65;
+ }
+ if (codePoint - 97 < 26) {
+ return codePoint - 97;
+ }
+ return base;
+ }
+
+ /**
+ * Converts a digit/integer into a basic code point.
+ * @see `basicToDigit()`
+ * @private
+ * @param {Number} digit The numeric value of a basic code point.
+ * @returns {Number} The basic code point whose value (when used for
+ * representing integers) is `digit`, which needs to be in the range
+ * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+ * used; else, the lowercase form is used. The behavior is undefined
+ * if `flag` is non-zero and `digit` has no uppercase form.
+ */
+ function digitToBasic(digit, flag) {
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+ }
+
+ /**
+ * Bias adaptation function as per section 3.4 of RFC 3492.
+ * http://tools.ietf.org/html/rfc3492#section-3.4
+ * @private
+ */
+ function adapt(delta, numPoints, firstTime) {
+ var k = 0;
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
+ delta += floor(delta / numPoints);
+ for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+ delta = floor(delta / baseMinusTMin);
+ }
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+ }
+
+ /**
+ * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The Punycode string of ASCII-only symbols.
+ * @returns {String} The resulting string of Unicode symbols.
+ */
+ function decode(input) {
+ // Don't use UCS-2
+ var output = [],
+ inputLength = input.length,
+ out,
+ i = 0,
+ n = initialN,
+ bias = initialBias,
+ basic,
+ j,
+ index,
+ oldi,
+ w,
+ k,
+ digit,
+ t,
+ /** Cached calculation results */
+ baseMinusT;
+
+ // Handle the basic code points: let `basic` be the number of input code
+ // points before the last delimiter, or `0` if there is none, then copy
+ // the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (j = 0; j < basic; ++j) {
+ // if it's not a basic code point
+ if (input.charCodeAt(j) >= 0x80) {
+ error('not-basic');
+ }
+ output.push(input.charCodeAt(j));
+ }
+
+ // Main decoding loop: start just after the last delimiter if any basic code
+ // points were copied; start at the beginning otherwise.
+
+ for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+ // `index` is the index of the next character to be consumed.
+ // Decode a generalized variable-length integer into `delta`,
+ // which gets added to `i`. The overflow checking is easier
+ // if we increase `i` as we go, then subtract off its starting
+ // value at the end to obtain `delta`.
+ for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+ if (index >= inputLength) {
+ error('invalid-input');
+ }
+
+ digit = basicToDigit(input.charCodeAt(index++));
+
+ if (digit >= base || digit > floor((maxInt - i) / w)) {
+ error('overflow');
+ }
+
+ i += digit * w;
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ baseMinusT = base - t;
+ if (w > floor(maxInt / baseMinusT)) {
+ error('overflow');
+ }
+
+ w *= baseMinusT;
+
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi == 0);
+
+ // `i` was supposed to wrap around from `out` to `0`,
+ // incrementing `n` each time, so we'll fix that now:
+ if (floor(i / out) > maxInt - n) {
+ error('overflow');
+ }
+
+ n += floor(i / out);
+ i %= out;
+
+ // Insert `n` at position `i` of the output
+ output.splice(i++, 0, n);
+
+ }
+
+ return ucs2encode(output);
+ }
+
+ /**
+ * Converts a string of Unicode symbols (e.g. a domain name label) to a
+ * Punycode string of ASCII-only symbols.
+ * @memberOf punycode
+ * @param {String} input The string of Unicode symbols.
+ * @returns {String} The resulting Punycode string of ASCII-only symbols.
+ */
+ function encode(input) {
+ var n,
+ delta,
+ handledCPCount,
+ basicLength,
+ bias,
+ j,
+ m,
+ q,
+ k,
+ t,
+ currentValue,
+ output = [],
+ /** `inputLength` will hold the number of code points in `input`. */
+ inputLength,
+ /** Cached calculation results */
+ handledCPCountPlusOne,
+ baseMinusT,
+ qMinusT;
+
+ // Convert the input in UCS-2 to Unicode
+ input = ucs2decode(input);
+
+ // Cache the length
+ inputLength = input.length;
+
+ // Initialize the state
+ n = initialN;
+ delta = 0;
+ bias = initialBias;
+
+ // Handle the basic code points
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue < 0x80) {
+ output.push(stringFromCharCode(currentValue));
+ }
+ }
+
+ handledCPCount = basicLength = output.length;
+
+ // `handledCPCount` is the number of code points that have been handled;
+ // `basicLength` is the number of basic code points.
+
+ // Finish the basic string - if it is not empty - with a delimiter
+ if (basicLength) {
+ output.push(delimiter);
+ }
+
+ // Main encoding loop:
+ while (handledCPCount < inputLength) {
+
+ // All non-basic code points < n have been handled already. Find the next
+ // larger one:
+ for (m = maxInt, j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue >= n && currentValue < m) {
+ m = currentValue;
+ }
+ }
+
+ // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+ // but guard against overflow
+ handledCPCountPlusOne = handledCPCount + 1;
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+ error('overflow');
+ }
+
+ delta += (m - n) * handledCPCountPlusOne;
+ n = m;
+
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+
+ if (currentValue < n && ++delta > maxInt) {
+ error('overflow');
+ }
+
+ if (currentValue == n) {
+ // Represent delta as a generalized variable-length integer
+ for (q = delta, k = base; /* no condition */; k += base) {
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+ if (q < t) {
+ break;
+ }
+ qMinusT = q - t;
+ baseMinusT = base - t;
+ output.push(
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+ );
+ q = floor(qMinusT / baseMinusT);
+ }
+
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+ delta = 0;
+ ++handledCPCount;
+ }
+ }
+
+ ++delta;
+ ++n;
+
+ }
+ return output.join('');
+ }
+
+ /**
+ * Converts a Punycode string representing a domain name or an email address
+ * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+ * it doesn't matter if you call it on a string that has already been
+ * converted to Unicode.
+ * @memberOf punycode
+ * @param {String} input The Punycoded domain name or email address to
+ * convert to Unicode.
+ * @returns {String} The Unicode representation of the given Punycode
+ * string.
+ */
+ function toUnicode(input) {
+ return mapDomain(input, function(string) {
+ return regexPunycode.test(string)
+ ? decode(string.slice(4).toLowerCase())
+ : string;
+ });
+ }
+
+ /**
+ * Converts a Unicode string representing a domain name or an email address to
+ * Punycode. Only the non-ASCII parts of the domain name will be converted,
+ * i.e. it doesn't matter if you call it with a domain that's already in
+ * ASCII.
+ * @memberOf punycode
+ * @param {String} input The domain name or email address to convert, as a
+ * Unicode string.
+ * @returns {String} The Punycode representation of the given domain name or
+ * email address.
+ */
+ function toASCII(input) {
+ return mapDomain(input, function(string) {
+ return regexNonASCII.test(string)
+ ? 'xn--' + encode(string)
+ : string;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Define the public API */
+ punycode = {
+ /**
+ * A string representing the current Punycode.js version number.
+ * @memberOf punycode
+ * @type String
+ */
+ 'version': '1.3.2',
+ /**
+ * An object of methods to convert from JavaScript's internal character
+ * representation (UCS-2) to Unicode code points, and back.
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode
+ * @type Object
+ */
+ 'ucs2': {
+ 'decode': ucs2decode,
+ 'encode': ucs2encode
+ },
+ 'decode': decode,
+ 'encode': encode,
+ 'toASCII': toASCII,
+ 'toUnicode': toUnicode
+ };
+
+ /** Expose `punycode` */
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (
+ typeof define == 'function' &&
+ typeof define.amd == 'object' &&
+ define.amd
+ ) {
+ define('punycode', function() {
+ return punycode;
+ });
+ } else if (freeExports && freeModule) {
+ if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = punycode;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (key in punycode) {
+ punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.punycode = punycode;
+ }
+
+}(this));
diff --git a/node_modules/url/package.json b/node_modules/url/package.json
new file mode 100644
index 000000000..e3024abe7
--- /dev/null
+++ b/node_modules/url/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "url",
+ "description": "The core `url` packaged standalone for use with Browserify.",
+ "version": "0.11.0",
+ "dependencies": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "main": "./url.js",
+ "devDependencies": {
+ "assert": "1.1.1",
+ "mocha": "1.18.2",
+ "zuul": "3.3.0"
+ },
+ "scripts": {
+ "test": "mocha --ui qunit test.js && zuul -- test.js",
+ "test-local": "zuul --local -- test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/defunctzombie/node-url.git"
+ },
+ "license": "MIT"
+}
diff --git a/node_modules/url/test.js b/node_modules/url/test.js
new file mode 100644
index 000000000..3b7d3358c
--- /dev/null
+++ b/node_modules/url/test.js
@@ -0,0 +1,1599 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var assert = require('assert');
+
+var url = require('./url');
+
+// URLs to parse, and expected data
+// { url : parsed }
+var parseTests = {
+ '//some_path' : {
+ 'href': '//some_path',
+ 'pathname': '//some_path',
+ 'path': '//some_path'
+ },
+
+ 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': {
+ protocol: 'http:',
+ slashes: true,
+ host: 'evil-phisher',
+ hostname: 'evil-phisher',
+ pathname: '/foo.html',
+ path: '/foo.html',
+ hash: '#h%5Ca%5Cs%5Ch',
+ href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch'
+ },
+
+ 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': {
+ protocol: 'http:',
+ slashes: true,
+ host: 'evil-phisher',
+ hostname: 'evil-phisher',
+ pathname: '/foo.html',
+ search: '?json=%22%5C%22foo%5C%22%22',
+ query: 'json=%22%5C%22foo%5C%22%22',
+ path: '/foo.html?json=%22%5C%22foo%5C%22%22',
+ hash: '#h%5Ca%5Cs%5Ch',
+ href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch'
+ },
+
+ 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': {
+ protocol: 'http:',
+ slashes: true,
+ host: 'evil-phisher',
+ hostname: 'evil-phisher',
+ pathname: '/foo.html',
+ path: '/foo.html',
+ hash: '#h%5Ca%5Cs%5Ch?blarg',
+ href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg'
+ },
+
+
+ 'http:\\\\evil-phisher\\foo.html': {
+ protocol: 'http:',
+ slashes: true,
+ host: 'evil-phisher',
+ hostname: 'evil-phisher',
+ pathname: '/foo.html',
+ path: '/foo.html',
+ href: 'http://evil-phisher/foo.html'
+ },
+
+ 'HTTP://www.example.com/' : {
+ 'href': 'http://www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'HTTP://www.example.com' : {
+ 'href': 'http://www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://www.ExAmPlE.com/' : {
+ 'href': 'http://www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://user:pw@www.ExAmPlE.com/' : {
+ 'href': 'http://user:pw@www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:pw',
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://USER:PW@www.ExAmPlE.com/' : {
+ 'href': 'http://USER:PW@www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'USER:PW',
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://user@www.example.com/' : {
+ 'href': 'http://user@www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user',
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://user%3Apw@www.example.com/' : {
+ 'href': 'http://user:pw@www.example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:pw',
+ 'host': 'www.example.com',
+ 'hostname': 'www.example.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://x.com/path?that\'s#all, folks' : {
+ 'href': 'http://x.com/path?that%27s#all,%20folks',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x.com',
+ 'hostname': 'x.com',
+ 'search': '?that%27s',
+ 'query': 'that%27s',
+ 'pathname': '/path',
+ 'hash': '#all,%20folks',
+ 'path': '/path?that%27s'
+ },
+
+ 'HTTP://X.COM/Y' : {
+ 'href': 'http://x.com/Y',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x.com',
+ 'hostname': 'x.com',
+ 'pathname': '/Y',
+ 'path': '/Y'
+ },
+
+ // + not an invalid host character
+ // per https://url.spec.whatwg.org/#host-parsing
+ 'http://x.y.com+a/b/c' : {
+ 'href': 'http://x.y.com+a/b/c',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x.y.com+a',
+ 'hostname': 'x.y.com+a',
+ 'pathname': '/b/c',
+ 'path': '/b/c'
+ },
+
+ // an unexpected invalid char in the hostname.
+ 'HtTp://x.y.cOm;a/b/c?d=e#f g<h>i' : {
+ 'href': 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x.y.com',
+ 'hostname': 'x.y.com',
+ 'pathname': ';a/b/c',
+ 'search': '?d=e',
+ 'query': 'd=e',
+ 'hash': '#f%20g%3Ch%3Ei',
+ 'path': ';a/b/c?d=e'
+ },
+
+ // make sure that we don't accidentally lcast the path parts.
+ 'HtTp://x.y.cOm;A/b/c?d=e#f g<h>i' : {
+ 'href': 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x.y.com',
+ 'hostname': 'x.y.com',
+ 'pathname': ';A/b/c',
+ 'search': '?d=e',
+ 'query': 'd=e',
+ 'hash': '#f%20g%3Ch%3Ei',
+ 'path': ';A/b/c?d=e'
+ },
+
+ 'http://x...y...#p': {
+ 'href': 'http://x...y.../#p',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x...y...',
+ 'hostname': 'x...y...',
+ 'hash': '#p',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://x/p/"quoted"': {
+ 'href': 'http://x/p/%22quoted%22',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'x',
+ 'hostname': 'x',
+ 'pathname': '/p/%22quoted%22',
+ 'path': '/p/%22quoted%22'
+ },
+
+ '<http://goo.corn/bread> Is a URL!': {
+ 'href': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
+ 'pathname': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
+ 'path': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!'
+ },
+
+ 'http://www.narwhaljs.org/blog/categories?id=news' : {
+ 'href': 'http://www.narwhaljs.org/blog/categories?id=news',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.narwhaljs.org',
+ 'hostname': 'www.narwhaljs.org',
+ 'search': '?id=news',
+ 'query': 'id=news',
+ 'pathname': '/blog/categories',
+ 'path': '/blog/categories?id=news'
+ },
+
+ 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' : {
+ 'href': 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'mt0.google.com',
+ 'hostname': 'mt0.google.com',
+ 'pathname': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'path': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s='
+ },
+
+ 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' : {
+ 'href': 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' +
+ '&x=2&y=2&z=3&s=',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'mt0.google.com',
+ 'hostname': 'mt0.google.com',
+ 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'pathname': '/vt/lyrs=m@114',
+ 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s='
+ },
+
+ 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=':
+ {
+ 'href': 'http://user:pass@mt0.google.com/vt/lyrs=m@114???' +
+ '&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'mt0.google.com',
+ 'auth': 'user:pass',
+ 'hostname': 'mt0.google.com',
+ 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=',
+ 'pathname': '/vt/lyrs=m@114',
+ 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s='
+ },
+
+ 'file:///etc/passwd' : {
+ 'href': 'file:///etc/passwd',
+ 'slashes': true,
+ 'protocol': 'file:',
+ 'pathname': '/etc/passwd',
+ 'hostname': '',
+ 'host': '',
+ 'path': '/etc/passwd'
+ },
+
+ 'file://localhost/etc/passwd' : {
+ 'href': 'file://localhost/etc/passwd',
+ 'protocol': 'file:',
+ 'slashes': true,
+ 'pathname': '/etc/passwd',
+ 'hostname': 'localhost',
+ 'host': 'localhost',
+ 'path': '/etc/passwd'
+ },
+
+ 'file://foo/etc/passwd' : {
+ 'href': 'file://foo/etc/passwd',
+ 'protocol': 'file:',
+ 'slashes': true,
+ 'pathname': '/etc/passwd',
+ 'hostname': 'foo',
+ 'host': 'foo',
+ 'path': '/etc/passwd'
+ },
+
+ 'file:///etc/node/' : {
+ 'href': 'file:///etc/node/',
+ 'slashes': true,
+ 'protocol': 'file:',
+ 'pathname': '/etc/node/',
+ 'hostname': '',
+ 'host': '',
+ 'path': '/etc/node/'
+ },
+
+ 'file://localhost/etc/node/' : {
+ 'href': 'file://localhost/etc/node/',
+ 'protocol': 'file:',
+ 'slashes': true,
+ 'pathname': '/etc/node/',
+ 'hostname': 'localhost',
+ 'host': 'localhost',
+ 'path': '/etc/node/'
+ },
+
+ 'file://foo/etc/node/' : {
+ 'href': 'file://foo/etc/node/',
+ 'protocol': 'file:',
+ 'slashes': true,
+ 'pathname': '/etc/node/',
+ 'hostname': 'foo',
+ 'host': 'foo',
+ 'path': '/etc/node/'
+ },
+
+ 'http:/baz/../foo/bar' : {
+ 'href': 'http:/baz/../foo/bar',
+ 'protocol': 'http:',
+ 'pathname': '/baz/../foo/bar',
+ 'path': '/baz/../foo/bar'
+ },
+
+ 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag' : {
+ 'href': 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com:8000',
+ 'auth': 'user:pass',
+ 'port': '8000',
+ 'hostname': 'example.com',
+ 'hash': '#frag',
+ 'search': '?baz=quux',
+ 'query': 'baz=quux',
+ 'pathname': '/foo/bar',
+ 'path': '/foo/bar?baz=quux'
+ },
+
+ '//user:pass@example.com:8000/foo/bar?baz=quux#frag' : {
+ 'href': '//user:pass@example.com:8000/foo/bar?baz=quux#frag',
+ 'slashes': true,
+ 'host': 'example.com:8000',
+ 'auth': 'user:pass',
+ 'port': '8000',
+ 'hostname': 'example.com',
+ 'hash': '#frag',
+ 'search': '?baz=quux',
+ 'query': 'baz=quux',
+ 'pathname': '/foo/bar',
+ 'path': '/foo/bar?baz=quux'
+ },
+
+ '/foo/bar?baz=quux#frag' : {
+ 'href': '/foo/bar?baz=quux#frag',
+ 'hash': '#frag',
+ 'search': '?baz=quux',
+ 'query': 'baz=quux',
+ 'pathname': '/foo/bar',
+ 'path': '/foo/bar?baz=quux'
+ },
+
+ 'http:/foo/bar?baz=quux#frag' : {
+ 'href': 'http:/foo/bar?baz=quux#frag',
+ 'protocol': 'http:',
+ 'hash': '#frag',
+ 'search': '?baz=quux',
+ 'query': 'baz=quux',
+ 'pathname': '/foo/bar',
+ 'path': '/foo/bar?baz=quux'
+ },
+
+ 'mailto:foo@bar.com?subject=hello' : {
+ 'href': 'mailto:foo@bar.com?subject=hello',
+ 'protocol': 'mailto:',
+ 'host': 'bar.com',
+ 'auth' : 'foo',
+ 'hostname' : 'bar.com',
+ 'search': '?subject=hello',
+ 'query': 'subject=hello',
+ 'path': '?subject=hello'
+ },
+
+ 'javascript:alert(\'hello\');' : {
+ 'href': 'javascript:alert(\'hello\');',
+ 'protocol': 'javascript:',
+ 'pathname': 'alert(\'hello\');',
+ 'path': 'alert(\'hello\');'
+ },
+
+ 'xmpp:isaacschlueter@jabber.org' : {
+ 'href': 'xmpp:isaacschlueter@jabber.org',
+ 'protocol': 'xmpp:',
+ 'host': 'jabber.org',
+ 'auth': 'isaacschlueter',
+ 'hostname': 'jabber.org'
+ },
+
+ 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar' : {
+ 'href' : 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar',
+ 'protocol' : 'http:',
+ 'slashes': true,
+ 'host' : '127.0.0.1:8080',
+ 'auth' : 'atpass:foo@bar',
+ 'hostname' : '127.0.0.1',
+ 'port' : '8080',
+ 'pathname': '/path',
+ 'search' : '?search=foo',
+ 'query' : 'search=foo',
+ 'hash' : '#bar',
+ 'path': '/path?search=foo'
+ },
+
+ 'svn+ssh://foo/bar': {
+ 'href': 'svn+ssh://foo/bar',
+ 'host': 'foo',
+ 'hostname': 'foo',
+ 'protocol': 'svn+ssh:',
+ 'pathname': '/bar',
+ 'path': '/bar',
+ 'slashes': true
+ },
+
+ 'dash-test://foo/bar': {
+ 'href': 'dash-test://foo/bar',
+ 'host': 'foo',
+ 'hostname': 'foo',
+ 'protocol': 'dash-test:',
+ 'pathname': '/bar',
+ 'path': '/bar',
+ 'slashes': true
+ },
+
+ 'dash-test:foo/bar': {
+ 'href': 'dash-test:foo/bar',
+ 'host': 'foo',
+ 'hostname': 'foo',
+ 'protocol': 'dash-test:',
+ 'pathname': '/bar',
+ 'path': '/bar'
+ },
+
+ 'dot.test://foo/bar': {
+ 'href': 'dot.test://foo/bar',
+ 'host': 'foo',
+ 'hostname': 'foo',
+ 'protocol': 'dot.test:',
+ 'pathname': '/bar',
+ 'path': '/bar',
+ 'slashes': true
+ },
+
+ 'dot.test:foo/bar': {
+ 'href': 'dot.test:foo/bar',
+ 'host': 'foo',
+ 'hostname': 'foo',
+ 'protocol': 'dot.test:',
+ 'pathname': '/bar',
+ 'path': '/bar'
+ },
+
+ // IDNA tests
+ 'http://www.日本語.com/' : {
+ 'href': 'http://www.xn--wgv71a119e.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.xn--wgv71a119e.com',
+ 'hostname': 'www.xn--wgv71a119e.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://example.Bücher.com/' : {
+ 'href': 'http://example.xn--bcher-kva.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.xn--bcher-kva.com',
+ 'hostname': 'example.xn--bcher-kva.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://www.Äffchen.com/' : {
+ 'href': 'http://www.xn--ffchen-9ta.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.xn--ffchen-9ta.com',
+ 'hostname': 'www.xn--ffchen-9ta.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://www.Äffchen.cOm;A/b/c?d=e#f g<h>i' : {
+ 'href': 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'www.xn--ffchen-9ta.com',
+ 'hostname': 'www.xn--ffchen-9ta.com',
+ 'pathname': ';A/b/c',
+ 'search': '?d=e',
+ 'query': 'd=e',
+ 'hash': '#f%20g%3Ch%3Ei',
+ 'path': ';A/b/c?d=e'
+ },
+
+ 'http://SÉLIER.COM/' : {
+ 'href': 'http://xn--slier-bsa.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'xn--slier-bsa.com',
+ 'hostname': 'xn--slier-bsa.com',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://ليهمابتكلموشعربي؟.ي؟/' : {
+ 'href': 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f',
+ 'hostname': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://➡.ws/➡' : {
+ 'href': 'http://xn--hgi.ws/➡',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'xn--hgi.ws',
+ 'hostname': 'xn--hgi.ws',
+ 'pathname': '/➡',
+ 'path': '/➡'
+ },
+
+ 'http://bucket_name.s3.amazonaws.com/image.jpg': {
+ protocol: 'http:',
+ 'slashes': true,
+ slashes: true,
+ host: 'bucket_name.s3.amazonaws.com',
+ hostname: 'bucket_name.s3.amazonaws.com',
+ pathname: '/image.jpg',
+ href: 'http://bucket_name.s3.amazonaws.com/image.jpg',
+ 'path': '/image.jpg'
+ },
+
+ 'git+http://github.com/joyent/node.git': {
+ protocol: 'git+http:',
+ slashes: true,
+ host: 'github.com',
+ hostname: 'github.com',
+ pathname: '/joyent/node.git',
+ path: '/joyent/node.git',
+ href: 'git+http://github.com/joyent/node.git'
+ },
+
+ //if local1@domain1 is uses as a relative URL it may
+ //be parse into auth@hostname, but here there is no
+ //way to make it work in url.parse, I add the test to be explicit
+ 'local1@domain1': {
+ 'pathname': 'local1@domain1',
+ 'path': 'local1@domain1',
+ 'href': 'local1@domain1'
+ },
+
+ //While this may seem counter-intuitive, a browser will parse
+ //<a href='www.google.com'> as a path.
+ 'www.example.com' : {
+ 'href': 'www.example.com',
+ 'pathname': 'www.example.com',
+ 'path': 'www.example.com'
+ },
+
+ // ipv6 support
+ '[fe80::1]': {
+ 'href': '[fe80::1]',
+ 'pathname': '[fe80::1]',
+ 'path': '[fe80::1]'
+ },
+
+ 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': {
+ 'protocol': 'coap:',
+ 'slashes': true,
+ 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]',
+ 'hostname': 'fedc:ba98:7654:3210:fedc:ba98:7654:3210',
+ 'href': 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': {
+ 'protocol': 'coap:',
+ 'slashes': true,
+ 'host': '[1080:0:0:0:8:800:200c:417a]:61616',
+ 'port': '61616',
+ 'hostname': '1080:0:0:0:8:800:200c:417a',
+ 'href': 'coap://[1080:0:0:0:8:800:200c:417a]:61616/',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://user:password@[3ffe:2a00:100:7031::1]:8080': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:password',
+ 'host': '[3ffe:2a00:100:7031::1]:8080',
+ 'port': '8080',
+ 'hostname': '3ffe:2a00:100:7031::1',
+ 'href': 'http://user:password@[3ffe:2a00:100:7031::1]:8080/',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': {
+ 'protocol': 'coap:',
+ 'slashes': true,
+ 'auth': 'u:p',
+ 'host': '[::192.9.5.5]:61616',
+ 'port': '61616',
+ 'hostname': '::192.9.5.5',
+ 'href': 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature',
+ 'search': '?n=Temperature',
+ 'query': 'n=Temperature',
+ 'pathname': '/.well-known/r',
+ 'path': '/.well-known/r?n=Temperature'
+ },
+
+ // empty port
+ 'http://example.com:': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'href': 'http://example.com/',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://example.com:/a/b.html': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'href': 'http://example.com/a/b.html',
+ 'pathname': '/a/b.html',
+ 'path': '/a/b.html'
+ },
+
+ 'http://example.com:?a=b': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'href': 'http://example.com/?a=b',
+ 'search': '?a=b',
+ 'query': 'a=b',
+ 'pathname': '/',
+ 'path': '/?a=b'
+ },
+
+ 'http://example.com:#abc': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'href': 'http://example.com/#abc',
+ 'hash': '#abc',
+ 'pathname': '/',
+ 'path': '/'
+ },
+
+ 'http://[fe80::1]:/a/b?a=b#abc': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': '[fe80::1]',
+ 'hostname': 'fe80::1',
+ 'href': 'http://[fe80::1]/a/b?a=b#abc',
+ 'search': '?a=b',
+ 'query': 'a=b',
+ 'hash': '#abc',
+ 'pathname': '/a/b',
+ 'path': '/a/b?a=b'
+ },
+
+ 'http://-lovemonsterz.tumblr.com/rss': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': '-lovemonsterz.tumblr.com',
+ 'hostname': '-lovemonsterz.tumblr.com',
+ 'href': 'http://-lovemonsterz.tumblr.com/rss',
+ 'pathname': '/rss',
+ 'path': '/rss',
+ },
+
+ 'http://-lovemonsterz.tumblr.com:80/rss': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'port': '80',
+ 'host': '-lovemonsterz.tumblr.com:80',
+ 'hostname': '-lovemonsterz.tumblr.com',
+ 'href': 'http://-lovemonsterz.tumblr.com:80/rss',
+ 'pathname': '/rss',
+ 'path': '/rss',
+ },
+
+ 'http://user:pass@-lovemonsterz.tumblr.com/rss': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:pass',
+ 'host': '-lovemonsterz.tumblr.com',
+ 'hostname': '-lovemonsterz.tumblr.com',
+ 'href': 'http://user:pass@-lovemonsterz.tumblr.com/rss',
+ 'pathname': '/rss',
+ 'path': '/rss',
+ },
+
+ 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:pass',
+ 'port': '80',
+ 'host': '-lovemonsterz.tumblr.com:80',
+ 'hostname': '-lovemonsterz.tumblr.com',
+ 'href': 'http://user:pass@-lovemonsterz.tumblr.com:80/rss',
+ 'pathname': '/rss',
+ 'path': '/rss',
+ },
+
+ 'http://_jabber._tcp.google.com/test': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': '_jabber._tcp.google.com',
+ 'hostname': '_jabber._tcp.google.com',
+ 'href': 'http://_jabber._tcp.google.com/test',
+ 'pathname': '/test',
+ 'path': '/test',
+ },
+
+ 'http://user:pass@_jabber._tcp.google.com/test': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:pass',
+ 'host': '_jabber._tcp.google.com',
+ 'hostname': '_jabber._tcp.google.com',
+ 'href': 'http://user:pass@_jabber._tcp.google.com/test',
+ 'pathname': '/test',
+ 'path': '/test',
+ },
+
+ 'http://_jabber._tcp.google.com:80/test': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'port': '80',
+ 'host': '_jabber._tcp.google.com:80',
+ 'hostname': '_jabber._tcp.google.com',
+ 'href': 'http://_jabber._tcp.google.com:80/test',
+ 'pathname': '/test',
+ 'path': '/test',
+ },
+
+ 'http://user:pass@_jabber._tcp.google.com:80/test': {
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'auth': 'user:pass',
+ 'port': '80',
+ 'host': '_jabber._tcp.google.com:80',
+ 'hostname': '_jabber._tcp.google.com',
+ 'href': 'http://user:pass@_jabber._tcp.google.com:80/test',
+ 'pathname': '/test',
+ 'path': '/test',
+ },
+
+ 'http://x:1/\' <>"`/{}|\\^~`/': {
+ protocol: 'http:',
+ slashes: true,
+ host: 'x:1',
+ port: '1',
+ hostname: 'x',
+ pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
+ path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
+ href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/'
+ },
+
+ 'http://a@b@c/': {
+ protocol: 'http:',
+ slashes: true,
+ auth: 'a@b',
+ host: 'c',
+ hostname: 'c',
+ href: 'http://a%40b@c/',
+ path: '/',
+ pathname: '/'
+ },
+
+ 'http://a@b?@c': {
+ protocol: 'http:',
+ slashes: true,
+ auth: 'a',
+ host: 'b',
+ hostname: 'b',
+ href: 'http://a@b/?@c',
+ path: '/?@c',
+ pathname: '/',
+ search: '?@c',
+ query: '@c'
+ },
+
+ 'http://a\r" \t\n<\'b:b@c\r\nd/e?f':{
+ protocol: 'http:',
+ slashes: true,
+ auth: 'a\r" \t\n<\'b:b',
+ host: 'c',
+ port: null,
+ hostname: 'c',
+ hash: null,
+ search: '?f',
+ query: 'f',
+ pathname: '%0D%0Ad/e',
+ path: '%0D%0Ad/e?f',
+ href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f'
+ },
+
+ // git urls used by npm
+ 'git+ssh://git@github.com:npm/npm': {
+ protocol: 'git+ssh:',
+ slashes: true,
+ auth: 'git',
+ host: 'github.com',
+ port: null,
+ hostname: 'github.com',
+ hash: null,
+ search: null,
+ query: null,
+ pathname: '/:npm/npm',
+ path: '/:npm/npm',
+ href: 'git+ssh://git@github.com/:npm/npm'
+ }
+
+};
+
+Object.keys(parseTests).forEach(function(u) {
+ test('parse(' + u + ')', function() {
+ var actual = url.parse(u),
+ spaced = url.parse(' \t ' + u + '\n\t');
+ expected = parseTests[u];
+
+ Object.keys(actual).forEach(function (i) {
+ if (expected[i] === undefined && actual[i] === null) {
+ expected[i] = null;
+ }
+ });
+
+ assert.deepEqual(actual, expected);
+ assert.deepEqual(spaced, expected);
+
+ var expected = parseTests[u].href,
+ actual = url.format(parseTests[u]);
+
+ assert.equal(actual, expected,
+ 'format(' + u + ') == ' + u + '\nactual:' + actual);
+ });
+});
+
+var parseTestsWithQueryString = {
+ '/foo/bar?baz=quux#frag' : {
+ 'href': '/foo/bar?baz=quux#frag',
+ 'hash': '#frag',
+ 'search': '?baz=quux',
+ 'query': {
+ 'baz': 'quux'
+ },
+ 'pathname': '/foo/bar',
+ 'path': '/foo/bar?baz=quux'
+ },
+ 'http://example.com' : {
+ 'href': 'http://example.com/',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'query': {},
+ 'search': '',
+ 'pathname': '/',
+ 'path': '/'
+ },
+ '/example': {
+ protocol: null,
+ slashes: null,
+ auth: null,
+ host: null,
+ port: null,
+ hostname: null,
+ hash: null,
+ search: '',
+ query: {},
+ pathname: '/example',
+ path: '/example',
+ href: '/example'
+ },
+ '/example?query=value':{
+ protocol: null,
+ slashes: null,
+ auth: null,
+ host: null,
+ port: null,
+ hostname: null,
+ hash: null,
+ search: '?query=value',
+ query: { query: 'value' },
+ pathname: '/example',
+ path: '/example?query=value',
+ href: '/example?query=value'
+ }
+};
+
+Object.keys(parseTestsWithQueryString).forEach(function(u) {
+ test('parse(' + u + ')', function() {
+ var actual = url.parse(u, true);
+ var expected = parseTestsWithQueryString[u];
+ for (var i in actual) {
+ if (actual[i] === null && expected[i] === undefined) {
+ expected[i] = null;
+ }
+ }
+
+ assert.deepEqual(actual, expected);
+ });
+});
+
+// some extra formatting tests, just to verify
+// that it'll format slightly wonky content to a valid url.
+var formatTests = {
+ 'http://example.com?' : {
+ 'href': 'http://example.com/?',
+ 'protocol': 'http:',
+ 'slashes': true,
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'search': '?',
+ 'query': {},
+ 'pathname': '/'
+ },
+ 'http://example.com?foo=bar#frag' : {
+ 'href': 'http://example.com/?foo=bar#frag',
+ 'protocol': 'http:',
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'hash': '#frag',
+ 'search': '?foo=bar',
+ 'query': 'foo=bar',
+ 'pathname': '/'
+ },
+ 'http://example.com?foo=@bar#frag' : {
+ 'href': 'http://example.com/?foo=@bar#frag',
+ 'protocol': 'http:',
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'hash': '#frag',
+ 'search': '?foo=@bar',
+ 'query': 'foo=@bar',
+ 'pathname': '/'
+ },
+ 'http://example.com?foo=/bar/#frag' : {
+ 'href': 'http://example.com/?foo=/bar/#frag',
+ 'protocol': 'http:',
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'hash': '#frag',
+ 'search': '?foo=/bar/',
+ 'query': 'foo=/bar/',
+ 'pathname': '/'
+ },
+ 'http://example.com?foo=?bar/#frag' : {
+ 'href': 'http://example.com/?foo=?bar/#frag',
+ 'protocol': 'http:',
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'hash': '#frag',
+ 'search': '?foo=?bar/',
+ 'query': 'foo=?bar/',
+ 'pathname': '/'
+ },
+ 'http://example.com#frag=?bar/#frag' : {
+ 'href': 'http://example.com/#frag=?bar/#frag',
+ 'protocol': 'http:',
+ 'host': 'example.com',
+ 'hostname': 'example.com',
+ 'hash': '#frag=?bar/#frag',
+ 'pathname': '/'
+ },
+ 'http://google.com" onload="alert(42)/' : {
+ 'href': 'http://google.com/%22%20onload=%22alert(42)/',
+ 'protocol': 'http:',
+ 'host': 'google.com',
+ 'pathname': '/%22%20onload=%22alert(42)/'
+ },
+ 'http://a.com/a/b/c?s#h' : {
+ 'href': 'http://a.com/a/b/c?s#h',
+ 'protocol': 'http',
+ 'host': 'a.com',
+ 'pathname': 'a/b/c',
+ 'hash': 'h',
+ 'search': 's'
+ },
+ 'xmpp:isaacschlueter@jabber.org' : {
+ 'href': 'xmpp:isaacschlueter@jabber.org',
+ 'protocol': 'xmpp:',
+ 'host': 'jabber.org',
+ 'auth': 'isaacschlueter',
+ 'hostname': 'jabber.org'
+ },
+ 'http://atpass:foo%40bar@127.0.0.1/' : {
+ 'href': 'http://atpass:foo%40bar@127.0.0.1/',
+ 'auth': 'atpass:foo@bar',
+ 'hostname': '127.0.0.1',
+ 'protocol': 'http:',
+ 'pathname': '/'
+ },
+ 'http://atslash%2F%40:%2F%40@foo/' : {
+ 'href': 'http://atslash%2F%40:%2F%40@foo/',
+ 'auth': 'atslash/@:/@',
+ 'hostname': 'foo',
+ 'protocol': 'http:',
+ 'pathname': '/'
+ },
+ 'svn+ssh://foo/bar': {
+ 'href': 'svn+ssh://foo/bar',
+ 'hostname': 'foo',
+ 'protocol': 'svn+ssh:',
+ 'pathname': '/bar',
+ 'slashes': true
+ },
+ 'dash-test://foo/bar': {
+ 'href': 'dash-test://foo/bar',
+ 'hostname': 'foo',
+ 'protocol': 'dash-test:',
+ 'pathname': '/bar',
+ 'slashes': true
+ },
+ 'dash-test:foo/bar': {
+ 'href': 'dash-test:foo/bar',
+ 'hostname': 'foo',
+ 'protocol': 'dash-test:',
+ 'pathname': '/bar'
+ },
+ 'dot.test://foo/bar': {
+ 'href': 'dot.test://foo/bar',
+ 'hostname': 'foo',
+ 'protocol': 'dot.test:',
+ 'pathname': '/bar',
+ 'slashes': true
+ },
+ 'dot.test:foo/bar': {
+ 'href': 'dot.test:foo/bar',
+ 'hostname': 'foo',
+ 'protocol': 'dot.test:',
+ 'pathname': '/bar'
+ },
+ // ipv6 support
+ 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': {
+ 'href': 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature',
+ 'protocol': 'coap:',
+ 'auth': 'u:p',
+ 'hostname': '::1',
+ 'port': '61616',
+ 'pathname': '/.well-known/r',
+ 'search': 'n=Temperature'
+ },
+ 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': {
+ 'href': 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton',
+ 'protocol': 'coap',
+ 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616',
+ 'pathname': '/s/stopButton'
+ },
+
+ // encode context-specific delimiters in path and query, but do not touch
+ // other non-delimiter chars like `%`.
+ // <https://github.com/joyent/node/issues/4082>
+
+ // `#`,`?` in path
+ '/path/to/%%23%3F+=&.txt?foo=theA1#bar' : {
+ href : '/path/to/%%23%3F+=&.txt?foo=theA1#bar',
+ pathname: '/path/to/%#?+=&.txt',
+ query: {
+ foo: 'theA1'
+ },
+ hash: "#bar"
+ },
+
+ // `#`,`?` in path + `#` in query
+ '/path/to/%%23%3F+=&.txt?foo=the%231#bar' : {
+ href : '/path/to/%%23%3F+=&.txt?foo=the%231#bar',
+ pathname: '/path/to/%#?+=&.txt',
+ query: {
+ foo: 'the#1'
+ },
+ hash: "#bar"
+ },
+
+ // `?` and `#` in path and search
+ 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': {
+ href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag',
+ protocol: 'http:',
+ hostname: 'ex.com',
+ hash: '#frag',
+ search: '?abc=the#1?&foo=bar',
+ pathname: '/foo?100%m#r',
+ },
+
+ // `?` and `#` in search only
+ 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': {
+ href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag',
+ protocol: 'http:',
+ hostname: 'ex.com',
+ hash: '#frag',
+ search: '?abc=the#1?&foo=bar',
+ pathname: '/fooA100%mBr',
+ }
+};
+
+Object.keys(formatTests).forEach(function(u) {
+ test('format(' + u + ')', function() {
+ var expect = formatTests[u].href;
+ delete formatTests[u].href;
+ var actual = url.format(u);
+ var actualObj = url.format(formatTests[u]);
+ assert.equal(actual, expect,
+ 'wonky format(' + u + ') == ' + expect +
+ '\nactual:' + actual);
+ assert.equal(actualObj, expect,
+ 'wonky format(' + JSON.stringify(formatTests[u]) +
+ ') == ' + expect +
+ '\nactual: ' + actualObj);
+ });
+});
+
+/*
+ [from, path, expected]
+*/
+var relativeTests = [
+ ['/foo/bar/baz', 'quux', '/foo/bar/quux'],
+ ['/foo/bar/baz', 'quux/asdf', '/foo/bar/quux/asdf'],
+ ['/foo/bar/baz', 'quux/baz', '/foo/bar/quux/baz'],
+ ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'],
+ ['/foo/bar/baz', '/bar', '/bar'],
+ ['/foo/bar/baz/', 'quux', '/foo/bar/baz/quux'],
+ ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'],
+ ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'],
+ ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'],
+ ['/foo', '.', '/'],
+ ['/foo', '..', '/'],
+ ['/foo/', '.', '/foo/'],
+ ['/foo/', '..', '/'],
+ ['/foo/bar', '.', '/foo/'],
+ ['/foo/bar', '..', '/'],
+ ['/foo/bar/', '.', '/foo/bar/'],
+ ['/foo/bar/', '..', '/foo/'],
+ ['foo/bar', '../../../baz', '../../baz'],
+ ['foo/bar/', '../../../baz', '../baz'],
+ ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'https:/p/a/t/h?s#hash2',
+ 'https://p/a/t/h?s#hash2'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'https://u:p@h.com/p/a/t/h?s#hash2',
+ 'https://u:p@h.com/p/a/t/h?s#hash2'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'https:/a/b/c/d',
+ 'https://a/b/c/d'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'http:#hash2',
+ 'http://example.com/b//c//d;p?q#hash2'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'http:/p/a/t/h?s#hash2',
+ 'http://example.com/p/a/t/h?s#hash2'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'http://u:p@h.com/p/a/t/h?s#hash2',
+ 'http://u:p@h.com/p/a/t/h?s#hash2'],
+ ['http://example.com/b//c//d;p?q#blarg',
+ 'http:/a/b/c/d',
+ 'http://example.com/a/b/c/d'],
+ ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd']
+];
+
+relativeTests.forEach(function(relativeTest) {
+ test('resolve(' + [relativeTest[0], relativeTest[1]] + ')', function() {
+ var a = url.resolve(relativeTest[0], relativeTest[1]),
+ e = relativeTest[2];
+ assert.equal(a, e,
+ 'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e +
+ '\n actual=' + a);
+ });
+});
+
+
+// https://github.com/joyent/node/issues/568
+[
+ undefined,
+ null,
+ true,
+ false,
+ 0.0,
+ 0,
+ [],
+ {}
+].forEach(function(val) {
+ test('parse(' + val + ')', function() {
+ assert.throws(function() { url.parse(val); }, TypeError);
+ });
+});
+
+
+//
+// Tests below taken from Chiron
+// http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js
+//
+// Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
+// used with permission under MIT License
+//
+// Changes marked with @isaacs
+
+var bases = [
+ 'http://a/b/c/d;p?q',
+ 'http://a/b/c/d;p?q=1/2',
+ 'http://a/b/c/d;p=1/2?q',
+ 'fred:///s//a/b/c',
+ 'http:///s//a/b/c'
+];
+
+//[to, from, result]
+var relativeTests2 = [
+ // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html
+ ['../c', 'foo:a/b', 'foo:c'],
+ ['foo:.', 'foo:a', 'foo:'],
+ ['/foo/../../../bar', 'zz:abc', 'zz:/bar'],
+ ['/foo/../bar', 'zz:abc', 'zz:/bar'],
+ // @isaacs Disagree. Not how web browsers resolve this.
+ ['foo/../../../bar', 'zz:abc', 'zz:bar'],
+ // ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added
+ ['foo/../bar', 'zz:abc', 'zz:bar'],
+ ['zz:.', 'zz:abc', 'zz:'],
+ ['/.', bases[0], 'http://a/'],
+ ['/.foo', bases[0], 'http://a/.foo'],
+ ['.foo', bases[0], 'http://a/b/c/.foo'],
+
+ // http://gbiv.com/protocols/uri/test/rel_examples1.html
+ // examples from RFC 2396
+ ['g:h', bases[0], 'g:h'],
+ ['g', bases[0], 'http://a/b/c/g'],
+ ['./g', bases[0], 'http://a/b/c/g'],
+ ['g/', bases[0], 'http://a/b/c/g/'],
+ ['/g', bases[0], 'http://a/g'],
+ ['//g', bases[0], 'http://g/'],
+ // changed with RFC 2396bis
+ //('?y', bases[0], 'http://a/b/c/d;p?y'],
+ ['?y', bases[0], 'http://a/b/c/d;p?y'],
+ ['g?y', bases[0], 'http://a/b/c/g?y'],
+ // changed with RFC 2396bis
+ //('#s', bases[0], CURRENT_DOC_URI + '#s'],
+ ['#s', bases[0], 'http://a/b/c/d;p?q#s'],
+ ['g#s', bases[0], 'http://a/b/c/g#s'],
+ ['g?y#s', bases[0], 'http://a/b/c/g?y#s'],
+ [';x', bases[0], 'http://a/b/c/;x'],
+ ['g;x', bases[0], 'http://a/b/c/g;x'],
+ ['g;x?y#s' , bases[0], 'http://a/b/c/g;x?y#s'],
+ // changed with RFC 2396bis
+ //('', bases[0], CURRENT_DOC_URI],
+ ['', bases[0], 'http://a/b/c/d;p?q'],
+ ['.', bases[0], 'http://a/b/c/'],
+ ['./', bases[0], 'http://a/b/c/'],
+ ['..', bases[0], 'http://a/b/'],
+ ['../', bases[0], 'http://a/b/'],
+ ['../g', bases[0], 'http://a/b/g'],
+ ['../..', bases[0], 'http://a/'],
+ ['../../', bases[0], 'http://a/'],
+ ['../../g' , bases[0], 'http://a/g'],
+ ['../../../g', bases[0], ('http://a/../g', 'http://a/g')],
+ ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')],
+ // changed with RFC 2396bis
+ //('/./g', bases[0], 'http://a/./g'],
+ ['/./g', bases[0], 'http://a/g'],
+ // changed with RFC 2396bis
+ //('/../g', bases[0], 'http://a/../g'],
+ ['/../g', bases[0], 'http://a/g'],
+ ['g.', bases[0], 'http://a/b/c/g.'],
+ ['.g', bases[0], 'http://a/b/c/.g'],
+ ['g..', bases[0], 'http://a/b/c/g..'],
+ ['..g', bases[0], 'http://a/b/c/..g'],
+ ['./../g', bases[0], 'http://a/b/g'],
+ ['./g/.', bases[0], 'http://a/b/c/g/'],
+ ['g/./h', bases[0], 'http://a/b/c/g/h'],
+ ['g/../h', bases[0], 'http://a/b/c/h'],
+ ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'],
+ ['g;x=1/../y', bases[0], 'http://a/b/c/y'],
+ ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'],
+ ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'],
+ ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'],
+ ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'],
+ ['http:g', bases[0], ('http:g', 'http://a/b/c/g')],
+ ['http:', bases[0], ('http:', bases[0])],
+ // not sure where this one originated
+ ['/a/b/c/./../../g', bases[0], 'http://a/a/g'],
+
+ // http://gbiv.com/protocols/uri/test/rel_examples2.html
+ // slashes in base URI's query args
+ ['g', bases[1], 'http://a/b/c/g'],
+ ['./g', bases[1], 'http://a/b/c/g'],
+ ['g/', bases[1], 'http://a/b/c/g/'],
+ ['/g', bases[1], 'http://a/g'],
+ ['//g', bases[1], 'http://g/'],
+ // changed in RFC 2396bis
+ //('?y', bases[1], 'http://a/b/c/?y'],
+ ['?y', bases[1], 'http://a/b/c/d;p?y'],
+ ['g?y', bases[1], 'http://a/b/c/g?y'],
+ ['g?y/./x' , bases[1], 'http://a/b/c/g?y/./x'],
+ ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'],
+ ['g#s', bases[1], 'http://a/b/c/g#s'],
+ ['g#s/./x' , bases[1], 'http://a/b/c/g#s/./x'],
+ ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'],
+ ['./', bases[1], 'http://a/b/c/'],
+ ['../', bases[1], 'http://a/b/'],
+ ['../g', bases[1], 'http://a/b/g'],
+ ['../../', bases[1], 'http://a/'],
+ ['../../g' , bases[1], 'http://a/g'],
+
+ // http://gbiv.com/protocols/uri/test/rel_examples3.html
+ // slashes in path params
+ // all of these changed in RFC 2396bis
+ ['g', bases[2], 'http://a/b/c/d;p=1/g'],
+ ['./g', bases[2], 'http://a/b/c/d;p=1/g'],
+ ['g/', bases[2], 'http://a/b/c/d;p=1/g/'],
+ ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'],
+ [';x', bases[2], 'http://a/b/c/d;p=1/;x'],
+ ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'],
+ ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'],
+ ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'],
+ ['./', bases[2], 'http://a/b/c/d;p=1/'],
+ ['../', bases[2], 'http://a/b/c/'],
+ ['../g', bases[2], 'http://a/b/c/g'],
+ ['../../', bases[2], 'http://a/b/'],
+ ['../../g' , bases[2], 'http://a/b/g'],
+
+ // http://gbiv.com/protocols/uri/test/rel_examples4.html
+ // double and triple slash, unknown scheme
+ ['g:h', bases[3], 'g:h'],
+ ['g', bases[3], 'fred:///s//a/b/g'],
+ ['./g', bases[3], 'fred:///s//a/b/g'],
+ ['g/', bases[3], 'fred:///s//a/b/g/'],
+ ['/g', bases[3], 'fred:///g'], // may change to fred:///s//a/g
+ ['//g', bases[3], 'fred://g'], // may change to fred:///s//g
+ ['//g/x', bases[3], 'fred://g/x'], // may change to fred:///s//g/x
+ ['///g', bases[3], 'fred:///g'],
+ ['./', bases[3], 'fred:///s//a/b/'],
+ ['../', bases[3], 'fred:///s//a/'],
+ ['../g', bases[3], 'fred:///s//a/g'],
+
+ ['../../', bases[3], 'fred:///s//'],
+ ['../../g' , bases[3], 'fred:///s//g'],
+ ['../../../g', bases[3], 'fred:///s/g'],
+ // may change to fred:///s//a/../../../g
+ ['../../../../g', bases[3], 'fred:///g'],
+
+ // http://gbiv.com/protocols/uri/test/rel_examples5.html
+ // double and triple slash, well-known scheme
+ ['g:h', bases[4], 'g:h'],
+ ['g', bases[4], 'http:///s//a/b/g'],
+ ['./g', bases[4], 'http:///s//a/b/g'],
+ ['g/', bases[4], 'http:///s//a/b/g/'],
+ ['/g', bases[4], 'http:///g'], // may change to http:///s//a/g
+ ['//g', bases[4], 'http://g/'], // may change to http:///s//g
+ ['//g/x', bases[4], 'http://g/x'], // may change to http:///s//g/x
+ ['///g', bases[4], 'http:///g'],
+ ['./', bases[4], 'http:///s//a/b/'],
+ ['../', bases[4], 'http:///s//a/'],
+ ['../g', bases[4], 'http:///s//a/g'],
+ ['../../', bases[4], 'http:///s//'],
+ ['../../g' , bases[4], 'http:///s//g'],
+ // may change to http:///s//a/../../g
+ ['../../../g', bases[4], 'http:///s/g'],
+ // may change to http:///s//a/../../../g
+ ['../../../../g', bases[4], 'http:///g'],
+
+ // from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py
+ ['bar:abc', 'foo:xyz', 'bar:abc'],
+ ['../abc', 'http://example/x/y/z', 'http://example/x/abc'],
+ ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'],
+ ['../r', 'http://ex/x/y/z', 'http://ex/x/r'],
+ ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'],
+ ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'],
+ ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'],
+ ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'],
+ ['', 'http://ex/x/y', 'http://ex/x/y'],
+ ['', 'http://ex/x/y/', 'http://ex/x/y/'],
+ ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'],
+ ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'],
+ ['#Animal',
+ 'file:/swap/test/animal.rdf',
+ 'file:/swap/test/animal.rdf#Animal'],
+ ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'],
+ ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'],
+ ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'],
+ ['/r', 'file:/ex/x/y/z', 'file:/r'],
+ ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'],
+ ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'],
+ ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'],
+ ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'],
+ ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'],
+ ['', 'file:/ex/x/y', 'file:/ex/x/y'],
+ ['', 'file:/ex/x/y/', 'file:/ex/x/y/'],
+ ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'],
+ ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'],
+ ['file://meetings.example.com/cal#m1',
+ 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3',
+ 'file://meetings.example.com/cal#m1'],
+ ['file://meetings.example.com/cal#m1',
+ 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3',
+ 'file://meetings.example.com/cal#m1'],
+ ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'],
+ ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'],
+ // Ryan Lee
+ ['./', 'http://example/x/abc.efg', 'http://example/x/'],
+
+
+ // Graham Klyne's tests
+ // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls
+ // 01-31 are from Connelly's cases
+
+ // 32-49
+ ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'],
+ ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'],
+ ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'],
+ ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'],
+ ['local/qual@domain.org#frag',
+ 'mailto:local',
+ 'mailto:local/qual@domain.org#frag'],
+ ['more/qual2@domain2.org#frag',
+ 'mailto:local/qual1@domain1.org',
+ 'mailto:local/more/qual2@domain2.org#frag'],
+ ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'],
+ ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'],
+ ['c/d', 'foo:a/b', 'foo:a/c/d'],
+ ['/c/d', 'foo:a/b', 'foo:/c/d'],
+ ['', 'foo:a/b?c#d', 'foo:a/b?c'],
+ ['b/c', 'foo:a', 'foo:b/c'],
+ ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'],
+ ['./b/c', 'foo:a', 'foo:b/c'],
+ ['/./b/c', 'foo:a', 'foo:/b/c'],
+ ['../../d', 'foo://a//b/c', 'foo://a/d'],
+ ['.', 'foo:a', 'foo:'],
+ ['..', 'foo:a', 'foo:'],
+
+ // 50-57[cf. TimBL comments --
+ // http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html,
+ // http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html)
+ ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'],
+ ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'],
+ ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'],
+ ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'],
+ ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'],
+ ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
+ ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'],
+ ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
+
+ // 70-77
+ ['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'],
+ ['local2@domain2?query2',
+ 'mailto:local1@domain1',
+ 'mailto:local2@domain2?query2'],
+ ['local2@domain2?query2',
+ 'mailto:local1@domain1?query1',
+ 'mailto:local2@domain2?query2'],
+ ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
+ ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'],
+ ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
+ ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'],
+ ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'],
+
+ // 82-88
+ // @isaacs Disagree. Not how browsers do it.
+ // ['http:this', 'http://example.org/base/uri', 'http:this'],
+ // @isaacs Added
+ ['http:this', 'http://example.org/base/uri', 'http://example.org/base/this'],
+ ['http:this', 'http:base', 'http:this'],
+ ['.//g', 'f:/a', 'f://g'],
+ ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'],
+ ['m2@example.ord/c2@example.org',
+ 'mid:m@example.ord/c@example.org',
+ 'mid:m@example.ord/m2@example.ord/c2@example.org'],
+ ['mini1.xml',
+ 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/',
+ 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'],
+ ['../b/c', 'foo:a/y/z', 'foo:a/b/c'],
+
+ //changeing auth
+ ['http://diff:auth@www.example.com',
+ 'http://asdf:qwer@www.example.com',
+ 'http://diff:auth@www.example.com/']
+];
+
+relativeTests2.forEach(function(relativeTest) {
+ test('resolve(' + [relativeTest[1], relativeTest[0]] + ')', function() {
+ var a = url.resolve(relativeTest[1], relativeTest[0]),
+ e = relativeTest[2];
+ assert.equal(a, e,
+ 'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e +
+ '\n actual=' + a);
+ });
+});
+
+//if format and parse are inverse operations then
+//resolveObject(parse(x), y) == parse(resolve(x, y))
+
+//host and hostname are special, in this case a '' value is important
+var emptyIsImportant = {'host': true, 'hostname': ''};
+
+//format: [from, path, expected]
+relativeTests.forEach(function(relativeTest) {
+test('resolveObject(' + [relativeTest[0], relativeTest[1]] + ')', function() {
+ var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]),
+ expected = url.parse(relativeTest[2]);
+
+
+ assert.deepEqual(actual, expected);
+
+ expected = relativeTest[2];
+ actual = url.format(actual);
+
+ assert.equal(actual, expected,
+ 'format(' + actual + ') == ' + expected + '\nactual:' + actual);
+ });
+});
+
+//format: [to, from, result]
+// the test: ['.//g', 'f:/a', 'f://g'] is a fundamental problem
+// url.parse('f:/a') does not have a host
+// url.resolve('f:/a', './/g') does not have a host because you have moved
+// down to the g directory. i.e. f: //g, however when this url is parsed
+// f:// will indicate that the host is g which is not the case.
+// it is unclear to me how to keep this information from being lost
+// it may be that a pathname of ////g should collapse to /g but this seems
+// to be a lot of work for an edge case. Right now I remove the test
+if (relativeTests2[181][0] === './/g' &&
+ relativeTests2[181][1] === 'f:/a' &&
+ relativeTests2[181][2] === 'f://g') {
+ relativeTests2.splice(181, 1);
+}
+
+relativeTests2.forEach(function(relativeTest) {
+ test('resolveObject(' + [relativeTest[1], relativeTest[0]] + ')', function() {
+ var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]),
+ expected = url.parse(relativeTest[2]);
+
+ assert.deepEqual(actual, expected);
+
+ var expected = relativeTest[2],
+ actual = url.format(actual);
+
+ assert.equal(actual, expected,
+ 'format(' + relativeTest[1] + ') == ' + expected +
+ '\nactual:' + actual);
+ });
+});
diff --git a/node_modules/url/url.js b/node_modules/url/url.js
new file mode 100644
index 000000000..23ac6f5db
--- /dev/null
+++ b/node_modules/url/url.js
@@ -0,0 +1,732 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict';
+
+var punycode = require('punycode');
+var util = require('./util');
+
+exports.parse = urlParse;
+exports.resolve = urlResolve;
+exports.resolveObject = urlResolveObject;
+exports.format = urlFormat;
+
+exports.Url = Url;
+
+function Url() {
+ this.protocol = null;
+ this.slashes = null;
+ this.auth = null;
+ this.host = null;
+ this.port = null;
+ this.hostname = null;
+ this.hash = null;
+ this.search = null;
+ this.query = null;
+ this.pathname = null;
+ this.path = null;
+ this.href = null;
+}
+
+// Reference: RFC 3986, RFC 1808, RFC 2396
+
+// define these here so at least they only have to be
+// compiled once on the first module load.
+var protocolPattern = /^([a-z0-9.+-]+:)/i,
+ portPattern = /:[0-9]*$/,
+
+ // Special case for a simple path URL
+ simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
+
+ // RFC 2396: characters reserved for delimiting URLs.
+ // We actually just auto-escape these.
+ delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
+
+ // RFC 2396: characters not allowed for various reasons.
+ unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
+
+ // Allowed by RFCs, but cause of XSS attacks. Always escape these.
+ autoEscape = ['\''].concat(unwise),
+ // Characters that are never ever allowed in a hostname.
+ // Note that any invalid chars are also handled, but these
+ // are the ones that are *expected* to be seen, so we fast-path
+ // them.
+ nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
+ hostEndingChars = ['/', '?', '#'],
+ hostnameMaxLen = 255,
+ hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
+ hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
+ // protocols that can allow "unsafe" and "unwise" chars.
+ unsafeProtocol = {
+ 'javascript': true,
+ 'javascript:': true
+ },
+ // protocols that never have a hostname.
+ hostlessProtocol = {
+ 'javascript': true,
+ 'javascript:': true
+ },
+ // protocols that always contain a // bit.
+ slashedProtocol = {
+ 'http': true,
+ 'https': true,
+ 'ftp': true,
+ 'gopher': true,
+ 'file': true,
+ 'http:': true,
+ 'https:': true,
+ 'ftp:': true,
+ 'gopher:': true,
+ 'file:': true
+ },
+ querystring = require('querystring');
+
+function urlParse(url, parseQueryString, slashesDenoteHost) {
+ if (url && util.isObject(url) && url instanceof Url) return url;
+
+ var u = new Url;
+ u.parse(url, parseQueryString, slashesDenoteHost);
+ return u;
+}
+
+Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
+ if (!util.isString(url)) {
+ throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
+ }
+
+ // Copy chrome, IE, opera backslash-handling behavior.
+ // Back slashes before the query string get converted to forward slashes
+ // See: https://code.google.com/p/chromium/issues/detail?id=25916
+ var queryIndex = url.indexOf('?'),
+ splitter =
+ (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
+ uSplit = url.split(splitter),
+ slashRegex = /\\/g;
+ uSplit[0] = uSplit[0].replace(slashRegex, '/');
+ url = uSplit.join(splitter);
+
+ var rest = url;
+
+ // trim before proceeding.
+ // This is to support parse stuff like " http://foo.com \n"
+ rest = rest.trim();
+
+ if (!slashesDenoteHost && url.split('#').length === 1) {
+ // Try fast path regexp
+ var simplePath = simplePathPattern.exec(rest);
+ if (simplePath) {
+ this.path = rest;
+ this.href = rest;
+ this.pathname = simplePath[1];
+ if (simplePath[2]) {
+ this.search = simplePath[2];
+ if (parseQueryString) {
+ this.query = querystring.parse(this.search.substr(1));
+ } else {
+ this.query = this.search.substr(1);
+ }
+ } else if (parseQueryString) {
+ this.search = '';
+ this.query = {};
+ }
+ return this;
+ }
+ }
+
+ var proto = protocolPattern.exec(rest);
+ if (proto) {
+ proto = proto[0];
+ var lowerProto = proto.toLowerCase();
+ this.protocol = lowerProto;
+ rest = rest.substr(proto.length);
+ }
+
+ // figure out if it's got a host
+ // user@server is *always* interpreted as a hostname, and url
+ // resolution will treat //foo/bar as host=foo,path=bar because that's
+ // how the browser resolves relative URLs.
+ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+ var slashes = rest.substr(0, 2) === '//';
+ if (slashes && !(proto && hostlessProtocol[proto])) {
+ rest = rest.substr(2);
+ this.slashes = true;
+ }
+ }
+
+ if (!hostlessProtocol[proto] &&
+ (slashes || (proto && !slashedProtocol[proto]))) {
+
+ // there's a hostname.
+ // the first instance of /, ?, ;, or # ends the host.
+ //
+ // If there is an @ in the hostname, then non-host chars *are* allowed
+ // to the left of the last @ sign, unless some host-ending character
+ // comes *before* the @-sign.
+ // URLs are obnoxious.
+ //
+ // ex:
+ // http://a@b@c/ => user:a@b host:c
+ // http://a@b?@c => user:a host:c path:/?@c
+
+ // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+ // Review our test case against browsers more comprehensively.
+
+ // find the first instance of any hostEndingChars
+ var hostEnd = -1;
+ for (var i = 0; i < hostEndingChars.length; i++) {
+ var hec = rest.indexOf(hostEndingChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+ hostEnd = hec;
+ }
+
+ // at this point, either we have an explicit point where the
+ // auth portion cannot go past, or the last @ char is the decider.
+ var auth, atSign;
+ if (hostEnd === -1) {
+ // atSign can be anywhere.
+ atSign = rest.lastIndexOf('@');
+ } else {
+ // atSign must be in auth portion.
+ // http://a@b/c@d => host:b auth:a path:/c@d
+ atSign = rest.lastIndexOf('@', hostEnd);
+ }
+
+ // Now we have a portion which is definitely the auth.
+ // Pull that off.
+ if (atSign !== -1) {
+ auth = rest.slice(0, atSign);
+ rest = rest.slice(atSign + 1);
+ this.auth = decodeURIComponent(auth);
+ }
+
+ // the host is the remaining to the left of the first non-host char
+ hostEnd = -1;
+ for (var i = 0; i < nonHostChars.length; i++) {
+ var hec = rest.indexOf(nonHostChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+ hostEnd = hec;
+ }
+ // if we still have not hit it, then the entire thing is a host.
+ if (hostEnd === -1)
+ hostEnd = rest.length;
+
+ this.host = rest.slice(0, hostEnd);
+ rest = rest.slice(hostEnd);
+
+ // pull out port.
+ this.parseHost();
+
+ // we've indicated that there is a hostname,
+ // so even if it's empty, it has to be present.
+ this.hostname = this.hostname || '';
+
+ // if hostname begins with [ and ends with ]
+ // assume that it's an IPv6 address.
+ var ipv6Hostname = this.hostname[0] === '[' &&
+ this.hostname[this.hostname.length - 1] === ']';
+
+ // validate a little.
+ if (!ipv6Hostname) {
+ var hostparts = this.hostname.split(/\./);
+ for (var i = 0, l = hostparts.length; i < l; i++) {
+ var part = hostparts[i];
+ if (!part) continue;
+ if (!part.match(hostnamePartPattern)) {
+ var newpart = '';
+ for (var j = 0, k = part.length; j < k; j++) {
+ if (part.charCodeAt(j) > 127) {
+ // we replace non-ASCII char with a temporary placeholder
+ // we need this to make sure size of hostname is not
+ // broken by replacing non-ASCII by nothing
+ newpart += 'x';
+ } else {
+ newpart += part[j];
+ }
+ }
+ // we test again with ASCII char only
+ if (!newpart.match(hostnamePartPattern)) {
+ var validParts = hostparts.slice(0, i);
+ var notHost = hostparts.slice(i + 1);
+ var bit = part.match(hostnamePartStart);
+ if (bit) {
+ validParts.push(bit[1]);
+ notHost.unshift(bit[2]);
+ }
+ if (notHost.length) {
+ rest = '/' + notHost.join('.') + rest;
+ }
+ this.hostname = validParts.join('.');
+ break;
+ }
+ }
+ }
+ }
+
+ if (this.hostname.length > hostnameMaxLen) {
+ this.hostname = '';
+ } else {
+ // hostnames are always lower case.
+ this.hostname = this.hostname.toLowerCase();
+ }
+
+ if (!ipv6Hostname) {
+ // IDNA Support: Returns a punycoded representation of "domain".
+ // It only converts parts of the domain name that
+ // have non-ASCII characters, i.e. it doesn't matter if
+ // you call it with a domain that already is ASCII-only.
+ this.hostname = punycode.toASCII(this.hostname);
+ }
+
+ var p = this.port ? ':' + this.port : '';
+ var h = this.hostname || '';
+ this.host = h + p;
+ this.href += this.host;
+
+ // strip [ and ] from the hostname
+ // the host field still retains them, though
+ if (ipv6Hostname) {
+ this.hostname = this.hostname.substr(1, this.hostname.length - 2);
+ if (rest[0] !== '/') {
+ rest = '/' + rest;
+ }
+ }
+ }
+
+ // now rest is set to the post-host stuff.
+ // chop off any delim chars.
+ if (!unsafeProtocol[lowerProto]) {
+
+ // First, make 100% sure that any "autoEscape" chars get
+ // escaped, even if encodeURIComponent doesn't think they
+ // need to be.
+ for (var i = 0, l = autoEscape.length; i < l; i++) {
+ var ae = autoEscape[i];
+ if (rest.indexOf(ae) === -1)
+ continue;
+ var esc = encodeURIComponent(ae);
+ if (esc === ae) {
+ esc = escape(ae);
+ }
+ rest = rest.split(ae).join(esc);
+ }
+ }
+
+
+ // chop off from the tail first.
+ var hash = rest.indexOf('#');
+ if (hash !== -1) {
+ // got a fragment string.
+ this.hash = rest.substr(hash);
+ rest = rest.slice(0, hash);
+ }
+ var qm = rest.indexOf('?');
+ if (qm !== -1) {
+ this.search = rest.substr(qm);
+ this.query = rest.substr(qm + 1);
+ if (parseQueryString) {
+ this.query = querystring.parse(this.query);
+ }
+ rest = rest.slice(0, qm);
+ } else if (parseQueryString) {
+ // no query string, but parseQueryString still requested
+ this.search = '';
+ this.query = {};
+ }
+ if (rest) this.pathname = rest;
+ if (slashedProtocol[lowerProto] &&
+ this.hostname && !this.pathname) {
+ this.pathname = '/';
+ }
+
+ //to support http.request
+ if (this.pathname || this.search) {
+ var p = this.pathname || '';
+ var s = this.search || '';
+ this.path = p + s;
+ }
+
+ // finally, reconstruct the href based on what has been validated.
+ this.href = this.format();
+ return this;
+};
+
+// format a parsed object into a url string
+function urlFormat(obj) {
+ // ensure it's an object, and not a string url.
+ // If it's an obj, this is a no-op.
+ // this way, you can call url_format() on strings
+ // to clean up potentially wonky urls.
+ if (util.isString(obj)) obj = urlParse(obj);
+ if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
+ return obj.format();
+}
+
+Url.prototype.format = function() {
+ var auth = this.auth || '';
+ if (auth) {
+ auth = encodeURIComponent(auth);
+ auth = auth.replace(/%3A/i, ':');
+ auth += '@';
+ }
+
+ var protocol = this.protocol || '',
+ pathname = this.pathname || '',
+ hash = this.hash || '',
+ host = false,
+ query = '';
+
+ if (this.host) {
+ host = auth + this.host;
+ } else if (this.hostname) {
+ host = auth + (this.hostname.indexOf(':') === -1 ?
+ this.hostname :
+ '[' + this.hostname + ']');
+ if (this.port) {
+ host += ':' + this.port;
+ }
+ }
+
+ if (this.query &&
+ util.isObject(this.query) &&
+ Object.keys(this.query).length) {
+ query = querystring.stringify(this.query);
+ }
+
+ var search = this.search || (query && ('?' + query)) || '';
+
+ if (protocol && protocol.substr(-1) !== ':') protocol += ':';
+
+ // only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
+ // unless they had them to begin with.
+ if (this.slashes ||
+ (!protocol || slashedProtocol[protocol]) && host !== false) {
+ host = '//' + (host || '');
+ if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
+ } else if (!host) {
+ host = '';
+ }
+
+ if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
+ if (search && search.charAt(0) !== '?') search = '?' + search;
+
+ pathname = pathname.replace(/[?#]/g, function(match) {
+ return encodeURIComponent(match);
+ });
+ search = search.replace('#', '%23');
+
+ return protocol + host + pathname + search + hash;
+};
+
+function urlResolve(source, relative) {
+ return urlParse(source, false, true).resolve(relative);
+}
+
+Url.prototype.resolve = function(relative) {
+ return this.resolveObject(urlParse(relative, false, true)).format();
+};
+
+function urlResolveObject(source, relative) {
+ if (!source) return relative;
+ return urlParse(source, false, true).resolveObject(relative);
+}
+
+Url.prototype.resolveObject = function(relative) {
+ if (util.isString(relative)) {
+ var rel = new Url();
+ rel.parse(relative, false, true);
+ relative = rel;
+ }
+
+ var result = new Url();
+ var tkeys = Object.keys(this);
+ for (var tk = 0; tk < tkeys.length; tk++) {
+ var tkey = tkeys[tk];
+ result[tkey] = this[tkey];
+ }
+
+ // hash is always overridden, no matter what.
+ // even href="" will remove it.
+ result.hash = relative.hash;
+
+ // if the relative url is empty, then there's nothing left to do here.
+ if (relative.href === '') {
+ result.href = result.format();
+ return result;
+ }
+
+ // hrefs like //foo/bar always cut to the protocol.
+ if (relative.slashes && !relative.protocol) {
+ // take everything except the protocol from relative
+ var rkeys = Object.keys(relative);
+ for (var rk = 0; rk < rkeys.length; rk++) {
+ var rkey = rkeys[rk];
+ if (rkey !== 'protocol')
+ result[rkey] = relative[rkey];
+ }
+
+ //urlParse appends trailing / to urls like http://www.example.com
+ if (slashedProtocol[result.protocol] &&
+ result.hostname && !result.pathname) {
+ result.path = result.pathname = '/';
+ }
+
+ result.href = result.format();
+ return result;
+ }
+
+ if (relative.protocol && relative.protocol !== result.protocol) {
+ // if it's a known url protocol, then changing
+ // the protocol does weird things
+ // first, if it's not file:, then we MUST have a host,
+ // and if there was a path
+ // to begin with, then we MUST have a path.
+ // if it is file:, then the host is dropped,
+ // because that's known to be hostless.
+ // anything else is assumed to be absolute.
+ if (!slashedProtocol[relative.protocol]) {
+ var keys = Object.keys(relative);
+ for (var v = 0; v < keys.length; v++) {
+ var k = keys[v];
+ result[k] = relative[k];
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ result.protocol = relative.protocol;
+ if (!relative.host && !hostlessProtocol[relative.protocol]) {
+ var relPath = (relative.pathname || '').split('/');
+ while (relPath.length && !(relative.host = relPath.shift()));
+ if (!relative.host) relative.host = '';
+ if (!relative.hostname) relative.hostname = '';
+ if (relPath[0] !== '') relPath.unshift('');
+ if (relPath.length < 2) relPath.unshift('');
+ result.pathname = relPath.join('/');
+ } else {
+ result.pathname = relative.pathname;
+ }
+ result.search = relative.search;
+ result.query = relative.query;
+ result.host = relative.host || '';
+ result.auth = relative.auth;
+ result.hostname = relative.hostname || relative.host;
+ result.port = relative.port;
+ // to support http.request
+ if (result.pathname || result.search) {
+ var p = result.pathname || '';
+ var s = result.search || '';
+ result.path = p + s;
+ }
+ result.slashes = result.slashes || relative.slashes;
+ result.href = result.format();
+ return result;
+ }
+
+ var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
+ isRelAbs = (
+ relative.host ||
+ relative.pathname && relative.pathname.charAt(0) === '/'
+ ),
+ mustEndAbs = (isRelAbs || isSourceAbs ||
+ (result.host && relative.pathname)),
+ removeAllDots = mustEndAbs,
+ srcPath = result.pathname && result.pathname.split('/') || [],
+ relPath = relative.pathname && relative.pathname.split('/') || [],
+ psychotic = result.protocol && !slashedProtocol[result.protocol];
+
+ // if the url is a non-slashed url, then relative
+ // links like ../.. should be able
+ // to crawl up to the hostname, as well. This is strange.
+ // result.protocol has already been set by now.
+ // Later on, put the first path part into the host field.
+ if (psychotic) {
+ result.hostname = '';
+ result.port = null;
+ if (result.host) {
+ if (srcPath[0] === '') srcPath[0] = result.host;
+ else srcPath.unshift(result.host);
+ }
+ result.host = '';
+ if (relative.protocol) {
+ relative.hostname = null;
+ relative.port = null;
+ if (relative.host) {
+ if (relPath[0] === '') relPath[0] = relative.host;
+ else relPath.unshift(relative.host);
+ }
+ relative.host = null;
+ }
+ mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
+ }
+
+ if (isRelAbs) {
+ // it's absolute.
+ result.host = (relative.host || relative.host === '') ?
+ relative.host : result.host;
+ result.hostname = (relative.hostname || relative.hostname === '') ?
+ relative.hostname : result.hostname;
+ result.search = relative.search;
+ result.query = relative.query;
+ srcPath = relPath;
+ // fall through to the dot-handling below.
+ } else if (relPath.length) {
+ // it's relative
+ // throw away the existing file, and take the new path instead.
+ if (!srcPath) srcPath = [];
+ srcPath.pop();
+ srcPath = srcPath.concat(relPath);
+ result.search = relative.search;
+ result.query = relative.query;
+ } else if (!util.isNullOrUndefined(relative.search)) {
+ // just pull out the search.
+ // like href='?foo'.
+ // Put this after the other two cases because it simplifies the booleans
+ if (psychotic) {
+ result.hostname = result.host = srcPath.shift();
+ //occationaly the auth can get stuck only in host
+ //this especially happens in cases like
+ //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+ var authInHost = result.host && result.host.indexOf('@') > 0 ?
+ result.host.split('@') : false;
+ if (authInHost) {
+ result.auth = authInHost.shift();
+ result.host = result.hostname = authInHost.shift();
+ }
+ }
+ result.search = relative.search;
+ result.query = relative.query;
+ //to support http.request
+ if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+ result.path = (result.pathname ? result.pathname : '') +
+ (result.search ? result.search : '');
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ if (!srcPath.length) {
+ // no path at all. easy.
+ // we've already handled the other stuff above.
+ result.pathname = null;
+ //to support http.request
+ if (result.search) {
+ result.path = '/' + result.search;
+ } else {
+ result.path = null;
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ // if a url ENDs in . or .., then it must get a trailing slash.
+ // however, if it ends in anything else non-slashy,
+ // then it must NOT get a trailing slash.
+ var last = srcPath.slice(-1)[0];
+ var hasTrailingSlash = (
+ (result.host || relative.host || srcPath.length > 1) &&
+ (last === '.' || last === '..') || last === '');
+
+ // strip single dots, resolve double dots to parent dir
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = srcPath.length; i >= 0; i--) {
+ last = srcPath[i];
+ if (last === '.') {
+ srcPath.splice(i, 1);
+ } else if (last === '..') {
+ srcPath.splice(i, 1);
+ up++;
+ } else if (up) {
+ srcPath.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (!mustEndAbs && !removeAllDots) {
+ for (; up--; up) {
+ srcPath.unshift('..');
+ }
+ }
+
+ if (mustEndAbs && srcPath[0] !== '' &&
+ (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
+ srcPath.unshift('');
+ }
+
+ if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
+ srcPath.push('');
+ }
+
+ var isAbsolute = srcPath[0] === '' ||
+ (srcPath[0] && srcPath[0].charAt(0) === '/');
+
+ // put the host back
+ if (psychotic) {
+ result.hostname = result.host = isAbsolute ? '' :
+ srcPath.length ? srcPath.shift() : '';
+ //occationaly the auth can get stuck only in host
+ //this especially happens in cases like
+ //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+ var authInHost = result.host && result.host.indexOf('@') > 0 ?
+ result.host.split('@') : false;
+ if (authInHost) {
+ result.auth = authInHost.shift();
+ result.host = result.hostname = authInHost.shift();
+ }
+ }
+
+ mustEndAbs = mustEndAbs || (result.host && srcPath.length);
+
+ if (mustEndAbs && !isAbsolute) {
+ srcPath.unshift('');
+ }
+
+ if (!srcPath.length) {
+ result.pathname = null;
+ result.path = null;
+ } else {
+ result.pathname = srcPath.join('/');
+ }
+
+ //to support request.http
+ if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+ result.path = (result.pathname ? result.pathname : '') +
+ (result.search ? result.search : '');
+ }
+ result.auth = relative.auth || result.auth;
+ result.slashes = result.slashes || relative.slashes;
+ result.href = result.format();
+ return result;
+};
+
+Url.prototype.parseHost = function() {
+ var host = this.host;
+ var port = portPattern.exec(host);
+ if (port) {
+ port = port[0];
+ if (port !== ':') {
+ this.port = port.substr(1);
+ }
+ host = host.substr(0, host.length - port.length);
+ }
+ if (host) this.hostname = host;
+};
diff --git a/node_modules/url/util.js b/node_modules/url/util.js
new file mode 100644
index 000000000..97dcf31cd
--- /dev/null
+++ b/node_modules/url/util.js
@@ -0,0 +1,16 @@
+'use strict';
+
+module.exports = {
+ isString: function(arg) {
+ return typeof(arg) === 'string';
+ },
+ isObject: function(arg) {
+ return typeof(arg) === 'object' && arg !== null;
+ },
+ isNull: function(arg) {
+ return arg === null;
+ },
+ isNullOrUndefined: function(arg) {
+ return arg == null;
+ }
+};