diff --git a/hledger-web/static/jquery.url.js b/hledger-web/static/jquery.url.js index 0e146e443..91e435421 100644 --- a/hledger-web/static/jquery.url.js +++ b/hledger-web/static/jquery.url.js @@ -1,164 +1,181 @@ -// JQuery URL Parser plugin - https://github.com/allmarkedup/jQuery-URL-Parser +// 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 +jQuery.url = function() +{ + var segments = {}; - 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 - }, + var parsed = {}; - querystring_parser = /(?:^|&|;)([^&=;]*)=?([^&;]*)/g, // supports both ampersand and semicolon-delimted query string key/value pairs + /** + * Options object. Only the URI and strictMode values can be changed via the setters below. + */ + var options = { - 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; + url : window.location, // default URI is the page in which the script is running - while ( i-- ) - { - uri.attr[ key[i] ] = res[i] || ""; + 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 } - // build query and fragment parameters + }; + + /** + * 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 ); - uri.param['query'] = {}; - uri.param['fragment'] = {}; - - uri.attr['query'].replace( querystring_parser, function ( $0, $1, $2 ){ - if ($1) - { - uri.param['query'][$1] = $2; + 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; } }); - - 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 : url, strict : strictMode }); - }; - - $.url = function( opts ) - { - var url = '', - strict = false; - if ( typeof opts === 'string' ) - { - url = opts; - } - else - { - opts = opts || {}; - strict = opts.strict || strict; - url = opts.url === undefined ? window.location.toString() : opts.url; - } - - return { - - data : parseUri(url, strict), - - // 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]; - } - } - - }; - + /** + * 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]; }; -})(jQuery); \ No newline at end of file + /** + * 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... + } + if ( item === undefined ) + { + return parsed.queryKey; + } + else + { + 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 + + }; + +}();