/** --------------------------------------------------------------------------
 *	jQuery URL Decoder
 *	Version 1.0
 *	Parses URL and return its components. Can also build URL from components
 *	
 * ---------------------------------------------------------------------------
 *	HOW TO USE:
 *
 *	$.url.decode('http://username:password@hostname/path?arg1=value%40+1&arg2=touch%C3%A9#anchor')
 *	// returns
 *	// http://username:password@hostname/path?arg1=value@ 1&arg2=touché#anchor
 *	// Note: "%40" is replaced with "@", "+" is replaced with " " and "%C3%A9" is replaced with "é"
 *	
 *	$.url.encode('file.htm?arg1=value1 @#456&amp;arg2=value2 touché')
 *	// returns
 *	// file.htm%3Farg1%3Dvalue1%20%40%23456%26arg2%3Dvalue2%20touch%C3%A9
 *	// Note: "@" is replaced with "%40" and "é" is replaced with "%C3%A9"
 *	
 *	$.url.parse('http://username:password@hostname/path?arg1=value%40+1&arg2=touch%C3%A9#anchor')
 *	// returns
 *	{
 *		source: 'http://username:password@hostname/path?arg1=value%40+1&arg2=touch%C3%A9#anchor',
 *		protocol: 'http',
 *		authority: 'username:password@hostname',
 *		userInfo: 'username:password',
 *		user: 'username',
 *		password: 'password',
 *		host: 'hostname',
 *		port: '',
 *		path: '/path',
 *		directory: '/path',
 *		file: '',
 *		relative: '/path?arg1=value%40+1&arg2=touch%C3%A9#anchor',
 *		query: 'arg1=value%40+1&arg2=touch%C3%A9',
 *		anchor: 'anchor',
 *		params: {
 *			'arg1': 'value@ 1',
 *			'arg2': 'touché'
 *		}
 *	}
 *	
 *	$.url.build({
 *		protocol: 'http',
 *		username: 'username',
 *		password: 'password',
 *		host: 'hostname',
 *		path: '/path',
 *		query: 'arg1=value%40+1&arg2=touch%C3%A9',
 *		// or 
 *		//params: {
 *		//	'arg1': 'value@ 1',
 *		//	'arg2': 'touché'
 *		//}
 *		anchor: 'anchor',
 *	})
 *	// returns
 *	// http://username:password@hostname/path?arg1=value%40+1&arg2=touch%C3%A9#anchor	
 *	
 * ---------------------------------------------------------------------------
 * OTHER PARTIES' CODE:
 *
 * Parser based on the Regex-based URI parser by Steven Levithan.
 * For more information visit http://blog.stevenlevithan.com/archives/parseuri
 *
 * Deparam taken from jQuery BBQ by Ben Alman. Dual licensed under the MIT and GPL licenses (http://benalman.com/about/license/)
 * http://benalman.com/projects/jquery-bbq-plugin/
 *  
 * ---------------------------------------------------------------------------
	
*/
jQuery.url = function(){ 
	
    /**
	 * private function to encode URL
  	 * 
	 * @param {String} string //required
	 * @return {String}
     */
	function utf8_encode(string) { 
		string = string.replace(/\r\n/g,"\n"); 
		var utftext = ""; 
 
		for (var n = 0; n < string.length; n++) { 
 
			var c = string.charCodeAt(n); 
 
			if (c < 128) { 
				utftext += String.fromCharCode(c); 
			} 
			else if((c > 127) && (c < 2048)) { 
				utftext += String.fromCharCode((c >> 6) | 192); 
				utftext += String.fromCharCode((c & 63) | 128); 
			} 
			else { 
				utftext += String.fromCharCode((c >> 12) | 224); 
				utftext += String.fromCharCode(((c >> 6) & 63) | 128); 
				utftext += String.fromCharCode((c & 63) | 128); 
			} 
 
		} 
 
		return utftext; 
	}
 
    /**
     * private function to decode URL
  	 * 
	 * @param {String} utftext //required
	 * @return {String}
     */
	function utf8_decode(utftext) { 
		var string = ""; 
		var i = 0; 
		var c = 0;
		var c2 = 0; 
 
		while ( i < utftext.length ) { 
 
			c = utftext.charCodeAt(i); 
 
			if (c < 128) { 
				string += String.fromCharCode(c); 
				i++; 
			} 
			else if((c > 191) && (c < 224)) { 
				c2 = utftext.charCodeAt(i+1); 
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 
				i += 2; 
			} 
			else { 
				c2 = utftext.charCodeAt(i+1); 
				c3 = utftext.charCodeAt(i+2); 
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 
				i += 3; 
			} 
 
		} 
 
		return string; 
	} 
 
 
    /**
     * private function to convert urlencoded query string to javascript object
  	 * 
	 * @param {String} params //required
	 * @param {Boolean} coerce //optional
	 * @return {Object}
	 *
	 * @author Ben Alman
     */		
	function deparam(params, coerce) {
		var obj = {},
			coerce_types = {
			'true': !0,
			'false': !1,
			'null': null
		};
	
		// Iterate over all name=value pairs.
		$.each(params.replace(/\+/g, ' ').split('&'), function (j, v) {
			var param = v.split('='),
				key = decode(param[0]),
				val, cur = obj,
				i = 0,
	
			// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
			// into its component parts.
			keys = key.split(']['),
				keys_last = keys.length - 1;
	
			// If the first keys part contains [ and the last ends with ], then []
			// are correctly balanced.
			if (/\[/.test(keys[0]) && /\]$/.test(keys[keys_last])) {
				// Remove the trailing ] from the last keys part.
				keys[keys_last] = keys[keys_last].replace(/\]$/, '');
	
				// Split first keys part into two parts on the [ and add them back onto
				// the beginning of the keys array.
				keys = keys.shift().split('[').concat(keys);
	
				keys_last = keys.length - 1;
			} else {
				// Basic 'foo' style key.
				keys_last = 0;
			}
	
			// Are we dealing with a name=value pair, or just a name?
			if (param.length === 2) {
				val = decode(param[1]);
	
				// Coerce values.
				if (coerce) {
					val = val && !isNaN(val) ? +val // number
					: val === 'undefined' ? undefined // undefined
					: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
					: val; // string
				}
	
				if (keys_last) {
					// Complex key, build deep object structure based on a few rules:
					// * The 'cur' pointer starts at the object top-level.
					// * [] = array push (n is set to array length), [n] = array if n is 
					//   numeric, otherwise object.
					// * If at the last keys part, set the value.
					// * For each keys part, if the current level is undefined create an
					//   object or array based on the type of the next keys part.
					// * Move the 'cur' pointer to the next level.
					// * Rinse & repeat.
					for (; i <= keys_last; i++) {
						key = keys[i] === '' ? cur.length : keys[i];
						cur = cur[key] = i < keys_last ? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : []) : val;
					}
	
				} else {
					// Simple key, even simpler rules, since only scalars and shallow
					// arrays are allowed.
					if ($.isArray(obj[key])) {
						// val is already an array, so push on the next value.
						obj[key].push(val);
	
					} else if (obj[key] !== undefined) {
						// val isn't an array, but since a second value has been specified,
						// convert val into an array.
						obj[key] = [obj[key], val];
	
					} else {
						// val is a scalar.
						obj[key] = val;
					}
				}
	
			} else if (key) {
				// No value was defined, so set something meaningful.
				obj[key] = coerce ? undefined : '';
			}
		});
	
		return obj;
	}
	
     /**
     * private function to parse URL to components
  	 * 
	 * @param {String} url_str //optional, if omited using current location
	 * @return {Object}
     */		
	function parse(url_str) {
		url_str = url_str || window.location;
		
		/**
		* @author of RegExp Steven Levithan 
		*/
		var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
		
		var keys = ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];
		
		var m = re.exec( url_str );
		var uri = {};
		var i = keys.length;
		
		while ( i-- ) {
			uri[ keys[i] ] = m[i] || "";
		}
		/*
		uri.params = {};
		
		uri.query.replace( /(?:^|&)([^&=]*)=?([^&]*)/g, function ( $0, $1, $2 ) {
			if ($1) {
				uri.params[decode($1)] = decode($2);
			}
		});
		*/
		if(uri.query){
			uri.params = deparam(uri.query,true);
		}
		
		return uri;
	}


     /**
     * private function to build URL string from components
  	 * 
	 * @param {Object} url_obj //required
	 * @return {String}
     */		
	function build(url_obj) {
		
		if (url_obj.source){
			return encodeURI(url_obj.source);
		}
		
		var resultArr = [];
		
		if (url_obj.protocol){
			if (url_obj.protocol == 'file'){
				resultArr.push('file:///');
			} else if (url_obj.protocol == 'mailto'){
				resultArr.push('mailto:');
			} else {
				resultArr.push(url_obj.protocol + '://');
			}
		}
		
		if (url_obj.authority){
			resultArr.push(url_obj.authority);
		} else {
			if (url_obj.userInfo){
				resultArr.push(url_obj.userInfo + '@');
			} else if(url_obj.user){
				resultArr.push(url_obj.user);
				if(url_obj.password){
					resultArr.push(':' + url_obj.password);
				}
				resultArr.push('@');
			}
			
			if (url_obj.host){
				resultArr.push(url_obj.host);
				if(url_obj.port){
					resultArr.push(':' + url_obj.port);
				}
			}
		}
		
		if (url_obj.path){
			resultArr.push(url_obj.path);
		} else {
			if(url_obj.directory){
				resultArr.push(url_obj.directory);
			}
			if(url_obj.file){
				resultArr.push(url_obj.file);
			}
			
		}

		if (url_obj.query){
			resultArr.push('?' + url_obj.query);
		} else  if(url_obj.params){
			resultArr.push('?' + $.param(url_obj.params));
		}
		
		if (url_obj.anchor){
			resultArr.push('#' + url_obj.anchor);
		}
		
		return resultArr.join('');
	}

	/**
     * wrapper around encoder
  	 * 
	 * @param {String} string //required
	 * @return {String}
     */		
	function encode(string) { 
		//return build(parse(string));
		//return escape(utf8_encode(string));
		return encodeURIComponent(string);
	} 

	/**
     * wrapper around decoder
  	 * 
	 * @param {String} string //optional, if omited using current location
	 * @return {String}
     */		
	function decode(string) { 
		string = string ||  window.location.toString();
		return utf8_decode(unescape(string.replace(/\+/g, ' '))); 
	}

	/**
     * public functions
  	 * 
	 * @see #encode
	 * @see #decode
	 * @see #parse
	 * @see #build
	 *
	 * @return {Object}
     */		
	return {
		encode: encode,
		decode: decode,
		parse: parse,
		build: build
	};
}();