/* 
Thanks to Steven Levithan <http://stevenlevithan.com> 
for parseUri http://blog.stevenlevithan.com/archives/parseuri
*/

function ParseUri (source) {
	if (source) {
		this.parse(source);
	}
}

(function () {
	ParseUri.prototype.options = {
		strictMode: false,
		key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "basename", "extension", "query", "anchor"],
		q: {
			name: "queryKey",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},
		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)(([^?#\.]*)\.?([^?#\.]*)))(?:\?([^#]*))?(?:#(.*))?)/,
			loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?(([^?#\/\.]*)\.?([^?#\/]*)?))(?:\?([^#]*))?(?:#(.*))?)/
		}
	};
	
	ParseUri.prototype.parse = function (source) {
		var o = this.options, value = o.parser[o.strictMode ? "strict" : "loose"].exec(source);
		
		for (var i = 0, uri = {}; i < o.key.length; i++) {
			uri[o.key[i]] = value[i] || "";
		}
		
		uri[o.q.name] = {};
		uri.query.replace(o.q.parser, function ($0, $1, $2) {
			if ($1) {
				uri[o.q.name][$1] = $2;
			}
		});
		
		this.uri = uri;
		//return uri;
	};
	
	ParseUri.prototype.source = function (source) {
		if (source) {
			this.parse(source);
		}
		
		return this.uri.source;
	};
	
	ParseUri.prototype.authority = function (val) {
		if (typeof val === 'string') {
			this._source({
				authority: val,
				userInfo: '',
				user: '',
				password: '',
				host: '',
				port: ''
			});
		} else if (typeof val === 'object') {
			this._source(val);
		}
		
		return this.uri.authority;
	};
	
	ParseUri.prototype.userInfo = function (val) {
		if (typeof val === 'string') {
			this._source({
				userInfo: val,
				user: '',
				password: ''
			});
		} else if (typeof val === 'object') {
			this._source(val);
		}
		
		return this.uri.userInfo;
	};
	
	ParseUri.prototype.relative = function (val) {
		if (typeof val === 'string') {
			this._source({
				relative: val,
				path: '',
				directory: '',
				file: '',
				query: '',
				queryKey: {},
				anchor: ''
			});
		} else if (typeof val === 'object') {
			this._source(val);
		}
		
		return this.uri.relative;
	};

	ParseUri.prototype.path = function (val) {
		if (typeof val === 'string') {
			this._source({
				path: val,
				directory: '',
				file: ''
			});
		} else if (typeof val === 'object') {
			this._source(val);
		}
		
		return this.uri.path;
	};

	ParseUri.prototype.file = function (val) {
		if (typeof val === 'string') {
			this._source({
				file: val,
				basename: '',
				extension: ''
			});
		} else if (typeof val === 'object') {
			this._source(val);
		}
		
		return this.uri.file;
	};
	
	ParseUri.prototype.query = function (val) {
		if (typeof val === 'string') {
			this._source({
				query: val,
				queryKey: {}
			});
		} else if (typeof val === 'object') {
			this._source(val);
		}
		
		return this.uri.query;
	};

	ParseUri.prototype.queryKey = function (val) {
		if (typeof val !== 'undefined') {
			this._source({
				queryKey: val,
				'query': ''
			});
		}
		
		return this.uri.queryKey;
	};

	ParseUri.prototype.queryKeyAdd = function (val) {
		if (typeof val !== 'undefined') {
			var queryKey = this.queryKey();
			val = this._extend(queryKey, val);
			this._source({
				queryKey: val,
				'query': ''
			});
		}
		
		return this.uri.queryKey;
	};

	var keys = ['protocol', 'user', 'password', 'host', 'port', 'directory', 'basename', 'extension', 'anchor'];
	
	for (var key in keys) {
		if (typeof keys[key] === 'string') {
			key = keys[key];
			(function(key){
				ParseUri.prototype[key] = function (val) {
					if (typeof val !== 'undefined') {
						var uri = {};
						uri[key] = val;
						this._source(uri);
					}
				
					return this.uri[key];
				};
			})(key);
		}
	}
	
	ParseUri.prototype._source = function (uri) {
		var source = '';
		
		uri = this._extend(this.uri, uri);
		
		if (uri.protocol) {
			source = uri.protocol + '://';
		}

		var authority = '';

		var userInfo = '';
		if (uri.user) {
			userInfo = uri.user;
		}
		if (uri.password) {
			userInfo = userInfo + ':' + uri.password;
		}
		if (userInfo === '' && uri.userInfo) {
			userInfo = uri.userInfo;
		}
		if (userInfo) {
			authority = userInfo + '@';
		}
		
		if (this.uri.host) {
			authority = authority + uri.host;
		}
		if (uri.port) {
			authority = authority + ':' + uri.port;
		}
		if (authority === '' && uri.authority) {
			authority = uri.authority;
		}
		source = source + authority;

		var relative = '';

		var path = '';
		if (uri.directory) {
			path = path + uri.directory;
		}
		
		var file = '';
		if (uri.basename) {
			file = uri.basename;
		}
		if (uri.extension) {
			file = file + '.' + uri.extension;
		}
		if (file === '' && uri.file) {
			file = uri.file;
		}
		if (file) {
			path = path + file;
		}
		
		if (path === '' && uri.path) {
			path = uri.path;
		}
		relative = path;

		var query = '';		
		if (uri.queryKey) {
			query = [];
			for (var key in uri.queryKey) {
				var value = encodeURI(uri.queryKey[key]);
				query.push(key + '=' + value);
			}
			query = query.join('&');
		}
		if (query === '' && uri.query) {
			query = uri.query;
		}
		if (query) {
			relative = relative + '?' + query;
		}
		if (uri.anchor) {
			relative = relative + '#' + uri.anchor;
		}

		if (relative === '' && uri.relative) {
			relative = uri.relative;
		}
		
		source = source + relative;

		this.parse(source);
		return source;
	};
	
	ParseUri.prototype._extend = function (obj1, obj2) {
		for (var i in obj2) {
			obj1[i] = obj2[i];
		}
		
		return obj1;
	};
})();