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(".") :
(object.password ? ":" + object.password : "")
) :
object.userInfo;
- var authority = (
+ var authority = object.authority || ((
userInfo ||
domain ||
object.port
(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 ? "//" : "";
) || 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.
*/
var expected = url,
actual = uri.format(parseTests[url]);
+
assert.equal(expected, actual, "format("+url+") == "+url+"\nactual:"+actual);
}
// [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];