// JQuery URL Parser
// Written by Mark Perkins, mark@allmarkedup.com
// License: http://unlicense.org/ (i.e. do what you want with it!)

/*

jQuery URL Parser
=================

A jQuery plugin to parse urls and provides easy access to information within them, such as the protocol, host, port, the segments that make up the path and the various query string values.

The parser is based on the Regex URI parser by Steven Levithan - http://blog.stevenlevithan.com/archives/parseuri.

License
-------

http://unlicense.org/ - i.e. do what you want with it :-)

Usage
-----

By default, the parser will use the url of the current page. This can be changed to use a url passed in manually if required (see code example below).

There are two modes of url parser - strict and loose. Loose is the default parsing mode, it deviates from the specs slightly but splits the url up in a more intuitive manner. It is the recommended parsing mode. For more information on the two different parsing modes, see Steven Levithan's blog post (linked above) on the url parser used in the parser.

The parser can return the following information about the url:

*    **source** - the url itself
*    **protocol** - eg. http, https, file, etc
*    **host** - eg. www.mydomain.com, localhost etc
*    **port** - eg. 80
*    **query** - the entire query string if it exists, eg. item=value&item2=value2
*    **individual query string parameter values**
*    **file** - the basename of the file eg. index.html
*    **anchor** - the hash (anchor) value
*    **path** - the path to the file (eg. /folder/dir/index.html)
*    **relative** - the relative path to the file including the query string (eg. /folder/dir/index.html?item=value)
*    **directory** - the directory part of the path (eg. /folder/dir/)
*    **individual segments of the path**

The source, protocol, host, port, relative, path, directory, file, query and anchor strings are available through the `.attr()` method.

The query string parameters are available through the `.param()` method

The individual path segements are available through the `.segment()` method

Examples of use
---------------

Using the current page's url (for these examples  https://mysite.com/information/about/index.html?itemID=2&user=dave):

    // get the protocol
    jQuery.url.attr("protocol") // returns 'http'

    // get the path
    jQuery.url.attr("path") // returns '/information/about/index.html'

    // get the host
    jQuery.url.attr("host") // returns 'mysite.com'

    // get the value for the itemID query parameter
    jQuery.url.param("itemID") // returns 2

    // get the second segment from the url path
    jQuery.url.segment(2) // returns 'about'
    
Using a different url to the current page:

    // set a different URL and return the anchor string
    jQuery.url.setUrl("http://allmarkedup.com/category/javascript/#footer").attr("anchor") // returns 'footer'
*/


jQuery.url = function()
{
	var segments = {};
	
	var parsed = {};
	
	/**
    * Options object. Only the URI and strictMode values can be changed via the setters below.
    */
  	var options = {
	
		url : window.location, // default URI is the page in which the script is running
		
		strictMode: false, // 'loose' parsing by default
	
		key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], // keys available to query 
		
		q: {
			name: "queryKey",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},
		
		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
			loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
		}
		
	};
	
    /**
     * Deals with the parsing of the URI according to the regex above.
 	 * Written by Steven Levithan - see credits at top.
     */		
	var parseUri = function()
	{
		str = decodeURI( options.url );
		
		var m = options.parser[ options.strictMode ? "strict" : "loose" ].exec( str );
		var uri = {};
		var i = 14;

		while ( i-- ) {
			uri[ options.key[i] ] = m[i] || "";
		}

		uri[ options.q.name ] = {};
		uri[ options.key[12] ].replace( options.q.parser, function ( $0, $1, $2 ) {
			if ($1) {
				uri[options.q.name][$1] = $2;
			}
		});

		return uri;
	};

    /**
     * Returns the value of the passed in key from the parsed URI.
  	 * 
	 * @param string key The key whose value is required
     */		
	var key = function( key )
	{
		if ( jQuery.isEmptyObject(parsed) )
		{
			setUp(); // if the URI has not been parsed yet then do this first...	
		} 
		if ( key == "base" )
		{
			if ( parsed.port !== null && parsed.port !== "" )
			{
				return parsed.protocol+"://"+parsed.host+":"+parsed.port+"/";	
			}
			else
			{
				return parsed.protocol+"://"+parsed.host+"/";
			}
		}
	
		return ( parsed[key] === "" ) ? null : parsed[key];
	};
	
	/**
     * Returns the value of the required query string parameter.
  	 * 
	 * @param string item The parameter whose value is required
     */		
	var param = function( item )
	{
		if ( jQuery.isEmptyObject(parsed) )
		{
			setUp(); // if the URI has not been parsed yet then do this first...	
		}
		return ( parsed.queryKey[item] === null ) ? null : parsed.queryKey[item];
	};

    /**
     * 'Constructor' (not really!) function.
     *  Called whenever the URI changes to kick off re-parsing of the URI and splitting it up into segments. 
     */	
	var setUp = function()
	{
		parsed = parseUri();
		
		getSegments();	
	};
	
    /**
     * Splits up the body of the URI into segments (i.e. sections delimited by '/')
     */
	var getSegments = function()
	{
		var p = parsed.path;
		segments = []; // clear out segments array
		segments = parsed.path.length == 1 ? {} : ( p.charAt( p.length - 1 ) == "/" ? p.substring( 1, p.length - 1 ) : path = p.substring( 1 ) ).split("/");
	};
	
	return {
		
	    /**
	     * Sets the parsing mode - either strict or loose. Set to loose by default.
	     *
	     * @param string mode The mode to set the parser to. Anything apart from a value of 'strict' will set it to loose!
	     */
		setMode : function( mode )
		{
			options.strictMode = mode == "strict" ? true : false;
			return this;
		},
		
		/**
	     * Sets URI to parse if you don't want to to parse the current page's URI.
		 * Calling the function with no value for newUri resets it to the current page's URI.
	     *
	     * @param string newUri The URI to parse.
	     */		
		setUrl : function( newUri )
		{
			options.url = newUri === undefined ? window.location : newUri;
			setUp();
			return this;
		},		
		
		/**
	     * Returns the value of the specified URI segment. Segments are numbered from 1 to the number of segments.
		 * For example the URI http://test.com/about/company/ segment(1) would return 'about'.
		 *
		 * If no integer is passed into the function it returns the number of segments in the URI.
	     *
	     * @param int pos The position of the segment to return. Can be empty.
	     */	
		segment : function( pos )
		{
			if ( jQuery.isEmptyObject(parsed) )
			{
				setUp(); // if the URI has not been parsed yet then do this first...	
			} 
			if ( pos === undefined )
			{
				return segments.length;
			}
			return ( segments[pos] === "" || segments[pos] === undefined ) ? null : segments[pos];
		},
		
		attr : key, // provides public access to private 'key' function - see above
		
		param : param // provides public access to private 'param' function - see above
		
	};
	
}();
