From 5cb20e92447a78b12bb626fb6086f7163d51f595 Mon Sep 17 00:00:00 2001 From: Jia Hao Date: Fri, 22 Jan 2016 02:05:50 +0800 Subject: [PATCH] Implement authentication that requires a new window to be opened referenced in #19 - Issue https://github.com/jiahaog/nativefier/issues/19#issuecomment-173364234 - When a link that requests a new window is clicked, the app will check if the link is an external link. If so, the default desktop browser (Chrome, Safari etc.) will be opened. Otherwise, a new Electron BrowserWindow will be opened, which supports the use case of logging in to OAuth websites such as feedly.com --- app/.gitignore | 1 + app/main.js | 13 +- app/node_modules/wurl/README.md | 26 ++++ app/node_modules/wurl/package.json | 76 +++++++++ app/node_modules/wurl/test.js | 178 +++++++++++++++++++++ app/node_modules/wurl/wurl.js | 241 +++++++++++++++++++++++++++++ app/package.json | 2 +- 7 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 app/.gitignore create mode 100644 app/node_modules/wurl/README.md create mode 100644 app/node_modules/wurl/package.json create mode 100644 app/node_modules/wurl/test.js create mode 100644 app/node_modules/wurl/wurl.js diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..cf4bab9 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +!node_modules diff --git a/app/main.js b/app/main.js index 8438aa0..e8fdc2e 100644 --- a/app/main.js +++ b/app/main.js @@ -5,6 +5,8 @@ var fs = require('fs'); var os = require('os'); var electron = require('electron'); + +var wurl = require('wurl'); var app = electron.app; var BrowserWindow = electron.BrowserWindow; var shell = electron.shell; @@ -74,7 +76,10 @@ app.on('ready', function () { } }); - mainWindow.webContents.on('new-window', function (event, url) { + mainWindow.webContents.on('new-window', function (event, urlToGo) { + if (linkIsInternal(appArgs.targetUrl, urlToGo)) { + return; + } event.preventDefault(); shell.openExternal(url); }); @@ -99,3 +104,9 @@ app.on('ready', function () { function isOSX() { return os.platform() === 'darwin'; } + +function linkIsInternal(currentUrl, newUrl) { + var currentDomain = wurl('domain', currentUrl); + var newDomain = wurl('domain', newUrl); + return currentDomain === newDomain; +} diff --git a/app/node_modules/wurl/README.md b/app/node_modules/wurl/README.md new file mode 100644 index 0000000..64a7c28 --- /dev/null +++ b/app/node_modules/wurl/README.md @@ -0,0 +1,26 @@ +# wurl() + +A simple url parsing library for Node.js. + +Note this is based directly from the [js-url](https://github.com/websanova/js-url) library. To see the documentation and release notes pleas check the [README](https://github.com/websanova/js-url) page there. + +## Install + +``` +npm install wurl +``` + +## Usage + +```js +var wurl = require('wurl'); + +wurl('domain', url); +// etc +``` + +## License + +MIT licensed + +Copyright (C) 2011-2012 Websanova http://www.websanova.com \ No newline at end of file diff --git a/app/node_modules/wurl/package.json b/app/node_modules/wurl/package.json new file mode 100644 index 0000000..54744bd --- /dev/null +++ b/app/node_modules/wurl/package.json @@ -0,0 +1,76 @@ +{ + "_args": [ + [ + "wurl@^2.1.0", + "/Users/JiaHao/Projects/Web/nativefier/app" + ] + ], + "_from": "wurl@>=2.1.0 <3.0.0", + "_id": "wurl@2.1.0", + "_inCache": true, + "_installable": true, + "_location": "/wurl", + "_nodeVersion": "0.10.22", + "_npmUser": { + "email": "rob@websanova.com", + "name": "websanova" + }, + "_npmVersion": "2.14.1", + "_phantomChildren": {}, + "_requested": { + "name": "wurl", + "raw": "wurl@^2.1.0", + "rawSpec": "^2.1.0", + "scope": null, + "spec": ">=2.1.0 <3.0.0", + "type": "range" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/wurl/-/wurl-2.1.0.tgz", + "_shasum": "7224bd0e662e5135221d259d8d60141c2d13d310", + "_shrinkwrap": null, + "_spec": "wurl@^2.1.0", + "_where": "/Users/JiaHao/Projects/Web/nativefier/app", + "author": { + "email": "rob@websanova.com", + "name": "Websanova", + "url": "http://websanova.com" + }, + "bugs": { + "url": "https://github.com/websanova/node-url/issues" + }, + "dependencies": {}, + "description": "A simple url parsing library.", + "devDependencies": {}, + "directories": {}, + "dist": { + "shasum": "7224bd0e662e5135221d259d8d60141c2d13d310", + "tarball": "http://registry.npmjs.org/wurl/-/wurl-2.1.0.tgz" + }, + "gitHead": "9ba441e47c7ff03a9b9aad747418cc094c53d167", + "homepage": "https://github.com/websanova/node-url#readme", + "keywords": [ + "parse", + "url", + "util" + ], + "licenses": "MIT, GPL", + "main": "wurl.js", + "maintainers": [ + { + "name": "websanova", + "email": "rob@websanova.com" + } + ], + "name": "wurl", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git+https://github.com/websanova/node-url.git" + }, + "scripts": {}, + "version": "2.1.0" +} diff --git a/app/node_modules/wurl/test.js b/app/node_modules/wurl/test.js new file mode 100644 index 0000000..4be5855 --- /dev/null +++ b/app/node_modules/wurl/test.js @@ -0,0 +1,178 @@ +/** + * Usage: node test.js + */ + +var wurl = require('./wurl.js'), + assert = require('assert'); + +function strictEqual(a, b) { + console.log('Test: ' + a + ' === ' + b); + assert.strictEqual.apply(null, arguments); +} + +// Test URLs. +var url = 'http://rob:abcd1234@www.domain.com/path/index.html?query1=test&silly=willy#test=hash&chucky=cheese', + urlHttps = 'https://rob:abcd1234@www.domain.com/path/index.html?query1=test&silly=willy#test=hash&chucky=cheese', + urlIp = 'https://rob:abcd1234@1.2.3.4/path/index.html?query1=test&silly=willy#test=hash&chucky=cheese'; + +/*strictEqual( wurl('{}', url), { + 'auth': 'rob:abcd1234', + 'domain': 'domain.com', + 'file': 'index.html', + 'fileext': 'html', + 'filename': 'index', + 'hash': 'test=hash&chucky=cheese', + 'hostname': 'www.domain.com', + 'pass': 'abcd1234', + 'path': '/path/index.html', + 'port': '80', + 'protocol': 'http', + 'query': 'query1=test&silly=willy', + 'sub': 'www', + 'tld': 'com', + 'user': 'rob' +});*/ + +strictEqual( wurl('tld', 'http://sub.www.domain.co.uk'), 'co.uk' ); +strictEqual( wurl('tld', 'http://www.domain.org.uk'), 'org.uk' ); +strictEqual( wurl('tld', 'http://domain.la'), 'la' ); +strictEqual( wurl('tld', 'http://in'), 'in' ); +strictEqual( wurl('tld', 'http://.asia'), 'asia' ); +strictEqual( wurl('tld', 'http://.cao.uk'), undefined ); +strictEqual( wurl('tld', 'http://'), undefined ); +strictEqual( wurl('tld', 'http://domain.zoo'), undefined ); +strictEqual( wurl('tld', url), 'com' ); + +strictEqual( wurl('domain', 'http://sub.www.domain.co.uk'), 'domain.co.uk' ); +strictEqual( wurl('domain', 'http://www.domain.org.uk'), 'domain.org.uk' ); +strictEqual( wurl('domain', 'http://domain.la'), 'domain.la' ); +strictEqual( wurl('domain', 'http://in'), undefined ); +strictEqual( wurl('domain', 'http://.asia'), undefined ); +strictEqual( wurl('domain', 'http://.cao.uk'), undefined ); +strictEqual( wurl('domain', 'http://'), undefined ); +strictEqual( wurl('domain', 'http://domain.zoo'), undefined ); +strictEqual( wurl('domain', url), 'domain.com' ); + +strictEqual( wurl('sub', 'http://sub.www.domain.co.uk'), 'sub.www' ); +strictEqual( wurl('sub', 'http://www.domain.org.uk'), 'www' ); +strictEqual( wurl('sub', 'http://domain.la'), undefined ); +strictEqual( wurl('sub', 'http://in'), undefined ); +strictEqual( wurl('sub', 'http://.asia'), undefined ); +strictEqual( wurl('sub', 'http://.cao.uk'), undefined ); +strictEqual( wurl('sub', 'http://'), undefined ); +strictEqual( wurl('sub', 'http://domain.zoo'), undefined ); +strictEqual( wurl('sub', url), 'www' ); + +strictEqual( wurl( 'hostname', url ), 'www.domain.com' ); +strictEqual( wurl( 'hostname', urlIp ), '1.2.3.4' ); + +//strictEqual( wurl( '.', url ), ['www', 'domain', 'com'] ); +strictEqual( wurl( '.0', url ), undefined ); +strictEqual( wurl( '.1', url ), 'www' ); +strictEqual( wurl( '.2', url ), 'domain' ); +strictEqual( wurl( '.-1', url ), 'com' ); + +strictEqual( wurl( 'auth', url ), 'rob:abcd1234' ); + +strictEqual( wurl( 'user', url ), 'rob' ); +strictEqual( wurl( 'email', 'mailto:rob@websanova.com' ), 'rob@websanova.com' ); + +strictEqual( wurl( 'pass', url ), 'abcd1234' ); + +strictEqual( wurl( 'port', url ), '80' ); +strictEqual( wurl( 'port', url.toUpperCase() ), '80' ); +strictEqual( wurl( 'port', 'http://example.com:80' ), '80' ); +strictEqual( wurl( 'port', urlHttps ), '443' ); +strictEqual( wurl( 'port', urlHttps.toUpperCase() ), '443' ); +strictEqual( wurl( 'port', 'https://example.com:443' ), '443' ); +strictEqual( wurl( 'port', 'http://domain.com:400?poo=a:b' ), '400' ); +strictEqual( wurl( 'port', 'https://domain.com:80' ), '80' ); +strictEqual( wurl( 'port', 'http://domain.com:443' ), '443' ); +strictEqual( wurl( 'port', 'http://domain.com' ), '80' ); +strictEqual( wurl( 'port', 'https://domain.com' ), '443' ); + +strictEqual( wurl( 'protocol', 'http://domain.com' ), 'http' ); +strictEqual( wurl( 'protocol', 'http://domain.com:80' ), 'http' ); +strictEqual( wurl( 'protocol', 'http://domain.com:443' ), 'http' ); +strictEqual( wurl( 'protocol', 'domain.com' ), 'http' ); +strictEqual( wurl( 'protocol', 'domain.com:80' ), 'http' ); +strictEqual( wurl( 'protocol', 'domain.com:443' ), 'https' ); +strictEqual( wurl( 'protocol', 'https://domain.com:443' ), 'https' ); +strictEqual( wurl( 'protocol', 'https://domain.com:80' ), 'https' ); +strictEqual( wurl( 'protocol', 'mailto:rob@websanova.com' ), 'mailto' ); + +strictEqual( wurl( 'path', url ), '/path/index.html' ); +strictEqual( wurl( 'path', 'http://www.domain.com/first/second' ), '/first/second' ); +strictEqual( wurl( 'path', 'http://www.domain.com/first/second/' ), '/first/second' ); +strictEqual( wurl( 'path', 'http://www.domain.com:8080/first/second' ), '/first/second' ); +strictEqual( wurl( 'path', 'http://www.domain.com:8080/first/second/' ), '/first/second' ); +strictEqual( wurl( 'path', 'http://www.domain.com/first/second?test=foo' ), '/first/second' ); +strictEqual( wurl( 'path', 'http://www.domain.com/first/second/?test=foo' ), '/first/second' ); +strictEqual( wurl( 'path', 'http://www.domain.com/path#anchor' ), '/path' ); +strictEqual( wurl( 'path', 'http://www.domain.com/path/#anchor' ), '/path' ); +strictEqual( wurl( 'path', 'http://www.domain.com' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com/' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com#anchor' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com/#anchor' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com?test=foo' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com/?test=foo' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com:80' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com:80/' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com:80#anchor' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com:80/#anchor' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com:80?test=foo' ), '' ); +strictEqual( wurl( 'path', 'http://www.domain.com:80/?test=foo' ), '' ); + +strictEqual( wurl( 'file', url ), 'index.html' ); +strictEqual( wurl( 'filename', url ), 'index' ); +strictEqual( wurl( 'fileext', url ), 'html' ); + +//strictEqual( wurl( '/', url ), ['path', 'index.html'] ); +strictEqual( wurl( '0', url ), undefined ); +strictEqual( wurl( '-4', url ), undefined ); +strictEqual( wurl( '1', url ), 'path' ); +strictEqual( wurl( 1, url ), 'path' ); +strictEqual( wurl( '2', url ), 'index.html' ); +strictEqual( wurl( '3', url ), undefined ); +strictEqual( wurl( '-1', url ), 'index.html' ); +strictEqual( wurl( '1', 'http://www.domain.com/first/second' ), 'first' ); +strictEqual( wurl( '1', 'http://www.domain.com/first/second/' ), 'first' ); +strictEqual( wurl( '-1', 'http://www.domain.com/first/second?test=foo' ), 'second' ); +strictEqual( wurl( '-1', 'http://www.domain.com/first/second/?test=foo' ), 'second' ); + +strictEqual( wurl( 'query', url ), 'query1=test&silly=willy' ); +//strictEqual( wurl( '?', url ), {'query1': 'test', 'silly': 'willy'} ); +strictEqual( wurl( '?silly', url ), 'willy' ); +strictEqual( wurl( '?poo', url ), undefined ); +strictEqual( wurl( '?poo', 'http://domain.com?poo=' ), '' ); +strictEqual( wurl( '?poo', 'http://domain.com/?poo' ), '' ); +strictEqual( wurl( '?poo', 'http://domain.com?poo' ), '' ); +strictEqual( wurl( '?poo', 'http://domain.com?' ), undefined ); +strictEqual( wurl( '?poo', 'http://domain.com' ), undefined ); +strictEqual( wurl( '?poo', 'http://domain.com?poo=a+b' ), 'a b' ); +strictEqual( wurl( '?poo', 'http://domain.com?poo=javascript%20decode%20uri%20%2B%20sign%20to%20space' ), 'javascript decode uri + sign to space' ); +strictEqual( wurl( '?key', 'http://domain.com?key=value=va?key2=value' ), 'value=va?key2=value'); +strictEqual( wurl( '?poo', 'http://domain.com:400?poo=a:b' ), 'a:b' ); +strictEqual( wurl( '?poo', 'http://domain.com:400? & & &' ), undefined ); + +strictEqual( wurl( '?field[0]', 'http://domain.com?field[0]=zero&field[1]=one' ), 'zero' ); +//strictEqual( wurl( '?field', 'http://domain.com?field[0]=zero&field[1]=one&var=test' ), ['zero', 'one'] ); +//strictEqual( wurl( '?field', 'http://domain.com?field[0]=zero&field[3]=one&var=test' ), ['zero', undefined, undefined, 'one'] ); +strictEqual( wurl( '?var', 'http://domain.com?field[0]=zero&field[3]=one&var=test' ), 'test' ); +//strictEqual( wurl( '?', 'http://domain.com?field[0]=zero&field[1]=one&var=test' ), {'field': ['zero', 'one'], 'var': 'test'} ); + +strictEqual( wurl( 'hash', url ), 'test=hash&chucky=cheese' ); +//strictEqual( wurl( '#', url ), {'chucky': 'cheese', 'test': 'hash'} ); +strictEqual( wurl( '#chucky', url ), 'cheese' ); +strictEqual( wurl( '#poo', url ), undefined ); +strictEqual( wurl( '#poo', 'http://domain.com#poo=' ), '' ); +strictEqual( wurl( '#poo', 'http://domain.com/#poo' ), '' ); +strictEqual( wurl( '#poo', 'http://domain.com#poo' ), '' ); +strictEqual( wurl( '#poo', 'http://domain.com#' ), undefined ); +strictEqual( wurl( '#poo', 'http://domain.com' ), undefined ); + +strictEqual( wurl( '#field[0]', 'http://domain.com#field[0]=zero&field[1]=one' ), 'zero' ); +//strictEqual( wurl( '#field', 'http://domain.com#field[0]=zero&field[1]=one&var=test' ), ['zero', 'one'] ); +//strictEqual( wurl( '#field', 'http://domain.com#field[0]=zero&field[3]=one&var=test' ), ['zero', undefined, undefined, 'one'] ); +strictEqual( wurl( '#var', 'http://domain.com#field[0]=zero&field[3]=one&var=test' ), 'test' ); +//strictEqual( wurl( '#', 'http://domain.com#field[0]=zero&field[1]=one&var=test' ), {'field': ['zero', 'one'], 'var': 'test'} ); diff --git a/app/node_modules/wurl/wurl.js b/app/node_modules/wurl/wurl.js new file mode 100644 index 0000000..5b98888 --- /dev/null +++ b/app/node_modules/wurl/wurl.js @@ -0,0 +1,241 @@ +module.exports = function (arg, url) { + + function _t() { + return new RegExp(/(.*?)\.?([^\.]*?)\.?(com|net|org|biz|ws|in|me|co\.uk|co|org\.uk|ltd\.uk|plc\.uk|me\.uk|edu|mil|br\.com|cn\.com|eu\.com|hu\.com|no\.com|qc\.com|sa\.com|se\.com|se\.net|us\.com|uy\.com|ac|co\.ac|gv\.ac|or\.ac|ac\.ac|af|am|as|at|ac\.at|co\.at|gv\.at|or\.at|asn\.au|com\.au|edu\.au|org\.au|net\.au|id\.au|be|ac\.be|adm\.br|adv\.br|am\.br|arq\.br|art\.br|bio\.br|cng\.br|cnt\.br|com\.br|ecn\.br|eng\.br|esp\.br|etc\.br|eti\.br|fm\.br|fot\.br|fst\.br|g12\.br|gov\.br|ind\.br|inf\.br|jor\.br|lel\.br|med\.br|mil\.br|net\.br|nom\.br|ntr\.br|odo\.br|org\.br|ppg\.br|pro\.br|psc\.br|psi\.br|rec\.br|slg\.br|tmp\.br|tur\.br|tv\.br|vet\.br|zlg\.br|br|ab\.ca|bc\.ca|mb\.ca|nb\.ca|nf\.ca|ns\.ca|nt\.ca|on\.ca|pe\.ca|qc\.ca|sk\.ca|yk\.ca|ca|cc|ac\.cn|com\.cn|edu\.cn|gov\.cn|org\.cn|bj\.cn|sh\.cn|tj\.cn|cq\.cn|he\.cn|nm\.cn|ln\.cn|jl\.cn|hl\.cn|js\.cn|zj\.cn|ah\.cn|gd\.cn|gx\.cn|hi\.cn|sc\.cn|gz\.cn|yn\.cn|xz\.cn|sn\.cn|gs\.cn|qh\.cn|nx\.cn|xj\.cn|tw\.cn|hk\.cn|mo\.cn|cn|cx|cz|de|dk|fo|com\.ec|tm\.fr|com\.fr|asso\.fr|presse\.fr|fr|gf|gs|co\.il|net\.il|ac\.il|k12\.il|gov\.il|muni\.il|ac\.in|co\.in|org\.in|ernet\.in|gov\.in|net\.in|res\.in|is|it|ac\.jp|co\.jp|go\.jp|or\.jp|ne\.jp|ac\.kr|co\.kr|go\.kr|ne\.kr|nm\.kr|or\.kr|li|lt|lu|asso\.mc|tm\.mc|com\.mm|org\.mm|net\.mm|edu\.mm|gov\.mm|ms|nl|no|nu|pl|ro|org\.ro|store\.ro|tm\.ro|firm\.ro|www\.ro|arts\.ro|rec\.ro|info\.ro|nom\.ro|nt\.ro|se|si|com\.sg|org\.sg|net\.sg|gov\.sg|sk|st|tf|ac\.th|co\.th|go\.th|mi\.th|net\.th|or\.th|tm|to|com\.tr|edu\.tr|gov\.tr|k12\.tr|net\.tr|org\.tr|com\.tw|org\.tw|net\.tw|ac\.uk|uk\.com|uk\.net|gb\.com|gb\.net|vg|sh|kz|ch|info|ua|gov|name|pro|ie|hk|com\.hk|org\.hk|net\.hk|edu\.hk|us|tk|cd|by|ad|lv|eu\.lv|bz|es|jp|cl|ag|mobi|eu|co\.nz|org\.nz|net\.nz|maori\.nz|iwi\.nz|io|la|md|sc|sg|vc|tw|travel|my|se|tv|pt|com\.pt|edu\.pt|asia|fi|com\.ve|net\.ve|fi|org\.ve|web\.ve|info\.ve|co\.ve|tel|im|gr|ru|net\.ru|org\.ru|hr|com\.hr|ly|xyz)$/); + } + + function _d(s) { + return decodeURIComponent(s.replace(/\+/g, ' ')); + } + + function _i(arg, str) { + var sptr = arg.charAt(0), + split = str.split(sptr); + + if (sptr === arg) { return split; } + + arg = parseInt(arg.substring(1), 10); + + return split[arg < 0 ? split.length + arg : arg - 1]; + } + + function _f(arg, str) { + var sptr = arg.charAt(0), + split = str.split('&'), + field = [], + params = {}, + tmp = [], + arg2 = arg.substring(1); + + for (var i in split) { + field = split[i].split(/=(.*)/); + + if (field[0].replace(/\s/g, '') !== '') { + field[1] = _d(field[1] || ''); + + // If we have a match just return it right away. + if (arg2 === field[0]) { return field[1]; } + + // Check for array pattern. + tmp = field[0].match(/(.*)\[([0-9]+)\]/); + + if (tmp) { + params[tmp[1]] = params[tmp[1]] || []; + + params[tmp[1]][tmp[2]] = field[1]; + } + else { + params[field[0]] = field[1]; + } + } + } + + if (sptr === arg) { return params; } + + return params[arg2]; + } + + //return function(arg, url) { + var _l = {}, tmp, tmp2; + + if (arg === 'tld?') { return _t(); } + + url = url || window.location.toString(); + + if ( ! arg) { return url; } + + arg = arg.toString(); + + if (url.match(/^mailto:[^\/]/)) { + _l.protocol = 'mailto'; + _l.email = url.split(/mailto\:/)[1]; + } + else { + + // Anchor. + tmp = url.split(/#(.*)/); + _l.hash = tmp[1] ? tmp[1] : undefined; + + // Return anchor parts. + if (_l.hash && arg.match(/^#/)) { return _f(arg, _l.hash); } + + // Query + tmp = tmp[0].split(/\?(.*)/); + _l.query = tmp[1] ? tmp[1] : undefined; + + // Return query parts. + if (_l.query && arg.match(/^\?/)) { return _f(arg, _l.query); } + + // Protocol. + tmp = tmp[0].split(/\:?\/\//); + _l.protocol = tmp[1] ? tmp[0].toLowerCase() : undefined; + + // Path. + tmp = (tmp[1] ? tmp[1] : tmp[0]).split(/(\/.*)/); + _l.path = tmp[1] ? tmp[1] : ''; + + // Clean up path. + _l.path = _l.path.replace(/^([^\/])/, '/$1').replace(/\/$/, ''); + + // Return path parts. + if (arg.match(/^[\-0-9]+$/)) { arg = arg.replace(/^([^\/])/, '/$1'); } + if (arg.match(/^\//)) { return _i(arg, _l.path.substring(1)); } + + // File. + tmp2 = _i('/-1', _l.path.substring(1)); + tmp2 = tmp2.split(/\.(.*)/); + + // Filename and fileext. + if (tmp2[1]) { + _l.file = tmp2[0] + '.' + tmp2[1]; + _l.filename = tmp2[0]; + _l.fileext = tmp2[1]; + } + + // Port. + tmp = tmp[0].split(/\:([0-9]+)$/); + _l.port = tmp[1] ? tmp[1] : undefined; + + // Auth. + tmp = tmp[0].split(/@/); + _l.auth = tmp[1] ? tmp[0] : undefined; + + // User and pass. + if (_l.auth) { + tmp2 = _l.auth.split(/\:(.*)/); + _l.user = tmp2[0]; + _l.pass = tmp2[1]; + } + + // Hostname. + _l.hostname = (tmp[1] ? tmp[1] : tmp[0]).toLowerCase(); + + // Return hostname parts. + if (arg.charAt(0) === '.') { return _i(arg, _l.hostname); } + + // Domain, tld and sub domain. + if (_t()) { + tmp = _l.hostname.match(_t()); + + if (tmp) { + _l.tld = tmp[3]; + _l.domain = tmp[2] ? tmp[2] + '.' + tmp[3] : undefined; + _l.sub = tmp[1] || undefined; + } + } + + // Set port and protocol defaults if not set. + _l.port = _l.port || (_l.protocol === 'https' ? '443' : '80'); + _l.protocol = _l.protocol || (_l.port === '443' ? 'https' : 'http'); + } + + // Return arg. + if (arg in _l) { return _l[arg]; } + + // Return everything. + if (arg === '{}') { return _l; } + + // Default to undefined for no match. + return undefined; + + + + + /*function isNumeric(arg) { + return !isNaN(parseFloat(arg)) && isFinite(arg); + } + + function decode(str) { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } + + var _ls = url; + + if (!url) { return undefined; } + else if (!arg) { return _ls; } + else { arg = arg.toString(); } + + if (_ls.substring(0,2) === '//') { _ls = 'http:' + _ls; } + else if (_ls.split('://').length === 1) { _ls = 'http://' + _ls; } + + url = _ls.split('/'); + var _l = {auth:''}, host = url[2].split('@'); + + if (host.length === 1) { host = host[0].split(':'); } + else { _l.auth = host[0]; host = host[1].split(':'); } + + _l.protocol=url[0]; + _l.hostname=host[0]; + _l.port=(host[1] || ((_l.protocol.split(':')[0].toLowerCase() === 'https') ? '443' : '80')); + _l.pathname=( (url.length > 3 ? '/' : '') + url.slice(3, url.length).join('/').split('?')[0].split('#')[0]); + var _p = _l.pathname; + + if (_p.charAt(_p.length-1) === '/') { _p=_p.substring(0, _p.length-1); } + var _h = _l.hostname, _hs = _h.split('.'), _ps = _p.split('/'); + + if (arg === 'hostname') { return _h; } + else if (arg === 'domain') { + if (/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(_h)) { return _h; } + return _hs.slice(-2).join('.'); + } + //else if (arg === 'tld') { return _hs.slice(-1).join('.'); } + else if (arg === 'sub') { return _hs.slice(0, _hs.length - 2).join('.'); } + else if (arg === 'port') { return _l.port; } + else if (arg === 'protocol') { return _l.protocol.split(':')[0]; } + else if (arg === 'auth') { return _l.auth; } + else if (arg === 'user') { return _l.auth.split(':')[0]; } + else if (arg === 'pass') { return _l.auth.split(':')[1] || ''; } + else if (arg === 'path') { return _l.pathname; } + else if (arg.charAt(0) === '.') + { + arg = arg.substring(1); + if(isNumeric(arg)) {arg = parseInt(arg, 10); return _hs[arg < 0 ? _hs.length + arg : arg-1] || ''; } + } + else if (isNumeric(arg)) { arg = parseInt(arg, 10); return _ps[arg < 0 ? _ps.length + arg : arg] || ''; } + else if (arg === 'file') { return _ps.slice(-1)[0]; } + else if (arg === 'filename') { return _ps.slice(-1)[0].split('.')[0]; } + else if (arg === 'fileext') { return _ps.slice(-1)[0].split('.')[1] || ''; } + else if (arg.charAt(0) === '?' || arg.charAt(0) === '#') + { + var params = _ls, param = null; + + if(arg.charAt(0) === '?') { params = (params.split('?')[1] || '').split('#')[0]; } + else if(arg.charAt(0) === '#') { params = (params.split('#')[1] || ''); } + + if(!arg.charAt(1)) { return (params ? decode(params) : params); } + + arg = arg.substring(1); + params = params.split('&'); + + for(var i=0,ii=params.length; i