test runner: extra test case, [5501]
[jquery.git] / src / core.js
1 /*
2  * jQuery @VERSION - New Wave Javascript
3  *
4  * Copyright (c) 2008 John Resig (jquery.com)
5  * Dual licensed under the MIT (MIT-LICENSE.txt)
6  * and GPL (GPL-LICENSE.txt) licenses.
7  *
8  * $Date$
9  * $Rev$
10  */
11
12 // Map over jQuery in case of overwrite
13 var _jQuery = window.jQuery,
14 // Map over the $ in case of overwrite  
15         _$ = window.$;
16
17 var jQuery = window.jQuery = window.$ = function( selector, context ) {
18         // The jQuery object is actually just the init constructor 'enhanced'
19         return new jQuery.fn.init( selector, context );
20 };
21
22 // A simple way to check for HTML strings or ID strings
23 // (both of which we optimize for)
24 var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,
25
26 // Is it a simple selector
27         isSimple = /^.[^:#\[\.]*$/;
28
29 jQuery.fn = jQuery.prototype = {
30         init: function( selector, context ) {
31                 // Make sure that a selection was provided
32                 selector = selector || document;
33
34                 // Handle $(DOMElement)
35                 if ( selector.nodeType ) {
36                         this[0] = selector;
37                         this.length = 1;
38                         return this;
39                 }
40                 // Handle HTML strings
41                 if ( typeof selector == "string" ) {
42                         // Are we dealing with HTML string or an ID?
43                         var match = quickExpr.exec( selector );
44
45                         // Verify a match, and that no context was specified for #id
46                         if ( match && (match[1] || !context) ) {
47
48                                 // HANDLE: $(html) -> $(array)
49                                 if ( match[1] )
50                                         selector = jQuery.clean( [ match[1] ], context );
51
52                                 // HANDLE: $("#id")
53                                 else {
54                                         var elem = document.getElementById( match[3] );
55
56                                         // Make sure an element was located
57                                         if ( elem ){
58                                                 // Handle the case where IE and Opera return items
59                                                 // by name instead of ID
60                                                 if ( elem.id != match[3] )
61                                                         return jQuery().find( selector );
62
63                                                 // Otherwise, we inject the element directly into the jQuery object
64                                                 return jQuery( elem );
65                                         }
66                                         selector = [];
67                                 }
68
69                         // HANDLE: $(expr, [context])
70                         // (which is just equivalent to: $(content).find(expr)
71                         } else
72                                 return jQuery( context ).find( selector );
73
74                 // HANDLE: $(function)
75                 // Shortcut for document ready
76                 } else if ( jQuery.isFunction( selector ) )
77                         return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector );
78                 
79                 return this.setArray(jQuery.makeArray(selector));
80         },
81         
82         // The current version of jQuery being used
83         jquery: "@VERSION",
84
85         // The number of elements contained in the matched element set
86         size: function() {
87                 return this.length;
88         },
89         
90         // The number of elements contained in the matched element set
91         length: 0,
92
93         // Get the Nth element in the matched element set OR
94         // Get the whole matched element set as a clean array
95         get: function( num ) {
96                 return num == undefined ?
97
98                         // Return a 'clean' array
99                         jQuery.makeArray( this ) :
100
101                         // Return just the object
102                         this[ num ];
103         },
104         
105         // Take an array of elements and push it onto the stack
106         // (returning the new matched element set)
107         pushStack: function( elems ) {
108                 // Build a new jQuery matched element set
109                 var ret = jQuery( elems );
110
111                 // Add the old object onto the stack (as a reference)
112                 ret.prevObject = this;
113
114                 // Return the newly-formed element set
115                 return ret;
116         },
117         
118         // Force the current matched set of elements to become
119         // the specified array of elements (destroying the stack in the process)
120         // You should use pushStack() in order to do this, but maintain the stack
121         setArray: function( elems ) {
122                 // Resetting the length to 0, then using the native Array push
123                 // is a super-fast way to populate an object with array-like properties
124                 this.length = 0;
125                 Array.prototype.push.apply( this, elems );
126                 
127                 return this;
128         },
129
130         // Execute a callback for every element in the matched set.
131         // (You can seed the arguments with an array of args, but this is
132         // only used internally.)
133         each: function( callback, args ) {
134                 return jQuery.each( this, callback, args );
135         },
136
137         // Determine the position of an element within 
138         // the matched set of elements
139         index: function( elem ) {
140                 var ret = -1;
141
142                 // Locate the position of the desired element
143                 return jQuery.inArray( 
144                         // If it receives a jQuery object, the first element is used
145                         elem && elem.jquery ? elem[0] : elem
146                 , this );
147         },
148
149         attr: function( name, value, type ) {
150                 var options = name;
151                 
152                 // Look for the case where we're accessing a style value
153                 if ( name.constructor == String )
154                         if ( value == undefined )
155                                 return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined;
156
157                         else {
158                                 options = {};
159                                 options[ name ] = value;
160                         }
161                 
162                 // Check to see if we're setting style values
163                 return this.each(function(i){
164                         // Set all the styles
165                         for ( name in options )
166                                 jQuery.attr(
167                                         type ?
168                                                 this.style :
169                                                 this,
170                                         name, jQuery.prop( this, options[ name ], type, i, name )
171                                 );
172                 });
173         },
174
175         css: function( key, value ) {
176                 // ignore negative width and height values
177                 if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
178                         value = undefined;
179                 return this.attr( key, value, "curCSS" );
180         },
181
182         text: function( text ) {
183                 if ( typeof text != "object" && text != null )
184                         return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
185
186                 var ret = "";
187
188                 jQuery.each( text || this, function(){
189                         jQuery.each( this.childNodes, function(){
190                                 if ( this.nodeType != 8 )
191                                         ret += this.nodeType != 1 ?
192                                                 this.nodeValue :
193                                                 jQuery.fn.text( [ this ] );
194                         });
195                 });
196
197                 return ret;
198         },
199
200         wrapAll: function( html ) {
201                 if ( this[0] )
202                         // The elements to wrap the target around
203                         jQuery( html, this[0].ownerDocument )
204                                 .clone()
205                                 .insertBefore( this[0] )
206                                 .map(function(){
207                                         var elem = this;
208
209                                         while ( elem.firstChild )
210                                                 elem = elem.firstChild;
211
212                                         return elem;
213                                 })
214                                 .append(this);
215
216                 return this;
217         },
218
219         wrapInner: function( html ) {
220                 return this.each(function(){
221                         jQuery( this ).contents().wrapAll( html );
222                 });
223         },
224
225         wrap: function( html ) {
226                 return this.each(function(){
227                         jQuery( this ).wrapAll( html );
228                 });
229         },
230
231         append: function() {
232                 return this.domManip(arguments, true, false, function(elem){
233                         if (this.nodeType == 1)
234                                 this.appendChild( elem );
235                 });
236         },
237
238         prepend: function() {
239                 return this.domManip(arguments, true, true, function(elem){
240                         if (this.nodeType == 1)
241                                 this.insertBefore( elem, this.firstChild );
242                 });
243         },
244         
245         before: function() {
246                 return this.domManip(arguments, false, false, function(elem){
247                         this.parentNode.insertBefore( elem, this );
248                 });
249         },
250
251         after: function() {
252                 return this.domManip(arguments, false, true, function(elem){
253                         this.parentNode.insertBefore( elem, this.nextSibling );
254                 });
255         },
256
257         end: function() {
258                 return this.prevObject || jQuery( [] );
259         },
260
261         find: function( selector ) {
262                 var elems = jQuery.map(this, function(elem){
263                         return jQuery.find( selector, elem );
264                 });
265
266                 return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ?
267                         jQuery.unique( elems ) :
268                         elems );
269         },
270
271         clone: function( events ) {
272                 // Do the clone
273                 var ret = this.map(function(){
274                         if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) {
275                                 // IE copies events bound via attachEvent when
276                                 // using cloneNode. Calling detachEvent on the
277                                 // clone will also remove the events from the orignal
278                                 // In order to get around this, we use innerHTML.
279                                 // Unfortunately, this means some modifications to 
280                                 // attributes in IE that are actually only stored 
281                                 // as properties will not be copied (such as the
282                                 // the name attribute on an input).
283                                 var clone = this.cloneNode(true),
284                                         container = document.createElement("div");
285                                 container.appendChild(clone);
286                                 return jQuery.clean([container.innerHTML])[0];
287                         } else
288                                 return this.cloneNode(true);
289                 });
290
291                 // Need to set the expando to null on the cloned set if it exists
292                 // removeData doesn't work here, IE removes it from the original as well
293                 // this is primarily for IE but the data expando shouldn't be copied over in any browser
294                 var clone = ret.find("*").andSelf().each(function(){
295                         if ( this[ expando ] != undefined )
296                                 this[ expando ] = null;
297                 });
298                 
299                 // Copy the events from the original to the clone
300                 if ( events === true )
301                         this.find("*").andSelf().each(function(i){
302                                 if (this.nodeType == 3)
303                                         return;
304                                 var events = jQuery.data( this, "events" );
305
306                                 for ( var type in events )
307                                         for ( var handler in events[ type ] )
308                                                 jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
309                         });
310
311                 // Return the cloned set
312                 return ret;
313         },
314
315         filter: function( selector ) {
316                 return this.pushStack(
317                         jQuery.isFunction( selector ) &&
318                         jQuery.grep(this, function(elem, i){
319                                 return selector.call( elem, i );
320                         }) ||
321
322                         jQuery.multiFilter( selector, this ) );
323         },
324
325         not: function( selector ) {
326                 if ( selector.constructor == String )
327                         // test special case where just one selector is passed in
328                         if ( isSimple.test( selector ) )
329                                 return this.pushStack( jQuery.multiFilter( selector, this, true ) );
330                         else
331                                 selector = jQuery.multiFilter( selector, this );
332
333                 var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
334                 return this.filter(function() {
335                         return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
336                 });
337         },
338
339         add: function( selector ) {
340                 return !selector ? this : this.pushStack( jQuery.merge( 
341                         this.get(),
342                         selector.constructor == String ? 
343                                 jQuery( selector ).get() :
344                                 selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ?
345                                         selector : [selector] ) );
346         },
347
348         is: function( selector ) {
349                 return !!selector && jQuery.multiFilter( selector, this ).length > 0;
350         },
351
352         hasClass: function( selector ) {
353                 return this.is( "." + selector );
354         },
355         
356         val: function( value ) {
357                 if ( value == undefined ) {
358
359                         if ( this.length ) {
360                                 var elem = this[0];
361
362                                 // We need to handle select boxes special
363                                 if ( jQuery.nodeName( elem, "select" ) ) {
364                                         var index = elem.selectedIndex,
365                                                 values = [],
366                                                 options = elem.options,
367                                                 one = elem.type == "select-one";
368                                         
369                                         // Nothing was selected
370                                         if ( index < 0 )
371                                                 return null;
372
373                                         // Loop through all the selected options
374                                         for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
375                                                 var option = options[ i ];
376
377                                                 if ( option.selected ) {
378                                                         // Get the specifc value for the option
379                                                         value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value;
380                                                         
381                                                         // We don't need an array for one selects
382                                                         if ( one )
383                                                                 return value;
384                                                         
385                                                         // Multi-Selects return an array
386                                                         values.push( value );
387                                                 }
388                                         }
389                                         
390                                         return values;
391                                         
392                                 // Everything else, we just grab the value
393                                 } else
394                                         return (this[0].value || "").replace(/\r/g, "");
395
396                         }
397
398                         return undefined;
399                 }
400
401                 return this.each(function(){
402                         if ( this.nodeType != 1 )
403                                 return;
404
405                         if ( value.constructor == Array && /radio|checkbox/.test( this.type ) )
406                                 this.checked = (jQuery.inArray(this.value, value) >= 0 ||
407                                         jQuery.inArray(this.name, value) >= 0);
408
409                         else if ( jQuery.nodeName( this, "select" ) ) {
410                                 var values = value.constructor == Array ?
411                                         value :
412                                         [ value ];
413
414                                 jQuery( "option", this ).each(function(){
415                                         this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
416                                                 jQuery.inArray( this.text, values ) >= 0);
417                                 });
418
419                                 if ( !values.length )
420                                         this.selectedIndex = -1;
421
422                         } else
423                                 this.value = value;
424                 });
425         },
426         
427         html: function( value ) {
428                 return value == undefined ?
429                         (this.length ?
430                                 this[0].innerHTML :
431                                 null) :
432                         this.empty().append( value );
433         },
434
435         replaceWith: function( value ) {
436                 return this.after( value ).remove();
437         },
438
439         eq: function( i ) {
440                 return this.slice( i, i + 1 );
441         },
442
443         slice: function() {
444                 return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
445         },
446
447         map: function( callback ) {
448                 return this.pushStack( jQuery.map(this, function(elem, i){
449                         return callback.call( elem, i, elem );
450                 }));
451         },
452
453         andSelf: function() {
454                 return this.add( this.prevObject );
455         },
456
457         data: function( key, value ){
458                 var parts = key.split(".");
459                 parts[1] = parts[1] ? "." + parts[1] : "";
460
461                 if ( value === undefined ) {
462                         var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
463                         
464                         if ( data === undefined && this.length )
465                                 data = jQuery.data( this[0], key );
466
467                         return data === undefined && parts[1] ?
468                                 this.data( parts[0] ) :
469                                 data;
470                 } else
471                         return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
472                                 jQuery.data( this, key, value );
473                         });
474         },
475
476         removeData: function( key ){
477                 return this.each(function(){
478                         jQuery.removeData( this, key );
479                 });
480         },
481         
482         domManip: function( args, table, reverse, callback ) {
483                 var clone = this.length > 1, elems; 
484
485                 return this.each(function(){
486                         if ( !elems ) {
487                                 elems = jQuery.clean( args, this.ownerDocument );
488
489                                 if ( reverse )
490                                         elems.reverse();
491                         }
492
493                         var obj = this;
494
495                         if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) )
496                                 obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
497
498                         var scripts = jQuery( [] );
499
500                         jQuery.each(elems, function(){
501                                 var elem = clone ?
502                                         jQuery( this ).clone( true )[0] :
503                                         this;
504
505                                 // execute all scripts after the elements have been injected
506                                 if ( jQuery.nodeName( elem, "script" ) ) {
507                                         scripts = scripts.add( elem );
508                                 } else {
509                                         // Remove any inner scripts for later evaluation
510                                         if ( elem.nodeType == 1 )
511                                                 scripts = scripts.add( jQuery( "script", elem ).remove() );
512
513                                         // Inject the elements into the document
514                                         callback.call( obj, elem );
515                                 }
516                         });
517
518                         scripts.each( evalScript );
519                 });
520         }
521 };
522
523 // Give the init function the jQuery prototype for later instantiation
524 jQuery.fn.init.prototype = jQuery.fn;
525
526 function evalScript( i, elem ) {
527         if ( elem.src )
528                 jQuery.ajax({
529                         url: elem.src,
530                         async: false,
531                         dataType: "script"
532                 });
533
534         else
535                 jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
536
537         if ( elem.parentNode )
538                 elem.parentNode.removeChild( elem );
539 }
540
541 function now(){
542         return +new Date;
543 }
544
545 jQuery.extend = jQuery.fn.extend = function() {
546         // copy reference to target object
547         var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
548
549         // Handle a deep copy situation
550         if ( target.constructor == Boolean ) {
551                 deep = target;
552                 target = arguments[1] || {};
553                 // skip the boolean and the target
554                 i = 2;
555         }
556
557         // Handle case when target is a string or something (possible in deep copy)
558         if ( typeof target != "object" && typeof target != "function" )
559                 target = {};
560
561         // extend jQuery itself if only one argument is passed
562         if ( length == i ) {
563                 target = this;
564                 --i;
565         }
566
567         for ( ; i < length; i++ )
568                 // Only deal with non-null/undefined values
569                 if ( (options = arguments[ i ]) != null )
570                         // Extend the base object
571                         for ( var name in options ) {
572                                 var src = target[ name ], copy = options[ name ]; 
573                                 
574                                 // Prevent never-ending loop
575                                 if ( target === copy )
576                                         continue;
577
578                                 // Recurse if we're merging object values
579                                 if ( deep && copy && typeof copy == "object" && src && !copy.nodeType )
580                                         target[ name ] = jQuery.extend( deep, src, copy );
581
582                                 // Don't bring in undefined values
583                                 else if ( copy !== undefined )
584                                         target[ name ] = copy;
585
586                         }
587
588         // Return the modified object
589         return target;
590 };
591
592 var expando = "jQuery" + now(), uuid = 0, windowData = {},
593
594 // exclude the following css properties to add px
595         exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
596 // cache getComputedStyle
597         getComputedStyle = document.defaultView && document.defaultView.getComputedStyle;
598
599 jQuery.extend({
600         noConflict: function( deep ) {
601                 window.$ = _$;
602
603                 if ( deep )
604                         window.jQuery = _jQuery;
605
606                 return jQuery;
607         },
608
609         // See test/unit/core.js for details concerning this function.
610         isFunction: function( fn ) {
611                 return !!fn && typeof fn != "string" && !fn.nodeName && 
612                         fn.constructor != Array && /function/i.test( fn + "" );
613         },
614         
615         // check if an element is in a (or is an) XML document
616         isXMLDoc: function( elem ) {
617                 return elem.documentElement && !elem.body ||
618                         elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
619         },
620
621         // Evalulates a script in a global context
622         globalEval: function( data ) {
623                 data = jQuery.trim( data );
624
625                 if ( data ) {
626                         // Inspired by code by Andrea Giammarchi
627                         // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
628                         var head = document.getElementsByTagName("head")[0] || document.documentElement,
629                                 script = document.createElement("script");
630
631                         script.type = "text/javascript";
632                         if ( jQuery.browser.msie )
633                                 script.text = data;
634                         else
635                                 script.appendChild( document.createTextNode( data ) );
636
637                         head.appendChild( script );
638                         head.removeChild( script );
639                 }
640         },
641
642         nodeName: function( elem, name ) {
643                 return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
644         },
645         
646         cache: {},
647         
648         data: function( elem, name, data ) {
649                 elem = elem == window ?
650                         windowData :
651                         elem;
652
653                 var id = elem[ expando ];
654
655                 // Compute a unique ID for the element
656                 if ( !id ) 
657                         id = elem[ expando ] = ++uuid;
658
659                 // Only generate the data cache if we're
660                 // trying to access or manipulate it
661                 if ( name && !jQuery.cache[ id ] )
662                         jQuery.cache[ id ] = {};
663                 
664                 // Prevent overriding the named cache with undefined values
665                 if ( data !== undefined )
666                         jQuery.cache[ id ][ name ] = data;
667                 
668                 // Return the named cache data, or the ID for the element       
669                 return name ?
670                         jQuery.cache[ id ][ name ] :
671                         id;
672         },
673         
674         removeData: function( elem, name ) {
675                 elem = elem == window ?
676                         windowData :
677                         elem;
678
679                 var id = elem[ expando ];
680
681                 // If we want to remove a specific section of the element's data
682                 if ( name ) {
683                         if ( jQuery.cache[ id ] ) {
684                                 // Remove the section of cache data
685                                 delete jQuery.cache[ id ][ name ];
686
687                                 // If we've removed all the data, remove the element's cache
688                                 name = "";
689
690                                 for ( name in jQuery.cache[ id ] )
691                                         break;
692
693                                 if ( !name )
694                                         jQuery.removeData( elem );
695                         }
696
697                 // Otherwise, we want to remove all of the element's data
698                 } else {
699                         // Clean up the element expando
700                         try {
701                                 delete elem[ expando ];
702                         } catch(e){
703                                 // IE has trouble directly removing the expando
704                                 // but it's ok with using removeAttribute
705                                 if ( elem.removeAttribute )
706                                         elem.removeAttribute( expando );
707                         }
708
709                         // Completely remove the data cache
710                         delete jQuery.cache[ id ];
711                 }
712         },
713
714         // args is for internal usage only
715         each: function( object, callback, args ) {
716                 var name, i = 0, length = object.length;
717                 
718                 if ( args ) {
719                         if ( length == undefined ) {
720                                 for ( name in object )
721                                         if ( callback.apply( object[ name ], args ) === false )
722                                                 break;
723                         } else
724                                 for ( ; i < length; )
725                                         if ( callback.apply( object[ i++ ], args ) === false )
726                                                 break;
727
728                 // A special, fast, case for the most common use of each
729                 } else {
730                         if ( length == undefined ) {
731                                 for ( name in object )
732                                         if ( callback.call( object[ name ], name, object[ name ] ) === false )
733                                                 break;
734                         } else
735                                 for ( var value = object[0]; 
736                                         i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
737                 }
738
739                 return object;
740         },
741         
742         prop: function( elem, value, type, i, name ) {
743                         // Handle executable functions
744                         if ( jQuery.isFunction( value ) )
745                                 value = value.call( elem, i );
746                                 
747                         // Handle passing in a number to a CSS property
748                         return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ?
749                                 value + "px" :
750                                 value;
751         },
752
753         className: {
754                 // internal only, use addClass("class")
755                 add: function( elem, classNames ) {
756                         jQuery.each((classNames || "").split(/\s+/), function(i, className){
757                                 if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
758                                         elem.className += (elem.className ? " " : "") + className;
759                         });
760                 },
761
762                 // internal only, use removeClass("class")
763                 remove: function( elem, classNames ) {
764                         if (elem.nodeType == 1)
765                                 elem.className = classNames != undefined ?
766                                         jQuery.grep(elem.className.split(/\s+/), function(className){
767                                                 return !jQuery.className.has( classNames, className );  
768                                         }).join(" ") :
769                                         "";
770                 },
771
772                 // internal only, use is(".class")
773                 has: function( elem, className ) {
774                         return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
775                 }
776         },
777
778         // A method for quickly swapping in/out CSS properties to get correct calculations
779         swap: function( elem, options, callback ) {
780                 var old = {};
781                 // Remember the old values, and insert the new ones
782                 for ( var name in options ) {
783                         old[ name ] = elem.style[ name ];
784                         elem.style[ name ] = options[ name ];
785                 }
786
787                 callback.call( elem );
788
789                 // Revert the old values
790                 for ( var name in options )
791                         elem.style[ name ] = old[ name ];
792         },
793
794         css: function( elem, name, force ) {
795                 if ( name == "width" || name == "height" ) {
796                         var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
797                 
798                         function getWH() {
799                                 val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
800                                 var padding = 0, border = 0;
801                                 jQuery.each( which, function() {
802                                         padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
803                                         border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
804                                 });
805                                 val -= Math.round(padding + border);
806                         }
807                 
808                         if ( jQuery(elem).is(":visible") )
809                                 getWH();
810                         else
811                                 jQuery.swap( elem, props, getWH );
812                         
813                         return Math.max(0, val);
814                 }
815                 
816                 return jQuery.curCSS( elem, name, force );
817         },
818
819         curCSS: function( elem, name, force ) {
820                 var ret, style = elem.style;
821
822                 // A helper method for determining if an element's values are broken
823                 function color( elem ) {
824                         if ( !jQuery.browser.safari )
825                                 return false;
826                         
827                         // getComputedStyle is cached
828                         var ret = getComputedStyle( elem, null );
829                         return !ret || ret.getPropertyValue("color") == "";
830                 }
831
832                 // We need to handle opacity special in IE
833                 if ( name == "opacity" && jQuery.browser.msie ) {
834                         ret = jQuery.attr( style, "opacity" );
835
836                         return ret == "" ?
837                                 "1" :
838                                 ret;
839                 }
840                 // Opera sometimes will give the wrong display answer, this fixes it, see #2037
841                 if ( jQuery.browser.opera && name == "display" ) {
842                         var save = style.outline;
843                         style.outline = "0 solid black";
844                         style.outline = save;
845                 }
846                 
847                 // Make sure we're using the right name for getting the float value
848                 if ( name.match( /float/i ) )
849                         name = styleFloat;
850
851                 if ( !force && style && style[ name ] )
852                         ret = style[ name ];
853
854                 else if ( getComputedStyle ) {
855
856                         // Only "float" is needed here
857                         if ( name.match( /float/i ) )
858                                 name = "float";
859
860                         name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
861
862                         var computedStyle = getComputedStyle( elem, null );
863
864                         if ( computedStyle && !color( elem ) )
865                                 ret = computedStyle.getPropertyValue( name );
866
867                         // If the element isn't reporting its values properly in Safari
868                         // then some display: none elements are involved
869                         else {
870                                 var swap = [], stack = [], a = elem, i = 0;
871
872                                 // Locate all of the parent display: none elements
873                                 for ( ; a && color(a); a = a.parentNode )
874                                         stack.unshift(a);
875
876                                 // Go through and make them visible, but in reverse
877                                 // (It would be better if we knew the exact display type that they had)
878                                 for ( ; i < stack.length; i++ )
879                                         if ( color( stack[ i ] ) ) {
880                                                 swap[ i ] = stack[ i ].style.display;
881                                                 stack[ i ].style.display = "block";
882                                         }
883
884                                 // Since we flip the display style, we have to handle that
885                                 // one special, otherwise get the value
886                                 ret = name == "display" && swap[ stack.length - 1 ] != null ?
887                                         "none" :
888                                         ( computedStyle && computedStyle.getPropertyValue( name ) ) || "";
889
890                                 // Finally, revert the display styles back
891                                 for ( i = 0; i < swap.length; i++ )
892                                         if ( swap[ i ] != null )
893                                                 stack[ i ].style.display = swap[ i ];
894                         }
895
896                         // We should always get a number back from opacity
897                         if ( name == "opacity" && ret == "" )
898                                 ret = "1";
899
900                 } else if ( elem.currentStyle ) {
901                         var camelCase = name.replace(/\-(\w)/g, function(all, letter){
902                                 return letter.toUpperCase();
903                         });
904
905                         ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
906
907                         // From the awesome hack by Dean Edwards
908                         // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
909
910                         // If we're not dealing with a regular pixel number
911                         // but a number that has a weird ending, we need to convert it to pixels
912                         if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
913                                 // Remember the original values
914                                 var left = style.left, rsLeft = elem.runtimeStyle.left;
915
916                                 // Put in the new values to get a computed value out
917                                 elem.runtimeStyle.left = elem.currentStyle.left;
918                                 style.left = ret || 0;
919                                 ret = style.pixelLeft + "px";
920
921                                 // Revert the changed values
922                                 style.left = left;
923                                 elem.runtimeStyle.left = rsLeft;
924                         }
925                 }
926
927                 return ret;
928         },
929         
930         clean: function( elems, context ) {
931                 var ret = [];
932                 context = context || document;
933                 // !context.createElement fails in IE with an error but returns typeof 'object'
934                 if (typeof context.createElement == 'undefined') 
935                         context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
936
937                 jQuery.each(elems, function(i, elem){
938                         if ( !elem )
939                                 return;
940
941                         if ( elem.constructor == Number )
942                                 elem += '';
943                         
944                         // Convert html string into DOM nodes
945                         if ( typeof elem == "string" ) {
946                                 // Fix "XHTML"-style tags in all browsers
947                                 elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
948                                         return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
949                                                 all :
950                                                 front + "></" + tag + ">";
951                                 });
952
953                                 // Trim whitespace, otherwise indexOf won't work as expected
954                                 var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
955
956                                 var wrap =
957                                         // option or optgroup
958                                         !tags.indexOf("<opt") &&
959                                         [ 1, "<select multiple='multiple'>", "</select>" ] ||
960                                         
961                                         !tags.indexOf("<leg") &&
962                                         [ 1, "<fieldset>", "</fieldset>" ] ||
963                                         
964                                         tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
965                                         [ 1, "<table>", "</table>" ] ||
966                                         
967                                         !tags.indexOf("<tr") &&
968                                         [ 2, "<table><tbody>", "</tbody></table>" ] ||
969                                         
970                                         // <thead> matched above
971                                         (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
972                                         [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
973                                         
974                                         !tags.indexOf("<col") &&
975                                         [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
976
977                                         // IE can't serialize <link> and <script> tags normally
978                                         jQuery.browser.msie &&
979                                         [ 1, "div<div>", "</div>" ] ||
980                                         
981                                         [ 0, "", "" ];
982
983                                 // Go to html and back, then peel off extra wrappers
984                                 div.innerHTML = wrap[1] + elem + wrap[2];
985                                 
986                                 // Move to the right depth
987                                 while ( wrap[0]-- )
988                                         div = div.lastChild;
989                                 
990                                 // Remove IE's autoinserted <tbody> from table fragments
991                                 if ( jQuery.browser.msie ) {
992                                         
993                                         // String was a <table>, *may* have spurious <tbody>
994                                         var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
995                                                 div.firstChild && div.firstChild.childNodes :
996                                                 
997                                                 // String was a bare <thead> or <tfoot>
998                                                 wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
999                                                         div.childNodes :
1000                                                         [];
1001                                 
1002                                         for ( var j = tbody.length - 1; j >= 0 ; --j )
1003                                                 if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
1004                                                         tbody[ j ].parentNode.removeChild( tbody[ j ] );
1005                                         
1006                                         // IE completely kills leading whitespace when innerHTML is used        
1007                                         if ( /^\s/.test( elem ) )       
1008                                                 div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
1009                                 
1010                                 }
1011                                 
1012                                 elem = jQuery.makeArray( div.childNodes );
1013                         }
1014
1015                         if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
1016                                 return;
1017
1018                         if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
1019                                 ret.push( elem );
1020
1021                         else
1022                                 ret = jQuery.merge( ret, elem );
1023
1024                 });
1025
1026                 return ret;
1027         },
1028         
1029         attr: function( elem, name, value ) {
1030                 // don't set attributes on text and comment nodes
1031                 if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
1032                         return undefined;
1033
1034                 var fix = jQuery.isXMLDoc( elem ) ?
1035                         {} :
1036                         jQuery.props;
1037
1038                 // Safari mis-reports the default selected property of a hidden option
1039                 // Accessing the parent's selectedIndex property fixes it
1040                 if ( name == "selected" && jQuery.browser.safari )
1041                         elem.parentNode.selectedIndex;
1042                 
1043                 // Certain attributes only work when accessed via the old DOM 0 way
1044                 if ( fix[ name ] ) {
1045                         if ( value != undefined )
1046                                 elem[ fix[ name ] ] = value;
1047
1048                         return elem[ fix[ name ] ];
1049
1050                 } else if ( jQuery.browser.msie && name == "style" )
1051                         return jQuery.attr( elem.style, "cssText", value );
1052
1053                 else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName( elem, "form" ) && (name == "action" || name == "method") )
1054                         return elem.getAttributeNode( name ).nodeValue;
1055
1056                 // IE elem.getAttribute passes even for style
1057                 else if ( elem.tagName ) {
1058
1059                         if ( value != undefined ) {
1060                                 // We can't allow the type property to be changed (since it causes problems in IE)
1061                                 if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
1062                                         throw "type property can't be changed";
1063
1064                                 // convert the value to a string (all browsers do this but IE) see #1070
1065                                 elem.setAttribute( name, "" + value );
1066                         }
1067
1068                         if ( jQuery.browser.msie && /href|src/.test( name ) && !jQuery.isXMLDoc( elem ) ) 
1069                                 return elem.getAttribute( name, 2 );
1070
1071                         return elem.getAttribute( name );
1072
1073                 // elem is actually elem.style ... set the style
1074                 } else {
1075                         // IE actually uses filters for opacity
1076                         if ( name == "opacity" && jQuery.browser.msie ) {
1077                                 if ( value != undefined ) {
1078                                         // IE has trouble with opacity if it does not have layout
1079                                         // Force it by setting the zoom level
1080                                         elem.zoom = 1; 
1081         
1082                                         // Set the alpha filter to set the opacity
1083                                         elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
1084                                                 (parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
1085                                 }
1086         
1087                                 return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
1088                                         (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() :
1089                                         "";
1090                         }
1091
1092                         name = name.replace(/-([a-z])/ig, function(all, letter){
1093                                 return letter.toUpperCase();
1094                         });
1095
1096                         if ( value != undefined )
1097                                 elem[ name ] = value;
1098
1099                         return elem[ name ];
1100                 }
1101         },
1102         
1103         trim: function( text ) {
1104                 return (text || "").replace( /^\s+|\s+$/g, "" );
1105         },
1106
1107         makeArray: function( array ) {
1108                 var ret = [];
1109
1110                 if( array != null ){
1111                         var i = array.length;
1112                         //the window, strings and functions also have 'length'
1113                         if( i == null || array.split || array.setInterval || array.call )
1114                                 ret[0] = array;
1115                         else
1116                                 while( i )
1117                                         ret[--i] = array[i];
1118                 }
1119
1120                 return ret;
1121         },
1122
1123         inArray: function( elem, array ) {
1124                 for ( var i = 0, length = array.length; i < length; i++ )
1125                 // Use === because on IE, window == document
1126                         if ( array[ i ] === elem )
1127                                 return i;
1128
1129                 return -1;
1130         },
1131
1132         merge: function( first, second ) {
1133                 // We have to loop this way because IE & Opera overwrite the length
1134                 // expando of getElementsByTagName
1135                 var i = 0;
1136                 // Also, we need to make sure that the correct elements are being returned
1137                 // (IE returns comment nodes in a '*' query)
1138                 if ( jQuery.browser.msie ) {
1139                         for ( ; second[ i ]; i++ )
1140                                 if ( second[ i ].nodeType != 8 )
1141                                         first.push( second[ i ] );
1142
1143                 } else
1144                         for ( ; second[ i ]; i++ )
1145                                 first.push( second[ i ] );
1146
1147                 return first;
1148         },
1149
1150         unique: function( array ) {
1151                 var ret = [], done = {};
1152
1153                 try {
1154
1155                         for ( var i = 0, length = array.length; i < length; i++ ) {
1156                                 var id = jQuery.data( array[ i ] );
1157
1158                                 if ( !done[ id ] ) {
1159                                         done[ id ] = true;
1160                                         ret.push( array[ i ] );
1161                                 }
1162                         }
1163
1164                 } catch( e ) {
1165                         ret = array;
1166                 }
1167
1168                 return ret;
1169         },
1170
1171         grep: function( elems, callback, inv ) {
1172                 var ret = [];
1173
1174                 // Go through the array, only saving the items
1175                 // that pass the validator function
1176                 for ( var i = 0, length = elems.length; i < length; i++ )
1177                         if ( !inv && callback( elems[ i ], i ) || inv && !callback( elems[ i ], i ) )
1178                                 ret.push( elems[ i ] );
1179
1180                 return ret;
1181         },
1182
1183         map: function( elems, callback ) {
1184                 var ret = [];
1185
1186                 // Go through the array, translating each of the items to their
1187                 // new value (or values).
1188                 for ( var i = 0, length = elems.length; i < length; i++ ) {
1189                         var value = callback( elems[ i ], i );
1190
1191                         if ( value != null )
1192                                 ret[ ret.length ] = value;
1193                 }
1194
1195                 return ret.concat.apply( [], ret );
1196         }
1197 });
1198
1199 var userAgent = navigator.userAgent.toLowerCase();
1200
1201 // Figure out what browser is being used
1202 jQuery.browser = {
1203         version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
1204         safari: /webkit/.test( userAgent ),
1205         opera: /opera/.test( userAgent ),
1206         msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
1207         mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
1208 };
1209
1210 var styleFloat = jQuery.browser.msie ?
1211         "styleFloat" :
1212         "cssFloat";
1213         
1214 jQuery.extend({
1215         // Check to see if the W3C box model is being used
1216         boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
1217         
1218         props: {
1219                 "for": "htmlFor",
1220                 "class": "className",
1221                 "float": styleFloat,
1222                 cssFloat: styleFloat,
1223                 styleFloat: styleFloat,
1224                 innerHTML: "innerHTML",
1225                 className: "className",
1226                 value: "value",
1227                 disabled: "disabled",
1228                 checked: "checked",
1229                 readonly: "readOnly",
1230                 selected: "selected",
1231                 maxlength: "maxLength",
1232                 selectedIndex: "selectedIndex",
1233                 defaultValue: "defaultValue",
1234                 tagName: "tagName",
1235                 nodeName: "nodeName"
1236         }
1237 });
1238
1239 jQuery.each({
1240         parent: function(elem){return elem.parentNode;},
1241         parents: function(elem){return jQuery.dir(elem,"parentNode");},
1242         next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
1243         prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
1244         nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
1245         prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
1246         siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
1247         children: function(elem){return jQuery.sibling(elem.firstChild);},
1248         contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
1249 }, function(name, fn){
1250         jQuery.fn[ name ] = function( selector ) {
1251                 var ret = jQuery.map( this, fn );
1252
1253                 if ( selector && typeof selector == "string" )
1254                         ret = jQuery.multiFilter( selector, ret );
1255
1256                 return this.pushStack( jQuery.unique( ret ) );
1257         };
1258 });
1259
1260 jQuery.each({
1261         appendTo: "append",
1262         prependTo: "prepend",
1263         insertBefore: "before",
1264         insertAfter: "after",
1265         replaceAll: "replaceWith"
1266 }, function(name, original){
1267         jQuery.fn[ name ] = function() {
1268                 var args = arguments;
1269
1270                 return this.each(function(){
1271                         for ( var i = 0, length = args.length; i < length; i++ )
1272                                 jQuery( args[ i ] )[ original ]( this );
1273                 });
1274         };
1275 });
1276
1277 jQuery.each({
1278         removeAttr: function( name ) {
1279                 jQuery.attr( this, name, "" );
1280                 if (this.nodeType == 1) 
1281                         this.removeAttribute( name );
1282         },
1283
1284         addClass: function( classNames ) {
1285                 jQuery.className.add( this, classNames );
1286         },
1287
1288         removeClass: function( classNames ) {
1289                 jQuery.className.remove( this, classNames );
1290         },
1291
1292         toggleClass: function( classNames ) {
1293                 jQuery.className[ jQuery.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
1294         },
1295
1296         remove: function( selector ) {
1297                 if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
1298                         // Prevent memory leaks
1299                         jQuery( "*", this ).add(this).each(function(){
1300                                 jQuery.event.remove(this);
1301                                 jQuery.removeData(this);
1302                         });
1303                         if (this.parentNode)
1304                                 this.parentNode.removeChild( this );
1305                 }
1306         },
1307
1308         empty: function() {
1309                 // Remove element nodes and prevent memory leaks
1310                 jQuery( ">*", this ).remove();
1311                 
1312                 // Remove any remaining nodes
1313                 while ( this.firstChild )
1314                         this.removeChild( this.firstChild );
1315         }
1316 }, function(name, fn){
1317         jQuery.fn[ name ] = function(){
1318                 return this.each( fn, arguments );
1319         };
1320 });
1321
1322 jQuery.each([ "Height", "Width" ], function(i, name){
1323         var type = name.toLowerCase();
1324         
1325         jQuery.fn[ type ] = function( size ) {
1326                 // Get window width or height
1327                 return this[0] == window ?
1328                         // Opera reports document.body.client[Width/Height] properly in both quirks and standards
1329                         jQuery.browser.opera && document.body[ "client" + name ] || 
1330                         
1331                         // Safari reports inner[Width/Height] just fine (Mozilla and Opera include scroll bar widths)
1332                         jQuery.browser.safari && window[ "inner" + name ] ||
1333                         
1334                         // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
1335                         document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || document.body[ "client" + name ] :
1336                 
1337                         // Get document width or height
1338                         this[0] == document ?
1339                                 // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
1340                                 Math.max( 
1341                                         Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), 
1342                                         Math.max(document.body["offset" + name], document.documentElement["offset" + name]) 
1343                                 ) :
1344
1345                                 // Get or set width or height on the element
1346                                 size == undefined ?
1347                                         // Get width or height on the element
1348                                         (this.length ? jQuery.css( this[0], type ) : null) :
1349
1350                                         // Set the width or height on the element (default to pixels if value is unitless)
1351                                         this.css( type, size.constructor == String ? size : size + "px" );
1352         };
1353 });