exports.parse = urlParse; exports.resolve = urlResolve; exports.resolveObject = urlResolveObject; exports.format = urlFormat; var protocolPattern = /^([a-z0-9]+:)/, portPattern = /:[0-9]+$/, nonHostChars = ['/', '?', ';', '#'] , hostlessProtocol = { 'file': true , 'file:': true } , slashedProtocol = { 'http': true , 'https': true , 'ftp': true , 'gopher': true , 'file': true , 'http:': true , 'https:': true , 'ftp:': true , 'gopher:': true , 'file:': true } , path = require('path'), querystring = require('querystring'); function urlParse(url, parseQueryString, slashesDenoteHost){ if (url && typeof (url) === 'object' && _AN_Read_href('href', url)) return url; var out = { href: url} , rest = url; var proto = protocolPattern.exec(rest); if (proto) { proto = proto[0]; _AN_Write_protocol('protocol', out, false , proto); rest = rest.substr(_AN_Read_length('length', proto)); } if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { var slashes = rest.substr(0, 2) === '//'; if (slashes && !(proto && hostlessProtocol[proto])) { rest = rest.substr(2); out.slashes = true ; } } if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { var firstNonHost = -1; for (var i = 0, l = _AN_Read_length('length', nonHostChars); i < l; i++ ){ var index = rest.indexOf(nonHostChars[i]); if (index !== -1 && (firstNonHost < 0 || index < firstNonHost)) firstNonHost = index; } if (firstNonHost !== -1) { _AN_Write_host('host', out, false , rest.substr(0, firstNonHost)); rest = rest.substr(firstNonHost); } else { _AN_Write_host('host', out, false , rest); rest = ''; } var p = parseHost(_AN_Read_host('host', out)); var keys = Object.keys(p); for (var i = 0, l = _AN_Read_length('length', keys); i < l; i++ ){ var key = keys[i]; out[key] = p[key]; } _AN_Write_hostname('hostname', out, false , _AN_Read_hostname('hostname', out) || ''); } var hash = rest.indexOf('#'); if (hash !== -1) { _AN_Write_hash('hash', out, false , rest.substr(hash)); rest = rest.slice(0, hash); } var qm = rest.indexOf('?'); if (qm !== -1) { _AN_Write_search('search', out, false , rest.substr(qm)); out.query = rest.substr(qm + 1); if (parseQueryString) { out.query = querystring.parse(out.query); } rest = rest.slice(0, qm); } else if (parseQueryString) { out.query = { } ; } if (rest) _AN_Write_pathname('pathname', out, false , rest); return out; } function urlFormat(obj){ if (typeof (obj) === 'string') obj = urlParse(obj); var protocol = _AN_Read_protocol('protocol', obj) || '', host = (_AN_Read_host('host', obj) !== undefined)? _AN_Read_host('host', obj): _AN_Read_hostname('hostname', obj) !== undefined? ((obj.auth? obj.auth + '@': '') + _AN_Read_hostname('hostname', obj) + (_AN_Read_port('port', obj)? ':' + _AN_Read_port('port', obj): '')): false , pathname = _AN_Read_pathname('pathname', obj) || '', search = _AN_Read_search('search', obj) || (obj.query && ('?' + (typeof (obj.query) === 'object'? querystring.stringify(obj.query): String(obj.query)))) || '', hash = _AN_Read_hash('hash', obj) || ''; if (protocol && protocol.substr(-1) !== ':') protocol += ':'; if (obj.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; return protocol + host + pathname + search + hash; } function urlResolve(source, relative){ return urlFormat(urlResolveObject(source, relative)); } function urlResolveObject(source, relative){ if (!source) return relative; source = urlParse(urlFormat(source), false , true ); relative = urlParse(urlFormat(relative), false , true ); _AN_Write_hash('hash', source, false , _AN_Read_hash('hash', relative)); if (_AN_Read_href('href', relative) === '') return source; if (relative.slashes && !_AN_Read_protocol('protocol', relative)) { _AN_Write_protocol('protocol', relative, false , _AN_Read_protocol('protocol', source)); return relative; } if (_AN_Read_protocol('protocol', relative) && _AN_Read_protocol('protocol', relative) !== _AN_Read_protocol('protocol', source)) { if (!slashedProtocol[_AN_Read_protocol('protocol', relative)]) return relative; _AN_Write_protocol('protocol', source, false , _AN_Read_protocol('protocol', relative)); if (!_AN_Read_host('host', relative) && !hostlessProtocol[_AN_Read_protocol('protocol', relative)]) { var relPath = (_AN_Read_pathname('pathname', relative) || '').split('/'); while (_AN_Read_length('length', relPath) && !(_AN_Write_host('host', relative, false , relPath.shift()))); if (!_AN_Read_host('host', relative)) _AN_Write_host('host', relative, false , ''); if (relPath[0] !== '') relPath.unshift(''); if (_AN_Read_length('length', relPath) < 2) relPath.unshift(''); _AN_Write_pathname('pathname', relative, false , relPath.join('/')); } _AN_Write_pathname('pathname', source, false , _AN_Read_pathname('pathname', relative)); _AN_Write_search('search', source, false , _AN_Read_search('search', relative)); source.query = relative.query; _AN_Write_host('host', source, false , _AN_Read_host('host', relative) || ''); delete source.auth; delete source.hostname; _AN_Write_port('port', source, false , _AN_Read_port('port', relative)); return source; } var isSourceAbs = (_AN_Read_pathname('pathname', source) && _AN_Read_pathname('pathname', source).charAt(0) === '/'), isRelAbs = (_AN_Read_host('host', relative) !== undefined || _AN_Read_pathname('pathname', relative) && _AN_Read_pathname('pathname', relative).charAt(0) === '/'), mustEndAbs = (isRelAbs || isSourceAbs || (_AN_Read_host('host', source) && _AN_Read_pathname('pathname', relative))), removeAllDots = mustEndAbs, srcPath = _AN_Read_pathname('pathname', source) && _AN_Read_pathname('pathname', source).split('/') || [] , relPath = _AN_Read_pathname('pathname', relative) && _AN_Read_pathname('pathname', relative).split('/') || [] , psychotic = _AN_Read_protocol('protocol', source) && !slashedProtocol[_AN_Read_protocol('protocol', source)] && _AN_Read_host('host', source) !== undefined; if (psychotic) { delete source.hostname; delete source.auth; delete source.port; if (source.host) { if (srcPath[0] === '') srcPath[0] = _AN_Read_host('host', source); else srcPath.unshift(_AN_Read_host('host', source)); } delete source.host; if (relative.protocol) { delete relative.hostname; delete relative.auth; delete relative.port; if (relative.host) { if (relPath[0] === '') relPath[0] = _AN_Read_host('host', relative); else relPath.unshift(_AN_Read_host('host', relative)); } delete relative.host; } mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); } if (isRelAbs) { _AN_Write_host('host', source, false , (_AN_Read_host('host', relative) || _AN_Read_host('host', relative) === '')? _AN_Read_host('host', relative): _AN_Read_host('host', source)); _AN_Write_search('search', source, false , _AN_Read_search('search', relative)); source.query = relative.query; srcPath = relPath; } else if (relPath.length) { if (!srcPath) srcPath = [] ; srcPath.pop(); srcPath = srcPath.concat(relPath); _AN_Write_search('search', source, false , _AN_Read_search('search', relative)); source.query = relative.query; } else if ('search' in relative) { if (psychotic) { _AN_Write_host('host', source, false , srcPath.shift()); } _AN_Write_search('search', source, false , _AN_Read_search('search', relative)); source.query = relative.query; return source; } if (!_AN_Read_length('length', srcPath)) { delete source.pathname; return source; } var last = srcPath.slice(-1)[0]; var hasTrailingSlash = ((_AN_Read_host('host', source) || _AN_Read_host('host', relative)) && (last === '.' || last === '..') || last === ''); srcPath = path.normalizeArray(srcPath, true ); if (_AN_Read_length('length', srcPath) === 1 && srcPath[0] === '.') srcPath = [] ; if (mustEndAbs || removeAllDots) { var dirs = [] ; srcPath.forEach(function (dir, i){ if (dir === '..') { dirs.pop(); } else if (dir !== '.') { dirs.push(dir); } } ); if (mustEndAbs && dirs[0] !== '' && (!dirs[0] || dirs[0].charAt(0) !== '/')) { dirs.unshift(''); } srcPath = dirs; } if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { srcPath.push(''); } var isAbsolute = srcPath[0] === '' || (srcPath[0] && srcPath[0].charAt(0) === '/'); if (psychotic) { _AN_Write_host('host', source, false , isAbsolute? '': srcPath.shift()); } mustEndAbs = mustEndAbs || (_AN_Read_host('host', source) && _AN_Read_length('length', srcPath)); if (mustEndAbs && !isAbsolute) { srcPath.unshift(''); } _AN_Write_pathname('pathname', source, false , srcPath.join('/')); return source; } function parseHost(host){ var out = { } ; var at = host.indexOf('@'); if (at !== -1) { out.auth = host.substr(0, at); host = host.substr(at + 1); } var port = portPattern.exec(host); if (port) { port = port[0]; _AN_Write_port('port', out, false , port.substr(1)); host = host.substr(0, _AN_Read_length('length', host) - _AN_Read_length('length', port)); } if (host) _AN_Write_hostname('hostname', out, false , host); return out; }