From 4526308560201bfcb9ae927abb96c2103ba31344 Mon Sep 17 00:00:00 2001 From: isaacs Date: Sat, 12 Dec 2009 16:29:12 -0800 Subject: [PATCH] Update to fix failing test. Rewrite of the resolveObject function, and some tweaks to format. --- lib/uri.js | 140 ++++++++++++++++++++------------------- test/mjsunit/test-uri.js | 8 ++- 2 files changed, 77 insertions(+), 71 deletions(-) diff --git a/lib/uri.js b/lib/uri.js index 0061460197..45187e0fa6 100644 --- a/lib/uri.js +++ b/lib/uri.js @@ -126,6 +126,7 @@ function uri_format (object) { throw new Error("UrlError: URL undefined for urls#format"); if (object instanceof String || typeof(object) === 'string') return object; + var domain = object.domains ? object.domains.join(".") : @@ -138,7 +139,7 @@ function uri_format (object) { (object.password ? ":" + object.password : "") ) : object.userInfo; - var authority = ( + var authority = object.authority || (( userInfo || domain || object.port @@ -146,20 +147,18 @@ function uri_format (object) { (userInfo ? userInfo + "@" : "") + (domain || "") + (object.port ? ":" + object.port : "") - ) : - object.authority || ""; - + ) : ""); + var directory = object.directories ? object.directories.join("/") : object.directory; var path = - directory || object.file ? - ( - (directory ? directory + "/" : "") + - (object.file || "") - ) : - object.path; + object.path ? object.path.substr(1) : ( + (directory || object.file) ? ( + (directory ? directory + "/" : "") + + (object.file || "") + ) : ""); var authorityRoot = object.authorityRoot || authority ? "//" : ""; @@ -175,74 +174,79 @@ function uri_format (object) { ) || object.url || ""); }; -/**** resolveObject - returns an object representing a URL resolved from - a relative location and a source location. -*/ function uri_resolveObject (source, relative) { - if (!source) + if (!source) return relative; + + // parse a string, or get new objects + source = uri_parse(uri_format(source)); + relative = uri_parse(uri_format(relative)); + + if (relative.url === "") return source; + + // links to xyz:... from abc:... are always absolute. + if (relative.protocol && source.protocol && relative.protocol !== source.protocol) { return relative; + } + + // if there's an authority root, but no protocol, then keep the current protocol + if (relative.authorityRoot && !relative.protocol) { + relative.protocol = source.protocol; + } + // if we have an authority root, then it's absolute + if (relative.authorityRoot) return relative; - source = uri_parse(source); - relative = uri_parse(relative); - - if (relative.url == "") - return source; - - delete source.url; - delete source.authority; - delete source.domain; - delete source.userInfo; - delete source.path; - delete source.directory; + + // at this point, we start doing the tricky stuff + // we know that relative doesn't have an authority, but might have root, + // path, file, query, etc. + // also, the directory segments might contain .. or . + // start mutating "source", and then return that. - if ( - relative.protocol && relative.protocol != source.protocol || - relative.authority && relative.authority != source.authority - ) { - source = relative; + // relative urls that start with / are absolute to the authority/protocol + if (relative.root) { + [ + "path", "root", "directory", "directories", "file", "query", "anchor" + ].forEach(function (part) { source[part] = relative[part] }); } else { - if (relative.root) { - source.directories = relative.directories; - } else { - - var directories = relative.directories; - for (var i = 0; i < directories.length; i++) { - var directory = directories[i]; - if (directory == ".") { - } else if (directory == "..") { - if (source.directories.length) { - source.directories.pop(); - } else { - source.directories.push('..'); - } - } else { - source.directories.push(directory); - } - } - - if (relative.file == ".") { - relative.file = ""; - } else if (relative.file == "..") { - source.directories.pop(); - relative.file = ""; - } + // if you have /a/b/c and you're going to x/y/z, then that's /a/b/x/y/z + // if you have /a/b/c/ and you're going ot x/y/z, then that's /a/b/c/x/y/z + // if you have /a/b/c and you're going to ?foo, then that's /a/b/c?foo + if (relative.file || relative.directory) { + source.file = relative.file; + source.query = relative.query; + source.anchor = relative.anchor; } + if (relative.query) source.query = relative.query; + if (relative.query || relative.anchor) source.anchor = relative.anchor; + + // just append the dirs. we'll sort out .. and . later + source.directories = source.directories.concat(relative.directories); } - - if (relative.root) - source.root = relative.root; - if (relative.protcol) - source.protocol = relative.protocol; - if (!(!relative.path && relative.anchor)) - source.file = relative.file; - source.query = relative.query; - source.anchor = relative.anchor; - + + // back up "file" to the first non-dot + // one step for ., two for .. + var file = source.file; + while (file === ".." || file === ".") { + if (file === "..") source.directories.pop(); + file = source.directories.pop(); + } + source.file = file || ""; + + // walk over the dirs, replacing a/b/c/.. with a/b/ and a/b/c/. with a/b/c + var dirs = []; + source.directories.forEach(function (dir, i) { + if (dir === "..") dirs.pop(); + else if (dir !== "." && dir !== "") dirs.push(dir); + }); + + // now construct path/etc. + source.directories = dirs; + source.directory = dirs.concat("").join("/"); + source.path = source.root + source.directory + source.file; + source.url = uri_format(source); return source; }; - /**** resolve returns a URL resovled to a relative URL from a source URL. */ diff --git a/test/mjsunit/test-uri.js b/test/mjsunit/test-uri.js index 13ac656621..6060fed99d 100644 --- a/test/mjsunit/test-uri.js +++ b/test/mjsunit/test-uri.js @@ -168,6 +168,7 @@ for (var url in parseTests) { var expected = url, actual = uri.format(parseTests[url]); + assert.equal(expected, actual, "format("+url+") == "+url+"\nactual:"+actual); } @@ -175,12 +176,13 @@ for (var url in parseTests) { // [from, path, expected] ["/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"], //TODO: failing test - ["/foo/bar/baz", "../quux/baz", "/foo/quux/baz"], //TODO: failing test + ["/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/bar/baz", "../../../../../../../quux/baz", "/quux/baz"] ].forEach(function (relativeTest) { var a = uri.resolve(relativeTest[0], relativeTest[1]), e = relativeTest[2];