// JQuery URL Parser plugin - https://github.com/allmarkedup/jQuery-URL-Parser
// Written by Mark Perkins, mark@allmarkedup.com
// License: http://unlicense.org/ (i.e. do what you want with it!)

;(function($, undefined) {
	
    var tag2attr = {
	 a		: 'href',
	 img    : 'src',
	 form   : 'action',
	 base   : 'href',
	 script : 'src',
	 iframe : 'src',
	 link   : 'href'
    },
    
	key = ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"], // keys available to query

	aliases = { "anchor" : "fragment" }, // aliases for backwards compatability

	parser = {
		strict  : /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
		loose   :  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
	},

	querystring_parser = /(?:^|&|;)([^&=;]*)=?([^&;]*)/g, // supports both ampersand and semicolon-delimted query string key/value pairs

	fragment_parser = /(?:^|&|;)([^&=;]*)=?([^&;]*)/g; // supports both ampersand and semicolon-delimted fragment key/value pairs

	function parseUri( url, strictMode )
	{
		var str = decodeURI( url ),
		    res   = parser[ strictMode || false ? "strict" : "loose" ].exec( str ),
		    uri = { attr : {}, param : {}, seg : {} },
		    i   = 14;

		while ( i-- )
		{
			uri.attr[ key[i] ] = res[i] || "";
		}

		// build query and fragment parameters

		uri.param['query'] = {};
		uri.param['fragment'] = {};

		uri.attr['query'].replace( querystring_parser, function ( $0, $1, $2 ){
			if ($1)
			{
				uri.param['query'][$1] = $2;
			}
		});

		uri.attr['fragment'].replace( fragment_parser, function ( $0, $1, $2 ){
			if ($1)
			{
				uri.param['fragment'][$1] = $2;
			}
		});

		// split path and fragement into segments

	 uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g,'').split('/');
	 
	 uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g,'').split('/');
	 
	 // compile a 'base' domain attribute
	 
	 uri.attr['base'] = uri.attr.host ? uri.attr.protocol+"://"+uri.attr.host + (uri.attr.port ? ":"+uri.attr.port : '') : '';
	 
		return uri;
	};

	function getAttrName( elm )
	{
		var tn = elm.tagName;
		if ( tn !== undefined ) return tag2attr[tn.toLowerCase()];
		return tn;
	}

	$.fn.url = function( strictMode )
	{
	    var url = '';

	    if ( this.length )
	    {
		 url = $(this).attr( getAttrName(this[0]) ) || '';
	    }

	 return $.url( url, strictMode );
	};

	$.url = function( url, strictMode )
	{
	    if ( arguments.length === 1 && url === true )
	 {
	     strictMode = true;
	     url = undefined;
	 }
	 
	 strictMode = strictMode || false;
	 url = url || window.location.toString();
	 	    		     
	 return {
	     
	     data : parseUri(url, strictMode),
	     
	     // get various attributes from the URI
	     attr : function( attr )
	     {
		  attr = aliases[attr] || attr;
		  return attr !== undefined ? this.data.attr[attr] : this.data.attr;
	     },
	     
	     // return query string parameters
	     param : function( param )
	     {
		  return param !== undefined ? this.data.param.query[param] : this.data.param.query;
	     },
	     
	     // return fragment parameters
	     fparam : function( param )
	     {
		  return param !== undefined ? this.data.param.fragment[param] : this.data.param.fragment;
	     },
	     
	     // return path segments
	     segment : function( seg )
	     {
		  if ( seg === undefined )
		  {
		      return this.data.seg.path;		      
		  }
		  else
		  {
		      seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end
		      return this.data.seg.path[seg];		      
		  }
	     },
	     
	     // return fragment segments
	     fsegment : function( seg )
	     {
		  if ( seg === undefined )
		  {
		      return this.data.seg.fragment;		      
		  }
		  else
		  {
		      seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end
		      return this.data.seg.fragment[seg];		      
		  }
	     }
	     
	 };
	 
	};

})(jQuery);
