fume 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1091 @@
1
+ /*!
2
+ * URL Utils - v1.11 - 9/10/2009
3
+ * http://benalman.com/
4
+ *
5
+ * Copyright (c) 2009 "Cowboy" Ben Alman
6
+ * Licensed under the MIT license
7
+ * http://benalman.com/about/license/
8
+ */
9
+
10
+ // Script: URL Utils
11
+ //
12
+ // Version: 1.11, Date: 9/10/2009
13
+ //
14
+ // Tested with jQuery 1.3.2 in Internet Explorer 6-8, Firefox 3-3.6a,
15
+ // Safari 3-4, Chrome, Opera 9.6.
16
+ //
17
+ // Home - http://benalman.com/projects/jquery-url-utils-plugin/
18
+ // Source - http://benalman.com/code/javascript/jquery/jquery.ba-url.js
19
+ // (Minified) - http://benalman.com/code/javascript/jquery/jquery.ba-url.min.js (3.9kb)
20
+ // Unit Tests - http://benalman.com/code/unittest/url.html
21
+ //
22
+ // About: License
23
+ //
24
+ // Copyright (c) 2009 "Cowboy" Ben Alman
25
+ //
26
+ // Licensed under the MIT license
27
+ //
28
+ // http://benalman.com/about/license/
29
+ //
30
+ // About: Revision History
31
+ //
32
+ // 1.11 - Minor bugfix for Firefox 3.0+
33
+ // 1.1 - Added support for onhashchange event
34
+ // 1.0 - Initial release
35
+
36
+ (function($) {
37
+ '$:nomunge'; // Used by YUI compressor.
38
+
39
+ var url_regexp,
40
+ tag_attributes = {},
41
+
42
+ // A few constants.
43
+ undefined,
44
+ window = this,
45
+ TRUE = true,
46
+ FALSE = false,
47
+ has_onhashchange = 'onhashchange' in window,
48
+
49
+ // Some convenient shortcuts.
50
+ aps = Array.prototype.slice,
51
+ loc = document.location,
52
+
53
+ // Internal plugin method references.
54
+ p_urlTagAttrList,
55
+ p_urlInternalHost,
56
+ p_urlInternalRegExp,
57
+ p_isUrlInternal,
58
+ p_isUrlExternal,
59
+ p_urlFilter,
60
+ p_urlFilterSelector,
61
+ p_setFragment,
62
+
63
+ // Reused internal strings.
64
+ str_urlInternal = 'urlInternal',
65
+ str_urlExternal = 'urlExternal',
66
+ str_queryString = 'queryString',
67
+ str_fragment = 'fragment',
68
+ str_update = 'update',
69
+ str_passQueryString = 'passQueryString',
70
+ str_passFragment = 'passFragment',
71
+ str_fragmentChange = 'fragmentChange',
72
+ str_hashchange = 'hashchange.' + str_fragmentChange,
73
+
74
+ // fragmentChange event handler
75
+ timeout_id,
76
+ last_fragment;
77
+
78
+
79
+ // A few commonly used bits, broken out to help reduce minified file size.
80
+
81
+ function is_string( arg ) {
82
+ return typeof arg === 'string';
83
+ };
84
+
85
+ function is_object( arg ) {
86
+ return typeof arg === 'object';
87
+ };
88
+
89
+ function curry() {
90
+ var args = aps.call( arguments ),
91
+ func = args.shift();
92
+
93
+ return function() {
94
+ return func.apply( this, args.concat( aps.call( arguments ) ) );
95
+ };
96
+ };
97
+
98
+ // Work-around for an annoying Firefox bug where document.location.hash gets
99
+ // urldecoded by default.
100
+
101
+ function get_fragment() {
102
+ return loc.href.replace( /^[^#]*#?/, '' );
103
+ };
104
+
105
+
106
+ // Method: jQuery.urlTagAttrList
107
+ //
108
+ // Get the internal "Default URL attribute per tag" list, or augment the list
109
+ // with additional tag-attribute pairs, in case the defaults are insufficient.
110
+ //
111
+ // This list contains the default attributes for the <jQuery.fn.urlInternal>
112
+ // and <jQuery.fn.urlExternal> methods, as well as the <:urlInternal> and
113
+ // <:urlExternal> selector filters, to determine which URL will be tested for
114
+ // internal- or external-ness. In the <jQuery.fn.queryString>,
115
+ // <jQuery.fn.fragment>, <jQuery.fn.passQueryString> and
116
+ // <jQuery.fn.passFragment> methods, this list is used to determine which URL
117
+ // will be modified.
118
+ //
119
+ // Default List:
120
+ //
121
+ // TAG - URL ATTRIBUTE
122
+ // a - href
123
+ // img - src
124
+ // form - action
125
+ // base - href
126
+ // script - src
127
+ // iframe - src
128
+ // link - href
129
+ //
130
+ // Usage:
131
+ //
132
+ // jQuery.urlTagAttrList( [ tag_attr_obj ] ); - -
133
+ //
134
+ // Arguments:
135
+ //
136
+ // tag_attr_obj - (Object) An list of tag names and associated default
137
+ // attribute names in the format { tag: 'attr', tag: 'attr', ... }.
138
+ //
139
+ // Returns:
140
+ //
141
+ // (Object) The current internal "Default URL attribute per tag" list.
142
+
143
+ $.urlTagAttrList = p_urlTagAttrList = function( attr_obj ) {
144
+ return $.extend( tag_attributes, attr_obj );
145
+ };
146
+
147
+ // Initialize the tag_attributes object with some reasonable defaults.
148
+
149
+ p_urlTagAttrList({
150
+ a: 'href',
151
+ img: 'src',
152
+ form: 'action',
153
+ base: 'href',
154
+ script: 'src',
155
+ iframe: 'src',
156
+ link: 'href'
157
+ });
158
+
159
+ // Get the default attribute for the specified DOM element, if it exists.
160
+
161
+ function get_attr( elem ) {
162
+ var n = elem.nodeName;
163
+ return n ? tag_attributes[ n.toLowerCase() ] : '';
164
+ };
165
+
166
+
167
+ // Section: URL Internal / External
168
+ //
169
+ // Method: jQuery.urlInternalHost
170
+ //
171
+ // Constructs the regular expression that matches an absolute-but-internal
172
+ // URL from the current page's protocol, hostname and port, allowing for
173
+ // an optional hostname (if specified). For example, if the current page is
174
+ // http://benalman.com/anything, specifying an alt_hostname of 'www' would
175
+ // yield this pattern:
176
+ //
177
+ // > /^http:\/\/(?:www\.)?benalman.com\//i
178
+ //
179
+ // This pattern will match URLs beginning with both http://benalman.com/ and
180
+ // http://www.benalman.com/. Specifying an empty alt_hostname will disable any
181
+ // alt-hostname matching.
182
+ //
183
+ // Note that the plugin is initialized by default to an alt_hostname of 'www'.
184
+ // Should you need more control, <jQuery.urlInternalRegExp> may be used to
185
+ // completely override this absolute-but-internal matching pattern.
186
+ //
187
+ // Usage:
188
+ //
189
+ // jQuery.urlInternalHost( [ alt_hostname ] ); - -
190
+ //
191
+ // Arguments:
192
+ //
193
+ // alt_hostname - (String) An optional alternate hostname to use when testing
194
+ // URL absolute-but-internal-ness.
195
+ //
196
+ // Returns:
197
+ //
198
+ // (RegExp) The absolute-but-internal pattern, as a RegExp.
199
+
200
+ $.urlInternalHost = p_urlInternalHost = function( alt_hostname ) {
201
+ alt_hostname = alt_hostname
202
+ ? '(?:' + alt_hostname + '\\.)?'
203
+ : '';
204
+
205
+ var re = new RegExp( '^' + alt_hostname + '(.*)', 'i' ),
206
+ pattern = '^' + loc.protocol + '//'
207
+ + loc.hostname.replace(re, alt_hostname + '$1')
208
+ //+ 'benalman.com'.replace(re, alt_hostname + '$1') // For testing on stage.benalman.com
209
+ + (loc.port ? ':' + loc.port : '') + '/';
210
+
211
+ return p_urlInternalRegExp( pattern );
212
+ };
213
+
214
+
215
+ // Method: jQuery.urlInternalRegExp
216
+ //
217
+ // Set or get the regular expression that matches an absolute-but-internal
218
+ // URL.
219
+ //
220
+ // Usage:
221
+ //
222
+ // jQuery.urlInternalRegExp( [ re ] ); - -
223
+ //
224
+ // Arguments:
225
+ //
226
+ // re - (String or RegExp) The regular expression pattern. If not passed,
227
+ // nothing is changed.
228
+ //
229
+ // Returns:
230
+ //
231
+ // (RegExp) The absolute-but-internal pattern, as a RegExp.
232
+
233
+ $.urlInternalRegExp = p_urlInternalRegExp = function( re ) {
234
+ if ( re ) {
235
+ url_regexp = is_string( re )
236
+ ? new RegExp( re, 'i' )
237
+ : re;
238
+ }
239
+
240
+ return url_regexp;
241
+ };
242
+
243
+
244
+ // Initialize url_regexp with a reasonable default.
245
+
246
+ p_urlInternalHost( 'www' );
247
+
248
+
249
+ // Method: jQuery.isUrlInternal
250
+ //
251
+ // Test whether or not a URL is internal. Non-navigating URLs (ie. #anchor,
252
+ // javascript:, mailto:, news:, tel:, im: or non-http/-https protocol://
253
+ // links) are not considered internal.
254
+ //
255
+ // Usage:
256
+ //
257
+ // jQuery.isUrlInternal( url ); - -
258
+ //
259
+ // Arguments:
260
+ //
261
+ // url - (String) a URL to test the internal-ness of.
262
+ //
263
+ // Returns:
264
+ //
265
+ // (Boolean) true if the URL is internal, false if external, or undefined if
266
+ // the URL is non-navigating.
267
+
268
+ $.isUrlInternal = p_isUrlInternal = function( url ) {
269
+
270
+ // non-navigating: url is nonexistent
271
+ if ( !url ) { return undefined; }
272
+
273
+ // internal: url is absolute-but-internal (see $.urlInternalRegExp)
274
+ if ( url_regexp.test(url) ) { return TRUE; }
275
+
276
+ // external: url is absolute (begins with http:// or https://)
277
+ if ( /^https?:\/\//i.test(url) ) { return FALSE; }
278
+
279
+ // non-navigating: url begins with # or scheme:
280
+ if ( /^(?:#|[a-z\d.-]+:)/i.test(url) ) { return undefined; }
281
+
282
+ return TRUE;
283
+ };
284
+
285
+
286
+ // Method: jQuery.isUrlExternal
287
+ //
288
+ // Test whether or not a URL is external. Non-navigating URLs (ie. #anchor,
289
+ // mailto:, javascript:, or non-http/-https protocol:// links) are not
290
+ // considered external.
291
+ //
292
+ // Usage:
293
+ //
294
+ // jQuery.isUrlExternal( url ); - -
295
+ //
296
+ // Arguments:
297
+ //
298
+ // url - (String) a URL to test the external-ness of.
299
+ //
300
+ // Returns:
301
+ //
302
+ // (Boolean) true if the URL is external, false if internal, or undefined if
303
+ // the URL is non-navigating.
304
+
305
+ $.isUrlExternal = p_isUrlExternal = function( url ) {
306
+ var result = p_isUrlInternal( url );
307
+
308
+ return typeof result === 'boolean'
309
+ ? !result
310
+ : result;
311
+ };
312
+
313
+
314
+ // Method: jQuery.fn.urlInternal
315
+ //
316
+ // Filter a jQuery collection of elements, returning only elements that have
317
+ // an internal URL (as determined by <jQuery.isUrlInternal>). If URL cannot
318
+ // be determined, remove the element from the collection.
319
+ //
320
+ // Usage:
321
+ //
322
+ // jQuery('selector').urlInternal( [ attr ] ); - -
323
+ //
324
+ // Arguments:
325
+ //
326
+ // attr - (String) Optional name of an attribute that will contain a URL to
327
+ // test internal-ness against. See <jQuery.urlTagAttrList> for a list of
328
+ // default attributes.
329
+ //
330
+ // Returns:
331
+ //
332
+ // (jQuery) A filtered jQuery collection of elements.
333
+
334
+ // Method: jQuery.fn.urlExternal
335
+ //
336
+ // Filter a jQuery collection of elements, returning only elements that have
337
+ // an external URL (as determined by <jQuery.isUrlExternal>). If URL cannot
338
+ // be determined, remove the element from the collection.
339
+ //
340
+ // Usage:
341
+ //
342
+ // jQuery('selector').urlExternal( [ attr ] ); - -
343
+ //
344
+ // Arguments:
345
+ //
346
+ // attr - (String) Optional name of an attribute that will contain a URL to
347
+ // test external-ness against. See <jQuery.urlTagAttrList> for a list of
348
+ // default attributes.
349
+ //
350
+ // Returns:
351
+ //
352
+ // (jQuery) A filtered jQuery collection of elements.
353
+
354
+ p_urlFilter = function( selector, attr ) {
355
+ return this.filter( ':' + selector + (attr ? '(' + attr + ')' : '') );
356
+ };
357
+
358
+ $.fn[str_urlInternal] = curry( p_urlFilter, str_urlInternal );
359
+ $.fn[str_urlExternal] = curry( p_urlFilter, str_urlExternal );
360
+
361
+
362
+ // Method: :urlInternal
363
+ //
364
+ // Filter a jQuery collection of elements, returning only elements that have
365
+ // an internal URL (as determined by <jQuery.isUrlInternal>). If URL cannot
366
+ // be determined, remove the element from the collection.
367
+ //
368
+ // Usage:
369
+ //
370
+ // jQuery('selector').filter(':urlInternal'); - -
371
+ // jQuery('selector').filter(':urlInternal(attr)'); - -
372
+ //
373
+ // Arguments:
374
+ //
375
+ // attr - (String) Optional name of an attribute that will contain a URL to
376
+ // test internal-ness against. See <jQuery.urlTagAttrList> for a list of
377
+ // default attributes.
378
+ //
379
+ // Returns:
380
+ //
381
+ // (jQuery) A filtered jQuery collection of elements.
382
+
383
+ // Method: :urlExternal
384
+ //
385
+ // Filter a jQuery collection of elements, returning only elements that have
386
+ // an external URL (as determined by <jQuery.isUrlExternal>). If URL cannot
387
+ // be determined, remove the element from the collection.
388
+ //
389
+ // Usage:
390
+ //
391
+ // jQuery('selector').filter(':urlExternal'); - -
392
+ // jQuery('selector').filter(':urlExternal(attr)'); - -
393
+ //
394
+ // Arguments:
395
+ //
396
+ // attr - (String) Optional name of an attribute that will contain a URL to
397
+ // test external-ness against. See <jQuery.urlTagAttrList> for a list of
398
+ // default attributes.
399
+ //
400
+ // Returns:
401
+ //
402
+ // (jQuery) A filtered jQuery collection of elements.
403
+
404
+ p_urlFilterSelector = function( func, elem, i, match ) {
405
+ var a = match[3] || get_attr( elem );
406
+
407
+ return a ? !!func( $(elem).attr(a) ) : FALSE;
408
+ };
409
+
410
+ $.expr[':'][str_urlInternal] = curry( p_urlFilterSelector, p_isUrlInternal );
411
+ $.expr[':'][str_urlExternal] = curry( p_urlFilterSelector, p_isUrlExternal );
412
+
413
+
414
+ // Section: URL Query String / Fragment
415
+ //
416
+ // Method: jQuery.queryString (deserialize)
417
+ //
418
+ // Deserialize any params string or the current document's query string into
419
+ // an object. Multiple sequential values will be converted into an array, ie.
420
+ // 'n=a&n=b&n=c' -> { n: ['a', 'b', 'c'] }.
421
+ //
422
+ // Usage:
423
+ //
424
+ // jQuery.queryString( [ params_str ] [ , cast_values ] ); - -
425
+ //
426
+ // Arguments:
427
+ //
428
+ // params_str - (String) A stand-alone params string or a URL containing
429
+ // params to be deserialized. If omitted, defaults to the current page
430
+ // query string (document.location.search).
431
+ // cast_values - (Boolean) If true, converts any numbers or true, false,
432
+ // null, and undefined to their appropriate literal. Defaults to false.
433
+ //
434
+ // Returns:
435
+ //
436
+ // (Object) The deserialized params string.
437
+
438
+ // Method: jQuery.queryString (serialize)
439
+ //
440
+ // Serialize an object into a params string. Arrays will be converted to
441
+ // multiple sequential values, ie. { n: ['a', 'b', 'c'] } -> 'n=a&n=b&n=c'
442
+ // (this method is just a wrapper for jQuery.param).
443
+ //
444
+ // Usage:
445
+ //
446
+ // jQuery.queryString( params_obj ); - -
447
+ //
448
+ // Arguments:
449
+ //
450
+ // params_obj - (Object) An object to be serialized. Note: A JSON string (or
451
+ // some analog) should be used for deep structures, since nested data
452
+ // structures other than shallow arrays can't be serialized into a query
453
+ // string in a meaningful way.
454
+ //
455
+ // Returns:
456
+ //
457
+ // (String) A params string with urlencoded data in the format 'a=b&c=d&e=f'.
458
+
459
+ // Method: jQuery.queryString (build url)
460
+ //
461
+ // Merge a URL (with or without a pre-existing params) plus any object or
462
+ // params string into a new URL.
463
+ //
464
+ // Usage:
465
+ //
466
+ // jQuery.queryString( url, params [ , merge_mode ] ); - -
467
+ //
468
+ // Arguments:
469
+ //
470
+ // url - (String) A valid URL, optionally containing a query string and/or
471
+ // #anchor, or fragment.
472
+ // params - (String or Object) Either a serialized params string or a data
473
+ // object to be merged into the URL.
474
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
475
+ // specified, and is as-follows:
476
+ //
477
+ // * 0: params argument will override any params in url.
478
+ // * 1: any params in url will override params argument.
479
+ // * 2: params argument will completely replace any params in url.
480
+ //
481
+ // Returns:
482
+ //
483
+ // (String) A URL with urlencoded params in the format 'url?a=b&c=d&e=f'.
484
+
485
+ // Method: jQuery.fragment (deserialize)
486
+ //
487
+ // Deserialize any params string or the current document's fragment into an
488
+ // object. Multiple sequential values will be converted into an array, ie.
489
+ // 'n=a&n=b&n=c' -> { n: ['a', 'b', 'c'] }.
490
+ //
491
+ // Usage:
492
+ //
493
+ // jQuery.fragment( [ params_str ] [ , cast_values ] ); - -
494
+ //
495
+ // Arguments:
496
+ //
497
+ // params_str - (String) A stand-alone params string or a URL containing
498
+ // params to be deserialized. If omitted, defaults to the current page
499
+ // fragment (document.location.hash).
500
+ // cast_values - (Boolean) If true, converts any numbers or true, false,
501
+ // null, and undefined to their appropriate literal. Defaults to false.
502
+ //
503
+ // Returns:
504
+ //
505
+ // (Object) The deserialized params string.
506
+
507
+ // Method: jQuery.fragment (serialize)
508
+ //
509
+ // Serialize an object into a params string. Arrays will be converted to
510
+ // multiple sequential values, ie. { n: ['a', 'b', 'c'] } -> 'n=a&n=b&n=c'
511
+ // (this method is just a wrapper for jQuery.param).
512
+ //
513
+ // Usage:
514
+ //
515
+ // jQuery.fragment( params_obj ); - -
516
+ //
517
+ // Arguments:
518
+ //
519
+ // params_obj - (Object) An object to be serialized. Note: A JSON string (or
520
+ // some analog) should be used for deep structures, since nested data
521
+ // structures other than shallow arrays can't be serialized into a query
522
+ // string in a meaningful way.
523
+ //
524
+ // Returns:
525
+ //
526
+ // (String) A params string with urlencoded data in the format 'a=b&c=d&e=f'.
527
+
528
+ // Method: jQuery.fragment (build url)
529
+ //
530
+ // Merge a URL (with or without a pre-existing params) plus any object or
531
+ // params string into a new URL.
532
+ //
533
+ // Usage:
534
+ //
535
+ // jQuery.fragment( url, params [ , merge_mode ] ); - -
536
+ //
537
+ // Arguments:
538
+ //
539
+ // url - (String) A valid URL, optionally containing a query string and/or
540
+ // #anchor, or fragment.
541
+ // params - (String or Object) Either a serialized params string or a data
542
+ // object to be merged into the URL.
543
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
544
+ // specified, and is as-follows:
545
+ //
546
+ // * 0: params argument will override any params in url.
547
+ // * 1: any params in url will override params argument.
548
+ // * 2: params argument will completely replace any params in url.
549
+ //
550
+ // Returns:
551
+ //
552
+ // (String) A URL with urlencoded params in the format 'url#a=b&c=d&e=f'.
553
+
554
+ function p_params( fragment_mode, arg0, arg1, arg2 ) {
555
+ var params;
556
+
557
+ if ( is_string(arg1) || is_object(arg1) ) {
558
+ // Build URL.
559
+ return build_url( arg0, arg1, arg2, fragment_mode );
560
+
561
+ } else if ( is_object(arg0) ) {
562
+ // Serialize.
563
+ return $.param( arg0 );
564
+
565
+ } else if ( is_string(arg0) ) {
566
+ // Deserialize.
567
+ return deserialize( arg0, arg1, fragment_mode );
568
+
569
+ } else {
570
+ // Deserialize document query string / fragment.
571
+ params = fragment_mode
572
+ ? get_fragment()
573
+ : loc.search;
574
+
575
+ return deserialize( params, arg0, fragment_mode );
576
+ }
577
+ };
578
+
579
+ $[str_queryString] = curry( p_params, 0 );
580
+ $[str_fragment] = curry( p_params, 1 );
581
+
582
+ // Method: jQuery.fn.queryString
583
+ //
584
+ // Update URL attribute in one or more elements, merging the current URL (with
585
+ // or without pre-existing params) plus any object or params string into a new
586
+ // URL, which is then set into that attribute. Like <jQuery.queryString (build
587
+ // url)>, but for all elements in a jQuery collection.
588
+ //
589
+ // Usage:
590
+ //
591
+ // jQuery('selector').queryString( [ attr, ] params [ , merge_mode ] ); - -
592
+ //
593
+ // Arguments:
594
+ //
595
+ // attr - (String) Optional name of an attribute that will contain a URL to
596
+ // merge params into. See <jQuery.urlTagAttrList> for a list of default
597
+ // attributes.
598
+ // params - (String or Object) Either a serialized params string or a data
599
+ // object to be merged into the URL.
600
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
601
+ // specified, and is as-follows:
602
+ //
603
+ // * 0: params argument will override any params in attr URL.
604
+ // * 1: any params in attr URL will override params argument.
605
+ // * 2: params argument will completely replace any params in attr URL.
606
+ //
607
+ // Returns:
608
+ //
609
+ // (jQuery) The initial jQuery collection of elements, but with modified URL
610
+ // attribute values.
611
+
612
+ // Method: jQuery.fn.fragment
613
+ //
614
+ // Update URL attribute in one or more elements, merging the current URL (with
615
+ // or without pre-existing params) plus any object or params string into a new
616
+ // URL, which is then set into that attribute. Like <jQuery.fragment (build
617
+ // url)>, but for all elements in a jQuery collection.
618
+ //
619
+ // Usage:
620
+ //
621
+ // jQuery('selector').fragment( [ attr, ] params [ , merge_mode ] ); - -
622
+ //
623
+ // Arguments:
624
+ //
625
+ // attr - (String) Optional name of an attribute that will contain a URL to
626
+ // merge params into. See <jQuery.urlTagAttrList> for a list of default
627
+ // attributes.
628
+ // params - (String or Object) Either a serialized params string or a data
629
+ // object to be merged into the URL.
630
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
631
+ // specified, and is as-follows:
632
+ //
633
+ // * 0: params argument will override any params in attr URL.
634
+ // * 1: any params in attr URL will override params argument.
635
+ // * 2: params argument will completely replace any params in attr URL.
636
+ //
637
+ // Returns:
638
+ //
639
+ // (jQuery) The initial jQuery collection of elements, but with modified URL
640
+ // attribute values.
641
+
642
+ function p_fn_params() {
643
+ var attr,
644
+ params,
645
+ merge_mode,
646
+ args = aps.call( arguments ),
647
+ fragment_mode = args.shift();
648
+
649
+ if ( is_string(args[1]) || is_object(args[1]) ) {
650
+ attr = args.shift();
651
+ }
652
+ params = args.shift();
653
+ merge_mode = args.shift();
654
+
655
+ return this.each(function(){
656
+
657
+ var that = $(this),
658
+ a = attr || get_attr( this ),
659
+ url = a && that.attr( a ) || '';
660
+
661
+ url = p_params( fragment_mode, url, params, merge_mode );
662
+ that.attr( a, url );
663
+
664
+ });
665
+ };
666
+
667
+ $.fn[str_queryString] = curry( p_fn_params, 0 );
668
+ $.fn[str_fragment] = curry( p_fn_params, 1 );
669
+
670
+ // Method: jQuery.passQueryString
671
+ //
672
+ // Merge a URL (with or without pre-existing params) plus the document query
673
+ // string into a new URL, optionally omitting specified params or parsing the
674
+ // document params with a callback function pre-merge.
675
+ //
676
+ // Usage:
677
+ //
678
+ // jQuery.passQueryString( url [ , parse ] [ , merge_mode ] ); - -
679
+ //
680
+ // Arguments:
681
+ //
682
+ // url - (String) A valid URL, optionally containing a query string and/or
683
+ // #anchor, or fragment.
684
+ // parse - (Array or Function) An optional array of key names to -not- merge
685
+ // into the URL, or a function that returns the object that will be merged
686
+ // into url (the document params are deserialized and passed to this
687
+ // function as its only argument).
688
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
689
+ // specified, and is as-follows:
690
+ //
691
+ // * 0: document params will override any params in url.
692
+ // * 1: any params in url will override document params.
693
+ // * 2: document params will completely replace any params in url.
694
+ //
695
+ // Returns:
696
+ //
697
+ // (String) A URL with urlencoded params in the format 'url?a=b&c=d&e=f'.
698
+
699
+ // Method: jQuery.passFragment
700
+ //
701
+ // Merge a URL (with or without pre-existing params) plus the document
702
+ // fragment into a new URL, optionally omitting specified params or parsing
703
+ // the document params with a callback function pre-merge.
704
+ //
705
+ // Usage:
706
+ //
707
+ // jQuery.passFragment( url [ , parse ] [ , merge_mode ] ); - -
708
+ //
709
+ // Arguments:
710
+ //
711
+ // url - (String) A valid URL, optionally containing a query string and/or
712
+ // #anchor, or fragment.
713
+ // parse - (Array or Function) An optional array of key names to -not- merge
714
+ // into the URL, or a function that returns the object that will be merged
715
+ // into url (the document params are deserialized and passed to this
716
+ // function as its only argument).
717
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
718
+ // specified, and is as-follows:
719
+ //
720
+ // * 0: document params will override any params in url.
721
+ // * 1: any params in url will override document params.
722
+ // * 2: document params will completely replace any params in url.
723
+ //
724
+ // Returns:
725
+ //
726
+ // (String) A URL with urlencoded params in the format 'url#a=b&c=d&e=f'.
727
+
728
+ function p_passParams() {
729
+ var args = aps.call( arguments ),
730
+ fragment_mode = args.shift(),
731
+ url = args.shift(),
732
+ params = p_params( fragment_mode );
733
+
734
+ if ( $.isFunction(args[0]) ) {
735
+ params = args.shift()( params );
736
+ } else if ( $.isArray(args[0]) ) {
737
+ $.each(args.shift(), function(i,v){
738
+ delete params[v];
739
+ });
740
+ }
741
+
742
+ return p_params( fragment_mode, url, params, args.shift() );
743
+ };
744
+
745
+ $[str_passQueryString] = curry( p_passParams, 0 );
746
+ $[str_passFragment] = curry( p_passParams, 1 );
747
+
748
+
749
+ // Method: jQuery.fn.passQueryString
750
+ //
751
+ // Update URL attribute in one or more elements, merging the current URL (with
752
+ // or without pre-existing params) plus the document query string into a new
753
+ // URL (optionally parsing the document params with a callback function
754
+ // pre-merge), which is then set into that attribute. Like
755
+ // <jQuery.passQueryString>, but for all elements in a jQuery collection.
756
+ //
757
+ // Usage:
758
+ //
759
+ // jQuery('selector').passQueryString( [ attr ] [ , parse ] [ , merge_mode ] ); - -
760
+ //
761
+ // Arguments:
762
+ //
763
+ // attr - (String) Optional name of an attribute that will contain a URL to
764
+ // merge params into. See <jQuery.urlTagAttrList> for a list of default
765
+ // attributes.
766
+ // parse - (Array or Function) An optional array of key names to -not- merge
767
+ // into the URL, or a function that returns the object that will be merged
768
+ // into url (the document params are deserialized and passed to this
769
+ // function as its only argument).
770
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
771
+ // specified, and is as-follows:
772
+ //
773
+ // * 0: document params will override any params in attr URL.
774
+ // * 1: any params in attr URL will override document params.
775
+ // * 2: document params will completely replace any params in attr URL.
776
+ //
777
+ // Returns:
778
+ //
779
+ // (jQuery) The initial jQuery collection of elements, but with modified URL
780
+ // attribute values.
781
+
782
+ // Method: jQuery.fn.passFragment
783
+ //
784
+ // Update URL attribute in one or more elements, merging the current URL (with
785
+ // or without pre-existing params) plus the document fragment into a new URL
786
+ // (optionally parsing the document params with a callback function
787
+ // pre-merge), which is then set into that attribute. Like
788
+ // <jQuery.passFragment>, but for all elements in a jQuery collection.
789
+ //
790
+ // Usage:
791
+ //
792
+ // jQuery('selector').passFragment( [ attr ] [ , parse ] [ , merge_mode ] ); - -
793
+ //
794
+ // Arguments:
795
+ //
796
+ // attr - (String) Optional name of an attribute that will contain a URL to
797
+ // merge params into. See <jQuery.urlTagAttrList> for a list of default
798
+ // attributes.
799
+ // parse - (Array or Function) An optional array of key names to -not- merge
800
+ // into the URL, or a function that returns the object that will be merged
801
+ // into url (the document params are deserialized and passed to this
802
+ // function as its only argument).
803
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
804
+ // specified, and is as-follows:
805
+ //
806
+ // * 0: document params will override any params in attr URL.
807
+ // * 1: any params in attr URL will override document params.
808
+ // * 2: document params will completely replace any params in attr URL.
809
+ //
810
+ // Returns:
811
+ //
812
+ // (jQuery) The initial jQuery collection of elements, but with modified URL
813
+ // attribute values.
814
+
815
+ function p_fn_passParams() {
816
+ var attr,
817
+ args = aps.call(arguments),
818
+ fragment_mode = args.shift();
819
+
820
+ if ( is_string(args[0]) ) {
821
+ attr = args.shift();
822
+ }
823
+
824
+ return this.each(function(){
825
+
826
+ var that = $(this),
827
+ a = attr || get_attr( this ),
828
+ url = a && that.attr( a ) || '';
829
+
830
+ url = p_passParams.apply( this, [fragment_mode, url].concat(args) );
831
+ that.attr( a, url );
832
+
833
+ });
834
+ };
835
+
836
+ $.fn[str_passQueryString] = curry( p_fn_passParams, 0 );
837
+ $.fn[str_passFragment] = curry( p_fn_passParams, 1 );
838
+
839
+
840
+ // Deserialize a params string, optionally preceded by a url? or ?, or
841
+ // followed by an #anchor (or if fragment_mode, optionally preceded by a url#
842
+ // or #) into an object, optionally casting numbers, null, true, false, and
843
+ // undefined values appropriately.
844
+
845
+ function deserialize( params, cast_values, fragment_mode ) {
846
+ var p,
847
+ key,
848
+ val,
849
+ obj = {},
850
+ cast_types = { 'null': null, 'true': TRUE, 'false': FALSE },
851
+ decode_uri_component = decodeURIComponent,
852
+ re = fragment_mode
853
+ ? /^.*[#]/
854
+ : /^.*[?]|#.*$/g;
855
+
856
+ params = params.replace( re, '' ).replace( /\+/g, ' ' ).split('&');
857
+
858
+ while ( params.length ) {
859
+
860
+ p = params.shift().split('=');
861
+ key = decode_uri_component( p[0] );
862
+
863
+ if ( p.length === 2 ) {
864
+ val = decode_uri_component( p[1] );
865
+
866
+ if ( cast_values ) {
867
+ if ( val && !isNaN(val) ) {
868
+ val = Number( val );
869
+ } else if ( val === 'undefined' ) {
870
+ val = undefined;
871
+ } else if ( cast_types[val] !== undefined ) {
872
+ val = cast_types[val];
873
+ }
874
+ }
875
+
876
+ if ( $.isArray(obj[key]) ) {
877
+ obj[key].push( val );
878
+ } else if ( obj[key] !== undefined ) {
879
+ obj[key] = [obj[key], val];
880
+ } else {
881
+ obj[key] = val;
882
+ }
883
+
884
+ } else if ( key ) {
885
+ obj[key] = cast_values
886
+ ? undefined
887
+ : '';
888
+ }
889
+ }
890
+
891
+ return obj;
892
+ };
893
+
894
+ // Merge a URL (with or without a pre-existing params string and/or #anchor,
895
+ // or fragment) plus any object or params string into a new URL.
896
+
897
+ function build_url( url, params, merge_mode, fragment_mode ) {
898
+ var qs,
899
+ re = fragment_mode
900
+ ? /^([^#]*)[#]?(.*)$/
901
+ : /^([^#?]*)[?]?([^#]*)(#?.*)/,
902
+ matches = url.match( re ),
903
+ url_params = deserialize( matches[2], 0, fragment_mode ),
904
+ hash = matches[3] || '';
905
+
906
+ if ( is_string(params) ) {
907
+ params = deserialize( params, 0, fragment_mode );
908
+ }
909
+
910
+ if ( merge_mode === 2 ) {
911
+ qs = params;
912
+ } else if ( merge_mode === 1 ) {
913
+ qs = $.extend( {}, params, url_params );
914
+ } else {
915
+ qs = $.extend( {}, url_params, params );
916
+ }
917
+
918
+ qs = $.param( qs );
919
+ return matches[1] + ( fragment_mode ? '#' : qs || !matches[1] ? '?' : '' ) + qs + hash;
920
+ };
921
+
922
+
923
+ // Method: jQuery.setFragment
924
+ //
925
+ // Set the document fragment. Will trigger the <fragmentChange> event if
926
+ // <jQuery.fragmentChange> has been enabled, and the new fragment is actually
927
+ // different than the previous fragment.
928
+ //
929
+ // Usage:
930
+ //
931
+ // jQuery.setFragment( [ params [ , merge_mode ] ] ); - -
932
+ //
933
+ // Arguments:
934
+ //
935
+ // params - (String or Object) Either a serialized params string or a data
936
+ // object to set as the current document's fragment. If omitted, sets the
937
+ // document fragment to # (this may cause your browser to scroll).
938
+ // merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
939
+ // specified, and is as-follows:
940
+ //
941
+ // * 0: params argument will override any params in document fragment.
942
+ // * 1: any params in document fragment will override params argument.
943
+ // * 2: params argument will completely replace any params in document
944
+ // fragment.
945
+ //
946
+ // Returns:
947
+ //
948
+ // Nothing.
949
+
950
+ $.setFragment = p_setFragment = function( params, merge_mode ) {
951
+ var frag = is_object( params )
952
+ ? p_params( TRUE, params )
953
+ : (params || '').replace( /^#/, '' );
954
+
955
+ frag = params
956
+ ? build_url( '#' + get_fragment(), '#' + frag, merge_mode, 1 )
957
+ : '#';
958
+
959
+ //loc.hash = frag; // Safari 3 & Chrome barf if frag === '#'.
960
+ loc.href = loc.href.replace( /#.*$/, '' ) + frag;
961
+ };
962
+
963
+
964
+ // Method: jQuery.fragmentChange
965
+ //
966
+ // Enable or disable the polling loop that watches the document fragment for
967
+ // changes and triggers the <fragmentChange> event. The event object passed to
968
+ // a bound event callback has the property "fragment" which contains the
969
+ // fragment params string. Disabled by default.
970
+ //
971
+ // In browsers that support it, the onhashchange event is used (IE8, FF3.6)
972
+ //
973
+ // Note: When this is enabled for the first time, a hidden IFRAME is written
974
+ // into the body for Internet Explorer 6 and 7 to enable fragment-based
975
+ // browser history.
976
+ //
977
+ // Usage:
978
+ //
979
+ // jQuery.fragmentChange( [ state ] ); - -
980
+ //
981
+ // Arguments:
982
+ //
983
+ // state - (Boolean or Number) If true, a polling loop is started with the
984
+ // default delay of 100 and the fragmentChange event is enabled. If omitted
985
+ // or false, the polling loop is stopped and the fragmentChange event is
986
+ // disabled. A zero-or-greater numeric polling loop delay in milliseconds
987
+ // may also be specified.
988
+ //
989
+ // Returns:
990
+ //
991
+ // Nothing.
992
+
993
+ // Event: fragmentChange
994
+ //
995
+ // Fired when the document fragment changes, provided <jQuery.fragmentChange>
996
+ // has been enabled.
997
+ //
998
+ // The event object that is passed as the sole argument to the callback has a
999
+ // .fragment property, which is a URI encoded string reflecting the current
1000
+ // location.hash, with any leading # removed. Using e.fragment should be more
1001
+ // reliable than accessing location.hash directly, as only Firefox URI decodes
1002
+ // the location.hash property automatically.
1003
+ //
1004
+ // Usage:
1005
+ //
1006
+ // > $(document).bind('fragmentChange', function(e) {
1007
+ // > var fragment_str = e.fragment,
1008
+ // > fragment_obj = $.fragment();
1009
+ // > ...
1010
+ // > });
1011
+
1012
+ $[str_fragmentChange] = function( delay ) {
1013
+ if ( delay === TRUE ) { delay = 100; }
1014
+
1015
+ function trigger() {
1016
+ var event = $.Event( str_fragmentChange );
1017
+ event[str_fragment] = get_fragment();
1018
+
1019
+ $(document).trigger( event );
1020
+ };
1021
+
1022
+ has_onhashchange && $(window).unbind( str_hashchange );
1023
+
1024
+ timeout_id && clearTimeout( timeout_id );
1025
+ timeout_id = null;
1026
+
1027
+ if ( typeof delay === 'number' ) {
1028
+ if ( has_onhashchange ) {
1029
+ $(window).bind( str_hashchange, trigger );
1030
+
1031
+ } else {
1032
+ last_fragment = get_fragment();
1033
+
1034
+ if ( $.isFunction(ie_history) ) {
1035
+ ie_history = ie_history();
1036
+ }
1037
+
1038
+ (function loopy(){
1039
+ var frag = get_fragment(),
1040
+ ie_frag = ie_history[str_fragment]( last_fragment );
1041
+
1042
+ if ( frag !== last_fragment ) {
1043
+ ie_history[str_update]( frag, ie_frag );
1044
+
1045
+ last_fragment = frag;
1046
+ trigger();
1047
+
1048
+ } else if ( ie_frag !== last_fragment ) {
1049
+ p_setFragment( ie_frag, 2 );
1050
+ }
1051
+
1052
+ timeout_id = setTimeout( loopy, delay < 0 ? 0 : delay );
1053
+ })();
1054
+ }
1055
+ }
1056
+ };
1057
+
1058
+ // Handle fragment-based browser history in IE 6-7.
1059
+
1060
+ function ie_history() {
1061
+ var iframe,
1062
+ browser = $.browser,
1063
+ that = {};
1064
+
1065
+ that[str_update] = that[str_fragment] = function( val ){ return val; };
1066
+
1067
+ if ( browser.msie && browser.version < 8 ) {
1068
+
1069
+ that[str_update] = function( frag, ie_frag ) {
1070
+ var doc = iframe.document;
1071
+ if ( frag !== ie_frag ) {
1072
+ doc.open();
1073
+ doc.close();
1074
+ doc.location.hash = '#' + frag;
1075
+ }
1076
+ };
1077
+
1078
+ that[str_fragment] = function() {
1079
+ return iframe.document.location.hash.replace( /^#/, '' );
1080
+ };
1081
+
1082
+ iframe = $('<iframe/>').hide().appendTo( 'body' )
1083
+ .get(0).contentWindow;
1084
+
1085
+ that[str_update]( get_fragment() );
1086
+ }
1087
+
1088
+ return that;
1089
+ };
1090
+
1091
+ })(jQuery);