/public/jquery/event/hashchange/hashchange.js
JavaScript | 245 lines | 75 code | 34 blank | 136 comment | 16 complexity | 2301f87f8c7b59142a16cc142e957f84 MD5 | raw file
- /*!
- * jQuery hashchange event - v1.2 - 2/11/2010
- * http://benalman.com/projects/jquery-hashchange-plugin/
- *
- * Copyright (c) 2010 "Cowboy" Ben Alman
- * Dual licensed under the MIT and GPL licenses.
- * http://benalman.com/about/license/
- */
-
- // Script: jQuery hashchange event
- //
- // *Version: 1.2, Last updated: 2/11/2010*
- //
- // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
- // GitHub - http://github.com/cowboy/jquery-hashchange/
- // Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
- // (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (1.1kb)
- //
- // About: License
- //
- // Copyright (c) 2010 "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, illustrate one way
- // in which this plugin can be used.
- //
- // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
- //
- // 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.2.6, 1.3.2, 1.4.1, 1.4.2
- // Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.1.
- // Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/
- //
- // About: Known issues
- //
- // While this jQuery hashchange event implementation is quite stable and robust,
- // there are a few unfortunate browser bugs surrounding expected hashchange
- // event-based behaviors, independent of any JavaScript window.onhashchange
- // abstraction. See the following examples for more information:
- //
- // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
- // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
- // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
- // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
- //
- // About: Release History
- //
- // 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin
- // from a page on another domain would cause an error in Safari 4. Also,
- // IE6/7 Iframe is now inserted after the body (this actually works),
- // which prevents the page from scrolling when the event is first bound.
- // Event can also now be bound before DOM ready, but it won't be usable
- // before then in IE6/7.
- // 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
- // where browser version is incorrectly reported as 8.0, despite
- // inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
- // 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
- // window.onhashchange functionality into a separate plugin for users
- // who want just the basic event & back button support, without all the
- // extra awesomeness that BBQ provides. This plugin will be included as
- // part of jQuery BBQ, but also be available separately.
-
- (function($,window,undefined){
- '$:nomunge'; // Used by YUI compressor.
-
- // Method / object references.
- var fake_onhashchange,
- jq_event_special = $.event.special,
-
- // Reused strings.
- str_location = 'location',
- str_hashchange = 'hashchange',
- str_href = 'href',
-
- // IE6/7 specifically need some special love when it comes to back-button
- // support, so let's do a little browser sniffing..
- browser = $.browser,
- mode = document.documentMode,
- is_old_ie = browser.msie && ( mode === undefined || mode < 8 ),
-
- // Does the browser support window.onhashchange? Test for IE version, since
- // IE8 incorrectly reports this when in "IE7" or "IE8 Compatibility View"!
- supports_onhashchange = 'on' + str_hashchange in window && !is_old_ie;
-
- // Get location.hash (or what you'd expect location.hash to be) sans any
- // leading #. Thanks for making this necessary, Firefox!
- function get_fragment( url ) {
- url = url || window[ str_location ][ str_href ];
- return url.replace( /^[^#]*#?(.*)$/, '$1' );
- };
-
- // Property: jQuery.hashchangeDelay
- //
- // The numeric interval (in milliseconds) at which the <hashchange event>
- // polling loop executes. Defaults to 100.
-
- $[ str_hashchange + 'Delay' ] = 100;
-
- // Event: hashchange event
- //
- // Fired when location.hash changes. In browsers that support it, the native
- // window.onhashchange event is used (IE8, FF3.6), otherwise a polling loop is
- // initialized, running every <jQuery.hashchangeDelay> milliseconds to see if
- // the hash has changed. In IE 6 and 7, a hidden Iframe is created to allow
- // the back button and hash-based history to work.
- //
- // Usage:
- //
- // > $(window).bind( 'hashchange', function(e) {
- // > var hash = location.hash;
- // > ...
- // > });
- //
- // Additional Notes:
- //
- // * The polling loop and Iframe are not created until at least one callback
- // is actually bound to 'hashchange'.
- // * If you need the bound callback(s) to execute immediately, in cases where
- // the page 'state' exists on page load (via bookmark or page refresh, for
- // example) use $(window).trigger( 'hashchange' );
- // * The event can be bound before DOM ready, but since it won't be usable
- // before then in IE6/7 (due to the necessary Iframe), recommended usage is
- // to bind it inside a $(document).ready() callback.
-
- jq_event_special[ str_hashchange ] = $.extend( jq_event_special[ str_hashchange ], {
-
- // Called only when the first 'hashchange' event is bound to window.
- setup: function() {
- // If window.onhashchange is supported natively, there's nothing to do..
- if ( supports_onhashchange ) { return false; }
-
- // Otherwise, we need to create our own. And we don't want to call this
- // until the user binds to the event, just in case they never do, since it
- // will create a polling loop and possibly even a hidden Iframe.
- $( fake_onhashchange.start );
- },
-
- // Called only when the last 'hashchange' event is unbound from window.
- teardown: function() {
- // If window.onhashchange is supported natively, there's nothing to do..
- if ( supports_onhashchange ) { return false; }
-
- // Otherwise, we need to stop ours (if possible).
- $( fake_onhashchange.stop );
- }
-
- });
-
- // fake_onhashchange does all the work of triggering the window.onhashchange
- // event for browsers that don't natively support it, including creating a
- // polling loop to watch for hash changes and in IE 6/7 creating a hidden
- // Iframe to enable back and forward.
- fake_onhashchange = (function(){
- var self = {},
- timeout_id,
- iframe,
- set_history,
- get_history;
-
- // Initialize. In IE 6/7, creates a hidden Iframe for history handling.
- function init(){
- // Most browsers don't need special methods here..
- set_history = get_history = function(val){ return val; };
-
- // But IE6/7 do!
- if ( is_old_ie ) {
-
- // Create hidden Iframe after the end of the body to prevent initial
- // page load from scrolling unnecessarily.
- iframe = $('<iframe src="javascript:0"/>').hide().insertAfter( 'body' )[0].contentWindow;
-
- // Get history by looking at the hidden Iframe's location.hash.
- get_history = function() {
- return get_fragment( iframe.document[ str_location ][ str_href ] );
- };
-
- // Set a new history item by opening and then closing the Iframe
- // document, *then* setting its location.hash.
- set_history = function( hash, history_hash ) {
- if ( hash !== history_hash ) {
- var doc = iframe.document;
- doc.open().close();
- doc[ str_location ].hash = '#' + hash;
- }
- };
-
- // Set initial history.
- set_history( get_fragment() );
- }
- };
-
- // Start the polling loop.
- self.start = function() {
- // Polling loop is already running!
- if ( timeout_id ) { return; }
-
- // Remember the initial hash so it doesn't get triggered immediately.
- var last_hash = get_fragment();
-
- // Initialize if not yet initialized.
- set_history || init();
-
- // This polling loop checks every $.hashchangeDelay milliseconds to see if
- // location.hash has changed, and triggers the 'hashchange' event on
- // window when necessary.
- if(!navigator.userAgent.match(/Rhino/))
- (function loopy(){
- var hash = get_fragment(),
- history_hash = get_history( last_hash );
-
- if ( hash !== last_hash ) {
- set_history( last_hash = hash, history_hash );
-
- $(window).trigger( str_hashchange );
-
- } else if ( history_hash !== last_hash ) {
- window[ str_location ][ str_href ] = window[ str_location ][ str_href ].replace( /#.*/, '' ) + '#' + history_hash;
- }
-
- timeout_id = setTimeout( loopy, $[ str_hashchange + 'Delay' ] );
- })();
- };
-
- // Stop the polling loop, but only if an IE6/7 Iframe wasn't created. In
- // that case, even if there are no longer any bound event handlers, the
- // polling loop is still necessary for back/next to work at all!
- self.stop = function() {
- if ( !iframe ) {
- timeout_id && clearTimeout( timeout_id );
- timeout_id = 0;
- }
- };
-
- return self;
- })();
-
- })(jQuery,this);