jquery.ba-bbq.js 47 KB

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