Files
cappuccino/Objective-J/CFURL.js
T
2010-03-03 02:36:40 -08:00

651 lines
16 KiB
JavaScript

// Based on the regex in RFC2396 Appendix B.
var URI_RE = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
var CFURLsForCachedUIDs,
CFURLCachingEnableCount = 0;
function enableCFURLCaching()
{
++CFURLCachingEnableCount;
CFURLsForCachedUIDs = { };
}
function disableCFURLCaching()
{
CFURLCachingEnableCount = MAX(CFURLCachingEnableCount - 1, 0);
if (!CFURLCachingEnableCount === 0)
delete CFURLsForCachedUIDs;
}
GLOBAL(CFURL) = function CFURL(/*CFURL|String*/ aURL, /*CFURL*/ aBaseURL)
{
if (aURL instanceof CFURL)
if (!aBaseURL)
return aURL
else
{
var existingBaseURL = aURL.baseURL();
if (existingBaseURL)
aBaseURL = new CFURL(existingBaseURL, aBaseURL);
return new CFURL(aURL.string(), aBaseURL);
}
// Use the cache if it's enabled.
if (CFURLsForCachedUIDs)
{
var cacheUID = aURL + " " + (aBaseURL && aBaseURL.UID() || "");
if (hasOwnProperty.call(CFURLsForCachedUIDs, cacheUID))
return CFURLsForCachedUIDs[cacheUID];
CFURLsForCachedUIDs[cacheUID] = this;
}
this._UID = objj_generateObjectUID();
this._string = aURL;
var result = (aURL || "").match(URI_RE);
this._baseURL = aBaseURL;
this._resourcePropertiesForKeys = new CFMutableDictionary();
this._scheme = result[2];
this._authority = result[4];
this._path = result[5] || "";
this._queryString = result[7] || NULL;
this._fragment = result[9] || NULL;
}
var URLMap = { };
CFURL.prototype.UID = function()
{
return this._UID;
}
CFURL.prototype.mappedURL = function()
{
return URLMap[this.absoluteString()] || this;
}
CFURL.setMappedURLForURL = function(/*CFURL*/ fromURL, /*CFURL*/ toURL)
{
URLMap[fromURL.absoluteString()] = toURL;
}
CFURL.prototype.schemeAndAuthority = function()
{
var string = "",
scheme = this.scheme();
if (scheme)
string += scheme + ":";
var authority = this.authority();
if (authority)
string += "//" + authority;
return string;
}
CFURL.prototype.absoluteString = function()
{
return this.absoluteURL().string();
}
CFURL.prototype.toString = function()
{
return this.absoluteString();
}
CFURL.prototype.absoluteURL = function()
{
if (this._absoluteURL === undefined)
{
var baseURL = this._baseURL;
this._absoluteURL = baseURL ? new CFURL(resolve(baseURL.absoluteString(), this.string())) : this;
}
return this._absoluteURL;
}
CFURL.prototype.string = function()
{
return this._string;
}
CFURL.prototype.authority = function()
{
var authority = this._authority;
if (authority === undefined)
{
var baseURL = this.baseURL();
authority = baseURL && baseURL.authority() || NULL;
this._authority = authority;
}
return authority;
}
CFURL.prototype.hasDirectoryPath = function()
{
var path = this.path();
if (!path)
return NO;
return path.charAt(path.length - 1) === "/";
}
CFURL.prototype.hostName = function()
{
return this._authority;
}
CFURL.prototype.fragment = function()
{
return this._fragment;
}
CFURL.prototype.lastPathComponent = function()
{
var path = this.path();
if (!path)
return "";
// Root is a special case.
if (path === "/")
return "/";
// Remove the trailing slash, since "path/directory/" and "path/directory" should both return a.
var components = path.replace(/\/$/g, "").split("/");
return components[components.length - 1];
}
CFURL.prototype.path = function()
{
return this._path;
}
CFURL.prototype.pathComponents = function()
{
if (!this._pathComponents)
{
var path = this.path();
if (!path)
this._pathComponents = [];
else
{
var components = path.split("/"),
result = [],
index = 0,
count = components.length;
for (; index < count; ++index)
{
var component = components[index];
if (component)
result.push(component);
else if (index === 0)
result.push("/");
}
this._pathComponents = result;
}
}
return this._pathComponents;
}
CFURL.prototype.pathExtension = function()
{
var lastPathComponent = this.lastPathComponent();
if (!lastPathComponent)
return NULL;
lastPathComponent = lastPathComponent.replace(/^\.*/, '');
var index = lastPathComponent.lastIndexOf(".");
return index <= 0 ? "" : lastPathComponent.substring(index + 1);
}
CFURL.prototype.queryString = function()
{
return this._queryString;
}
CFURL.prototype.scheme = function()
{
var scheme = this._scheme;
if (scheme === undefined)
{
var baseURL = this.baseURL();
scheme = baseURL && baseURL.scheme() || NULL;
this._scheme = scheme;
}
return scheme;
}
CFURL.prototype.baseURL = function()
{
return this._baseURL;
}
CFURL.prototype.asDirectoryPathURL = function()
{
if (this.hasDirectoryPath())
return this;
return new CFURL(this.lastPathComponent() + "/", this);
}
CFURL.prototype.resourcePropertyForKey = function(/*String*/ aKey)
{
return this._resourcePropertiesForKeys.objectForKey(aKey);
}
CFURL.prototype.setResourcePropertyForKey = function(/*String*/ aKey, /*id*/ aValue)
{
this._resourcePropertiesForKeys.setObjectForKey(aKey, aValue);
}
CFURL.prototype.staticResourceData = function()
{
var data = new CFMutableData();
data.setRawString(StaticResource.resourceAtURL(this).contents());
return data;
}
/*
function parseURL(aURL)
{
var parts = expression.exec(aURL.string()),
index = 0,
count = parts.length;
for (; index < count; ++index)
aURL[expressionKeys[index]] = parts[index] || "";
aURL._root = (aURL._root || aURL._authorityRoot) ? "/" : "";
var components = aPath.split("/"),
results = [],
index = 0,
count = components.length,
isRoot = aPath.charAt(0) === "/";
for (; index < count; ++index)
{
var component = components[index];
// These simply remain in the current directory.
if (component === "" || component === ".")
continue;
if (component !== "..")
{
results.push(component);
continue;
}
var resultsCount = results.length;
// If we have a valid previous component, "climb" it.
if (resultsCount > 0 && results[resultsCount - 1] !== "..")
results.pop();
// If this isn't a root listing, and we are preceded by only ..'s, or
// nothing at all, then add it since it makes sense for relative paths.
else if (!isRoot && resultsCount === 0 || results[resultsCount - 1] === "..")
results.push(component);
}
return (isRoot ? "/" : "") + results.join("/");
items.direcotries = that;
items.domains = items.domain.split(".");
};
}
*/
// from Chiron's HTTP module:
/**** keys
members of a parsed URI object.
*/
var 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.
*/
var expressionKeys = [
"url",
"protocol",
"authorityRoot",
"authority",
"userInfo",
"user",
"password",
"domain",
"port",
"path",
"root",
"directory",
"file",
"query",
"anchor"
];
/**** strictExpression
*/
var 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.
*/
var Parser = function (expression) {
return function p_p(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[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.
*/
var parse = Parser(strictExpression);
/**** format
accepts a parsed URI object and returns
the corresponding string.
*/
var 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.
*/
var resolveObject = function (source, relative) {
if (!source)
return relative;
source = parse(source);
relative = 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 || relative.protocol) {
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 || relative.protocol)
source.root = relative.root;
if (relative.protocol)
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.
*/
var relativeObject = function (source, target) {
target = parse(target);
source = 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.
*/
var resolve = function (source, relative) {
return format(resolveObject(source, relative));
};
/**** relative
returns a relative URL to a target from a source.
*/
var relative = function (source, target) {
return format(relativeObject(source, target));
};