Pull in the uri.js from Narwhal and create tests, stripping out the cruft from a previous code-surgery.
This commit is contained in:
Родитель
f3b0cefd0b
Коммит
2f9722cca0
|
@ -0,0 +1,236 @@
|
|||
/**
|
||||
* uri.js
|
||||
* A URI parser, compliant with assorted RFCs, providing parsing and resolution utilities.
|
||||
**/
|
||||
|
||||
exports.parse = uri_parse;
|
||||
exports.format = uri_format;
|
||||
exports.resolve = uri_resolve;
|
||||
exports.resolveObject = uri_resolveObject;
|
||||
|
||||
|
||||
/**** expressionKeys
|
||||
members of a parsed URI object that you get
|
||||
from evaluting the strict regular expression.
|
||||
*/
|
||||
var expressionKeys = [
|
||||
"url",
|
||||
"protocol",
|
||||
"authorityRoot",
|
||||
"authority",
|
||||
"userInfo",
|
||||
"user",
|
||||
"password",
|
||||
"domain",
|
||||
"port",
|
||||
"path",
|
||||
"root",
|
||||
"directory",
|
||||
"file",
|
||||
"query",
|
||||
"anchor"
|
||||
],
|
||||
strictExpression = new RegExp( /* url */
|
||||
"^" +
|
||||
"(?:" +
|
||||
"([^:/?#]+):" + /* protocol */
|
||||
")?" +
|
||||
"(?:" +
|
||||
"(//)" + /* authorityRoot */
|
||||
"(" + /* authority */
|
||||
"(?:" +
|
||||
"(" + /* userInfo */
|
||||
"([^:@/]*)" + /* user */
|
||||
":?" +
|
||||
"([^@/]*)" + /* password */
|
||||
")?" +
|
||||
"@" +
|
||||
")?" +
|
||||
"([^:/?#]*)" + /* domain */
|
||||
"(?::(\\d*))?" + /* port */
|
||||
")" +
|
||||
")?" +
|
||||
"(" + /* path */
|
||||
"(/?)" + /* root */
|
||||
"((?:[^?#/]*/)*)" +
|
||||
"([^?#]*)" + /* file */
|
||||
")" +
|
||||
"(?:\\?([^#]*))?" + /* query */
|
||||
"(?:#(.*))?" /*anchor */
|
||||
);
|
||||
|
||||
/**** parse
|
||||
a URI parser function that uses the `strictExpression`
|
||||
and `expressionKeys` and returns an `Object`
|
||||
mapping all `keys` to values.
|
||||
*/
|
||||
function uri_parse (url) {
|
||||
var items = {},
|
||||
parts = strictExpression.exec(url);
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
items[expressionKeys[i]] = parts[i] ? parts[i] : "";
|
||||
}
|
||||
|
||||
items.root = (items.root || items.authorityRoot) ? '/' : '';
|
||||
|
||||
items.directories = items.directory.split("/");
|
||||
if (items.directories[items.directories.length - 1] == "") {
|
||||
items.directories.pop();
|
||||
}
|
||||
|
||||
/* normalize */
|
||||
var directories = [];
|
||||
for (var i = 0; i < items.directories.length; i++) {
|
||||
var directory = items.directories[i];
|
||||
if (directory == '.') {
|
||||
} else if (directory == '..') {
|
||||
if (directories.length && directories[directories.length - 1] != '..')
|
||||
directories.pop();
|
||||
else
|
||||
directories.push('..');
|
||||
} else {
|
||||
directories.push(directory);
|
||||
}
|
||||
}
|
||||
items.directories = directories;
|
||||
|
||||
items.domains = items.domain.split(".");
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
|
||||
/**** format
|
||||
accepts a parsed URI object and returns
|
||||
the corresponding string.
|
||||
*/
|
||||
function uri_format (object) {
|
||||
if (typeof(object) == 'undefined')
|
||||
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.domain;
|
||||
var userInfo = (
|
||||
object.user ||
|
||||
object.password
|
||||
) ? (
|
||||
(object.user || "") +
|
||||
(object.password ? ":" + object.password : "")
|
||||
) :
|
||||
object.userInfo;
|
||||
var 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;
|
||||
var authorityRoot =
|
||||
object.authorityRoot
|
||||
|| authority ? "//" : "";
|
||||
|
||||
return object.url = ((
|
||||
(object.protocol ? object.protocol + ":" : "") +
|
||||
(authorityRoot) +
|
||||
(authority) +
|
||||
(object.root || (authority && path) ? "/" : "") +
|
||||
(path ? path : "") +
|
||||
(object.query ? "?" + object.query : "") +
|
||||
(object.anchor ? "#" + object.anchor : "")
|
||||
) || 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)
|
||||
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;
|
||||
|
||||
if (
|
||||
relative.protocol && relative.protocol != source.protocol ||
|
||||
relative.authority && relative.authority != source.authority
|
||||
) {
|
||||
source = relative;
|
||||
} 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 (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;
|
||||
|
||||
return source;
|
||||
};
|
||||
|
||||
|
||||
/**** resolve
|
||||
returns a URL resovled to a relative URL from a source URL.
|
||||
*/
|
||||
function uri_resolve (source, relative) {
|
||||
return uri_format(uri_resolveObject(source, relative));
|
||||
};
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
process.mixin(require("./common"));
|
||||
|
||||
var uri = require("uri"),
|
||||
sys = require("sys");
|
||||
|
||||
// URIs to parse, and expected data
|
||||
// { url : parsed }
|
||||
var parseTests = {
|
||||
"http://www.narwhaljs.org/blog/categories?id=news" : {
|
||||
"url": "http://www.narwhaljs.org/blog/categories?id=news",
|
||||
"protocol": "http",
|
||||
"authorityRoot": "//",
|
||||
"authority": "www.narwhaljs.org",
|
||||
"userInfo": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"domain": "www.narwhaljs.org",
|
||||
"port": "",
|
||||
"path": "/blog/categories",
|
||||
"root": "/",
|
||||
"directory": "blog/",
|
||||
"file": "categories",
|
||||
"query": "id=news",
|
||||
"anchor": "",
|
||||
"directories": [
|
||||
"blog"
|
||||
],
|
||||
"domains": [
|
||||
"www",
|
||||
"narwhaljs",
|
||||
"org"
|
||||
]
|
||||
},
|
||||
"http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=" : {
|
||||
"url": "http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"protocol": "http",
|
||||
"authorityRoot": "//",
|
||||
"authority": "mt0.google.com",
|
||||
"userInfo": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"domain": "mt0.google.com",
|
||||
"port": "",
|
||||
"path": "/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"root": "/",
|
||||
"directory": "vt/",
|
||||
"file": "lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"query": "",
|
||||
"anchor": "",
|
||||
"directories": [
|
||||
"vt"
|
||||
],
|
||||
"domains": [
|
||||
"mt0",
|
||||
"google",
|
||||
"com"
|
||||
]
|
||||
},
|
||||
"http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
|
||||
"url": "http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"protocol": "http",
|
||||
"authorityRoot": "//",
|
||||
"authority": "mt0.google.com",
|
||||
"userInfo": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"domain": "mt0.google.com",
|
||||
"port": "",
|
||||
"path": "/vt/lyrs=m@114",
|
||||
"root": "/",
|
||||
"directory": "vt/",
|
||||
"file": "lyrs=m@114",
|
||||
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"anchor": "",
|
||||
"directories": [
|
||||
"vt"
|
||||
],
|
||||
"domains": [
|
||||
"mt0",
|
||||
"google",
|
||||
"com"
|
||||
]
|
||||
},
|
||||
"http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
|
||||
"url": "http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"protocol": "http",
|
||||
"authorityRoot": "//",
|
||||
"authority": "user:pass@mt0.google.com",
|
||||
"userInfo": "user:pass",
|
||||
"user": "user",
|
||||
"password": "pass",
|
||||
"domain": "mt0.google.com",
|
||||
"port": "",
|
||||
"path": "/vt/lyrs=m@114",
|
||||
"root": "/",
|
||||
"directory": "vt/",
|
||||
"file": "lyrs=m@114",
|
||||
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
|
||||
"anchor": "",
|
||||
"directories": [
|
||||
"vt"
|
||||
],
|
||||
"domains": [
|
||||
"mt0",
|
||||
"google",
|
||||
"com"
|
||||
]
|
||||
},
|
||||
"file:///etc/passwd" : {
|
||||
"url": "file:///etc/passwd",
|
||||
"protocol": "file",
|
||||
"authorityRoot": "//",
|
||||
"authority": "",
|
||||
"userInfo": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"domain": "",
|
||||
"port": "",
|
||||
"path": "/etc/passwd",
|
||||
"root": "/",
|
||||
"directory": "etc/",
|
||||
"file": "passwd",
|
||||
"query": "",
|
||||
"anchor": "",
|
||||
"directories": [
|
||||
"etc"
|
||||
],
|
||||
"domains": [
|
||||
""
|
||||
]
|
||||
},
|
||||
"file:///etc/node/" : {
|
||||
"url": "file:///etc/node/",
|
||||
"protocol": "file",
|
||||
"authorityRoot": "//",
|
||||
"authority": "",
|
||||
"userInfo": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"domain": "",
|
||||
"port": "",
|
||||
"path": "/etc/node/",
|
||||
"root": "/",
|
||||
"directory": "etc/node/",
|
||||
"file": "",
|
||||
"query": "",
|
||||
"anchor": "",
|
||||
"directories": [
|
||||
"etc",
|
||||
"node"
|
||||
],
|
||||
"domains": [
|
||||
""
|
||||
]
|
||||
}
|
||||
};
|
||||
for (var url in parseTests) {
|
||||
var actual = uri.parse(url),
|
||||
expected = parseTests[url];
|
||||
for (var i in expected) {
|
||||
// sys.debug(i);
|
||||
// sys.debug("expect: " + JSON.stringify(expected[i]));
|
||||
// sys.debug("actual: " + JSON.stringify(actual[i]));
|
||||
var e = JSON.stringify(expected[i]),
|
||||
a = JSON.stringify(actual[i]);
|
||||
assert.equal(e, a, "parse(" + url + ")."+i+" == "+e+"\nactual: "+a);
|
||||
}
|
||||
|
||||
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", "/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"]
|
||||
].forEach(function (relativeTest) {
|
||||
var a = uri.resolve(relativeTest[0], relativeTest[1]),
|
||||
e = relativeTest[2];
|
||||
assert.equal(e, a,
|
||||
"resolve("+[relativeTest[0], relativeTest[1]]+") == "+e+
|
||||
"\n actual="+a);
|
||||
});
|
||||
|
Загрузка…
Ссылка в новой задаче