PageSourceSearch

https://opencashadvance.com/assets/d21c19d/jquery.ba-bbq.js

js opencashadvance.com collected 2026-09-29 09:34:04 UTC 52,795 bytes, 1,378 lines download raw bytes

1/*!
2 * jQuery BBQ: Back Button & Query Library - v1.4pre - 1/15/2013
3 * https://benalman.com/projects/jquery-bbq-plugin/
4 * 
5 * Copyright (c) 2010-2013 "Cowboy" Ben Alman
6 * Dual licensed under the MIT and GPL licenses.
7 * https://benalman.com/about/license/
8 */
9
10// Script: jQuery BBQ: Back Button & Query Library
11//
12// *Version: 1.4pre, Last updated: 1/15/2013*
13// 
14// Project Home - https://benalman.com/projects/jquery-bbq-plugin/
15// GitHub       - https://github.com/cowboy/jquery-bbq/
16// Source       - https://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
17// (Minified)   - https://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (2.2kb gzipped)
18// 
19// About: License
20// 
21// Copyright (c) 2010-2013 "Cowboy" Ben Alman,
22// Dual licensed under the MIT and GPL licenses.
23// https://benalman.com/about/license/
24// 
25// About: Examples
26// 
27// These working examples, complete with fully commented code, illustrate a few
28// ways in which this plugin can be used.
29// 
30// Basic AJAX     - https://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
31// Advanced AJAX  - https://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
32// jQuery UI Tabs - https://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
33// Deparam        - https://benalman.com/code/projects/jquery-bbq/examples/deparam/
34// 
35// About: Support and Testing
36// 
37// Information about what version or versions of jQuery this plugin has been
38// tested with, what browsers it has been tested in, and where the unit tests
39// reside (so you can test it yourself).
40// 
41// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
42// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
43//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
44// Unit Tests      - https://benalman.com/code/projects/jquery-bbq/unit/
45// 
46// About: Release History
47// 
48// 1.4pre - (1/15/2013) Removed $.browser reference to work with jQuery 1.9
49// 1.3pre - (8/26/2010) Integrated <jQuery hashchange event> v1.3, which adds
50//         document.title and document.domain support in IE6/7, BlackBerry
51//         support, better Iframe hiding for accessibility reasons, and the new
52//         <jQuery.fn.hashchange> "shortcut" method. Added the
53//         <jQuery.param.sorted> method which reduces the possibility of
54//         extraneous hashchange event triggering. Added the
55//         <jQuery.param.fragment.ajaxCrawlable> method which can be used to
56//         enable Google "AJAX Crawlable mode."
57// 1.2.1 - (2/17/2010) Actually fixed the stale window.location Safari bug from
58//         <jQuery hashchange event> in BBQ, which was the main reason for the
59//         previous release!
60// 1.2   - (2/16/2010) Integrated <jQuery hashchange event> v1.2, which fixes a
61//         Safari bug, the event can now be bound before DOM ready, and IE6/7
62//         page should no longer scroll when the event is first bound. Also
63//         added the <jQuery.param.fragment.noEscape> method, and reworked the
64//         <hashchange event (BBQ)> internal "add" method to be compatible with
65//         changes made to the jQuery 1.4.2 special events API.
66// 1.1.1 - (1/22/2010) Integrated <jQuery hashchange event> v1.1, which fixes an
67//         obscure IE8 EmulateIE7 meta tag compatibility mode bug.
68// 1.1   - (1/9/2010) Broke out the jQuery BBQ event.special <hashchange event>
69//         functionality into a separate plugin for users who want just the
70//         basic event & back button support, without all the extra awesomeness
71//         that BBQ provides. This plugin will be included as part of jQuery BBQ,
72//         but also be available separately. See <jQuery hashchange event>
73//         plugin for more information. Also added the <jQuery.bbq.removeState>
74//         method and added additional <jQuery.deparam> examples.
75// 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
76//         location.hash would report incorrectly if the hash contained the ?
77//         character. Also <jQuery.param.querystring> and <jQuery.param.fragment>
78//         will no longer parse params out of a URL that doesn't contain ? or #,
79//         respectively.
80// 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
81//         a "This page contains both secure and nonsecure items." warning when
82//         used on an https:// page.
83// 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
84//         Compatibility View" modes erroneously report that the browser
85//         supports the native window.onhashchange event, a slightly more
86//         robust test needed to be added.
87// 1.0   - (10/2/2009) Initial release
88
89(function($,window){
90  '$:nomunge'; // Used by YUI compressor.
91  
92  // Some convenient shortcuts.
93  var undefined,
94    aps = Array.prototype.slice,
95    decode = decodeURIComponent,
96    
97    // Method / object references.
98    jq_param = $.param,
99    jq_param_sorted,
100    jq_param_fragment,
101    jq_deparam,
102    jq_deparam_fragment,
103    jq_bbq = $.bbq = $.bbq || {},
104    jq_bbq_pushState,
105    jq_bbq_getState,
106    jq_elemUrlAttr,
107    special = $.event.special,
108    
109    // Reused strings.
110    str_hashchange = 'hashchange',
111    str_querystring = 'querystring',
112    str_fragment = 'fragment',
113    str_elemUrlAttr = 'elemUrlAttr',
114    str_href = 'href',
115    str_src = 'src',
116    
117    // Reused RegExp.
118    re_params_querystring = /^.*\?|#.*$/g,
119    re_params_fragment,
120    re_fragment,
121    re_no_escape,
122    
123    ajax_crawlable,
124    fragment_prefix,
125    
126    // Used by jQuery.elemUrlAttr.
127    elemUrlAttr_cache = {};
128  
129  // A few commonly used bits, broken out to help reduce minified file size.
130  
131  function is_string( arg ) {
132    return typeof arg === 'string';
133  };
134  
135  // Why write the same function twice? Let's curry! Mmmm, curry..
136  
137  function curry( func ) {
138    var args = aps.call( arguments, 1 );
139    
140    return function() {
141      return func.apply( this, args.concat( aps.call( arguments ) ) );
142    };
143  };
144  
145  // Get location.hash (or what you'd expect location.hash to be) sans any
146  // leading #. Thanks for making this necessary, Firefox!
147  function get_fragment( url ) {
148    return url.replace( re_fragment, '$2' );
149  };
150  
151  // Get location.search (or what you'd expect location.search to be) sans any
152  // leading #. Thanks for making this necessary, IE6!
153  function get_querystring( url ) {
154    return url.replace( /(?:^[^?#]*\?([^#]*).*$)?.*/, '$1' );
155  };
156  
157  // Section: Param (to string)
158  // 
159  // Method: jQuery.param.querystring
160  // 
161  // Retrieve the query string from a URL or if no arguments are passed, the
162  // current window.location.href.
163  // 
164  // Usage:
165  // 
166  // > jQuery.param.querystring( [ url ] );
167  // 
168  // Arguments:
169  // 
170  //  url - (String) A URL containing query string params to be parsed. If url
171  //    is not passed, the current window.location.href is used.
172  // 
173  // Returns:
174  // 
175  //  (String) The parsed query string, with any leading "?" removed.
176  //
177  
178  // Method: jQuery.param.querystring (build url)
179  // 
180  // Merge a URL, with or without pre-existing query string params, plus any
181  // object, params string or URL containing query string params into a new URL.
182  // 
183  // Usage:
184  // 
185  // > jQuery.param.querystring( url, params [, merge_mode ] );
186  // 
187  // Arguments:
188  // 
189  //  url - (String) A valid URL for params to be merged into. This URL may
190  //    contain a query string and/or fragment (hash).
191  //  params - (String) A params string or URL containing query string params to
192  //    be merged into url.
193  //  params - (Object) A params object to be merged into url.
194  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
195  //    specified, and is as-follows:
196  // 
197  //    * 0: params in the params argument will override any query string
198  //         params in url.
199  //    * 1: any query string params in url will override params in the params
200  //         argument.
201  //    * 2: params argument will completely replace any query string in url.
202  // 
203  // Returns:
204  // 
205  //  (String) A URL with a urlencoded query string in the format '?a=b&c=d&e=f'.
206  
207  // Method: jQuery.param.fragment
208  // 
209  // Retrieve the fragment (hash) from a URL or if no arguments are passed, the
210  // current window.location.href.
211  // 
212  // Usage:
213  // 
214  // > jQuery.param.fragment( [ url ] );
215  // 
216  // Arguments:
217  // 
218  //  url - (String) A URL containing fragment (hash) params to be parsed. If
219  //    url is not passed, the current window.location.href is used.
220  // 
221  // Returns:
222  // 
223  //  (String) The parsed fragment (hash) string, with any leading "#" removed.
224  
225  // Method: jQuery.param.fragment (build url)
226  // 
227  // Merge a URL, with or without pre-existing fragment (hash) params, plus any
228  // object, params string or URL containing fragment (hash) params into a new
229  // URL.
230  // 
231  // Usage:
232  // 
233  // > jQuery.param.fragment( url, params [, merge_mode ] );
234  // 
235  // Arguments:
236  // 
237  //  url - (String) A valid URL for params to be merged into. This URL may
238  //    contain a query string and/or fragment (hash).
239  //  params - (String) A params string or URL containing fragment (hash) params
240  //    to be merged into url.
241  //  params - (Object) A params object to be merged into url.
242  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
243  //    specified, and is as-follows:
244  // 
245  //    * 0: params in the params argument will override any fragment (hash)
246  //         params in url.
247  //    * 1: any fragment (hash) params in url will override params in the
248  //         params argument.
249  //    * 2: params argument will completely replace any query string in url.
250  // 
251  // Returns:
252  // 
253  //  (String) A URL with a urlencoded fragment (hash) in the format '#a=b&c=d&e=f'.
254  
255  function jq_param_sub( is_fragment, get_func, url, params, merge_mode ) {
256    var result,
257      qs,
258      matches,
259      url_params,
260      hash;
261    
262    if ( params !== undefined ) {
263      // Build URL by merging params into url string.
264      
265      // matches[1] = url part that precedes params, not including trailing ?/#
266      // matches[2] = params, not including leading ?/#
267      // matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
268      matches = url.match( is_fragment ? re_fragment : /^([^#?]*)\??([^#]*)(#?.*)/ );
269      
270      // Get the hash if in 'querystring' mode, and it exists.
271      hash = matches[3] || '';
272      
273      if ( merge_mode === 2 && is_string( params ) ) {
274        // If merge_mode is 2 and params is a string, merge the fragment / query
275        // string into the URL wholesale, without converting it into an object.
276        qs = params.replace( is_fragment ? re_params_fragment : re_params_querystring, '' );
277        
278      } else {
279        // Convert relevant params in url to object.
280        url_params = jq_deparam( matches[2] );
281        
282        params = is_string( params )
283          
284          // Convert passed params string into object.
285          ? jq_deparam[ is_fragment ? str_fragment : str_querystring ]( params )
286          
287          // Passed params object.
288          : params;
289        
290        qs = merge_mode === 2 ? params                              // passed params replace url params
291          : merge_mode === 1  ? $.extend( {}, params, url_params )  // url params override passed params
292          : $.extend( {}, url_params, params );                     // passed params override url params
293        
294        // Convert params object into a sorted params string.
295        qs = jq_param_sorted( qs );
296        
297        // Unescape characters specified via $.param.noEscape. Since only hash-
298        // history users have requested this feature, it's only enabled for
299        // fragment-related params strings.
300        if ( is_fragment ) {
301          qs = qs.replace( re_no_escape, decode );
302        }
303      }
304      
305      // Build URL from the base url, querystring and hash. In 'querystring'
306      // mode, ? is only added if a query string exists. In 'fragment' mode, #
307      // is always added.
308      result = matches[1] + ( is_fragment ? fragment_prefix : qs || !matches[1] ? '?' : '' ) + qs + hash;
309      
310    } else {
311      // If URL was passed in, parse params from URL string, otherwise parse
312      // params from window.location.href.
313      result = get_func( url !== undefined ? url : location.href );
314    }
315    
316    return result;
317  };
318  
319  jq_param[ str_querystring ]                  = curry( jq_param_sub, 0, get_querystring );
320  jq_param[ str_fragment ] = jq_param_fragment = curry( jq_param_sub, 1, get_fragment );
321  
322  // Method: jQuery.param.sorted
323  // 
324  // Returns a params string equivalent to that returned by the internal
325  // jQuery.param method, but sorted, which makes it suitable for use as a
326  // cache key.
327  // 
328  // For example, in most browsers jQuery.param({z:1,a:2}) returns "z=1&a=2"
329  // and jQuery.param({a:2,z:1}) returns "a=2&z=1". Even though both the
330  // objects being serialized and the resulting params strings are equivalent,
331  // if these params strings were set into the location.hash fragment
332  // sequentially, the hashchange event would be triggered unnecessarily, since
333  // the strings are different (even though the data described by them is the
334  // same). By sorting the params string, unecessary hashchange event triggering
335  // can be avoided.
336  // 
337  // Usage:
338  // 
339  // > jQuery.param.sorted( obj [, traditional ] );
340  // 
341  // Arguments:
342  // 
343  //  obj - (Object) An object to be serialized.
344  //  traditional - (Boolean) Params deep/shallow serialization mode. See the
345  //    documentation at https://api.jquery.com/jQuery.param/ for more detail.
346  // 
347  // Returns:
348  // 
349  //  (String) A sorted params string.
350  
351  jq_param.sorted = jq_param_sorted = function( a, traditional ) {
352    var arr = [],
353      obj = {};
354    
355    $.each( jq_param( a, traditional ).split( '&' ), function(i,v){
356      var key = v.replace( /(?:%5B|=).*$/, '' ),
357        key_obj = obj[ key ];
358      
359      if ( !key_obj ) {
360        key_obj = obj[ key ] = [];
361        arr.push( key );
362      }
363      
364      key_obj.push( v );
365    });
366    
367    return $.map( arr.sort(), function(v){
368      return obj[ v ];
369    }).join( '&' );
370  };
371  
372  // Method: jQuery.param.fragment.noEscape
373  // 
374  // Specify characters that will be left unescaped when fragments are created
375  // or merged using <jQuery.param.fragment>, or when the fragment is modified
376  // using <jQuery.bbq.pushState>. This option only applies to serialized data
377  // object fragments, and not set-as-string fragments. Does not affect the
378  // query string. Defaults to ",/" (comma, forward slash).
379  // 
380  // Note that this is considered a purely aesthetic option, and will help to
381  // create URLs that "look pretty" in the address bar or bookmarks, without
382  // affecting functionality in any way. That being said, be careful to not
383  // unescape characters that are used as delimiters or serve a special
384  // purpose, such as the "#?&=+" (octothorpe, question mark, ampersand,
385  // equals, plus) characters.
386  // 
387  // Usage:
388  // 
389  // > jQuery.param.fragment.noEscape( [ chars ] );
390  // 
391  // Arguments:
392  // 
393  //  chars - (String) The characters to not escape in the fragment. If
394  //    unspecified, defaults to empty string (escape all characters).
395  // 
396  // Returns:
397  // 
398  //  Nothing.
399  
400  jq_param_fragment.noEscape = function( chars ) {
401    chars = chars || '';
402    var arr = $.map( chars.split(''), encodeURIComponent );
403    re_no_escape = new RegExp( arr.join('|'), 'g' );
404  };
405  
406  // A sensible default. These are the characters people seem to complain about
407  // "uglifying up the URL" the most.
408  jq_param_fragment.noEscape( ',/' );
409  
410  // Method: jQuery.param.fragment.ajaxCrawlable
411  // 
412  // TODO: DESCRIBE
413  // 
414  // Usage:
415  // 
416  // > jQuery.param.fragment.ajaxCrawlable( [ state ] );
417  // 
418  // Arguments:
419  // 
420  //  state - (Boolean) TODO: DESCRIBE
421  // 
422  // Returns:
423  // 
424  //  (Boolean) The current ajaxCrawlable state.
425  
426  jq_param_fragment.ajaxCrawlable = function( state ) {
427    if ( state !== undefined ) {
428      if ( state ) {
429        re_params_fragment = /^.*(?:#!|#)/;
430        re_fragment = /^([^#]*)(?:#!|#)?(.*)$/;
431        fragment_prefix = '#!';
432      } else {
433        re_params_fragment = /^.*#/;
434        re_fragment = /^([^#]*)#?(.*)$/;
435        fragment_prefix = '#';
436      }
437      ajax_crawlable = !!state;
438    }
439    
440    return ajax_crawlable;
441  };
442  
443  jq_param_fragment.ajaxCrawlable( 0 );
444  
445  // Section: Deparam (from string)
446  // 
447  // Method: jQuery.deparam
448  // 
449  // Deserialize a params string into an object, optionally coercing numbers,
450  // booleans, null and undefined values; this method is the counterpart to the
451  // internal jQuery.param method.
452  // 
453  // Usage:
454  // 
455  // > jQuery.deparam( params [, coerce ] );
456  // 
457  // Arguments:
458  // 
459  //  params - (String) A params string to be parsed.
460  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
461  //    undefined to their actual value. Defaults to false if omitted.
462  // 
463  // Returns:
464  // 
465  //  (Object) An object representing the deserialized params string.
466  
467  $.deparam = jq_deparam = function( params, coerce ) {
468    var obj = {},
469      coerce_types = { 'true': !0, 'false': !1, 'null': null };
470    
471    // Iterate over all name=value pairs.
472    $.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){
473      var param = v.split( '=' ),
vendor: 6,096 bytes, lines 474-647
474        key = decode( param[0] ),
475        val,
476        cur = obj,
477        i = 0,
478        
479        // If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
480        // into its component parts.
481        keys = key.split( '][' ),
482        keys_last = keys.length - 1;
483      
484      // If the first keys part contains [ and the last ends with ], then []
485      // are correctly balanced.
486      if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
487        // Remove the trailing ] from the last keys part.
488        keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
489        
490        // Split first keys part into two parts on the [ and add them back onto
491        // the beginning of the keys array.
492        keys = keys.shift().split('[').concat( keys );
493        
494        keys_last = keys.length - 1;
495      } else {
496        // Basic 'foo' style key.
497        keys_last = 0;
498      }
499      
500      // Are we dealing with a name=value pair, or just a name?
501      if ( param.length === 2 ) {
502        val = decode( param[1] );
503        
504        // Coerce values.
505        if ( coerce ) {
506          val = val && !isNaN(val)            ? +val              // number
507            : val === 'undefined'             ? undefined         // undefined
508            : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
509            : val;                                                // string
510        }
511        
512        if ( keys_last ) {
513          // Complex key, build deep object structure based on a few rules:
514          // * The 'cur' pointer starts at the object top-level.
515          // * [] = array push (n is set to array length), [n] = array if n is 
516          //   numeric, otherwise object.
517          // * If at the last keys part, set the value.
518          // * For each keys part, if the current level is undefined create an
519          //   object or array based on the type of the next keys part.
520          // * Move the 'cur' pointer to the next level.
521          // * Rinse & repeat.
522          for ( ; i <= keys_last; i++ ) {
523            key = keys[i] === '' ? cur.length : keys[i];
524            cur = cur[key] = i < keys_last
525              ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
526              : val;
527          }
528          
529        } else {
530          // Simple key, even simpler rules, since only scalars and shallow
531          // arrays are allowed.
532          
533          if ( $.isArray( obj[key] ) ) {
534            // val is already an array, so push on the next value.
535            obj[key].push( val );
536            
537          } else if ( obj[key] !== undefined ) {
538            // val isn't an array, but since a second value has been specified,
539            // convert val into an array.
540            obj[key] = [ obj[key], val ];
541            
542          } else {
543            // val is a scalar.
544            obj[key] = val;
545          }
546        }
547        
548      } else if ( key ) {
549        // No value was defined, so set something meaningful.
550        obj[key] = coerce
551          ? undefined
552          : '';
553      }
554    });
555    
556    return obj;
557  };
558  
559  // Method: jQuery.deparam.querystring
560  // 
561  // Parse the query string from a URL or the current window.location.href,
562  // deserializing it into an object, optionally coercing numbers, booleans,
563  // null and undefined values.
564  // 
565  // Usage:
566  // 
567  // > jQuery.deparam.querystring( [ url ] [, coerce ] );
568  // 
569  // Arguments:
570  // 
571  //  url - (String) An optional params string or URL containing query string
572  //    params to be parsed. If url is omitted, the current
573  //    window.location.href is used.
574  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
575  //    undefined to their actual value. Defaults to false if omitted.
576  // 
577  // Returns:
578  // 
579  //  (Object) An object representing the deserialized params string.
580  
581  // Method: jQuery.deparam.fragment
582  // 
583  // Parse the fragment (hash) from a URL or the current window.location.href,
584  // deserializing it into an object, optionally coercing numbers, booleans,
585  // null and undefined values.
586  // 
587  // Usage:
588  // 
589  // > jQuery.deparam.fragment( [ url ] [, coerce ] );
590  // 
591  // Arguments:
592  // 
593  //  url - (String) An optional params string or URL containing fragment (hash)
594  //    params to be parsed. If url is omitted, the current window.location.href
595  //    is used.
596  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
597  //    undefined to their actual value. Defaults to false if omitted.
598  // 
599  // Returns:
600  // 
601  //  (Object) An object representing the deserialized params string.
602  
603  function jq_deparam_sub( is_fragment, url_or_params, coerce ) {
604    if ( url_or_params === undefined || typeof url_or_params === 'boolean' ) {
605      // url_or_params not specified.
606      coerce = url_or_params;
607      url_or_params = jq_param[ is_fragment ? str_fragment : str_querystring ]();
608    } else {
609      url_or_params = is_string( url_or_params )
610        ? url_or_params.replace( is_fragment ? re_params_fragment : re_params_querystring, '' )
611        : url_or_params;
612    }
613    
614    return jq_deparam( url_or_params, coerce );
615  };
616  
617  jq_deparam[ str_querystring ]                    = curry( jq_deparam_sub, 0 );
618  jq_deparam[ str_fragment ] = jq_deparam_fragment = curry( jq_deparam_sub, 1 );
619  
620  // Section: Element manipulation
621  // 
622  // Method: jQuery.elemUrlAttr
623  // 
624  // Get the internal "Default URL attribute per tag" list, or augment the list
625  // with additional tag-attribute pairs, in case the defaults are insufficient.
626  // 
627  // In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
628  // is used to determine which attribute contains the URL to be modified, if
629  // an "attr" param is not specified.
630  // 
631  // Default Tag-Attribute List:
632  // 
633  //  a      - href
634  //  base   - href
635  //  iframe - src
636  //  img    - src
637  //  input  - src
638  //  form   - action
639  //  link   - href
640  //  script - src
641  // 
642  // Usage:
643  // 
644  // > jQuery.elemUrlAttr( [ tag_attr ] );
645  // 
646  // Arguments:
647  // 
648  //  tag_attr - (Object) An object containing a list of tag names and their
649  //    associated default attribute names in the format { tag: 'attr', ... } to
650  //    be merged into the internal tag-attribute list.
651  // 
652  // Returns:
653  // 
654  //  (Object) An object containing all stored tag-attribute values.
655  
656  // Only define function and set defaults if function doesn't already exist, as
657  // the urlInternal plugin will provide this method as well.
658  $[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) {
659    return $.extend( elemUrlAttr_cache, obj );
660  })({
661    a: str_href,
662    base: str_href,
663    iframe: str_src,
664    img: str_src,
665    input: str_src,
666    form: 'action',
667    link: str_href,
668    script: str_src
669  });
670  
671  jq_elemUrlAttr = $[ str_elemUrlAttr ];
672  
673  // Method: jQuery.fn.querystring
674  // 
675  // Update URL attribute in one or more elements, merging the current URL (with
676  // or without pre-existing query string params) plus any params object or
677  // string into a new URL, which is then set into that attribute. Like
678  // <jQuery.param.querystring (build url)>, but for all elements in a jQuery
679  // collection.
680  // 
681  // Usage:
682  // 
683  // > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
684  // 
685  // Arguments:
686  // 
687  //  attr - (String) Optional name of an attribute that will contain a URL to
688  //    merge params or url into. See <jQuery.elemUrlAttr> for a list of default
689  //    attributes.
690  //  params - (Object) A params object to be merged into the URL attribute.
691  //  params - (String) A URL containing query string params, or params string
692  //    to be merged into the URL attribute.
693  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
694  //    specified, and is as-follows:
695  //    
696  //    * 0: params in the params argument will override any params in attr URL.
697  //    * 1: any params in attr URL will override params in the params argument.
698  //    * 2: params argument will completely replace any query string in attr
699  //         URL.
700  // 
701  // Returns:
702  // 
703  //  (jQuery) The initial jQuery collection of elements, but with modified URL
704  //  attribute values.
705  
706  // Method: jQuery.fn.fragment
707  // 
708  // Update URL attribute in one or more elements, merging the current URL (with
709  // or without pre-existing fragment/hash params) plus any params object or
710  // string into a new URL, which is then set into that attribute. Like
711  // <jQuery.param.fragment (build url)>, but for all elements in a jQuery
712  // collection.
713  // 
714  // Usage:
715  // 
716  // > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
717  // 
718  // Arguments:
719  // 
720  //  attr - (String) Optional name of an attribute that will contain a URL to
721  //    merge params into. See <jQuery.elemUrlAttr> for a list of default
722  //    attributes.
723  //  params - (Object) A params object to be merged into the URL attribute.
724  //  params - (String) A URL containing fragment (hash) params, or params
725  //    string to be merged into the URL attribute.
726  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
727  //    specified, and is as-follows:
728  //    
729  //    * 0: params in the params argument will override any params in attr URL.
730  //    * 1: any params in attr URL will override params in the params argument.
731  //    * 2: params argument will completely replace any fragment (hash) in attr
732  //         URL.
733  // 
734  // Returns:
735  // 
736  //  (jQuery) The initial jQuery collection of elements, but with modified URL
737  //  attribute values.
738  
739  function jq_fn_sub( mode, force_attr, params, merge_mode ) {
740    if ( !is_string( params ) && typeof params !== 'object' ) {
741      // force_attr not specified.
742      merge_mode = params;
743      params = force_attr;
744      force_attr = undefined;
745    }
746    
747    return this.each(function(){
748      var that = $(this),
749        
750        // Get attribute specified, or default specified via $.elemUrlAttr.
751        attr = force_attr || jq_elemUrlAttr()[ ( this.nodeName || '' ).toLowerCase() ] || '',
752        
753        // Get URL value.
754        url = attr && that.attr( attr ) || '';
755      
756      // Update attribute with new URL.
757      that.attr( attr, jq_param[ mode ]( url, params, merge_mode ) );
758    });
759    
760  };
761  
762  $.fn[ str_querystring ] = curry( jq_fn_sub, str_querystring );
763  $.fn[ str_fragment ]    = curry( jq_fn_sub, str_fragment );
764  
765  // Section: History, hashchange event
766  // 
767  // Method: jQuery.bbq.pushState
768  // 
769  // Adds a 'state' into the browser history at the current position, setting
770  // location.hash and triggering any bound <hashchange event> callbacks
771  // (provided the new state is different than the previous state).
772  // 
773  // If no arguments are passed, an empty state is created, which is just a
774  // shortcut for jQuery.bbq.pushState( {}, 2 ).
775  // 
776  // Usage:
777  // 
778  // > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
779  // 
780  // Arguments:
781  // 
782  //  params - (String) A serialized params string or a hash string beginning
783  //    with # to merge into location.hash.
784  //  params - (Object) A params object to merge into location.hash.
785  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
786  //    specified (unless a hash string beginning with # is specified, in which
787  //    case merge behavior defaults to 2), and is as-follows:
788  // 
789  //    * 0: params in the params argument will override any params in the
790  //         current state.
791  //    * 1: any params in the current state will override params in the params
792  //         argument.
793  //    * 2: params argument will completely replace current state.
794  // 
795  // Returns:
796  // 
797  //  Nothing.
798  // 
799  // Additional Notes:
800  // 
801  //  * Setting an empty state may cause the browser to scroll.
802  //  * Unlike the fragment and querystring methods, if a hash string beginning
803  //    with # is specified as the params agrument, merge_mode defaults to 2.
804  
805  jq_bbq.pushState = jq_bbq_pushState = function( params, merge_mode ) {
806    if ( is_string( params ) && /^#/.test( params ) && merge_mode === undefined ) {
807      // Params string begins with # and merge_mode not specified, so completely
808      // overwrite window.location.hash.
809      merge_mode = 2;
810    }
811    
812    var has_args = params !== undefined,
813      // Merge params into window.location using $.param.fragment.
814      url = jq_param_fragment( location.href,
815        has_args ? params : {}, has_args ? merge_mode : 2 );
816    
817    // Set new window.location.href. Note that Safari 3 & Chrome barf on
818    // location.hash = '#' so the entire URL is set.
819    location.href = url;
820  };
821  
822  // Method: jQuery.bbq.getState
823  // 
824  // Retrieves the current 'state' from the browser history, parsing
825  // location.hash for a specific key or returning an object containing the
826  // entire state, optionally coercing numbers, booleans, null and undefined
827  // values.
828  // 
829  // Usage:
830  // 
831  // > jQuery.bbq.getState( [ key ] [, coerce ] );
832  // 
833  // Arguments:
834  // 
835  //  key - (String) An optional state key for which to return a value.
836  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
837  //    undefined to their actual value. Defaults to false.
838  // 
839  // Returns:
840  // 
841  //  (Anything) If key is passed, returns the value corresponding with that key
842  //    in the location.hash 'state', or undefined. If not, an object
843  //    representing the entire 'state' is returned.
844  
845  jq_bbq.getState = jq_bbq_getState = function( key, coerce ) {
846    return key === undefined || typeof key === 'boolean'
847      ? jq_deparam_fragment( key ) // 'key' really means 'coerce' here
848      : jq_deparam_fragment( coerce )[ key ];
849  };
850  
851  // Method: jQuery.bbq.removeState
852  // 
853  // Remove one or more keys from the current browser history 'state', creating
854  // a new state, setting location.hash and triggering any bound
855  // <hashchange event> callbacks (provided the new state is different than
856  // the previous state).
857  // 
858  // If no arguments are passed, an empty state is created, which is just a
859  // shortcut for jQuery.bbq.pushState( {}, 2 ).
860  // 
861  // Usage:
862  // 
863  // > jQuery.bbq.removeState( [ key [, key ... ] ] );
864  // 
865  // Arguments:
866  // 
867  //  key - (String) One or more key values to remove from the current state,
868  //    passed as individual arguments.
869  //  key - (Array) A single array argument that contains a list of key values
870  //    to remove from the current state.
871  // 
872  // Returns:
873  // 
874  //  Nothing.
875  // 
876  // Additional Notes:
877  // 
878  //  * Setting an empty state may cause the browser to scroll.
879  
880  jq_bbq.removeState = function( arr ) {
881    var state = {};
882    
883    // If one or more arguments is passed..
884    if ( arr !== undefined ) {
885      
886      // Get the current state.
887      state = jq_bbq_getState();
888      
889      // For each passed key, delete the corresponding property from the current
890      // state.
891      $.each( $.isArray( arr ) ? arr : arguments, function(i,v){
892        delete state[ v ];
893      });
894    }
895    
896    // Set the state, completely overriding any existing state.
897    jq_bbq_pushState( state, 2 );
898  };
899  
900  // Event: hashchange event (BBQ)
901  // 
902  // Usage in jQuery 1.4 and newer:
903  // 
904  // In jQuery 1.4 and newer, the event object passed into any hashchange event
905  // callback is augmented with a copy of the location.hash fragment at the time
906  // the event was triggered as its event.fragment property. In addition, the
907  // event.getState method operates on this property (instead of location.hash)
908  // which allows this fragment-as-a-state to be referenced later, even after
909  // window.location may have changed.
910  // 
911  // Note that event.fragment and event.getState are not defined according to
912  // W3C (or any other) specification, but will still be available whether or
913  // not the hashchange event exists natively in the browser, because of the
914  // utility they provide.
915  // 
916  // The event.fragment property contains the output of <jQuery.param.fragment>
917  // and the event.getState method is equivalent to the <jQuery.bbq.getState>
918  // method.
919  // 
920  // > $(window).bind( 'hashchange', function( event ) {
921  // >   var hash_str = event.fragment,
922  // >     param_obj = event.getState(),
923  // >     param_val = event.getState( 'param_name' ),
924  // >     param_val_coerced = event.getState( 'param_name', true );
925  // >   ...
926  // > });
927  // 
928  // Usage in jQuery 1.3.2:
929  // 
930  // In jQuery 1.3.2, the event object cannot to be augmented as in jQuery 1.4+,
931  // so the fragment state isn't bound to the event object and must instead be
932  // parsed using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
933  // 
934  // > $(window).bind( 'hashchange', function( event ) {
935  // >   var hash_str = $.param.fragment(),
936  // >     param_obj = $.bbq.getState(),
937  // >     param_val = $.bbq.getState( 'param_name' ),
938  // >     param_val_coerced = $.bbq.getState( 'param_name', true );
939  // >   ...
940  // > });
941  // 
942  // Additional Notes:
943  // 
944  // * Due to changes in the special events API, jQuery BBQ v1.2 or newer is
945  //   required to enable the augmented event object in jQuery 1.4.2 and newer.
946  // * See <jQuery hashchange event> for more detailed information.
947  
948  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
949    
950    // Augmenting the event object with the .fragment property and .getState
951    // method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
952    // work, but the event won't be augmented)
953    add: function( handleObj ) {
954      var old_handler;
955      
956      function new_handler(e) {
957        // e.fragment is set to the value of location.hash (with any leading #
958        // removed) at the time the event is triggered.
959        var hash = e[ str_fragment ] = jq_param_fragment();
960        
961        // e.getState() works just like $.bbq.getState(), but uses the
962        // e.fragment property stored on the event object.
963        e.getState = function( key, coerce ) {
964          return key === undefined || typeof key === 'boolean'
965            ? jq_deparam( hash, key ) // 'key' really means 'coerce' here
966            : jq_deparam( hash, coerce )[ key ];
967        };
968        
969        old_handler.apply( this, arguments );
970      };
971      
972      // This may seem a little complicated, but it normalizes the special event
973      // .add method between jQuery 1.4/1.4.1 and 1.4.2+
974      if ( $.isFunction( handleObj ) ) {
975        // 1.4, 1.4.1
976        old_handler = handleObj;
977        return new_handler;
978      } else {
979        // 1.4.2+
980        old_handler = handleObj.handler;
981        handleObj.handler = new_handler;
982      }
983    }
984    
985  });
986  
987})(jQuery,this);
988
989/*!
990 * jQuery hashchange event - v1.3 - 7/21/2010
991 * https://benalman.com/projects/jquery-hashchange-plugin/
992 * 
993 * Copyright (c) 2010 "Cowboy" Ben Alman
994 * Dual licensed under the MIT and GPL licenses.
995 * https://benalman.com/about/license/
996 */
997
998// Script: jQuery hashchange event
999//
1000// *Version: 1.3, Last updated: 7/21/2010*
1001// 
1002// Project Home - https://benalman.com/projects/jquery-hashchange-plugin/
1003// GitHub       - https://github.com/cowboy/jquery-hashchange/
1004// Source       - https://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
1005// (Minified)   - https://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
1006// 
1007// About: License
1008// 
1009// Copyright (c) 2010 "Cowboy" Ben Alman,
1010// Dual licensed under the MIT and GPL licenses.
1011// https://benalman.com/about/license/
1012// 
1013// About: Examples
1014// 
1015// These working examples, complete with fully commented code, illustrate a few
1016// ways in which this plugin can be used.
1017// 
1018// hashchange event - https://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
1019// document.domain - https://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
1020//
1021// About: Support and Testing
1022// 
1023// Information about what version or versions of jQuery this plugin has been
1024// tested with, what browsers it has been tested in, and where the unit tests
1025// reside (so you can test it yourself).
1026// 
1027// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
1028// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
1029//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
1030// Unit Tests      - https://benalman.com/code/projects/jquery-hashchange/unit/
1031// 
1032// About: Known issues
1033// 
1034// While this jQuery hashchange event implementation is quite stable and
1035// robust, there are a few unfortunate browser bugs surrounding expected
1036// hashchange event-based behaviors, independent of any JavaScript
1037// window.onhashchange abstraction. See the following examples for more
1038// information:
1039// 
1040// Chrome: Back Button - https://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
1041// Firefox: Remote XMLHttpRequest - https://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
1042// WebKit: Back Button in an Iframe - https://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
1043// Safari: Back Button from a different domain - https://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
1044// 
1045// Also note that should a browser natively support the window.onhashchange 
1046// event, but not report that it does, the fallback polling loop will be used.
1047// 
1048// About: Release History
1049// 
1050// 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
1051//         "removable" for mobile-only development. Added IE6/7 document.title
1052//         support. Attempted to make Iframe as hidden as possible by using
1053//         techniques from https://www.paciellogroup.com/blog/?p=604. Added
1054//         support for the "shortcut" format $(window).hashchange( fn ) and
1055//         $(window).hashchange() like jQuery provides for built-in events.
1056//         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
1057//         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
1058//         and <jQuery.fn.hashchange.src> properties plus document-domain.html
1059//         file to address access denied issues when setting document.domain in
1060//         IE6/7.
1061// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
1062//         from a page on another domain would cause an error in Safari 4. Also,
1063//         IE6/7 Iframe is now inserted after the body (this actually works),
1064//         which prevents the page from scrolling when the event is first bound.
1065//         Event can also now be bound before DOM ready, but it won't be usable
1066//         before then in IE6/7.
1067// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
1068//         where browser version is incorrectly reported as 8.0, despite
1069//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
1070// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
1071//         window.onhashchange functionality into a separate plugin for users
1072//         who want just the basic event & back button support, without all the
1073//         extra awesomeness that BBQ provides. This plugin will be included as
1074//         part of jQuery BBQ, but also be available separately.
1075
1076(function($,window,undefined){
1077  '$:nomunge'; // Used by YUI compressor.
1078  
1079  // Reused string.
1080  var str_hashchange = 'hashchange',
1081    
1082    // Method / object references.
1083    doc = document,
1084    fake_onhashchange,
1085    special = $.event.special,
1086    
1087    // Does the browser support window.onhashchange? Note that IE8 running in
1088    // IE7 compatibility mode reports true for 'onhashchange' in window, even
1089    // though the event isn't supported, so also test document.documentMode.
1090    doc_mode = doc.documentMode,
1091    supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
1092  
1093  // Get location.hash (or what you'd expect location.hash to be) sans any
1094  // leading #. Thanks for making this necessary, Firefox!
1095  function get_fragment( url ) {
1096    url = url || location.href;
1097    return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
1098  };
1099  
1100  // Method: jQuery.fn.hashchange
1101  // 
1102  // Bind a handler to the window.onhashchange event or trigger all bound
1103  // window.onhashchange event handlers. This behavior is consistent with
1104  // jQuery's built-in event handlers.
1105  // 
1106  // Usage:
1107  // 
1108  // > jQuery(window).hashchange( [ handler ] );
1109  // 
1110  // Arguments:
1111  // 
1112  //  handler - (Function) Optional handler to be bound to the hashchange
1113  //    event. This is a "shortcut" for the more verbose form:
1114  //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
1115  //    all bound window.onhashchange event handlers will be triggered. This
1116  //    is a shortcut for the more verbose
1117  //    jQuery(window).trigger( 'hashchange' ). These forms are described in
1118  //    the <hashchange event> section.
1119  // 
1120  // Returns:
1121  // 
1122  //  (jQuery) The initial jQuery collection of elements.
1123  
1124  // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
1125  // $(elem).hashchange() for triggering, like jQuery does for built-in events.
1126  $.fn[ str_hashchange ] = function( fn ) {
1127    return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
1128  };
1129  
1130  // Property: jQuery.fn.hashchange.delay
1131  // 
1132  // The numeric interval (in milliseconds) at which the <hashchange event>
1133  // polling loop executes. Defaults to 50.
1134  
1135  // Property: jQuery.fn.hashchange.domain
1136  // 
1137  // If you're setting document.domain in your JavaScript, and you want hash
1138  // history to work in IE6/7, not only must this property be set, but you must
1139  // also set document.domain BEFORE jQuery is loaded into the page. This
1140  // property is only applicable if you are supporting IE6/7 (or IE8 operating
1141  // in "IE7 compatibility" mode).
1142  // 
1143  // In addition, the <jQuery.fn.hashchange.src> property must be set to the
1144  // path of the included "document-domain.html" file, which can be renamed or
1145  // modified if necessary (note that the document.domain specified must be the
1146  // same in both your main JavaScript as well as in this file).
1147  // 
1148  // Usage:
1149  // 
1150  // jQuery.fn.hashchange.domain = document.domain;
1151  
1152  // Property: jQuery.fn.hashchange.src
1153  // 
1154  // If, for some reason, you need to specify an Iframe src file (for example,
1155  // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
1156  // do so using this property. Note that when using this property, history
1157  // won't be recorded in IE6/7 until the Iframe src file loads. This property
1158  // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
1159  // compatibility" mode).
1160  // 
1161  // Usage:
1162  // 
1163  // jQuery.fn.hashchange.src = 'path/to/file.html';
1164  
1165  $.fn[ str_hashchange ].delay = 50;
1166  /*
1167  $.fn[ str_hashchange ].domain = null;
1168  $.fn[ str_hashchange ].src = null;
1169  */
1170  
1171  // Event: hashchange event
1172  // 
1173  // Fired when location.hash changes. In browsers that support it, the native
1174  // HTML5 window.onhashchange event is used, otherwise a polling loop is
1175  // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
1176  // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
1177  // compatibility" mode), a hidden Iframe is created to allow the back button
1178  // and hash-based history to work.
1179  // 
1180  // Usage as described in <jQuery.fn.hashchange>:
1181  // 
1182  // > // Bind an event handler.
1183  // > jQuery(window).hashchange( function(e) {
1184  // >   var hash = location.hash;
1185  // >   ...
1186  // > });
1187  // > 
1188  // > // Manually trigger the event handler.
1189  // > jQuery(window).hashchange();
1190  // 
1191  // A more verbose usage that allows for event namespacing:
1192  // 
1193  // > // Bind an event handler.
1194  // > jQuery(window).bind( 'hashchange', function(e) {
1195  // >   var hash = location.hash;
1196  // >   ...
1197  // > });
1198  // > 
1199  // > // Manually trigger the event handler.
1200  // > jQuery(window).trigger( 'hashchange' );
1201  // 
1202  // Additional Notes:
1203  // 
1204  // * The polling loop and Iframe are not created until at least one handler
1205  //   is actually bound to the 'hashchange' event.
1206  // * If you need the bound handler(s) to execute immediately, in cases where
1207  //   a location.hash exists on page load, via bookmark or page refresh for
1208  //   example, use jQuery(window).hashchange() or the more verbose 
1209  //   jQuery(window).trigger( 'hashchange' ).
1210  // * The event can be bound before DOM ready, but since it won't be usable
1211  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
1212  //   to bind it inside a DOM ready handler.
1213  
1214  // Override existing $.event.special.hashchange methods (allowing this plugin
1215  // to be defined after jQuery BBQ in BBQ's source code).
1216  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
1217    
1218    // Called only when the first 'hashchange' event is bound to window.
1219    setup: function() {
1220      // If window.onhashchange is supported natively, there's nothing to do..
1221      if ( supports_onhashchange ) { return false; }
1222      
1223      // Otherwise, we need to create our own. And we don't want to call this
1224      // until the user binds to the event, just in case they never do, since it
1225      // will create a polling loop and possibly even a hidden Iframe.
1226      $( fake_onhashchange.start );
1227    },
1228    
1229    // Called only when the last 'hashchange' event is unbound from window.
1230    teardown: function() {
1231      // If window.onhashchange is supported natively, there's nothing to do..
1232      if ( supports_onhashchange ) { return false; }
1233      
1234      // Otherwise, we need to stop ours (if possible).
1235      $( fake_onhashchange.stop );
1236    }
1237    
1238  });
1239  
1240  // fake_onhashchange does all the work of triggering the window.onhashchange
1241  // event for browsers that don't natively support it, including creating a
1242  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
1243  // Iframe to enable back and forward.
1244  fake_onhashchange = (function(){
1245    var self = {},
1246      timeout_id,
1247      
1248      // Remember the initial hash so it doesn't get triggered immediately.
1249      last_hash = get_fragment(),
1250      
1251      fn_retval = function(val){ return val; },
1252      history_set = fn_retval,
1253      history_get = fn_retval;
1254    
1255    // Start the polling loop.
1256    self.start = function() {
1257      timeout_id || poll();
1258    };
1259    
1260    // Stop the polling loop.
1261    self.stop = function() {
1262      timeout_id && clearTimeout( timeout_id );
1263      timeout_id = undefined;
1264    };
1265    
1266    // This polling loop checks every $.fn.hashchange.delay milliseconds to see
1267    // if location.hash has changed, and triggers the 'hashchange' event on
1268    // window when necessary.
1269    function poll() {
1270      var hash = get_fragment(),
1271        history_hash = history_get( last_hash );
1272      
1273      if ( hash !== last_hash ) {
1274        history_set( last_hash = hash, history_hash );
1275        
1276        $(window).trigger( str_hashchange );
1277        
1278      } else if ( history_hash !== last_hash ) {
1279        location.href = location.href.replace( /#.*/, '' ) + history_hash;
1280      }
1281      
1282      timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
1283    };
1284    
1285    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1286    // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
1287    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1288    (navigator.userAgent.match(/MSIE/i) !== null) && !supports_onhashchange && (function(){
1289      // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
1290      // when running in "IE7 compatibility" mode.
1291      
1292      var iframe,
1293        iframe_src;
1294      
1295      // When the event is bound and polling starts in IE 6/7, create a hidden
1296      // Iframe for history handling.
1297      self.start = function(){
1298        if ( !iframe ) {
1299          iframe_src = $.fn[ str_hashchange ].src;
1300          iframe_src = iframe_src && iframe_src + get_fragment();
1301          
1302          // Create hidden Iframe. Attempt to make Iframe as hidden as possible
1303          // by using techniques from https://www.paciellogroup.com/blog/?p=604.
1304          iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
1305            
1306            // When Iframe has completely loaded, initialize the history and
1307            // start polling.
1308            .one( 'load', function(){
1309              iframe_src || history_set( get_fragment() );
1310              poll();
1311            })
1312            
1313            // Load Iframe src if specified, otherwise nothing.
1314            .attr( 'src', iframe_src || 'javascript:0' )
1315            
1316            // Append Iframe after the end of the body to prevent unnecessary
1317            // initial page scrolling (yes, this works).
1318            .insertAfter( 'body' )[0].contentWindow;
1319          
1320          // Whenever `document.title` changes, update the Iframe's title to
1321          // prettify the back/next history menu entries. Since IE sometimes
1322          // errors with "Unspecified error" the very first time this is set
1323          // (yes, very useful) wrap this with a try/catch block.
1324          doc.onpropertychange = function(){
1325            try {
1326              if ( event.propertyName === 'title' ) {
1327                iframe.document.title = doc.title;
1328              }
1329            } catch(e) {}
1330          };
1331          
1332        }
1333      };
1334      
1335      // Override the "stop" method since an IE6/7 Iframe was created. Even
1336      // if there are no longer any bound event handlers, the polling loop
1337      // is still necessary for back/next to work at all!
1338      self.stop = fn_retval;
1339      
1340      // Get history by looking at the hidden Iframe's location.hash.
1341      history_get = function() {
1342        return get_fragment( iframe.location.href );
1343      };
1344      
1345      // Set a new history item by opening and then closing the Iframe
1346      // document, *then* setting its location.hash. If document.domain has
1347      // been set, update that as well.
1348      history_set = function( hash, history_hash ) {
1349        var iframe_doc = iframe.document,
1350          domain = $.fn[ str_hashchange ].domain;
1351        
1352        if ( hash !== history_hash ) {
1353          // Update Iframe with any initial `document.title` that might be set.
1354          iframe_doc.title = doc.title;
1355          
1356          // Opening the Iframe's document after it has been closed is what
1357          // actually adds a history entry.
1358          iframe_doc.open();
1359          
1360          // Set document.domain for the Iframe document as well, if necessary.
1361          domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
1362          
1363          iframe_doc.close();
1364          
1365          // Update the Iframe's hash, for great justice.
1366          iframe.location.hash = hash;
1367        }
1368      };
1369      
1370    })();
1371    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1372    // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
1373    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1374    
1375    return self;
1376  })();
1377  
1378})(jQuery,this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.