diff options
Diffstat (limited to 'utils/lib/url.js')
-rwxr-xr-x | utils/lib/url.js | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/utils/lib/url.js b/utils/lib/url.js new file mode 100755 index 0000000..897ebc1 --- /dev/null +++ b/utils/lib/url.js @@ -0,0 +1,323 @@ + +/**** keys + members of a parsed URI object. +*/ +exports.keys = [ + "url", + "protocol", + "authorityRoot", + "authority", + "userInfo", + "user", + "password", + "domain", + "domains", + "port", + "path", + "root", + "directory", + "directories", + "file", + "query", + "anchor" +]; + +/**** expressionKeys + members of a parsed URI object that you get + from evaluting the strict regular expression. +*/ +exports.expressionKeys = [ + "url", + "protocol", + "authorityRoot", + "authority", + "userInfo", + "user", + "password", + "domain", + "port", + "path", + "root", + "directory", + "file", + "query", + "anchor" +]; + +/**** strictExpression +*/ +exports.strictExpression = new RegExp( /* url */ + "^" + + "(?:" + + "([^:/?#]+):" + /* protocol */ + ")?" + + "(?:" + + "(//)" + /* authorityRoot */ + "(" + /* authority */ + "(?:" + + "(" + /* userInfo */ + "([^:@]*)" + /* user */ + ":?" + + "([^:@]*)" + /* password */ + ")?" + + "@" + + ")?" + + "([^:/?#]*)" + /* domain */ + "(?::(\\d*))?" + /* port */ + ")" + + ")?" + + "(" + /* path */ + "(/?)" + /* root */ + "((?:[^?#/]*/)*)" + + "([^?#]*)" + /* file */ + ")" + + "(?:\\?([^#]*))?" + /* query */ + "(?:#(.*))?" /*anchor */ +); + +/**** Parser + returns a URI parser function given + a regular expression that renders + `expressionKeys` and returns an `Object` + mapping all `keys` to values. +*/ +exports.Parser = function (expression) { + return function (url) { + if (typeof url == "undefined") + throw new Error("HttpError: URL is undefined"); + if (typeof url != "string") return new Object(url); + + var items = {}; + var parts = expression.exec(url); + + for (var i = 0; i < parts.length; i++) { + items[exports.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; + }; +}; + +/**** parse + a strict URI parser. +*/ +exports.parse = exports.Parser(exports.strictExpression); + +/**** format + accepts a parsed URI object and returns + the corresponding string. +*/ +exports.format = function (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; + return ( + (object.protocol ? object.protocol + ":" : "") + + (authority ? "//" + 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. +*/ +exports.resolveObject = function (source, relative) { + if (!source) + return relative; + + source = exports.parse(source); + relative = exports.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; +}; + +/**** relativeObject + returns an object representing a relative URL to + a given target URL from a source URL. +*/ +exports.relativeObject = function (source, target) { + target = exports.parse(target); + source = exports.parse(source); + + delete target.url; + + if ( + target.protocol == source.protocol && + target.authority == source.authority + ) { + delete target.protocol; + delete target.authority; + delete target.userInfo; + delete target.user; + delete target.password; + delete target.domain; + delete target.domains; + delete target.port; + if ( + !!target.root == !!source.root && !( + target.root && + target.directories[0] != source.directories[0] + ) + ) { + delete target.path; + delete target.root; + delete target.directory; + while ( + source.directories.length && + target.directories.length && + target.directories[0] == source.directories[0] + ) { + target.directories.shift(); + source.directories.shift(); + } + while (source.directories.length) { + source.directories.shift(); + target.directories.unshift('..'); + } + + if (!target.root && !target.directories.length && !target.file && source.file) + target.directories.push('.'); + + if (source.file == target.file) + delete target.file; + if (source.query == target.query) + delete target.query; + if (source.anchor == target.anchor) + delete target.anchor; + } + } + + return target; +}; + +/**** resolve + returns a URL resovled to a relative URL from a source URL. +*/ +exports.resolve = function (source, relative) { + return exports.format(exports.resolveObject(source, relative)); +}; + +/**** relative + returns a relative URL to a target from a source. +*/ +exports.relative = function (source, target) { + return exports.format(exports.relativeObject(source, target)); +}; + |