PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

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

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