package/package.json000644 000765 000024 0000002217 12614700774013027 0ustar00000000 000000 { "name": "url-parse", "version": "1.0.5", "description": "Small footprint URL parser that works seamlessly across Node.js and browser environments", "main": "index.js", "scripts": { "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", "browserify": "mkdir -p dist && browserify index.js -s URLParse -o dist/url-parse.js", "test-node": "istanbul cover _mocha --report lcovonly -- test.js", "coverage": "istanbul cover _mocha -- test.js", "test-browser": "zuul -- test.js", "watch": "mocha --watch test.js", "test": "mocha test.js" }, "repository": { "type": "git", "url": "https://github.com/unshiftio/url-parse.git" }, "keywords": [ "URL", "parser", "uri", "url", "parse", "query", "string", "querystring", "stringify" ], "author": "Arnout Kazemier", "license": "MIT", "dependencies": { "querystringify": "0.0.x", "requires-port": "1.0.x" }, "devDependencies": { "assume": "1.3.x", "browserify": "12.0.x", "istanbul": "0.4.x", "mocha": "2.3.x", "pre-commit": "1.1.x", "zuul": "3.7.x" } } package/.npmignore000644 000765 000024 0000000104 12427374314012530 0ustar00000000 000000 node_modules coverage .tern-port browserified.js npm-debug.log dist package/README.md000644 000765 000024 0000012047 12614700740012013 0ustar00000000 000000 # url-parse [![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](https://img.shields.io/npm/v/url-parse.svg?style=flat-square)](http://browsenpm.org/package/url-parse)[![Build Status](https://img.shields.io/travis/unshiftio/url-parse/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/url-parse)[![Dependencies](https://img.shields.io/david/unshiftio/url-parse.svg?style=flat-square)](https://david-dm.org/unshiftio/url-parse)[![Coverage Status](https://img.shields.io/coveralls/unshiftio/url-parse/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/url-parse?branch=master)[![IRC channel](https://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](https://webchat.freenode.net/?channels=unshift) [![Sauce Test Status](https://saucelabs.com/browser-matrix/url-parse.svg)](https://saucelabs.com/u/url-parse) The `url-parse` method exposes two different API interfaces. The [`url`](https://nodejs.org/api/url.html) interface that you know from Node.js and the new [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) interface that is available in the latest browsers. Since `0.1` we've moved away from using the DOM's `` element for URL parsing and moving to a full Regular Expression solution. The main reason for this change is to make the URL parser available in different JavaScript environments as you don't always have access to the DOM like `Worker` environments. This module still have a really small foot print as this module's main intention is to be bundled with client-side code. The only problem however with a RegExp based solution is that it required a lot of lookups causing major problems in FireFox. So the last and the current solution was a pure string parsing solution which chops up the URL in smaller pieces. In addition to URL parsing we also expose the bundled `querystringify` module. ## Installation This module is designed to be used using either browserify or node.js it's released in the public npm registry and can be installed using: ``` npm install url-parse ``` ## Usage All examples assume that this library is bootstrapped using: ```js 'use strict'; var URL = require('url-parse'); ``` To parse an URL simply call the `URL` method with the URL that needs to be transformed in to an object. ```js var url = new URL('https://github.com/foo/bar'); ``` The `new` keyword is optional but it will save you an extra function invocation. In the example above we've demonstrated the URL interface, but as said in the module description we also support the node.js interface. So you could also use the library in this way: ```js 'use strict'; var parse = require('url-parse') , url = parse('https://github.com/foo/bar', true); ``` The returned `url` instance contains the following properties: - `protocol`: Requested protocol without slashes (e.g. `http:`). - `username`: Username of basic authentication. - `password`: Password of basic authentication. - `auth`: Authentication information portion (e.g. `username:password`). - `host`: Host name with port number. - `hostname`: Host name without port number. - `port`: Optional port number. - `pathname`: URL path. - `query`: Parsed object containing query string, unless parsing is set to false. - `hash`: The "fragment" portion of the URL including the pound-sign (`#`). - `href`: The full URL. ### URL.set(key, value) A simple helper function to change parts of the URL and propagating it through all properties. When you set a new `host` you want the same value to be applied to `port` if has a different port number, `hostname` so it has a correct name again and `href` so you have a complete URL. ```js var parsed = parse('http://google.com/parse-things'); parsed.set('hostname', 'yahoo.com'); console.log(parsed.href); // http://yahoo.com/parse-things ``` It's aware of default ports so you cannot set a port 80 on an URL which has `http` as protocol. ### URL.toString() The returned `url` object comes with a custom `toString` method which will generate a full URL again when called. The method accepts an extra function which will stringify the query string for you. If you don't supply a function we will use our default method. ```js var location = url.toString(); // http://example.com/whatever/?qs=32 ``` You would rarely need to use this method as the full URL is also available as `href` property. If you are using the `URL.set` method to make changes, this will automatically update. ## Testing The testing of this module is done in 3 different ways: 1. We have unit tests setup which run under Node.js using the normal `npm test` command. 2. Code coverage can be run manually using `npm run coverage` 3. For browser testing we use `testling` to startup a test server. We do assume that you `testling` installed globally, if not please run `npm install -g testling` and after that `testling -u` in the root of this repository. When you visit the outputted URL all unit tests that were written from the Node can now be ran inside browsers. ## License [MIT](LICENSE) package/LICENSE000644 000765 000024 0000002133 12467445554011553 0ustar00000000 000000 The MIT License (MIT) Copyright (c) 2015 Unshift.io, Arnout Kazemier, the 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. package/test.js000644 000765 000024 0000031221 12605727505012054 0ustar00000000 000000 describe('url-parse', function () { 'use strict'; var assume = require('assume') , parse = require('./'); it('exposes parse as a function', function () { assume(parse).is.a('function'); }); it('exposes the querystring module', function () { assume(parse.qs).equals(require('querystringify')); }); it('exposes the location function', function () { assume(parse.location).equals(require('./lolcation')); }); it('parses the query string into an object', function () { var url = 'http://google.com/?foo=bar' , data = parse(url, true); assume(data.query).is.a('object'); assume(data.query.foo).equals('bar'); url = 'http://google.com/'; data = parse(url, true); assume(data.query).is.a('object'); assume(data.query).is.empty(); }); it('does not add question mark to href if query string is empty', function () { var url = 'http://google.com/' , data = parse(url, true); assume(data.href).equals(url); }); it('allows a custom function as parser', function () { var url = 'http://google.com/?foo=bar' , data = parse(url, function () { return '1337'; }); assume(data.query).equals('1337'); }); it('allows a custom stringify function', function () { var url = 'http://google.com/?foo=bar' , data = parse(url, true) , str; str = data.toString(function () { return 'lolcakes'; }); assume(str).equals('http://google.com/?lolcakes'); }); it('allows a custom location object', function () { var url = '/foo?foo=bar' , data = parse(url, parse('http://google.com')); assume(data.href).equals('http://google.com/foo?foo=bar'); }); it('is blob: location aware', function () { var blob = { 'href': 'blob:https%3A//gist.github.com/3f272586-6dac-4e29-92d0-f674f2dde618', 'pathname': 'https%3A//gist.github.com/3f272586-6dac-4e29-92d0-f674f2dde618', 'origin': 'https://gist.github.com', 'protocol': 'blob:', 'hostname': '', 'search': '', 'hash': '', 'host': '', 'port': '' }; var url = '/unshiftio/url-parse' , data = parse(url, blob); assume(data.href).equals('https://gist.github.com/unshiftio/url-parse'); }); it('converts protocol to lowercase', function () { var url = 'HTTP://example.com'; assume(parse(url).protocol).equals('http:'); }); it('can parse complex urls multiple times without errors', function () { var url = 'https://www.mozilla.org/en-US/firefox/34.0/whatsnew/?oldversion=33.1'; for (var i = 0; i < 100; i++) { parse(url); } }); it('converts hostname to lowercase', function () { var url = 'HTTP://fOo.eXaMPle.com'; assume(parse(url).hostname).equals('foo.example.com'); }); it('does not lowercase the path', function () { var url = 'HTTP://X.COM/Y/Z'; assume(parse(url).pathname).equals('/Y/Z'); }); it('removes default port numbers', function () { var url = 'http://example.com:80' , parsed = parse(url); assume(parsed.port).equals(''); assume(parsed.host).equals('example.com'); assume(parsed.hostname).equals('example.com'); assume(parsed.href).equals('http://example.com'); }); it('understands an / as pathname', function () { var url = 'http://example.com:80/' , parsed = parse(url); assume(parsed.port).equals(''); assume(parsed.username).equals(''); assume(parsed.password).equals(''); assume(parsed.pathname).equals('/'); assume(parsed.host).equals('example.com'); assume(parsed.hostname).equals('example.com'); assume(parsed.href).equals('http://example.com/'); }); it('does not care about spaces', function () { var url = 'http://x.com/path?that\'s#all, folks' , parsed = parse(url); assume(parsed.port).equals(''); assume(parsed.username).equals(''); assume(parsed.password).equals(''); assume(parsed.pathname).equals('/path'); assume(parsed.hash).equal('#all, folks'); assume(parsed.query).equal('?that\'s'); assume(parsed.host).equals('x.com'); assume(parsed.hostname).equals('x.com'); }); it('accepts + in the url', function () { var url = 'http://x.y.com+a/b/c' , parsed = parse(url); assume(parsed.protocol).equals('http:'); assume(parsed.host).equals('x.y.com+a'); assume(parsed.hostname).equals('x.y.com+a'); assume(parsed.pathname).equals('/b/c'); }); describe('ip', function () { // coap:// // it('parses ipv6', function () { var url = 'http://[1080:0:0:0:8:800:200C:417A]:61616/foo/bar?q=z' , parsed = parse(url); assume(parsed.port).equals('61616'); assume(parsed.query).equals('?q=z'); assume(parsed.protocol).equals('http:'); assume(parsed.hostname).equals('[1080:0:0:0:8:800:200c:417a]'); assume(parsed.pathname).equals('/foo/bar'); assume(parsed.href).equals('http://[1080:0:0:0:8:800:200c:417a]:61616/foo/bar?q=z'); }); it('parses ipv6 with auth', function () { var url = 'http://user:password@[3ffe:2a00:100:7031::1]:8080' , parsed = parse(url); assume(parsed.username).equals('user'); assume(parsed.password).equals('password'); assume(parsed.host).equals('[3ffe:2a00:100:7031::1]:8080'); assume(parsed.hostname).equals('[3ffe:2a00:100:7031::1]'); assume(parsed.href).equals(url); }); it('parses ipv4', function () { var url = 'http://222.148.142.13:61616/foo/bar?q=z' , parsed = parse(url); assume(parsed.port).equals('61616'); assume(parsed.query).equals('?q=z'); assume(parsed.protocol).equals('http:'); assume(parsed.hostname).equals('222.148.142.13'); assume(parsed.pathname).equals('/foo/bar'); assume(parsed.href).equals(url); }); }); describe('auth', function () { it('does not lowercase the USER:PASS', function () { var url = 'HTTP://USER:PASS@EXAMPLE.COM' , parsed = parse(url); assume(parsed.username).equals('USER'); assume(parsed.password).equals('PASS'); assume(parsed.protocol).equals('http:'); assume(parsed.host).equals('example.com'); assume(parsed.hostname).equals('example.com'); }); it('accepts @ in pathnames', function () { var url = 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' , parsed = parse(url); assume(parsed.pathname).equals('/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s='); assume(parsed.username).equals(''); assume(parsed.password).equals(''); }); it('does not require passwords for auth', function () { var url = 'http://user@www.example.com/' , parsed = parse(url); assume(parsed.password).equals(''); assume(parsed.pathname).equals('/'); assume(parsed.username).equals('user'); assume(parsed.protocol).equals('http:'); assume(parsed.hostname).equals('www.example.com'); assume(parsed.href).equals(url); }); }); it('accepts multiple ???', function () { var url = 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s='; assume(parse(url).query).equals('???&hl=en&src=api&x=2&y=2&z=3&s='); }); it('accepts a string as source argument', function () { var data = parse('/foo', 'http://sub.example.com/bar?foo=bar#hash'); assume(data.port).equals(''); assume(data.host).equals('sub.example.com'); assume(data.href).equals('http://sub.example.com/foo'); }); describe('inheritance', function () { it('does not inherit port numbers for non relative urls', function () { var data = parse('http://localhost', parse('http://sub.example.com:808/')); assume(data.port).equals(''); assume(data.host).equals('localhost'); assume(data.href).equals('http://localhost'); }); it('does inherit port numbers from relative urls', function () { var data = parse('/foo', parse('http://sub.example.com:808/')); assume(data.port).equals('808'); assume(data.hostname).equals('sub.example.com'); assume(data.host).equals('sub.example.com:808'); assume(data.href).equals('http://sub.example.com:808/foo'); }); it('inherits protocol for relative protocols', function () { var data = parse('//foo.com/foo', parse('http://sub.example.com:808/')); assume(data.port).equals(''); assume(data.host).equals('foo.com'); assume(data.protocol).equals('http:'); assume(data.href).equals('http://foo.com/foo'); }); it('does not inherit pathnames from the source', function () { var data = parse('http://localhost', parse('http://foo:bar@sub.example.com/bar?foo=bar#hash')); assume(data.port).equals(''); assume(data.host).equals('localhost'); assume(data.href).equals('http://localhost'); }); it('does not inherit hashes and query strings from source object', function () { var data = parse('/foo', parse('http://sub.example.com/bar?foo=bar#hash')); assume(data.port).equals(''); assume(data.host).equals('sub.example.com'); assume(data.href).equals('http://sub.example.com/foo'); }); it('does not inherit auth from source object', function () { var from = parse('http://foo:bar@sub.example.com') , data = parse('/foo', from); assume(data.port).equals(''); assume(data.username).equals(''); assume(data.password).equals(''); assume(data.host).equals('sub.example.com'); assume(data.href).equals('http://sub.example.com/foo'); }); }); describe('#set', function () { it('correctly updates the host when setting port', function () { var data = parse('http://google.com/foo'); assume(data.set('port', 8080)).equals(data); assume(data.host).equals('google.com:8080'); assume(data.href).equals('http://google.com:8080/foo'); }); it('removes querystring and hash', function () { var data = parse('https://thisanurl.com/?swag=yolo#representing'); data.set('query', ''); data.set('hash', ''); assume(data.href).equals('https://thisanurl.com/'); }); it('only sets port when its not default', function () { var data = parse('http://google.com/foo'); assume(data.set('port', 80)).equals(data); assume(data.host).equals('google.com'); assume(data.href).equals('http://google.com/foo'); assume(data.set('port', 443)).equals(data); assume(data.host).equals('google.com:443'); assume(data.href).equals('http://google.com:443/foo'); }); it('updates query with object', function () { var data = parse('http://google.com/?foo=bar'); assume(data.set('query', { bar: 'foo' })).equals(data); assume(data.query.foo).equals(undefined); assume(data.query.bar).equals('foo'); assume(data.href).equals('http://google.com/?bar=foo'); }); it('updates query with a string', function () { var data = parse('http://google.com/?foo=bar'); assume(data.set('query', 'bar=foo')).equals(data); assume(data.query.foo).equals(undefined); assume(data.query.bar).equals('foo'); assume(data.href).equals('http://google.com/?bar=foo'); assume(data.set('query', '?baz=foo')).equals(data); assume(data.query.bar).equals(undefined); assume(data.query.baz).equals('foo'); assume(data.href).equals('http://google.com/?baz=foo'); }); it('updates the port when updating host', function () { var data = parse('http://google.com/?foo=bar'); assume(data.set('host', 'yahoo.com:808')).equals(data); assume(data.hostname).equals('yahoo.com'); assume(data.host).equals('yahoo.com:808'); assume(data.port).equals('808'); assume(data.href).equals('http://yahoo.com:808/?foo=bar'); }); it('updates the host when updating hostname', function () { var data = parse('http://google.com:808/?foo=bar'); assume(data.set('hostname', 'yahoo.com')).equals(data); assume(data.hostname).equals('yahoo.com'); assume(data.host).equals('yahoo.com:808'); assume(data.port).equals('808'); assume(data.href).equals('http://yahoo.com:808/?foo=bar'); }); it('updates other values', function () { var data = parse('http://google.com/?foo=bar'); assume(data.set('protocol', 'https:')).equals(data); assume(data.protocol).equals('https:'); assume(data.href).equals('https://google.com/?foo=bar'); }); }); describe('fuzzy', function () { var fuzz = require('./fuzzy') , times = 10; for (var i = 0; i < times; i++) { (function (spec) { it('parses: '+ spec.href, function () { var url = parse(spec.href) , prop; for (prop in spec) { assume(url[prop]).equals(spec[prop]); } }); })(fuzz()); } }); }); package/fuzzy.js000644 000765 000024 0000004511 12614700740012256 0ustar00000000 000000 'use strict'; var URL = require('./') , url = new URL(''); /** * A dictionary with all kind of different options that should generate a valid * and parse-able URL. * * @type {Object} * @api private */ var combinations = {}; combinations.protocol = [ 'http:', 'https:', 'ws:', 'wss:', 'blob:'/*, ''*/ ]; combinations.username = ['foo', 'bar']; combinations.password = combinations.username; combinations.hostname = [ 'example.com', 'www.example.com', 'travel.travel', 'sub.sub.sub.domain.nl', 'xn--n3h.com', 'localhost', '127.0.0.1', '255.255.255.255'/*, '3ffe:6a88:85a3:08d3:1319:8a2e:0370:7344', '2001:2353::1428:57ab', '2001:2353:0::0:1428:57ab', '2001:2353:0:0:0:0:1428:57ab', '2001:2353:0000:0000:0000::1428:57ab', '2001:2353:0000:0000:0000:0000:1428:57ab', '2001:2353:02de::0e13', '2001:2353:2de::e13'*/ ]; combinations.port = ['8080', '844', '3340']; combinations.pathname = [ '/', '/bar', '/bar/', '/foo/bar', '/foo.bar/foo', '/fav.ico', '/@3rd-Eden', '/a/b/c/d/e/f/g/j/1/d/4/' ]; combinations.query = ['foo=bar', 'foo[]=bar&foo[]=foo', 'email=foo@bar.travel', 'q=' ]; combinations.hash = [ 'name', 'moo-with-longer-name', '/what/about/slashes?querystring', '?querystring', '!/google/urls', 'use:foo@', 'http://' ]; /** * Get a random item from the given array. * * @param {String} name Name of the array we want to have a random item returned. * @returns {Mixed} * @api private */ function get(name) { var data = combinations[name]; return data[Math.floor(Math.random() * data.length)]; } /** * Return a random boolean. * * @returns {Boolean} * @api private */ function yep() { return !!Math.round(Math.random() * 1); } /** * Generate the actual URL. * * @returns {Object} specification * @api public */ module.exports = function generate() { var spec = {} , key; spec.protocol = get('protocol'); spec.hostname = get('hostname'); spec.pathname = get('pathname'); if (yep()) spec.port = get('port'); if (yep()) spec.query = '?'+ get('query'); if (yep()) spec.hash = '#'+ get('hash'); if (yep()) { spec.username = get('username'); spec.password = get('password'); } for (key in combinations) { url[key] = ''; } for (key in spec) { url[key] = spec[key]; } spec.href = url.toString(); return spec; }; package/index.js000644 000765 000024 0000014622 12605727505012212 0ustar00000000 000000 'use strict'; var required = require('requires-port') , lolcation = require('./lolcation') , qs = require('querystringify') , relativere = /^\/(?!\/)/; /** * These are the parse instructions for the URL parsers, it informs the parser * about: * * 0. The char it Needs to parse, if it's a string it should be done using * indexOf, RegExp using exec and NaN means set as current value. * 1. The property we should set when parsing this value. * 2. Indication if it's backwards or forward parsing, when set as number it's * the value of extra chars that should be split off. * 3. Inherit from location if non existing in the parser. * 4. `toLowerCase` the resulting value. */ var instructions = [ ['#', 'hash'], // Extract from the back. ['?', 'query'], // Extract from the back. ['//', 'protocol', 2, 1, 1], // Extract from the front. ['/', 'pathname'], // Extract from the back. ['@', 'auth', 1], // Extract from the front. [NaN, 'host', undefined, 1, 1], // Set left over value. [/\:(\d+)$/, 'port'], // RegExp the back. [NaN, 'hostname', undefined, 1, 1] // Set left over. ]; /** * The actual URL instance. Instead of returning an object we've opted-in to * create an actual constructor as it's much more memory efficient and * faster and it pleases my CDO. * * @constructor * @param {String} address URL we want to parse. * @param {Boolean|function} parser Parser for the query string. * @param {Object} location Location defaults for relative paths. * @api public */ function URL(address, location, parser) { if (!(this instanceof URL)) { return new URL(address, location, parser); } var relative = relativere.test(address) , parse, instruction, index, key , type = typeof location , url = this , i = 0; // // The following if statements allows this module two have compatibility with // 2 different API: // // 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments // where the boolean indicates that the query string should also be parsed. // // 2. The `URL` interface of the browser which accepts a URL, object as // arguments. The supplied object will be used as default values / fall-back // for relative paths. // if ('object' !== type && 'string' !== type) { parser = location; location = null; } if (parser && 'function' !== typeof parser) { parser = qs.parse; } location = lolcation(location); for (; i < instructions.length; i++) { instruction = instructions[i]; parse = instruction[0]; key = instruction[1]; if (parse !== parse) { url[key] = address; } else if ('string' === typeof parse) { if (~(index = address.indexOf(parse))) { if ('number' === typeof instruction[2]) { url[key] = address.slice(0, index); address = address.slice(index + instruction[2]); } else { url[key] = address.slice(index); address = address.slice(0, index); } } } else if (index = parse.exec(address)) { url[key] = index[1]; address = address.slice(0, address.length - index[0].length); } url[key] = url[key] || (instruction[3] || ('port' === key && relative) ? location[key] || '' : ''); // // Hostname, host and protocol should be lowercased so they can be used to // create a proper `origin`. // if (instruction[4]) { url[key] = url[key].toLowerCase(); } } // // Also parse the supplied query string in to an object. If we're supplied // with a custom parser as function use that instead of the default build-in // parser. // if (parser) url.query = parser(url.query); // // We should not add port numbers if they are already the default port number // for a given protocol. As the host also contains the port number we're going // override it with the hostname which contains no port number. // if (!required(url.port, url.protocol)) { url.host = url.hostname; url.port = ''; } // // Parse down the `auth` for the username and password. // url.username = url.password = ''; if (url.auth) { instruction = url.auth.split(':'); url.username = instruction[0] || ''; url.password = instruction[1] || ''; } // // The href is just the compiled result. // url.href = url.toString(); } /** * This is convenience method for changing properties in the URL instance to * insure that they all propagate correctly. * * @param {String} prop Property we need to adjust. * @param {Mixed} value The newly assigned value. * @returns {URL} * @api public */ URL.prototype.set = function set(part, value, fn) { var url = this; if ('query' === part) { if ('string' === typeof value && value.length) { value = (fn || qs.parse)(value); } url[part] = value; } else if ('port' === part) { url[part] = value; if (!required(value, url.protocol)) { url.host = url.hostname; url[part] = ''; } else if (value) { url.host = url.hostname +':'+ value; } } else if ('hostname' === part) { url[part] = value; if (url.port) value += ':'+ url.port; url.host = value; } else if ('host' === part) { url[part] = value; if (/\:\d+/.test(value)) { value = value.split(':'); url.hostname = value[0]; url.port = value[1]; } } else { url[part] = value; } url.href = url.toString(); return url; }; /** * Transform the properties back in to a valid and full URL string. * * @param {Function} stringify Optional query stringify function. * @returns {String} * @api public */ URL.prototype.toString = function toString(stringify) { if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify; var query , url = this , result = url.protocol +'//'; if (url.username) { result += url.username; if (url.password) result += ':'+ url.password; result += '@'; } result += url.hostname; if (url.port) result += ':'+ url.port; result += url.pathname; query = 'object' === typeof url.query ? stringify(url.query) : url.query; if (query) result += '?' !== query.charAt(0) ? '?'+ query : query; if (url.hash) result += url.hash; return result; }; // // Expose the URL parser and some additional properties that might be useful for // others. // URL.qs = qs; URL.location = lolcation; module.exports = URL; package/lolcation.js000644 000765 000024 0000002565 12434155716013071 0ustar00000000 000000 'use strict'; /** * These properties should not be copied or inherited from. This is only needed * for all non blob URL's as the a blob URL does not include a hash, only the * origin. * * @type {Object} * @private */ var ignore = { hash: 1, query: 1 } , URL; /** * The location object differs when your code is loaded through a normal page, * Worker or through a worker using a blob. And with the blobble begins the * trouble as the location object will contain the URL of the blob, not the * location of the page where our code is loaded in. The actual origin is * encoded in the `pathname` so we can thankfully generate a good "default" * location from it so we can generate proper relative URL's again. * * @param {Object} loc Optional default location object. * @returns {Object} lolcation object. * @api public */ module.exports = function lolcation(loc) { loc = loc || global.location || {}; URL = URL || require('./'); var finaldestination = {} , type = typeof loc , key; if ('blob:' === loc.protocol) { finaldestination = new URL(unescape(loc.pathname), {}); } else if ('string' === type) { finaldestination = new URL(loc, {}); for (key in ignore) delete finaldestination[key]; } else if ('object' === type) for (key in loc) { if (key in ignore) continue; finaldestination[key] = loc[key]; } return finaldestination; }; package/.travis.yml000644 000765 000024 0000001700 12614700740012637 0ustar00000000 000000 sudo: false language: node_js matrix: fast_finish: true include: - node_js: "0.10" env: TASK=test-node - node_js: "0.12" env: TASK=test-node - node_js: "iojs" env: TASK=test-node - node_js: "4" env: TASK=test-node - node_js: "4" env: TASK=test-browser env: global: - secure: edovUXd/s/VAVXTor0CT1XcGqAtBwGgLYN1zYq0JcNiJUgWFiZ5VGKYbKVpb6nKlwm0Fdi1xmByguSa6xLZllcXjtDgfXrkI1cQYmoVncJ63JpXNG+UKyy43BwnF2OqgUrAAOt/ic1YJr9kBe+IaRwDUsMxxIkuJ6Z8c4diX0HE= - secure: IF01oyIKSs0C5dARdYRTilKnU1TG4zenjjEPClkQxAWIpUOxl9xcNJWDVEOPxJ/4pVt+pozyT80Rp7efh6ZiREJIQI1tUboBKSqZzSbnD5uViQNSbQ90PaDP0FIUc0IQ5o07W36rijBB0DTmtU1VofzN9PKkJO7XiSSXevI8RcM= script: - "npm run ${TASK}" after_script: - 'if [ "${TASK}" == "test-node" ]; then npm i coveralls@2 && cat coverage/lcov.info | coveralls; fi' notifications: irc: channels: - "irc.freenode.org#unshift" on_success: change on_failure: change package/.zuul.yml000644 000765 000024 0000000714 12614700740012332 0ustar00000000 000000 ui: mocha-bdd browsers: - name: android version: [oldest, latest] - name: chrome version: [oldest, latest] - name: firefox version: [oldest, latest] - name: ie version: oldest..latest - name: iphone version: [oldest, latest] - name: opera version: oldest..latest - name: safari version: oldest..latest - name: microsoftedge version: oldest..latest capabilities: record-screenshots: false record-video: false