diff --git a/index.js b/index.js index 94e357e..f424acc 100644 --- a/index.js +++ b/index.js @@ -98,6 +98,24 @@ function lolcation(loc) { return finaldestination; } +/** + * Check whether a protocol scheme is special. + * + * @param {String} The protocol scheme of the URL + * @return {Boolean} `true` if the protocol scheme is special, else `false` + * @private + */ +function isSpecial(scheme) { + return ( + scheme === 'file:' || + scheme === 'ftp:' || + scheme === 'http:' || + scheme === 'https:' || + scheme === 'ws:' || + scheme === 'wss:' + ); +} + /** * @typedef ProtocolExtract * @type Object @@ -110,16 +128,32 @@ function lolcation(loc) { * Extract protocol information from a URL with/without double slash ("//"). * * @param {String} address URL we want to extract from. + * @param {Object} location * @return {ProtocolExtract} Extracted information. * @private */ -function extractProtocol(address) { +function extractProtocol(address, location) { address = trimLeft(address); + location = location || {}; - var match = protocolre.exec(address) - , protocol = match[1] ? match[1].toLowerCase() : '' - , slashes = !!(match[2] && match[2].length >= 2) - , rest = match[2] && match[2].length === 1 ? '/' + match[3] : match[3]; + var match = protocolre.exec(address); + var protocol = match[1] ? match[1].toLowerCase() : ''; + var rest = match[2] ? match[2] + match[3] : match[3]; + var slashes = !!(match[2] && match[2].length >= 2); + + if (protocol === 'file:') { + if (slashes) { + rest = rest.slice(2); + } + } else if (isSpecial(protocol)) { + rest = match[3]; + } else if (protocol) { + if (rest.indexOf('//') === 0) { + rest = rest.slice(2); + } + } else if (slashes && location.hostname) { + rest = match[3]; + } return { protocol: protocol, @@ -214,7 +248,7 @@ function Url(address, location, parser) { // // Extract protocol information before running the instructions. // - extracted = extractProtocol(address || ''); + extracted = extractProtocol(address || '', location); relative = !extracted.protocol && !extracted.slashes; url.slashes = extracted.slashes || relative && location.slashes; url.protocol = extracted.protocol || location.protocol || ''; @@ -224,7 +258,10 @@ function Url(address, location, parser) { // When the authority component is absent the URL starts with a path // component. // - if (!extracted.slashes || url.protocol === 'file:') { + if ( + url.protocol === 'file:' || + (!extracted.slashes && !isSpecial(extracted.protocol)) + ) { instructions[3] = [/(.*)/, 'pathname']; } diff --git a/test/test.js b/test/test.js index 38290ed..9a84fba 100644 --- a/test/test.js +++ b/test/test.js @@ -93,7 +93,7 @@ describe('url-parse', function () { assume(parse.extractProtocol('//foo/bar')).eql({ slashes: true, protocol: '', - rest: 'foo/bar' + rest: '//foo/bar' }); }); @@ -283,7 +283,7 @@ describe('url-parse', function () { assume(parsed.href).equals('http://what-is-up.com/'); }); - it('does not see a slash after the protocol as path', function () { + it('ignores slashes after the protocol for special URLs', function () { var url = 'https:\\/github.com/foo/bar' , parsed = parse(url); @@ -292,11 +292,59 @@ describe('url-parse', function () { assume(parsed.pathname).equals('/foo/bar'); url = 'https:/\\/\\/\\github.com/foo/bar'; + parsed = parse(url); assume(parsed.host).equals('github.com'); assume(parsed.hostname).equals('github.com'); assume(parsed.pathname).equals('/foo/bar'); + + url = 'https:/github.com/foo/bar'; + parsed = parse(url); + assume(parsed.host).equals('github.com'); + assume(parsed.pathname).equals('/foo/bar'); + + url = 'https:\\github.com/foo/bar'; + parsed = parse(url); + assume(parsed.host).equals('github.com'); + assume(parsed.pathname).equals('/foo/bar'); + + url = 'https:github.com/foo/bar'; + parsed = parse(url); + assume(parsed.host).equals('github.com'); + assume(parsed.pathname).equals('/foo/bar'); + + url = 'https:github.com/foo/bar'; + parsed = parse(url); + assume(parsed.host).equals('github.com'); + assume(parsed.pathname).equals('/foo/bar'); }); + it('handles slashes after the protocol for non special URLs', function () { + var url = 'foo:example.com' + , parsed = parse(url); + + assume(parsed.hostname).equals(''); + assume(parsed.pathname).equals('example.com'); + assume(parsed.href).equals('foo:example.com'); + + url = 'foo:/example.com'; + parsed = parse(url); + assume(parsed.hostname).equals(''); + assume(parsed.pathname).equals('/example.com'); + assume(parsed.href).equals('foo:/example.com'); + + url = 'foo://example.com'; + parsed = parse(url); + assume(parsed.hostname).equals('example.com'); + assume(parsed.pathname).equals('/'); + assume(parsed.href).equals('foo://example.com/'); + + url = 'foo:///example.com'; + parsed = parse(url); + assume(parsed.hostname).equals(''); + assume(parsed.pathname).equals('/example.com'); + assume(parsed.href).equals('foo:///example.com'); + }) + describe('origin', function () { it('generates an origin property', function () { var url = 'http://google.com:80/pathname' @@ -440,7 +488,7 @@ describe('url-parse', function () { }); it('handles the file: protocol', function () { - var slashes = ['', '/', '//', '///', '////', '/////']; + var slashes = ['', '/', '//', '///']; var data; var url; @@ -451,6 +499,18 @@ describe('url-parse', function () { assume(data.href).equals('file:///'); } + url = 'file:////'; + data = parse(url); + assume(data.protocol).equals('file:'); + assume(data.pathname).equals('//'); + assume(data.href).equals(url); + + url = 'file://///'; + data = parse(url); + assume(data.protocol).equals('file:'); + assume(data.pathname).equals('///'); + assume(data.href).equals(url); + url = 'file:///Users/foo/BAR/baz.pdf'; data = parse(url); assume(data.protocol).equals('file:');