﻿/*
Script: Uri.js
	Class to parse and modify Uris.

License:
	MIT-style license.
*/

var Uri = new Class({
	/*
	HTTP: href, protocol, user, password, hostname, port, directory, pathname, file, search, hash
	MAILTO: href, email, username, hostname, headers, subject, body
	*/

	initialize: function(baseUri, relativeUri){
		if(!relativeUri && relativeUri !== 0){
			if(!Uri.base){
				Uri.base = new Uri({}, window.location.href);
				var bases = document.getElementsByTagName('base');
				for(var i=bases.length-1;i>=0;i--) if (bases[i].href){ Uri.base = new Uri(bases[i].href); break; }
			}
			relativeUri = baseUri;
			baseUri = Uri.base;
			if(!relativeUri && relativeUri !== 0) return baseUri;
		}
		
		if(relativeUri.scheme) return $extend(this, relativeUri);

		this.href = relativeUri = relativeUri.toString();
		
		var match, scheme;
		if(match = /^([a-zA-Z]+)\:/.exec(relativeUri)){
			baseUri = null;
			scheme = Uri.schemes[this.scheme = match[1].toLowerCase()];
		} else {
			baseUri = new Uri(baseUri);
			if(scheme = Uri.schemes[baseUri.scheme]){
				$extend(this, baseUri);
				scheme = scheme.relative || scheme;
			}
		}

		if(!scheme || !(match = scheme.parse.exec(relativeUri))){
			this.href = relativeUri;
			return this;
		}
		
		var self = this, dir = this.directory;
		if(scheme.props) scheme.props.each(function(k,i){ self[k] = match[i+1]; });
		if(scheme.init) scheme.init(self, baseUri, match);

		return this;
	},
	
	
	toAbsolute: function(baseUri){
		var scheme = Uri.schemes[this.scheme];
		return !scheme || !scheme.relative || !scheme.relative.to || (baseUri = new Uri(this, baseUri)).scheme != this.scheme ? this.href : scheme.relative.to(this, baseUri, true);
	},
	
	toRelative: function(baseUri){
		var scheme = Uri.schemes[this.scheme];
		return !scheme || !scheme.relative || !scheme.relative.to || (baseUri = new Uri(this, baseUri)).scheme != this.scheme ? this.href : scheme.relative.to(this, baseUri);
	},

	toString: function(){ return this.href;	}
});

Uri.schemes = {
	http: {
		parse: /^([a-zA-Z]+:)(?:\/\/(?:(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]+)?(?::(\d*))?)?((?:[^?#\/]*\/)*)([^?#]*)(\?[^#]*)?(#.*)?/,
		props: ['protocol', 'user', 'password', 'hostname', 'port', 'directory', 'file', 'search', 'hash'],
		init: function(uri, baseUri){
			if(uri.directory){
				var b = !baseUri || !baseUri.directory || /^\/.?/.test(uri.directory) ? [] : baseUri.directory.replace(/\/$/, '').split('/'),
					r = uri.directory ? uri.directory.replace(/\/$/, '').split('/') : [];
				r.each(function(d, i){
					if(d == '..'){
						if(b.length > 1 || (b.length > 0 && b[0] != '')) b.pop();
					} else if(d != '.')
						b.push(d);
				});
				uri.directory = b.join('/') + '/';
			}
			else
				uri.directory = baseUri ? baseUri.directory : '/';

			var dp = Uri.defaultPorts[uri.protocol.substr(0, uri.protocol.length - 1).toLowerCase()];
			uri.port = uri.port || dp;
			uri.directory = uri.directory || '/';
			uri.pathname = uri.directory + (uri.file || '');
			uri.href = uri.protocol + '//' + (uri.user ? uri.user + (uri.password ? ':' + uri.password : '') + '@' : '') +
				   (uri.hostname || '') + (uri.port && uri.port != dp ? ':' + uri.port : '') +
				   (uri.directory || '/') + (uri.file || '') + (uri.search || '') + (uri.hash || '');
		},
		relative: {
			parse: /^(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(\?[^#]*)?(#.*)?/,
			props: ['directory', 'file', 'search', 'hash'],
			to: function(uri, baseUri, absolute){
				if(!uri.directory || !baseUri.directory || uri.protocol != baseUri.protocol || uri.hostname != baseUri.hostname || uri.port != baseUri.port)
					return uri.href;

				if(absolute) return uri.directory + (uri.file || '') + (uri.search || '') + (uri.hash || '');

				var b, r, p = '', o;
				b = baseUri.directory.split('/');
				r = uri.directory.split('/');
				
				for(o = 0; o < b.length && o < r.length && b[o] == r[o]; o++);
					
				for(var i = 0; i < b.length - o - 1; i++) p += "../";

				for(var i = o; i < r.length - 1; i++) p += r[i] + '/';
					
				return (p || (uri.file ? '' : './')) + (uri.file || '') + (uri.search || '') + (uri.hash || '');
			}
		}
	},
	mailto: {
		parse: (/^([a-z]+:)(([^\.:@]+(?:\.[^:@]+)*)@((?:[^?:\.]+\.)*[^?:\.]+))(\?(?:(?:.*?&|)subject=([^&]*))?(?:(?:.*?&|)body=([^&]*))?.*)?/i),
		props: ['protocol', 'email', 'username', 'hostname', 'headers', 'subject', 'body']
	},
	javascript: {
		parse: /^([a-z]+:)(.*)$/,
		props: ['protocol', 'script']
	}
};
Uri.schemes.http.relative.init = Uri.schemes.http.init;

Uri.validate = function(uri){
	for(var s in Uri.schemes) if(Uri.schemes[s].parse.test(uri)) return true;
	return false;
};

Uri.validate.mailto = function(uri){ return (/^mailto:/i).test(uri) && Uri.schemes.mailto.parse.test(uri); };
['http','https','ftp','file','rtsp','mms'].each(function(k){
	Uri.validate[k] = function(uri){ return new RegExp('^' + k + ':', 'i').test(uri) && Uri.schemes[k].parse.test(uri); };
	Uri.schemes[k] = Uri.schemes.http;
});

Uri.defaultPorts = { http: 80, https: 443, ftp: 21 };