Files
cappuccino/Foundation/CPURL.j
T

599 lines
15 KiB
Plaintext

@import <Foundation/CPObject.j>
CPURLNameKey = @"CPURLNameKey";
CPURLLocalizedNameKey = @"CPURLLocalizedNameKey";
CPURLIsRegularFileKey = @"CPURLIsRegularFileKey";
CPURLIsDirectoryKey = @"CPURLIsDirectoryKey";
CPURLIsSymbolicLinkKey = @"CPURLIsSymbolicLinkKey";
CPURLIsVolumeKey = @"CPURLIsVolumeKey";
CPURLIsPackageKey = @"CPURLIsPackageKey";
CPURLIsSystemImmutableKey = @"CPURLIsSystemImmutableKey";
CPURLIsUserImmutableKey = @"CPURLIsUserImmutableKey";
CPURLIsHiddenKey = @"CPURLIsHiddenKey";
CPURLHasHiddenExtensionKey = @"CPURLHasHiddenExtensionKey";
CPURLCreationDateKey = @"CPURLCreationDateKey";
CPURLContentAccessDateKey = @"CPURLContentAccessDateKey";
CPURLContentModificationDateKey = @"CPURLContentModificationDateKey";
CPURLAttributeModificationDateKey = @"CPURLAttributeModificationDateKey";
CPURLLinkCountKey = @"CPURLLinkCountKey";
CPURLParentDirectoryURLKey = @"CPURLParentDirectoryURLKey";
CPURLVolumeURLKey = @"CPURLTypeIdentifierKey";
CPURLTypeIdentifierKey = @"CPURLTypeIdentifierKey";
CPURLLocalizedTypeDescriptionKey = @"CPURLLocalizedTypeDescriptionKey";
CPURLLabelNumberKey = @"CPURLLabelNumberKey";
CPURLLabelColorKey = @"CPURLLabelColorKey";
CPURLLocalizedLabelKey = @"CPURLLocalizedLabelKey";
CPURLEffectiveIconKey = @"CPURLEffectiveIconKey";
CPURLCustomIconKey = @"CPURLCustomIconKey";
@implementation CPURL : CPObject
{
CPURL _base @accessors(readonly, property=baseURL);
CPString _relative @accessors(readonly, property=relativeString);
CPDictionary _resourceValues;
}
- (id)initWithScheme:(CPString)scheme host:(CPString)host path:(CPString)path
{
var uri = new URI();
uri.scheme = scheme;
uri.authority = host;
uri.path = path;
[self initWithString:uri.toString()];
}
- (id)initWithString:(CPString)URLString
{
return [self initWithString:URLString relativeToURL:nil];
}
+ (id)URLWithString:(CPString)URLString
{
return [[self alloc] initWithString:URLString];
}
- (id)initWithString:(CPString)URLString relativeToURL:(CPURL)baseURL
{
if (!URI_RE.test(URLString))
return nil;
if (self)
{
_base = baseURL;
_relative = URLString;
_resourceValues = [CPDictionary dictionary];
}
return self;
}
+ (id)URLWithString:(CPString)URLString relativeToURL:(CPURL)baseURL
{
return [[self alloc] initWithString:URLString relativeToURL:baseURL];
}
- (CPURL)absoluteURL
{
var absStr = [self absoluteString];
if (absStr !== _relative)
return [[CPURL alloc] initWithString:absStr];
return self;
}
- (CPString)absoluteString
{
return resolve([_base absoluteString] || "", _relative);
}
// if absolute, returns same as absoluteString
- (CPString)relativeString
{
return _relative;
}
- (CPString)path
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).path || nil) : nil;
}
// if absolute, returns the same as path
- (CPString)relativePath
{
return URI_RE.test(_relative) ? (parse(_relative).path || nil) : nil;
}
- (CPString)scheme
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).protocol || nil) : nil;
}
- (CPString)user
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).user || nil) : nil;
}
- (CPString)password
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).password || nil) : nil;
}
- (CPString)host
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).domain || nil) : nil;
}
- (CPString)port
{
var str = [self absoluteString];
if (URI_RE.test(str)) {
var port = parse(str).port;
if (port)
return parseInt(port, 10);
}
return nil;
}
- (CPString)parameterString
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).query || nil) : nil;
}
- (CPString)fragment
{
var str = [self absoluteString];
return URI_RE.test(str) ? (parse(str).anchor || nil) : nil;
}
- (BOOL)isEqual:(id)anObject
{
// Is checking if baseURL isEqual correct? Does "identical" mean same object or equivalent values?
return [self relativeString] === [anObject relativeString] &&
([self baseURL] === [anObject baseURL] || [[self baseURL] isEqual:[anObject baseURL]]);
}
- (CPString)lastPathComponent
{
var path = [self path];
return path ? path.split("/").pop() : nil;
}
- (CPString)pathExtension
{
var path = [self path],
ext = path.match(/\.(\w+)$/);
return ext ? ext[1] : "";
}
- (CPURL)standardizedURL
{
return [CPURL URLWithString:format(parse(_relative)) relativeToURL:_base];
}
- (BOOL)isFileURL
{
return [self scheme] === "file";
}
- (CPString)description
{
return [self absoluteString];
}
- (id)resourceValueForKey:(CPString)aKey
{
return [_resourceValues objectForKey:aKey];
}
- (id)setResourceValue:(id)anObject forKey:(CPString)aKey
{
[_resourceValues setObject:anObject forKey:aKey];
}
@end
@implementation CPURL (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
_base = [aCoder decodeObjectForKey:"CPURLBaseKey"];
_relative = [aCoder decodeObjectForKey:"CPURLRelativeKey"];
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_base forKey:"CPURLBaseKey"];
[aCoder encodeObject:_relative forKey:"CPURLRelativeKey"];
}
@end
// original code: http://code.google.com/p/js-uri/
// Based on the regex in RFC2396 Appendix B.
var URI_RE = /^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;
/**
* Uniform Resource Identifier (URI) - RFC3986
*/
var URI = function(str) {
if (!str) str = "";
var result = str.match(URI_RE);
this.scheme = result[1] || null;
this.authority = result[2] || null;
this.path = result[3] || null;
this.query = result[4] || null;
this.fragment = result[5] || null;
}
/**
* Convert the URI to a String.
*/
URI.prototype.toString = function () {
var str = "";
if (this.scheme)
str += this.scheme + ":";
if (this.authority)
str += "//" + this.authority;
if (this.path)
str += this.path;
if (this.query)
str += "?" + this.query;
if (this.fragment)
str += "#" + this.fragment;
return str;
}
var parse = function(uri) {
return new URI(uri);
}
var unescape = function(str, plus) {
return decodeURI(str).replace(/\+/g, " ");
}
var unescapeComponent = function(str, plus) {
return decodeURIComponent(str).replace(/\+/g, " ");
}
// 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 (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) {
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.
*/
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));
};