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.