From 5abf9f5804fabd427aecceba54cdccf70a8f6a64 Mon Sep 17 00:00:00 2001
From: Jeff Rose
Date: Sat, 15 Dec 2012 13:30:13 -0500
Subject: [PATCH 1/2] Updated jQuery Hashchange
Updated jQuery Hashchange from 1.2 to 1.3. It is now in sync with the
version in CanJS.
---
event/hashchange/hashchange.js | 684 ++++++++++++++++++++-------------
1 file changed, 415 insertions(+), 269 deletions(-)
diff --git a/event/hashchange/hashchange.js b/event/hashchange/hashchange.js
index 25bf7a86..b692fa65 100644
--- a/event/hashchange/hashchange.js
+++ b/event/hashchange/hashchange.js
@@ -1,270 +1,416 @@
-/*!
- * 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.
-/**
- * @add jQuery.event.special
- */
-//
-/**
- * @attribute hashchange
- *
- * Documentation on JMVC's hashchange event can
- * be found at [http://benalman.com/projects/jquery-hashchange-plugin/].
- *
- * ### Examples
- *
- * // with jQuery
- * $(window).bind('hashchange', function(){
- * // do something
- * })
- *
- * // with $.Controller
- * $.Controller('Nav',{
- * "{window} hashchange" : function(){
- * // do something
- * }
- * })
- *
- */
-steal('jquery', function($){
- '$: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
- // 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 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 = $('').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;
- })();
-
-return $;
+/*!
+ * jQuery hashchange event - v1.3 - 7/21/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.3, Last updated: 7/21/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 (0.8kb gzipped)
+//
+// About: License
+//
+// Copyright (c) 2010 "Cowboy" Ben Alman,
+// Dual licensed under the MIT and GPL licenses.
+// http://benalman.com/about/license/
+//
+// About: Examples
+//
+// These working examples, complete with fully commented code, illustrate a few
+// ways in which this plugin can be used.
+//
+// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
+// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
+//
+// 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-4, Chrome 5-6, Safari 3.2-5,
+// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
+// 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/
+//
+// Also note that should a browser natively support the window.onhashchange
+// event, but not report that it does, the fallback polling loop will be used.
+//
+// About: Release History
+//
+// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
+// "removable" for mobile-only development. Added IE6/7 document.title
+// support. Attempted to make Iframe as hidden as possible by using
+// techniques from http://www.paciellogroup.com/blog/?p=604. Added
+// support for the "shortcut" format $(window).hashchange( fn ) and
+// $(window).hashchange() like jQuery provides for built-in events.
+// Renamed jQuery.hashchangeDelay to and
+// lowered its default value to 50. Added
+// and properties plus document-domain.html
+// file to address access denied issues when setting document.domain in
+// IE6/7.
+// 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.
+/**
+ * @add jQuery.event.special
+ */
+//
+/**
+ * @attribute hashchange
+ *
+ * Documentation on JMVC's hashchange event can
+ * be found at [http://benalman.com/projects/jquery-hashchange-plugin/].
+ *
+ * ### Examples
+ *
+ * // with jQuery
+ * $(window).bind('hashchange', function(){
+ * // do something
+ * })
+ *
+ * // with $.Controller
+ * $.Controller('Nav',{
+ * "{window} hashchange" : function(){
+ * // do something
+ * }
+ * })
+ *
+ */
+steal('jquery', function($){
+ '$:nomunge'; // Used by YUI compressor.
+
+ // Reused string.
+ var str_hashchange = 'hashchange',
+
+ // Method / object references.
+ doc = document,
+ fake_onhashchange,
+ special = $.event.special,
+
+ // Does the browser support window.onhashchange? Note that IE8 running in
+ // IE7 compatibility mode reports true for 'onhashchange' in window, even
+ // though the event isn't supported, so also test document.documentMode.
+ doc_mode = doc.documentMode,
+ supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
+
+ // 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 || location.href;
+ return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
+ };
+
+ // Method: jQuery.fn.hashchange
+ //
+ // Bind a handler to the window.onhashchange event or trigger all bound
+ // window.onhashchange event handlers. This behavior is consistent with
+ // jQuery's built-in event handlers.
+ //
+ // Usage:
+ //
+ // > jQuery(window).hashchange( [ handler ] );
+ //
+ // Arguments:
+ //
+ // handler - (Function) Optional handler to be bound to the hashchange
+ // event. This is a "shortcut" for the more verbose form:
+ // jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
+ // all bound window.onhashchange event handlers will be triggered. This
+ // is a shortcut for the more verbose
+ // jQuery(window).trigger( 'hashchange' ). These forms are described in
+ // the section.
+ //
+ // Returns:
+ //
+ // (jQuery) The initial jQuery collection of elements.
+
+ // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
+ // $(elem).hashchange() for triggering, like jQuery does for built-in events.
+ $.fn[ str_hashchange ] = function( fn ) {
+ return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
+ };
+
+ // Property: jQuery.fn.hashchange.delay
+ //
+ // The numeric interval (in milliseconds) at which the
+ // polling loop executes. Defaults to 50.
+
+ // Property: jQuery.fn.hashchange.domain
+ //
+ // If you're setting document.domain in your JavaScript, and you want hash
+ // history to work in IE6/7, not only must this property be set, but you must
+ // also set document.domain BEFORE jQuery is loaded into the page. This
+ // property is only applicable if you are supporting IE6/7 (or IE8 operating
+ // in "IE7 compatibility" mode).
+ //
+ // In addition, the property must be set to the
+ // path of the included "document-domain.html" file, which can be renamed or
+ // modified if necessary (note that the document.domain specified must be the
+ // same in both your main JavaScript as well as in this file).
+ //
+ // Usage:
+ //
+ // jQuery.fn.hashchange.domain = document.domain;
+
+ // Property: jQuery.fn.hashchange.src
+ //
+ // If, for some reason, you need to specify an Iframe src file (for example,
+ // when setting document.domain as in ), you can
+ // do so using this property. Note that when using this property, history
+ // won't be recorded in IE6/7 until the Iframe src file loads. This property
+ // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
+ // compatibility" mode).
+ //
+ // Usage:
+ //
+ // jQuery.fn.hashchange.src = 'path/to/file.html';
+
+ $.fn[ str_hashchange ].delay = 50;
+ /*
+ $.fn[ str_hashchange ].domain = null;
+ $.fn[ str_hashchange ].src = null;
+ */
+
+ // Event: hashchange event
+ //
+ // Fired when location.hash changes. In browsers that support it, the native
+ // HTML5 window.onhashchange event is used, otherwise a polling loop is
+ // initialized, running every milliseconds to
+ // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
+ // compatibility" mode), a hidden Iframe is created to allow the back button
+ // and hash-based history to work.
+ //
+ // Usage as described in :
+ //
+ // > // Bind an event handler.
+ // > jQuery(window).hashchange( function(e) {
+ // > var hash = location.hash;
+ // > ...
+ // > });
+ // >
+ // > // Manually trigger the event handler.
+ // > jQuery(window).hashchange();
+ //
+ // A more verbose usage that allows for event namespacing:
+ //
+ // > // Bind an event handler.
+ // > jQuery(window).bind( 'hashchange', function(e) {
+ // > var hash = location.hash;
+ // > ...
+ // > });
+ // >
+ // > // Manually trigger the event handler.
+ // > jQuery(window).trigger( 'hashchange' );
+ //
+ // Additional Notes:
+ //
+ // * The polling loop and Iframe are not created until at least one handler
+ // is actually bound to the 'hashchange' event.
+ // * If you need the bound handler(s) to execute immediately, in cases where
+ // a location.hash exists on page load, via bookmark or page refresh for
+ // example, use jQuery(window).hashchange() or the more verbose
+ // jQuery(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 DOM ready handler.
+
+ // Override existing $.event.special.hashchange methods (allowing this plugin
+ // to be defined after jQuery BBQ in BBQ's source code).
+ special[ str_hashchange ] = $.extend( 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,
+
+ // Remember the initial hash so it doesn't get triggered immediately.
+ last_hash = get_fragment(),
+
+ fn_retval = function(val){ return val; },
+ history_set = fn_retval,
+ history_get = fn_retval;
+
+ // Start the polling loop.
+ self.start = function() {
+ timeout_id || poll();
+ };
+
+ // Stop the polling loop.
+ self.stop = function() {
+ timeout_id && clearTimeout( timeout_id );
+ timeout_id = undefined;
+ };
+
+ // This polling loop checks every $.fn.hashchange.delay milliseconds to see
+ // if location.hash has changed, and triggers the 'hashchange' event on
+ // window when necessary.
+ function poll() {
+ var hash = get_fragment(),
+ history_hash = history_get( last_hash );
+
+ if ( hash !== last_hash ) {
+ history_set( last_hash = hash, history_hash );
+
+ $(window).trigger( str_hashchange );
+
+ } else if ( history_hash !== last_hash ) {
+ location.href = location.href.replace( /#.*/, '' ) + history_hash;
+ }
+
+ timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
+ };
+
+ // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
+ // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ $.browser.msie && !supports_onhashchange && (function(){
+ // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
+ // when running in "IE7 compatibility" mode.
+
+ var iframe,
+ iframe_src;
+
+ // When the event is bound and polling starts in IE 6/7, create a hidden
+ // Iframe for history handling.
+ self.start = function(){
+ if ( !iframe ) {
+ iframe_src = $.fn[ str_hashchange ].src;
+ iframe_src = iframe_src && iframe_src + get_fragment();
+
+ // Create hidden Iframe. Attempt to make Iframe as hidden as possible
+ // by using techniques from http://www.paciellogroup.com/blog/?p=604.
+ iframe = $('