Dotclear

source: admin/js/ie7/ie7-hashchange.js @ 2113:a134102bfd9a

Revision 2113:a134102bfd9a, 16.2 KB checked in by Nicolas <nikrou77@…>, 12 years ago (diff)

Refactoring for jQuery pageTabs plugin
Navigation in history comes back (onhashchange)
Fix ie7-hashchange to not use $.browser
Fix use of trigger for onetabload and tabload events

Add tests for onhashchange
Add tests for onetabload and tabload event
(Tested on chrome, IE8 and firefox 24)

Line 
1/*!
2 * jQuery hashchange event - v1.3.1 - 09/27/2013
3 * http://benalman.com/projects/jquery-hashchange-plugin/
4 *
5 * Copyright (c) 2010 "Cowboy" Ben Alman
6 * Dual licensed under the MIT and GPL licenses.
7 * http://benalman.com/about/license/
8 */
9
10// Script: jQuery hashchange event
11//
12// *Version: 1.3.1, Last updated: 09/27/2013*
13// Fix $.browser usage for jQuery >= 1.9
14
15// *Version: 1.3, Last updated: 7/21/2010*
16//
17// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
18// GitHub       - http://github.com/cowboy/jquery-hashchange/
19// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
20// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
21//
22// About: License
23//
24// Copyright (c) 2010 "Cowboy" Ben Alman,
25// Dual licensed under the MIT and GPL licenses.
26// http://benalman.com/about/license/
27//
28// About: Examples
29//
30// These working examples, complete with fully commented code, illustrate a few
31// ways in which this plugin can be used.
32//
33// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
34// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
35//
36// About: Support and Testing
37//
38// Information about what version or versions of jQuery this plugin has been
39// tested with, what browsers it has been tested in, and where the unit tests
40// reside (so you can test it yourself).
41//
42// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
43// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
44//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
45// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
46//
47// About: Known issues
48//
49// While this jQuery hashchange event implementation is quite stable and
50// robust, there are a few unfortunate browser bugs surrounding expected
51// hashchange event-based behaviors, independent of any JavaScript
52// window.onhashchange abstraction. See the following examples for more
53// information:
54//
55// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
56// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
57// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
58// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
59//
60// Also note that should a browser natively support the window.onhashchange
61// event, but not report that it does, the fallback polling loop will be used.
62//
63// About: Release History
64//
65// 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
66//         "removable" for mobile-only development. Added IE6/7 document.title
67//         support. Attempted to make Iframe as hidden as possible by using
68//         techniques from http://www.paciellogroup.com/blog/?p=604. Added
69//         support for the "shortcut" format $(window).hashchange( fn ) and
70//         $(window).hashchange() like jQuery provides for built-in events.
71//         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
72//         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
73//         and <jQuery.fn.hashchange.src> properties plus document-domain.html
74//         file to address access denied issues when setting document.domain in
75//         IE6/7.
76// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
77//         from a page on another domain would cause an error in Safari 4. Also,
78//         IE6/7 Iframe is now inserted after the body (this actually works),
79//         which prevents the page from scrolling when the event is first bound.
80//         Event can also now be bound before DOM ready, but it won't be usable
81//         before then in IE6/7.
82// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
83//         where browser version is incorrectly reported as 8.0, despite
84//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
85// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
86//         window.onhashchange functionality into a separate plugin for users
87//         who want just the basic event & back button support, without all the
88//         extra awesomeness that BBQ provides. This plugin will be included as
89//         part of jQuery BBQ, but also be available separately.
90
91(function($,window,undefined){
92  '$:nomunge'; // Used by YUI compressor.
93 
94  // Reused string.
95  var str_hashchange = 'hashchange',
96   
97    // Method / object references.
98    doc = document,
99    fake_onhashchange,
100    special = $.event.special,
101   
102    // Does the browser support window.onhashchange? Note that IE8 running in
103    // IE7 compatibility mode reports true for 'onhashchange' in window, even
104    // though the event isn't supported, so also test document.documentMode.
105    doc_mode = doc.documentMode,
106    supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
107 
108  // Get location.hash (or what you'd expect location.hash to be) sans any
109  // leading #. Thanks for making this necessary, Firefox!
110  function get_fragment( url ) {
111    url = url || location.href;
112    return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
113  };
114 
115  // Method: jQuery.fn.hashchange
116  //
117  // Bind a handler to the window.onhashchange event or trigger all bound
118  // window.onhashchange event handlers. This behavior is consistent with
119  // jQuery's built-in event handlers.
120  //
121  // Usage:
122  //
123  // > jQuery(window).hashchange( [ handler ] );
124  //
125  // Arguments:
126  //
127  //  handler - (Function) Optional handler to be bound to the hashchange
128  //    event. This is a "shortcut" for the more verbose form:
129  //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
130  //    all bound window.onhashchange event handlers will be triggered. This
131  //    is a shortcut for the more verbose
132  //    jQuery(window).trigger( 'hashchange' ). These forms are described in
133  //    the <hashchange event> section.
134  //
135  // Returns:
136  //
137  //  (jQuery) The initial jQuery collection of elements.
138 
139  // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
140  // $(elem).hashchange() for triggering, like jQuery does for built-in events.
141  $.fn[ str_hashchange ] = function( fn ) {
142    return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
143  };
144 
145  // Property: jQuery.fn.hashchange.delay
146  //
147  // The numeric interval (in milliseconds) at which the <hashchange event>
148  // polling loop executes. Defaults to 50.
149 
150  // Property: jQuery.fn.hashchange.domain
151  //
152  // If you're setting document.domain in your JavaScript, and you want hash
153  // history to work in IE6/7, not only must this property be set, but you must
154  // also set document.domain BEFORE jQuery is loaded into the page. This
155  // property is only applicable if you are supporting IE6/7 (or IE8 operating
156  // in "IE7 compatibility" mode).
157  //
158  // In addition, the <jQuery.fn.hashchange.src> property must be set to the
159  // path of the included "document-domain.html" file, which can be renamed or
160  // modified if necessary (note that the document.domain specified must be the
161  // same in both your main JavaScript as well as in this file).
162  //
163  // Usage:
164  //
165  // jQuery.fn.hashchange.domain = document.domain;
166 
167  // Property: jQuery.fn.hashchange.src
168  //
169  // If, for some reason, you need to specify an Iframe src file (for example,
170  // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
171  // do so using this property. Note that when using this property, history
172  // won't be recorded in IE6/7 until the Iframe src file loads. This property
173  // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
174  // compatibility" mode).
175  //
176  // Usage:
177  //
178  // jQuery.fn.hashchange.src = 'path/to/file.html';
179 
180  $.fn[ str_hashchange ].delay = 50;
181  /*
182  $.fn[ str_hashchange ].domain = null;
183  $.fn[ str_hashchange ].src = null;
184  */
185 
186  // Event: hashchange event
187  //
188  // Fired when location.hash changes. In browsers that support it, the native
189  // HTML5 window.onhashchange event is used, otherwise a polling loop is
190  // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
191  // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
192  // compatibility" mode), a hidden Iframe is created to allow the back button
193  // and hash-based history to work.
194  //
195  // Usage as described in <jQuery.fn.hashchange>:
196  //
197  // > // Bind an event handler.
198  // > jQuery(window).hashchange( function(e) {
199  // >   var hash = location.hash;
200  // >   ...
201  // > });
202  // >
203  // > // Manually trigger the event handler.
204  // > jQuery(window).hashchange();
205  //
206  // A more verbose usage that allows for event namespacing:
207  //
208  // > // Bind an event handler.
209  // > jQuery(window).bind( 'hashchange', function(e) {
210  // >   var hash = location.hash;
211  // >   ...
212  // > });
213  // >
214  // > // Manually trigger the event handler.
215  // > jQuery(window).trigger( 'hashchange' );
216  //
217  // Additional Notes:
218  //
219  // * The polling loop and Iframe are not created until at least one handler
220  //   is actually bound to the 'hashchange' event.
221  // * If you need the bound handler(s) to execute immediately, in cases where
222  //   a location.hash exists on page load, via bookmark or page refresh for
223  //   example, use jQuery(window).hashchange() or the more verbose
224  //   jQuery(window).trigger( 'hashchange' ).
225  // * The event can be bound before DOM ready, but since it won't be usable
226  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
227  //   to bind it inside a DOM ready handler.
228 
229  // Override existing $.event.special.hashchange methods (allowing this plugin
230  // to be defined after jQuery BBQ in BBQ's source code).
231  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
232   
233    // Called only when the first 'hashchange' event is bound to window.
234    setup: function() {
235      // If window.onhashchange is supported natively, there's nothing to do..
236      if ( supports_onhashchange ) { return false; }
237     
238      // Otherwise, we need to create our own. And we don't want to call this
239      // until the user binds to the event, just in case they never do, since it
240      // will create a polling loop and possibly even a hidden Iframe.
241      $( fake_onhashchange.start );
242    },
243   
244    // Called only when the last 'hashchange' event is unbound from window.
245    teardown: function() {
246      // If window.onhashchange is supported natively, there's nothing to do..
247      if ( supports_onhashchange ) { return false; }
248     
249      // Otherwise, we need to stop ours (if possible).
250      $( fake_onhashchange.stop );
251    }
252   
253  });
254 
255  // fake_onhashchange does all the work of triggering the window.onhashchange
256  // event for browsers that don't natively support it, including creating a
257  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
258  // Iframe to enable back and forward.
259  fake_onhashchange = (function(){
260    var self = {},
261      timeout_id,
262     
263      // Remember the initial hash so it doesn't get triggered immediately.
264      last_hash = get_fragment(),
265     
266      fn_retval = function(val){ return val; },
267      history_set = fn_retval,
268      history_get = fn_retval;
269   
270    // Start the polling loop.
271    self.start = function() {
272      timeout_id || poll();
273    };
274   
275    // Stop the polling loop.
276    self.stop = function() {
277      timeout_id && clearTimeout( timeout_id );
278      timeout_id = undefined;
279    };
280   
281    // This polling loop checks every $.fn.hashchange.delay milliseconds to see
282    // if location.hash has changed, and triggers the 'hashchange' event on
283    // window when necessary.
284    function poll() {
285      var hash = get_fragment(),
286        history_hash = history_get( last_hash );
287     
288      if ( hash !== last_hash ) {
289        history_set( last_hash = hash, history_hash );
290       
291        $(window).trigger( str_hashchange );
292       
293      } else if ( history_hash !== last_hash ) {
294        location.href = location.href.replace( /#.*/, '' ) + history_hash;
295      }
296     
297      timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
298    };
299   
300    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
301    // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
302    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
303    (navigator.appName == 'Microsoft Internet Explorer') && !supports_onhashchange && (function(){
304      // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
305      // when running in "IE7 compatibility" mode.
306     
307      var iframe,
308        iframe_src;
309     
310      // When the event is bound and polling starts in IE 6/7, create a hidden
311      // Iframe for history handling.
312      self.start = function(){
313        if ( !iframe ) {
314          iframe_src = $.fn[ str_hashchange ].src;
315          iframe_src = iframe_src && iframe_src + get_fragment();
316         
317          // Create hidden Iframe. Attempt to make Iframe as hidden as possible
318          // by using techniques from http://www.paciellogroup.com/blog/?p=604.
319          iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
320           
321            // When Iframe has completely loaded, initialize the history and
322            // start polling.
323            .one( 'load', function(){
324              iframe_src || history_set( get_fragment() );
325              poll();
326            })
327           
328            // Load Iframe src if specified, otherwise nothing.
329            .attr( 'src', iframe_src || 'javascript:0' )
330           
331            // Append Iframe after the end of the body to prevent unnecessary
332            // initial page scrolling (yes, this works).
333            .insertAfter( 'body' )[0].contentWindow;
334         
335          // Whenever `document.title` changes, update the Iframe's title to
336          // prettify the back/next history menu entries. Since IE sometimes
337          // errors with "Unspecified error" the very first time this is set
338          // (yes, very useful) wrap this with a try/catch block.
339          doc.onpropertychange = function(){
340            try {
341              if ( event.propertyName === 'title' ) {
342                iframe.document.title = doc.title;
343              }
344            } catch(e) {}
345          };
346         
347        }
348      };
349     
350      // Override the "stop" method since an IE6/7 Iframe was created. Even
351      // if there are no longer any bound event handlers, the polling loop
352      // is still necessary for back/next to work at all!
353      self.stop = fn_retval;
354     
355      // Get history by looking at the hidden Iframe's location.hash.
356      history_get = function() {
357        return get_fragment( iframe.location.href );
358      };
359     
360      // Set a new history item by opening and then closing the Iframe
361      // document, *then* setting its location.hash. If document.domain has
362      // been set, update that as well.
363      history_set = function( hash, history_hash ) {
364        var iframe_doc = iframe.document,
365          domain = $.fn[ str_hashchange ].domain;
366       
367        if ( hash !== history_hash ) {
368          // Update Iframe with any initial `document.title` that might be set.
369          iframe_doc.title = doc.title;
370         
371          // Opening the Iframe's document after it has been closed is what
372          // actually adds a history entry.
373          iframe_doc.open();
374         
375          // Set document.domain for the Iframe document as well, if necessary.
376          domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
377         
378          iframe_doc.close();
379         
380          // Update the Iframe's hash, for great justice.
381          iframe.location.hash = hash;
382        }
383      };
384     
385    })();
386    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
387    // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
388    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
389   
390    return self;
391  })();
392 
393})(jQuery,this);
Note: See TracBrowser for help on using the repository browser.

Sites map