/*! * urlInternal - v1.0 - 10/7/2009 * http://benalman.com/projects/jquery-urlinternal-plugin/ * * Copyright (c) 2009 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ */ // Script: jQuery urlInternal: Easily test URL internal-, external or fragment-ness // // *Version: 1.0, Last updated: 10/7/2009* // // Project Home - http://benalman.com/projects/jquery-urlinternal-plugin/ // GitHub - http://github.com/cowboy/jquery-urlinternal/ // Source - http://github.com/cowboy/jquery-urlinternal/raw/master/jquery.ba-urlinternal.js // (Minified) - http://github.com/cowboy/jquery-urlinternal/raw/master/jquery.ba-urlinternal.min.js (1.7kb) // // About: License // // Copyright (c) 2009 "Cowboy" Ben Alman, // Dual licensed under the MIT and GPL licenses. // http://benalman.com/about/license/ // // About: Examples // // This working example, complete with fully commented code, illustrates a few // ways in which this plugin can be used. // // http://benalman.com/code/projects/jquery-urlinternal/examples/urlinternal/ // // About: Support and Testing // // Information about what version or versions of jQuery this plugin has been // tested with, what browsers it has been tested in, and where the unit tests // reside (so you can test it yourself). // // jQuery Versions - 1.3.2 // Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10. // Unit Tests - http://benalman.com/code/projects/jquery-urlinternal/unit/ // // About: Release History // // 1.0 - (10/7/2009) Initial release (function($){ '$:nomunge'; // Used by YUI compressor. // Some convenient shortcuts. var undefined, TRUE = !0, FALSE = !1, loc = window.location, aps = Array.prototype.slice, matches = loc.href.match( /^((https?:\/\/.*?\/)?[^#]*)#?.*$/ ), loc_fragbase = matches[1] + '#', loc_hostbase = matches[2], // Method references. jq_elemUrlAttr, jq_urlInternalHost, jq_urlInternalRegExp, jq_isUrlInternal, jq_isUrlExternal, jq_isUrlFragment, // Reused strings. str_elemUrlAttr = 'elemUrlAttr', str_href = 'href', str_src = 'src', str_urlInternal = 'urlInternal', str_urlExternal = 'urlExternal', str_urlFragment = 'urlFragment', url_regexp, // Used by jQuery.elemUrlAttr. elemUrlAttr_cache = {}; // Why write the same function twice? Let's curry! Mmmm, curry.. function curry( func ) { var args = aps.call( arguments, 1 ); return function() { return func.apply( this, args.concat( aps.call( arguments ) ) ); }; }; // Section: Methods // // Method: jQuery.isUrlInternal // // Test whether or not a URL is internal. Non-navigating URLs (ie. #anchor, // javascript:, mailto:, news:, tel:, im: or non-http/https protocol:// // links) are not considered internal. // // Usage: // // > jQuery.isUrlInternal( url ); // // Arguments: // // url - (String) a URL to test the internal-ness of. // // Returns: // // (Boolean) true if the URL is internal, false if external, or undefined if // the URL is non-navigating. $.isUrlInternal = jq_isUrlInternal = function( url ) { // non-navigating: url is nonexistent or a fragment if ( !url || jq_isUrlFragment( url ) ) { return undefined; } // internal: url is absolute-but-internal (see $.urlInternalRegExp) if ( url_regexp.test(url) ) { return TRUE; } // external: url is absolute (begins with http:// or https:// or //) if ( /^(?:https?:)?\/\//i.test(url) ) { return FALSE; } // non-navigating: url begins with scheme: if ( /^[a-z\d.-]+:/i.test(url) ) { return undefined; } return TRUE; }; // Method: jQuery.isUrlExternal // // Test whether or not a URL is external. Non-navigating URLs (ie. #anchor, // mailto:, javascript:, or non-http/https protocol:// links) are not // considered external. // // Usage: // // > jQuery.isUrlExternal( url ); // // Arguments: // // url - (String) a URL to test the external-ness of. // // Returns: // // (Boolean) true if the URL is external, false if internal, or undefined if // the URL is non-navigating. $.isUrlExternal = jq_isUrlExternal = function( url ) { var result = jq_isUrlInternal( url ); return typeof result === 'boolean' ? !result : result; }; // Method: jQuery.isUrlFragment // // Test whether or not a URL is a fragment in the context of the current page, // meaning the URL can either begin with # or be a partial URL or full URI, // but when it is navigated to, only the document.location.hash will change, // and the page will not reload. // // Usage: // // > jQuery.isUrlFragment( url ); // // Arguments: // // url - (String) a URL to test the fragment-ness of. // // Returns: // // (Boolean) true if the URL is a fragment, false otherwise. $.isUrlFragment = jq_isUrlFragment = function( url ) { var matches = ( url || '' ).match( /^([^#]?)([^#]*#).*$/ ); // url *might* be a fragment, since there were matches. return !!matches && ( // url is just a fragment. matches[2] === '#' // url is absolute and contains a fragment, but is otherwise the same URI. || url.indexOf( loc_fragbase ) === 0 // url is relative, begins with '/', contains a fragment, and is otherwise // the same URI. || ( matches[1] === '/' ? loc_hostbase + matches[2] === loc_fragbase // url is relative, but doesn't begin with '/', contains a fragment, and // is otherwise the same URI. This isn't even remotely efficient, but it's // significantly less code than parsing everything. Besides, it will only // even be tested on url values that contain '#', aren't absolute, and // don't begin with '/', which is not going to be many of them. : !/^https?:\/\//i.test( url ) && $('')[0].href.indexOf( loc_fragbase ) === 0 ) ); }; // Method: jQuery.fn.urlInternal // // Filter a jQuery collection of elements, returning only elements that have // an internal URL (as determined by ). If URL cannot // be determined, remove the element from the collection. // // Usage: // // > jQuery('selector').urlInternal( [ attr ] ); // // Arguments: // // attr - (String) Optional name of an attribute that will contain a URL to // test internal-ness against. See for a list of // default attributes. // // Returns: // // (jQuery) A filtered jQuery collection of elements. // Method: jQuery.fn.urlExternal // // Filter a jQuery collection of elements, returning only elements that have // an external URL (as determined by ). If URL cannot // be determined, remove the element from the collection. // // Usage: // // > jQuery('selector').urlExternal( [ attr ] ); // // Arguments: // // attr - (String) Optional name of an attribute that will contain a URL to // test external-ness against. See for a list of // default attributes. // // Returns: // // (jQuery) A filtered jQuery collection of elements. // Method: jQuery.fn.urlFragment // // Filter a jQuery collection of elements, returning only elements that have // an fragment URL (as determined by ). If URL cannot // be determined, remove the element from the collection. // // Note that in most browsers, selecting $("a[href^=#]") is reliable, but this // doesn't always work in IE6/7! In order to properly test whether a URL // attribute's value is a fragment in the context of the current page, you can // either make your selector a bit more complicated.. or use .urlFragment! // // Usage: // // > jQuery('selector').urlFragment( [ attr ] ); // // Arguments: // // attr - (String) Optional name of an attribute that will contain a URL to // test external-ness against. See for a list of // default attributes. // // Returns: // // (jQuery) A filtered jQuery collection of elements. function fn_filter( str, attr ) { return this.filter( ':' + str + (attr ? '(' + attr + ')' : '') ); }; $.fn[ str_urlInternal ] = curry( fn_filter, str_urlInternal ); $.fn[ str_urlExternal ] = curry( fn_filter, str_urlExternal ); $.fn[ str_urlFragment ] = curry( fn_filter, str_urlFragment ); // Section: Selectors // // Selector: :urlInternal // // Filter a jQuery collection of elements, returning only elements that have // an internal URL (as determined by ). If URL cannot // be determined, remove the element from the collection. // // Usage: // // > jQuery('selector').filter(':urlInternal'); // > jQuery('selector').filter(':urlInternal(attr)'); // // Arguments: // // attr - (String) Optional name of an attribute that will contain a URL to // test internal-ness against. See for a list of // default attributes. // // Returns: // // (jQuery) A filtered jQuery collection of elements. // Selector: :urlExternal // // Filter a jQuery collection of elements, returning only elements that have // an external URL (as determined by ). If URL cannot // be determined, remove the element from the collection. // // Usage: // // > jQuery('selector').filter(':urlExternal'); // > jQuery('selector').filter(':urlExternal(attr)'); // // Arguments: // // attr - (String) Optional name of an attribute that will contain a URL to // test external-ness against. See for a list of // default attributes. // // Returns: // // (jQuery) A filtered jQuery collection of elements. // Selector: :urlFragment // // Filter a jQuery collection of elements, returning only elements that have // an fragment URL (as determined by ). If URL cannot // be determined, remove the element from the collection. // // Note that in most browsers, selecting $("a[href^=#]") is reliable, but this // doesn't always work in IE6/7! In order to properly test whether a URL // attribute's value is a fragment in the context of the current page, you can // either make your selector a bit more complicated.. or use :urlFragment! // // Usage: // // > jQuery('selector').filter(':urlFragment'); // > jQuery('selector').filter(':urlFragment(attr)'); // // Arguments: // // attr - (String) Optional name of an attribute that will contain a URL to // test fragment-ness against. See for a list of // default attributes. // // Returns: // // (jQuery) A filtered jQuery collection of elements. function fn_selector( func, elem, i, match ) { var a = match[3] || jq_elemUrlAttr()[ ( elem.nodeName || '' ).toLowerCase() ] || ''; return a ? !!func( elem.getAttribute( a ) ) : FALSE; }; $.expr[':'][ str_urlInternal ] = curry( fn_selector, jq_isUrlInternal ); $.expr[':'][ str_urlExternal ] = curry( fn_selector, jq_isUrlExternal ); $.expr[':'][ str_urlFragment ] = curry( fn_selector, jq_isUrlFragment ); // Section: Support methods // // Method: jQuery.elemUrlAttr // // Get the internal "Default URL attribute per tag" list, or augment the list // with additional tag-attribute pairs, in case the defaults are insufficient. // // In the and methods, as well // as the <:urlInternal> and <:urlExternal> selectors, this list is used to // determine which attribute contains the URL to be modified, if an "attr" // param is not specified. // // Default Tag-Attribute List: // // a - href // base - href // iframe - src // img - src // input - src // form - action // link - href // script - src // // Usage: // // > jQuery.elemUrlAttr( [ tag_attr ] ); // // Arguments: // // tag_attr - (Object) An object containing a list of tag names and their // associated default attribute names in the format { tag: 'attr', ... } to // be merged into the internal tag-attribute list. // // Returns: // // (Object) An object containing all stored tag-attribute values. // Only define function and set defaults if function doesn't already exist, as // the jQuery BBQ plugin will provide this method as well. $[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) { return $.extend( elemUrlAttr_cache, obj ); })({ a: str_href, base: str_href, iframe: str_src, img: str_src, input: str_src, form: 'action', link: str_href, script: str_src }); jq_elemUrlAttr = $[ str_elemUrlAttr ]; // Method: jQuery.urlInternalHost // // Constructs the regular expression that matches an absolute-but-internal // URL from the current page's protocol, hostname and port, allowing for any // number of optional hostnames. For example, if the current page is // http://benalman.com/test or http://www.benalman.com/test, specifying an // argument of "www" would yield this pattern: // // > /^(?:http:)?\/\/(?:(?:www)\.)?benalman.com\//i // // This pattern will match URLs beginning with both http://benalman.com/ and // http://www.benalman.com/. If the current page is http://benalman.com/test, // http://www.benalman.com/test or http://foo.benalman.com/test, specifying // arguments "www", "foo" would yield this pattern: // // > /^(?:http:)?\/\/(?:(?:www|foo)\.)?benalman.com\//i // // This pattern will match URLs beginning with http://benalman.com/, // http://www.benalman.com/ and http://foo.benalman.com/. // // Not specifying any alt_hostname will disable any alt-hostname matching. // // Note that the plugin is initialized by default to an alt_hostname of "www". // Should you need more control, may be used to // completely customize the absolute-but-internal matching pattern. // // Usage: // // > jQuery.urlInternalHost( [ alt_hostname [, alt_hostname ] ... ] ); // // Arguments: // // alt_hostname - (String) An optional alternate hostname to use when testing // URL absolute-but-internal-ness. // // Returns: // // (RegExp) The absolute-but-internal pattern, as a RegExp. $.urlInternalHost = jq_urlInternalHost = function( alt_hostname ) { alt_hostname = alt_hostname ? '(?:(?:' + Array.prototype.join.call( arguments, '|' ) + ')\\.)?' : ''; var re = new RegExp( '^' + alt_hostname + '(.*)', 'i' ), pattern = '^(?:' + loc.protocol + ')?//' + loc.hostname.replace(re, alt_hostname + '$1').replace( /\\?\./g, '\\.' ) + (loc.port ? ':' + loc.port : '') + '/'; return jq_urlInternalRegExp( pattern ); }; // Method: jQuery.urlInternalRegExp // // Set or get the regular expression that matches an absolute-but-internal // URL. // // Usage: // // > jQuery.urlInternalRegExp( [ re ] ); // // Arguments: // // re - (String or RegExp) The regular expression pattern. If not passed, // nothing is changed. // // Returns: // // (RegExp) The absolute-but-internal pattern, as a RegExp. $.urlInternalRegExp = jq_urlInternalRegExp = function( re ) { if ( re ) { url_regexp = typeof re === 'string' ? new RegExp( re, 'i' ) : re; } return url_regexp; }; // Initialize url_regexp with a reasonable default. jq_urlInternalHost( 'www' ); })(jQuery);