PageRenderTime 63ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

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