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