PageRenderTime 70ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/jquery.ba-bbq/1.0.3/jquery.ba-bbq.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 883 lines | 244 code | 103 blank | 536 comment | 54 complexity | 80ee8cb466b60f62b4ce555d1a3e7f43 MD5 | raw file
  1. /*!
  2. * jQuery BBQ: Back Button & Query Library - v1.0.3 - 12/2/2009
  3. * http://benalman.com/projects/jquery-bbq-plugin/
  4. *
  5. * Copyright (c) 2009 "Cowboy" Ben Alman
  6. * Dual licensed under the MIT and GPL licenses.
  7. * http://benalman.com/about/license/
  8. */
  9. // Script: jQuery BBQ: Back Button & Query Library
  10. //
  11. // *Version: 1.0.3, Last updated: 12/2/2009*
  12. //
  13. // Project Home - http://benalman.com/projects/jquery-bbq-plugin/
  14. // GitHub - http://github.com/cowboy/jquery-bbq/
  15. // Source - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
  16. // (Minified) - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (3.2kb)
  17. //
  18. // About: License
  19. //
  20. // Copyright (c) 2009 "Cowboy" Ben Alman,
  21. // Dual licensed under the MIT and GPL licenses.
  22. // http://benalman.com/about/license/
  23. //
  24. // About: Examples
  25. //
  26. // These working examples, complete with fully commented code, illustrate a few
  27. // ways in which this plugin can be used.
  28. //
  29. // Basic AJAX - http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
  30. // Advanced AJAX - http://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
  31. // jQuery UI Tabs - http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
  32. // Deparam - http://benalman.com/code/projects/jquery-bbq/examples/deparam/
  33. //
  34. // About: Support and Testing
  35. //
  36. // Information about what version or versions of jQuery this plugin has been
  37. // tested with, what browsers it has been tested in, and where the unit tests
  38. // reside (so you can test it yourself).
  39. //
  40. // jQuery Versions - 1.3.2, 1.4pre
  41. // Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.
  42. // Unit Tests - http://benalman.com/code/projects/jquery-bbq/unit/
  43. //
  44. // About: Release History
  45. //
  46. // 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
  47. // location.hash would report incorrectly if the hash contained the ?
  48. // character. Also $.param.querystring and $.param.fragment will no
  49. // longer parse params out of a URL that doesn't contain ? or #,
  50. // respectively.
  51. // 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
  52. // a "This page contains both secure and nonsecure items." warning when
  53. // used on an https:// page.
  54. // 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
  55. // Compatibility View" modes erroneously report that the browser
  56. // supports the native window.onhashchange event, a slightly more
  57. // robust test needed to be added.
  58. // 1.0 - (10/2/2009) Initial release
  59. (function($,window){
  60. '$:nomunge'; // Used by YUI compressor.
  61. // Some convenient shortcuts.
  62. var undefined,
  63. loc = window.location,
  64. aps = Array.prototype.slice,
  65. decode = decodeURIComponent,
  66. // Method references.
  67. jq_param = $.param,
  68. jq_param_fragment,
  69. jq_deparam,
  70. jq_deparam_fragment,
  71. jq_bbq = $.bbq = $.bbq || {},
  72. jq_bbq_pushState,
  73. jq_elemUrlAttr,
  74. fake_onhashchange,
  75. // Reused strings.
  76. str_hashchange = 'hashchange',
  77. str_querystring = 'querystring',
  78. str_fragment = 'fragment',
  79. str_elemUrlAttr = 'elemUrlAttr',
  80. str_href = 'href',
  81. str_src = 'src',
  82. browser = $.browser,
  83. is_old_ie = browser.msie && browser.version < 8,
  84. // Does the browser support window.onhashchange? Test for IE version, since
  85. // IE8 incorrectly reports this when in "IE7" or "IE8 Compatibility View"!
  86. supports_onhashchange = 'on' + str_hashchange in window && !is_old_ie,
  87. // Reused RegExp.
  88. re_trim_querystring = /^.*\?|#.*$/g,
  89. re_trim_fragment = /^.*\#/,
  90. // Used by jQuery.elemUrlAttr.
  91. elemUrlAttr_cache = {};
  92. // A few commonly used bits, broken out to help reduce minified file size.
  93. function is_string( arg ) {
  94. return typeof arg === 'string';
  95. };
  96. // Why write the same function twice? Let's curry! Mmmm, curry..
  97. function curry( func ) {
  98. var args = aps.call( arguments, 1 );
  99. return function() {
  100. return func.apply( this, args.concat( aps.call( arguments ) ) );
  101. };
  102. };
  103. // Get location.hash (or what you'd expect location.hash to be) sans any
  104. // leading #. Thanks for making this necessary, Firefox!
  105. function get_fragment( url ) {
  106. return url.replace( /^[^#]*#?(.*)$/, '$1' );
  107. };
  108. // Get location.search (or what you'd expect location.search to be) sans any
  109. // leading #. Thanks for making this necessary, IE6!
  110. function get_querystring( url ) {
  111. return url.replace( /(?:^[^?#]*\?([^#]*).*$)?.*/, '$1' );
  112. };
  113. // Section: Param (to string)
  114. //
  115. // Method: jQuery.param.querystring
  116. //
  117. // Retrieve the query string from a URL or if no arguments are passed, the
  118. // current window.location.
  119. //
  120. // Usage:
  121. //
  122. // > jQuery.param.querystring( [ url ] );
  123. //
  124. // Arguments:
  125. //
  126. // url - (String) A URL containing query string params to be parsed. If url
  127. // is not passed, the current window.location is used.
  128. //
  129. // Returns:
  130. //
  131. // (String) The parsed query string, with any leading "?" removed.
  132. //
  133. // Method: jQuery.param.querystring (build url)
  134. //
  135. // Merge a URL, with or without pre-existing query string params, plus any
  136. // object, params string or URL containing query string params into a new URL.
  137. //
  138. // Usage:
  139. //
  140. // > jQuery.param.querystring( url, params [, merge_mode ] );
  141. //
  142. // Arguments:
  143. //
  144. // url - (String) A valid URL for params to be merged into. This URL may
  145. // contain a query string and/or fragment (hash).
  146. // params - (String) A params string or URL containing query string params to
  147. // be merged into url.
  148. // params - (Object) A params object to be merged into url.
  149. // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  150. // specified, and is as-follows:
  151. //
  152. // * 0: params in the params argument will override any query string
  153. // params in url.
  154. // * 1: any query string params in url will override params in the params
  155. // argument.
  156. // * 2: params argument will completely replace any query string in url.
  157. //
  158. // Returns:
  159. //
  160. // (String) Either a params string with urlencoded data or a URL with a
  161. // urlencoded query string in the format 'a=b&c=d&e=f'.
  162. // Method: jQuery.param.fragment
  163. //
  164. // Retrieve the fragment (hash) from a URL or if no arguments are passed, the
  165. // current window.location.
  166. //
  167. // Usage:
  168. //
  169. // > jQuery.param.fragment( [ url ] );
  170. //
  171. // Arguments:
  172. //
  173. // url - (String) A URL containing fragment (hash) params to be parsed. If
  174. // url is not passed, the current window.location is used.
  175. //
  176. // Returns:
  177. //
  178. // (String) The parsed fragment (hash) string, with any leading "#" removed.
  179. // Method: jQuery.param.fragment (build url)
  180. //
  181. // Merge a URL, with or without pre-existing fragment (hash) params, plus any
  182. // object, params string or URL containing fragment (hash) params into a new
  183. // URL.
  184. //
  185. // Usage:
  186. //
  187. // > jQuery.param.fragment( url, params [, merge_mode ] );
  188. //
  189. // Arguments:
  190. //
  191. // url - (String) A valid URL for params to be merged into. This URL may
  192. // contain a query string and/or fragment (hash).
  193. // params - (String) A params string or URL containing fragment (hash) params
  194. // to be merged into url.
  195. // params - (Object) A params object to be merged into url.
  196. // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  197. // specified, and is as-follows:
  198. //
  199. // * 0: params in the params argument will override any fragment (hash)
  200. // params in url.
  201. // * 1: any fragment (hash) params in url will override params in the
  202. // params argument.
  203. // * 2: params argument will completely replace any query string in url.
  204. //
  205. // Returns:
  206. //
  207. // (String) Either a params string with urlencoded data or a URL with a
  208. // urlencoded fragment (hash) in the format 'a=b&c=d&e=f'.
  209. function jq_param_sub( is_fragment, get_func, url, params, merge_mode ) {
  210. var result,
  211. qs,
  212. matches,
  213. url_params,
  214. hash;
  215. if ( params !== undefined ) {
  216. // Build URL by merging params into url string.
  217. // matches[1] = url part that precedes params, not including trailing ?/#
  218. // matches[2] = params, not including leading ?/#
  219. // matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
  220. matches = url.match( is_fragment ? /^([^#]*)\#?(.*)$/ : /^([^#?]*)\??([^#]*)(#?.*)/ );
  221. // Get the hash if in 'querystring' mode, and it exists.
  222. hash = matches[3] || '';
  223. if ( merge_mode === 2 && is_string( params ) ) {
  224. // If merge_mode is 2 and params is a string, merge the fragment / query
  225. // string into the URL wholesale, without converting it into an object.
  226. qs = params.replace( is_fragment ? re_trim_fragment : re_trim_querystring, '' );
  227. } else {
  228. // Convert relevant params in url to object.
  229. url_params = jq_deparam( matches[2] );
  230. params = is_string( params )
  231. // Convert passed params string into object.
  232. ? jq_deparam[ is_fragment ? str_fragment : str_querystring ]( params )
  233. // Passed params object.
  234. : params;
  235. qs = merge_mode === 2 ? params // passed params replace url params
  236. : merge_mode === 1 ? $.extend( {}, params, url_params ) // url params override passed params
  237. : $.extend( {}, url_params, params ); // passed params override url params
  238. // Convert params object to a string.
  239. qs = jq_param( qs );
  240. }
  241. // Build URL from the base url, querystring and hash. In 'querystring'
  242. // mode, ? is only added if a query string exists. In 'fragment' mode, #
  243. // is always added.
  244. result = matches[1] + ( is_fragment ? '#' : qs || !matches[1] ? '?' : '' ) + qs + hash;
  245. } else {
  246. // If URL was passed in, parse params from URL string, otherwise parse
  247. // params from window.location.
  248. result = get_func( url !== undefined ? url : loc[ str_href ] );
  249. }
  250. return result;
  251. };
  252. jq_param[ str_querystring ] = curry( jq_param_sub, 0, get_querystring );
  253. jq_param[ str_fragment ] = jq_param_fragment = curry( jq_param_sub, 1, get_fragment );
  254. // Section: Deparam (from string)
  255. //
  256. // Method: jQuery.deparam
  257. //
  258. // Deserialize a params string into an object, optionally coercing numbers,
  259. // booleans, null and undefined values; this method is the counterpart to the
  260. // internal jQuery.param method.
  261. //
  262. // Usage:
  263. //
  264. // > jQuery.deparam( params [, coerce ] );
  265. //
  266. // Arguments:
  267. //
  268. // params - (String) A params string to be parsed.
  269. // coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  270. // undefined to their actual value. Defaults to false if omitted.
  271. //
  272. // Returns:
  273. //
  274. // (Object) An object representing the deserialized params string.
  275. $.deparam = jq_deparam = function( params, coerce ) {
  276. var obj = {},
  277. coerce_types = { 'true': !0, 'false': !1, 'null': null };
  278. // Iterate over all name=value pairs.
  279. $.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){
  280. var param = v.split( '=' ),
  281. key = decode( param[0] ),
  282. val,
  283. cur = obj,
  284. i = 0,
  285. // If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
  286. // into its component parts.
  287. keys = key.split( '][' ),
  288. keys_last = keys.length - 1;
  289. // If the first keys part contains [ and the last ends with ], then []
  290. // are correctly balanced.
  291. if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
  292. // Remove the trailing ] from the last keys part.
  293. keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
  294. // Split first keys part into two parts on the [ and add them back onto
  295. // the beginning of the keys array.
  296. keys = keys.shift().split('[').concat( keys );
  297. keys_last = keys.length - 1;
  298. } else {
  299. // Basic 'foo' style key.
  300. keys_last = 0;
  301. }
  302. // Are we dealing with a name=value pair, or just a name?
  303. if ( param.length === 2 ) {
  304. val = decode( param[1] );
  305. // Coerce values.
  306. if ( coerce ) {
  307. val = val && !isNaN(val) ? +val // number
  308. : val === 'undefined' ? undefined // undefined
  309. : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
  310. : val; // string
  311. }
  312. if ( keys_last ) {
  313. // Complex key, build deep object structure based on a few rules:
  314. // * The 'cur' pointer starts at the object top-level.
  315. // * [] = array push (n is set to array length), [n] = array if n is
  316. // numeric, otherwise object.
  317. // * If at the last keys part, set the value.
  318. // * For each keys part, if the current level is undefined create an
  319. // object or array based on the type of the next keys part.
  320. // * Move the 'cur' pointer to the next level.
  321. // * Rinse & repeat.
  322. for ( ; i <= keys_last; i++ ) {
  323. key = keys[i] === '' ? cur.length : keys[i];
  324. cur = cur[key] = i < keys_last
  325. ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
  326. : val;
  327. }
  328. } else {
  329. // Simple key, even simpler rules, since only scalars and shallow
  330. // arrays are allowed.
  331. if ( $.isArray( obj[key] ) ) {
  332. // val is already an array, so push on the next value.
  333. obj[key].push( val );
  334. } else if ( obj[key] !== undefined ) {
  335. // val isn't an array, but since a second value has been specified,
  336. // convert val into an array.
  337. obj[key] = [ obj[key], val ];
  338. } else {
  339. // val is a scalar.
  340. obj[key] = val;
  341. }
  342. }
  343. } else if ( key ) {
  344. // No value was defined, so set something meaningful.
  345. obj[key] = coerce
  346. ? undefined
  347. : '';
  348. }
  349. });
  350. return obj;
  351. };
  352. // Method: jQuery.deparam.querystring
  353. //
  354. // Parse the query string from a URL or the current window.location,
  355. // deserializing it into an object, optionally coercing numbers, booleans,
  356. // null and undefined values.
  357. //
  358. // Usage:
  359. //
  360. // > jQuery.deparam.querystring( [ url ] [, coerce ] );
  361. //
  362. // Arguments:
  363. //
  364. // url - (String) An optional params string or URL containing query string
  365. // params to be parsed. If url is omitted, the current window.location
  366. // is used.
  367. // coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  368. // undefined to their actual value. Defaults to false if omitted.
  369. //
  370. // Returns:
  371. //
  372. // (Object) An object representing the deserialized params string.
  373. // Method: jQuery.deparam.fragment
  374. //
  375. // Parse the fragment (hash) from a URL or the current window.location,
  376. // deserializing it into an object, optionally coercing numbers, booleans,
  377. // null and undefined values.
  378. //
  379. // Usage:
  380. //
  381. // > jQuery.deparam.fragment( [ url ] [, coerce ] );
  382. //
  383. // Arguments:
  384. //
  385. // url - (String) An optional params string or URL containing fragment (hash)
  386. // params to be parsed. If url is omitted, the current window.location
  387. // is used.
  388. // coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  389. // undefined to their actual value. Defaults to false if omitted.
  390. //
  391. // Returns:
  392. //
  393. // (Object) An object representing the deserialized params string.
  394. function jq_deparam_sub( is_fragment, url_or_params, coerce ) {
  395. if ( url_or_params === undefined || typeof url_or_params === 'boolean' ) {
  396. // url_or_params not specified.
  397. coerce = url_or_params;
  398. url_or_params = jq_param[ is_fragment ? str_fragment : str_querystring ]();
  399. } else {
  400. url_or_params = is_string( url_or_params )
  401. ? url_or_params.replace( is_fragment ? re_trim_fragment : re_trim_querystring, '' )
  402. : url_or_params;
  403. }
  404. return jq_deparam( url_or_params, coerce );
  405. };
  406. jq_deparam[ str_querystring ] = curry( jq_deparam_sub, 0 );
  407. jq_deparam[ str_fragment ] = jq_deparam_fragment = curry( jq_deparam_sub, 1 );
  408. // Section: Element manipulation
  409. //
  410. // Method: jQuery.elemUrlAttr
  411. //
  412. // Get the internal "Default URL attribute per tag" list, or augment the list
  413. // with additional tag-attribute pairs, in case the defaults are insufficient.
  414. //
  415. // In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
  416. // is used to determine which attribute contains the URL to be modified, if
  417. // an "attr" param is not specified.
  418. //
  419. // Default Tag-Attribute List:
  420. //
  421. // a - href
  422. // base - href
  423. // iframe - src
  424. // img - src
  425. // input - src
  426. // form - action
  427. // link - href
  428. // script - src
  429. //
  430. // Usage:
  431. //
  432. // > jQuery.elemUrlAttr( [ tag_attr ] );
  433. //
  434. // Arguments:
  435. //
  436. // tag_attr - (Object) An object containing a list of tag names and their
  437. // associated default attribute names in the format { tag: 'attr', ... } to
  438. // be merged into the internal tag-attribute list.
  439. //
  440. // Returns:
  441. //
  442. // (Object) An object containing all stored tag-attribute values.
  443. // Only define function and set defaults if function doesn't already exist, as
  444. // the urlInternal plugin will provide this method as well.
  445. $[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) {
  446. return $.extend( elemUrlAttr_cache, obj );
  447. })({
  448. a: str_href,
  449. base: str_href,
  450. iframe: str_src,
  451. img: str_src,
  452. input: str_src,
  453. form: 'action',
  454. link: str_href,
  455. script: str_src
  456. });
  457. jq_elemUrlAttr = $[ str_elemUrlAttr ];
  458. // Method: jQuery.fn.querystring
  459. //
  460. // Update URL attribute in one or more elements, merging the current URL (with
  461. // or without pre-existing query string params) plus any params object or
  462. // string into a new URL, which is then set into that attribute. Like
  463. // <jQuery.param.querystring (build url)>, but for all elements in a jQuery
  464. // collection.
  465. //
  466. // Usage:
  467. //
  468. // > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
  469. //
  470. // Arguments:
  471. //
  472. // attr - (String) Optional name of an attribute that will contain a URL to
  473. // merge params or url into. See <jQuery.elemUrlAttr> for a list of default
  474. // attributes.
  475. // params - (Object) A params object to be merged into the URL attribute.
  476. // params - (String) A URL containing query string params, or params string
  477. // to be merged into the URL attribute.
  478. // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  479. // specified, and is as-follows:
  480. //
  481. // * 0: params in the params argument will override any params in attr URL.
  482. // * 1: any params in attr URL will override params in the params argument.
  483. // * 2: params argument will completely replace any query string in attr
  484. // URL.
  485. //
  486. // Returns:
  487. //
  488. // (jQuery) The initial jQuery collection of elements, but with modified URL
  489. // attribute values.
  490. // Method: jQuery.fn.fragment
  491. //
  492. // Update URL attribute in one or more elements, merging the current URL (with
  493. // or without pre-existing fragment/hash params) plus any params object or
  494. // string into a new URL, which is then set into that attribute. Like
  495. // <jQuery.param.fragment (build url)>, but for all elements in a jQuery
  496. // collection.
  497. //
  498. // Usage:
  499. //
  500. // > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
  501. //
  502. // Arguments:
  503. //
  504. // attr - (String) Optional name of an attribute that will contain a URL to
  505. // merge params into. See <jQuery.elemUrlAttr> for a list of default
  506. // attributes.
  507. // params - (Object) A params object to be merged into the URL attribute.
  508. // params - (String) A URL containing fragment (hash) params, or params
  509. // string to be merged into the URL attribute.
  510. // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  511. // specified, and is as-follows:
  512. //
  513. // * 0: params in the params argument will override any params in attr URL.
  514. // * 1: any params in attr URL will override params in the params argument.
  515. // * 2: params argument will completely replace any fragment (hash) in attr
  516. // URL.
  517. //
  518. // Returns:
  519. //
  520. // (jQuery) The initial jQuery collection of elements, but with modified URL
  521. // attribute values.
  522. function jq_fn_sub( mode, force_attr, params, merge_mode ) {
  523. if ( !is_string( params ) && typeof params !== 'object' ) {
  524. // force_attr not specified.
  525. merge_mode = params;
  526. params = force_attr;
  527. force_attr = undefined;
  528. }
  529. return this.each(function(){
  530. var that = $(this),
  531. // Get attribute specified, or default specified via $.elemUrlAttr.
  532. attr = force_attr || jq_elemUrlAttr()[ ( this.nodeName || '' ).toLowerCase() ] || '',
  533. // Get URL value.
  534. url = attr && that.attr( attr ) || '';
  535. // Update attribute with new URL.
  536. that.attr( attr, jq_param[ mode ]( url, params, merge_mode ) );
  537. });
  538. };
  539. $.fn[ str_querystring ] = curry( jq_fn_sub, str_querystring );
  540. $.fn[ str_fragment ] = curry( jq_fn_sub, str_fragment );
  541. // Section: History, hashchange event
  542. //
  543. // Method: jQuery.bbq.pushState
  544. //
  545. // Adds a 'state' into the browser history at the current position, setting
  546. // location.hash and triggering any bound <window.onhashchange> event
  547. // callbacks (provided the new state is different than the previous state).
  548. //
  549. // If no arguments are passed, an empty state is created, which is just a
  550. // shortcut for jQuery.bbq.pushState( {}, 2 ).
  551. //
  552. // Usage:
  553. //
  554. // > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
  555. //
  556. // Arguments:
  557. //
  558. // params - (String) A serialized params string or a hash string beginning
  559. // with # to merge into location.hash.
  560. // params - (Object) A params object to merge into location.hash.
  561. // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  562. // specified (unless a hash string beginning with # is specified, in which
  563. // case merge behavior defaults to 2), and is as-follows:
  564. //
  565. // * 0: params in the params argument will override any params in the
  566. // current state.
  567. // * 1: any params in the current state will override params in the params
  568. // argument.
  569. // * 2: params argument will completely replace current state.
  570. //
  571. // Returns:
  572. //
  573. // Nothing.
  574. //
  575. // Additional Notes:
  576. //
  577. // * Setting an empty state may cause the browser to scroll.
  578. // * Unlike the fragment and querystring methods, if a hash string beginning
  579. // with # is specified as the params agrument, merge_mode defaults to 2.
  580. jq_bbq.pushState = jq_bbq_pushState = function( params, merge_mode ) {
  581. if ( is_string( params ) && /^#/.test( params ) && merge_mode === undefined ) {
  582. // Params string begins with # and merge_mode not specified, so completely
  583. // overwrite window.location.hash.
  584. merge_mode = 2;
  585. }
  586. var has_args = params !== undefined,
  587. // Merge params into window.location using $.param.fragment.
  588. url = jq_param_fragment( loc[ str_href ], has_args ? params : {}, has_args ? merge_mode : 2 );
  589. // Set new window.location.href. If hash is empty, use just # to prevent
  590. // browser from reloading the page. Note that Safari 3 & Chrome barf on
  591. // location.hash = '#'.
  592. loc[ str_href ] = url + ( /#/.test( url ) ? '' : '#' );
  593. };
  594. // Method: jQuery.bbq.getState
  595. //
  596. // Retrieves the current 'state' from the browser history, parsing
  597. // location.hash for a specific key or returning an object containing the
  598. // entire state, optionally coercing numbers, booleans, null and undefined
  599. // values.
  600. //
  601. // Usage:
  602. //
  603. // > jQuery.bbq.getState( [ key ] [, coerce ] );
  604. //
  605. // Arguments:
  606. //
  607. // key - (String) An optional state key for which to return a value.
  608. // coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  609. // undefined to their actual value. Defaults to false.
  610. //
  611. // Returns:
  612. //
  613. // (Anything) If key is passed, returns the value corresponding with that key
  614. // in the location.hash 'state', or undefined. If not, an object
  615. // representing the entire 'state' is returned.
  616. jq_bbq.getState = function( key, coerce ) {
  617. return key === undefined || typeof key === 'boolean'
  618. ? jq_deparam_fragment( key ) // 'key' really means 'coerce' here
  619. : jq_deparam_fragment( coerce )[ key ];
  620. };
  621. // Property: jQuery.bbq.pollDelay
  622. //
  623. // The numeric interval (in milliseconds) at which the <window.onhashchange>
  624. // polling loop executes. Defaults to 100.
  625. jq_bbq.pollDelay = 100;
  626. // Event: window.onhashchange
  627. //
  628. // Fired when window.location.hash changes. In browsers that support it, the
  629. // native window.onhashchange event is used (IE8, FF3.6), otherwise a polling
  630. // loop is initialized, running every <jQuery.bbq.pollDelay> milliseconds to
  631. // see if the hash has changed. In IE 6 and 7, a hidden IFRAME is created
  632. // to allow hash-based history to work.
  633. //
  634. // Usage in 1.4pre and newer:
  635. //
  636. // In 1.4pre and newer, the event object that is passed into the callback is
  637. // augmented with an additional e.fragment property that contains the current
  638. // document location.hash state as a string, as well as an e.getState method.
  639. //
  640. // e.fragment is equivalent to the output of <jQuery.param.fragment>, and
  641. // e.getState() is equivalent to <jQuery.bbq.getState>, except that they refer
  642. // to the event-specific state value stored in the event object, instead of
  643. // the current window.location, allowing the event object to be referenced
  644. // later, even if window.location has changed.
  645. //
  646. // > $(window).bind( 'hashchange', function(e) {
  647. // > var hash_str = e.fragment,
  648. // > param_obj = e.getState(),
  649. // > param_val = e.getState( 'param_name' ),
  650. // > param_val_coerced = e.getState( 'param_name', true );
  651. // > ...
  652. // > });
  653. //
  654. // Usage in 1.3.2:
  655. //
  656. // In 1.3.2, the event object is unable to be augmented as in 1.4pre+, so the
  657. // fragment state isn't bound to the event object and must instead be parsed
  658. // using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
  659. //
  660. // > $(window).bind( 'hashchange', function(e) {
  661. // > var hash_str = $.param.fragment(),
  662. // > param_obj = $.bbq.getState(),
  663. // > param_val = $.bbq.getState( 'param_name' ),
  664. // > param_val_coerced = $.bbq.getState( 'param_name', true );
  665. // > ...
  666. // > });
  667. //
  668. // Additional Notes:
  669. //
  670. // * The polling loop and iframe are not created until at least one callback
  671. // is actually bound to 'hashchange'.
  672. // * If you need the bound callback(s) to execute immediately, in cases where
  673. // the page 'state' exists on page load (via bookmark or page refresh, for
  674. // example) use $(window).trigger( 'hashchange' );
  675. $.event.special[ str_hashchange ] = {
  676. // Called only when the first 'hashchange' event is bound to window.
  677. setup: function() {
  678. // If window.onhashchange is supported natively, there's nothing to do..
  679. if ( supports_onhashchange ) { return false; }
  680. // Otherwise, we need to create our own. And we don't want to call this
  681. // until the user binds to the event, just in case they never do, since it
  682. // will create a polling loop and possibly even a hidden IFRAME.
  683. fake_onhashchange.start();
  684. },
  685. // Called only when the last 'hashchange' event is unbound from window.
  686. teardown: function() {
  687. // If window.onhashchange is supported natively, there's nothing to do..
  688. if ( supports_onhashchange ) { return false; }
  689. // Otherwise, we need to stop ours (if possible).
  690. fake_onhashchange.stop();
  691. },
  692. // Augmenting the event object with the .fragment property and .getState
  693. // method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
  694. // work, but the event won't be augmented)
  695. add: function( handler, data, namespaces ) {
  696. return function(e) {
  697. // e.fragment is set to the value of location.hash (with any leading #
  698. // removed) at the time the event is triggered.
  699. var hash = e[ str_fragment ] = jq_param_fragment();
  700. // e.getState() works just like $.bbq.getState(), but uses the
  701. // e.fragment property stored on the event object.
  702. e.getState = function( key, coerce ) {
  703. return key === undefined || typeof key === 'boolean'
  704. ? jq_deparam( hash, key ) // 'key' really means 'coerce' here
  705. : jq_deparam( hash, coerce )[ key ];
  706. };
  707. handler.apply( this, arguments );
  708. };
  709. }
  710. };
  711. // fake_onhashchange does all the work of triggering the window.onhashchange
  712. // event for browsers that don't natively support it, including creating a
  713. // polling loop to watch for hash changes and in IE 6/7 creating a hidden
  714. // IFRAME to enable back and forward.
  715. fake_onhashchange = (function(){
  716. var self = {},
  717. timeout_id,
  718. iframe,
  719. set_history,
  720. get_history;
  721. // Initialize. In IE 6/7, creates a hidden IFRAME for history handling.
  722. function init(){
  723. // Most browsers don't need special methods here..
  724. set_history = get_history = function(val){ return val; };
  725. // But IE6/7 do!
  726. if ( is_old_ie ) {
  727. // Create hidden IFRAME at the end of the body.
  728. iframe = $('<iframe src="javascript:0"/>').hide().appendTo( 'body' )[0].contentWindow;
  729. // Get history by looking at the hidden IFRAME's location.hash.
  730. get_history = function() {
  731. return get_fragment( iframe.document.location[ str_href ] );
  732. };
  733. // Set a new history item by opening and then closing the IFRAME
  734. // document, *then* setting its location.hash.
  735. set_history = function( hash, history_hash ) {
  736. if ( hash !== history_hash ) {
  737. var doc = iframe.document;
  738. doc.open().close();
  739. doc.location.hash = '#' + hash;
  740. }
  741. };
  742. // Set initial history.
  743. set_history( jq_param_fragment() );
  744. }
  745. };
  746. // Start the polling loop.
  747. self.start = function() {
  748. // Polling loop is already running!
  749. if ( timeout_id ) { return; }
  750. // Remember the initial hash so it doesn't get triggered immediately.
  751. var last_hash = jq_param_fragment();
  752. // Initialize if not yet initialized.
  753. set_history || init();
  754. // This polling loop checks every $.bbq.pollDelay milliseconds to see if
  755. // location.hash has changed, and triggers the 'hashchange' event on
  756. // window when necessary.
  757. (function loopy(){
  758. var hash = jq_param_fragment(),
  759. history_hash = get_history( last_hash );
  760. if ( hash !== last_hash ) {
  761. set_history( last_hash = hash, history_hash );
  762. $(window).trigger( str_hashchange );
  763. } else if ( history_hash !== last_hash ) {
  764. jq_bbq_pushState( '#' + history_hash );
  765. }
  766. timeout_id = setTimeout( loopy, jq_bbq.pollDelay );
  767. })();
  768. };
  769. // Stop the polling loop, but only if an IE6/7 IFRAME wasn't created. In
  770. // that case, even if there are no longer any bound event handlers, the
  771. // polling loop is still necessary for back/next to work at all!
  772. self.stop = function() {
  773. if ( !iframe ) {
  774. timeout_id && clearTimeout( timeout_id );
  775. timeout_id = 0;
  776. }
  777. };
  778. return self;
  779. })();
  780. })(jQuery,this);