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