Fix inverted 'orig' and 'fix' event in mouseenter/mouseleave special events.
[jquery.git] / src / event.js
1 /*
2  * A number of helper functions used for managing events.
3  * Many of the ideas behind this code originated from
4  * Dean Edwards' addEvent library.
5  */
6 jQuery.event = {
7
8         // Bind an event to an element
9         // Original by Dean Edwards
10         add: function( elem, types, handler, data ) {
11                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
12                         return;
13                 }
14
15                 // For whatever reason, IE has trouble passing the window object
16                 // around, causing it to be cloned in the process
17                 if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
18                         elem = window;
19                 }
20
21                 // Make sure that the function being executed has a unique ID
22                 if ( !handler.guid ) {
23                         handler.guid = this.guid++;
24                 }
25
26                 // if data is passed, bind to handler
27                 if ( data !== undefined ) {
28                         // Create temporary function pointer to original handler
29                         var fn = handler;
30
31                         // Create unique handler function, wrapped around original handler
32                         handler = this.proxy( fn );
33
34                         // Store data in unique handler
35                         handler.data = data;
36                 }
37
38                 // Init the element's event structure
39                 var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
40                         handle = jQuery.data( elem, "handle" ) || jQuery.data( elem, "handle", function() {
41                                 // Handle the second event of a trigger and when
42                                 // an event is called after a page has unloaded
43                                 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
44                                         jQuery.event.handle.apply( arguments.callee.elem, arguments ) :
45                                         undefined;
46                         });
47                 // Add elem as a property of the handle function
48                 // This is to prevent a memory leak with non-native
49                 // event in IE.
50                 handle.elem = elem;
51
52                 // Handle multiple events separated by a space
53                 // jQuery(...).bind("mouseover mouseout", fn);
54                 types = types.split( /\s+/ );
55                 var type, i=0;
56                 while ( (type = types[ i++ ]) ) {
57                         // Namespaced event handlers
58                         var namespaces = type.split(".");
59                         type = namespaces.shift();
60                         handler.type = namespaces.slice(0).sort().join(".");
61
62                         // Get the current list of functions bound to this event
63                         var handlers = events[ type ],
64                                 special = this.special[ type ] || {};
65
66                         
67
68                         // Init the event handler queue
69                         if ( !handlers ) {
70                                 handlers = events[ type ] = {};
71
72                                 // Check for a special event handler
73                                 // Only use addEventListener/attachEvent if the special
74                                 // events handler returns false
75                                 if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
76                                         // Bind the global event handler to the element
77                                         if ( elem.addEventListener ) {
78                                                 elem.addEventListener( type, handle, false );
79                                         } else if ( elem.attachEvent ) {
80                                                 elem.attachEvent( "on" + type, handle );
81                                         }
82                                 }
83                         }
84                         
85                         if ( special.add ) { 
86                                 var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers ); 
87                                 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) { 
88                                         modifiedHandler.guid = modifiedHandler.guid || handler.guid; 
89                                         handler = modifiedHandler; 
90                                 } 
91                         } 
92                         
93                         // Add the function to the element's handler list
94                         handlers[ handler.guid ] = handler;
95
96                         // Keep track of which events have been used, for global triggering
97                         this.global[ type ] = true;
98                 }
99
100                 // Nullify elem to prevent memory leaks in IE
101                 elem = null;
102         },
103
104         guid: 1,
105         global: {},
106
107         // Detach an event or set of events from an element
108         remove: function( elem, types, handler ) {
109                 // don't do events on text and comment nodes
110                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
111                         return;
112                 }
113
114                 var events = jQuery.data( elem, "events" ), ret, type, fn;
115
116                 if ( events ) {
117                         // Unbind all events for the element
118                         if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
119                                 for ( type in events ) {
120                                         this.remove( elem, type + (types || "") );
121                                 }
122                         } else {
123                                 // types is actually an event object here
124                                 if ( types.type ) {
125                                         handler = types.handler;
126                                         types = types.type;
127                                 }
128
129                                 // Handle multiple events separated by a space
130                                 // jQuery(...).unbind("mouseover mouseout", fn);
131                                 types = types.split(/\s+/);
132                                 var i = 0;
133                                 while ( (type = types[ i++ ]) ) {
134                                         // Namespaced event handlers
135                                         var namespaces = type.split(".");
136                                         type = namespaces.shift();
137                                         var all = !namespaces.length,
138                                                 cleaned = jQuery.map( namespaces.slice(0).sort() , function(nm){ return nm.replace(/[^\w\s\.\|`]/g, function(ch){return "\\"+ch;  }); }),
139                                                 namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
140                                                 special = this.special[ type ] || {};
141
142                                         if ( events[ type ] ) {
143                                                 // remove the given handler for the given type
144                                                 if ( handler ) {
145                                                         fn = events[ type ][ handler.guid ];
146                                                         delete events[ type ][ handler.guid ];
147
148                                                 // remove all handlers for the given type
149                                                 } else {
150                                                         for ( var handle in events[ type ] ) {
151                                                                 // Handle the removal of namespaced events
152                                                                 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
153                                                                         delete events[ type ][ handle ];
154                                                                 }
155                                                         }
156                                                 }
157
158                                                 if ( special.remove ) {
159                                                         special.remove.call( elem, namespaces, fn);
160                                                 }
161
162                                                 // remove generic event handler if no more handlers exist
163                                                 for ( ret in events[ type ] ) {
164                                                         break;
165                                                 }
166                                                 if ( !ret ) {
167                                                         if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
168                                                                 if ( elem.removeEventListener ) {
169                                                                         elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
170                                                                 } else if ( elem.detachEvent ) {
171                                                                         elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
172                                                                 }
173                                                         }
174                                                         ret = null;
175                                                         delete events[ type ];
176                                                 }
177                                         }
178                                 }
179                         }
180
181                         // Remove the expando if it's no longer used
182                         for ( ret in events ) {
183                                 break;
184                         }
185                         if ( !ret ) {
186                                 var handle = jQuery.data( elem, "handle" );
187                                 if ( handle ) {
188                                         handle.elem = null;
189                                 }
190                                 jQuery.removeData( elem, "events" );
191                                 jQuery.removeData( elem, "handle" );
192                         }
193                 }
194         },
195
196         // bubbling is internal
197         trigger: function( event, data, elem /*, bubbling */ ) {
198                 // Event object or event type
199                 var type = event.type || event,
200                         bubbling = arguments[3];
201
202                 if ( !bubbling ) {
203                         event = typeof event === "object" ?
204                                 // jQuery.Event object
205                                 event[expando] ? event :
206                                 // Object literal
207                                 jQuery.extend( jQuery.Event(type), event ) :
208                                 // Just the event type (string)
209                                 jQuery.Event(type);
210
211                         if ( type.indexOf("!") >= 0 ) {
212                                 event.type = type = type.slice(0, -1);
213                                 event.exclusive = true;
214                         }
215
216                         // Handle a global trigger
217                         if ( !elem ) {
218                                 // Don't bubble custom events when global (to avoid too much overhead)
219                                 event.stopPropagation();
220                                 // Only trigger if we've ever bound an event for it
221                                 if ( this.global[ type ] ) {
222                                         jQuery.each( jQuery.cache, function() {
223                                                 if ( this.events && this.events[type] ) {
224                                                         jQuery.event.trigger( event, data, this.handle.elem );
225                                                 }
226                                         });
227                                 }
228                         }
229
230                         // Handle triggering a single element
231
232                         // don't do events on text and comment nodes
233                         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
234                                 return undefined;
235                         }
236
237                         // Clean up in case it is reused
238                         event.result = undefined;
239                         event.target = elem;
240
241                         // Clone the incoming data, if any
242                         data = jQuery.makeArray( data );
243                         data.unshift( event );
244                 }
245
246                 event.currentTarget = elem;
247
248                 // Trigger the event, it is assumed that "handle" is a function
249                 var handle = jQuery.data( elem, "handle" );
250                 if ( handle ) {
251                         handle.apply( elem, data );
252                 }
253
254                 var nativeFn, nativeHandler;
255                 try {
256                         nativeFn = elem[ type ];
257                         nativeHandler = elem[ "on" + type ];
258                 // prevent IE from throwing an error for some elements with some event types, see #3533
259                 } catch (e) {}
260
261                 var isClick = jQuery.nodeName(elem, "a") && type === "click";
262
263                 // Trigger the native events (except for clicks on links)
264                 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !isClick ) {
265                         this.triggered = true;
266                         try {
267                                 nativeFn();
268                         // prevent IE from throwing an error for some hidden elements
269                         } catch (e) {}
270
271                 // Handle triggering native .onfoo handlers
272                 } else if ( nativeHandler && nativeHandler.apply( elem, data ) === false ) {
273                         event.result = false;
274                 }
275
276                 this.triggered = false;
277
278                 if ( !event.isPropagationStopped() ) {
279                         var parent = elem.parentNode || elem.ownerDocument;
280                         if ( parent ) {
281                                 jQuery.event.trigger( event, data, parent, true );
282                         }
283                 }
284         },
285
286         handle: function( event ) {
287                 // returned undefined or false
288                 var all, handlers;
289
290                 event = arguments[0] = jQuery.event.fix( event || window.event );
291                 event.currentTarget = this;
292
293                 // Namespaced event handlers
294                 var namespaces = event.type.split(".");
295                 event.type = namespaces.shift();
296
297                 // Cache this now, all = true means, any handler
298                 all = !namespaces.length && !event.exclusive;
299
300                 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
301
302                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
303
304                 for ( var j in handlers ) {
305                         var handler = handlers[ j ];
306
307                         // Filter the functions by class
308                         if ( all || namespace.test(handler.type) ) {
309                                 // Pass in a reference to the handler function itself
310                                 // So that we can later remove it
311                                 event.handler = handler;
312                                 event.data = handler.data;
313
314                                 var ret = handler.apply( this, arguments );
315
316                                 if ( ret !== undefined ) {
317                                         event.result = ret;
318                                         if ( ret === false ) {
319                                                 event.preventDefault();
320                                                 event.stopPropagation();
321                                         }
322                                 }
323
324                                 if ( event.isImmediatePropagationStopped() ) {
325                                         break;
326                                 }
327
328                         }
329                 }
330         },
331
332         props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
333
334         fix: function( event ) {
335                 if ( event[ expando ] ) {
336                         return event;
337                 }
338
339                 // store a copy of the original event object
340                 // and "clone" to set read-only properties
341                 var originalEvent = event;
342                 event = jQuery.Event( originalEvent );
343
344                 for ( var i = this.props.length, prop; i; ) {
345                         prop = this.props[ --i ];
346                         event[ prop ] = originalEvent[ prop ];
347                 }
348
349                 // Fix target property, if necessary
350                 if ( !event.target ) {
351                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
352                 }
353
354                 // check if target is a textnode (safari)
355                 if ( event.target.nodeType === 3 ) {
356                         event.target = event.target.parentNode;
357                 }
358
359                 // Add relatedTarget, if necessary
360                 if ( !event.relatedTarget && event.fromElement ) {
361                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
362                 }
363
364                 // Calculate pageX/Y if missing and clientX/Y available
365                 if ( event.pageX == null && event.clientX != null ) {
366                         var doc = document.documentElement, body = document.body;
367                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
368                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
369                 }
370
371                 // Add which for key events
372                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
373                         event.which = event.charCode || event.keyCode;
374                 }
375
376                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
377                 if ( !event.metaKey && event.ctrlKey ) {
378                         event.metaKey = event.ctrlKey;
379                 }
380
381                 // Add which for click: 1 == left; 2 == middle; 3 == right
382                 // Note: button is not normalized, so don't use it
383                 if ( !event.which && event.button !== undefined ) {
384                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
385                 }
386
387                 return event;
388         },
389
390         proxy: function( fn, proxy, thisObject ) {
391                 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
392                         thisObject = proxy;
393                         proxy = undefined;
394                 }
395                 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
396                 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
397                 // Set the guid of unique handler to the same of original handler, so it can be removed
398                 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
399                 // So proxy can be declared as an argument
400                 return proxy;
401         },
402
403         special: {
404                 ready: {
405                         // Make sure the ready event is setup
406                         setup: bindReady,
407                         teardown: function() {}
408                 },
409
410                 live: {
411                         add: function( proxy, data, namespaces, live ) {
412                                 jQuery.extend( proxy, data || {} );
413
414                                 proxy.guid += data.selector + data.live; 
415                                 jQuery.event.add( this, data.live, liveHandler, data ); 
416                                 
417                         },
418
419                         remove: function( namespaces ) {
420                                 if ( namespaces.length ) {
421                                         var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
422
423                                         jQuery.each( (jQuery.data(this, "events").live || {}), function() {
424                                                 if ( name.test(this.type) ) {
425                                                         remove++;
426                                                 }
427                                         });
428
429                                         if ( remove < 1 ) {
430                                                 jQuery.event.remove( this, namespaces[0], liveHandler );
431                                         }
432                                 }
433                         },
434                         special: {}
435                 }
436         }
437 };
438
439 jQuery.Event = function( src ){
440         // Allow instantiation without the 'new' keyword
441         if ( !this.preventDefault ) {
442                 return new jQuery.Event( src );
443         }
444
445         // Event object
446         if ( src && src.type ) {
447                 this.originalEvent = src;
448                 this.type = src.type;
449         // Event type
450         } else {
451                 this.type = src;
452         }
453
454         // timeStamp is buggy for some events on Firefox(#3843)
455         // So we won't rely on the native value
456         this.timeStamp = now();
457
458         // Mark it as fixed
459         this[ expando ] = true;
460 };
461
462 function returnFalse() {
463         return false;
464 }
465 function returnTrue() {
466         return true;
467 }
468
469 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
470 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
471 jQuery.Event.prototype = {
472         preventDefault: function() {
473                 this.isDefaultPrevented = returnTrue;
474
475                 var e = this.originalEvent;
476                 if ( !e ) {
477                         return;
478                 }
479                 
480                 // if preventDefault exists run it on the original event
481                 if ( e.preventDefault ) {
482                         e.preventDefault();
483                 }
484                 // otherwise set the returnValue property of the original event to false (IE)
485                 e.returnValue = false;
486         },
487         stopPropagation: function() {
488                 this.isPropagationStopped = returnTrue;
489
490                 var e = this.originalEvent;
491                 if ( !e ) {
492                         return;
493                 }
494                 // if stopPropagation exists run it on the original event
495                 if ( e.stopPropagation ) {
496                         e.stopPropagation();
497                 }
498                 // otherwise set the cancelBubble property of the original event to true (IE)
499                 e.cancelBubble = true;
500         },
501         stopImmediatePropagation: function(){
502                 this.isImmediatePropagationStopped = returnTrue;
503                 this.stopPropagation();
504         },
505         isDefaultPrevented: returnFalse,
506         isPropagationStopped: returnFalse,
507         isImmediatePropagationStopped: returnFalse
508 };
509 // Checks if an event happened on an element within another element
510 // Used in jQuery.event.special.mouseenter and mouseleave handlers
511 var withinElement = function( event ) {
512         // Check if mouse(over|out) are still within the same parent element
513         var parent = event.relatedTarget;
514         // Traverse up the tree
515         while ( parent && parent != this ) {
516                 // Firefox sometimes assigns relatedTarget a XUL element
517                 // which we cannot access the parentNode property of
518                 try { parent = parent.parentNode; }
519                 // assuming we've left the element since we most likely mousedover a xul element
520                 catch(e) { break; }
521         }
522
523         if ( parent != this ) {
524                 // set the correct event type
525                 event.type = event.data;
526                 // handle event if we actually just moused on to a non sub-element
527                 jQuery.event.handle.apply( this, arguments );
528         }
529
530 },
531
532 // In case of event delegation, we only need to rename the event.type,
533 // liveHandler will take care of the rest.
534 delegate = function( event ) {
535         event.type = event.data;
536         jQuery.event.handle.apply( this, arguments );
537 };
538
539 // Create mouseenter and mouseleave events
540 jQuery.each({
541         mouseenter: "mouseover",
542         mouseleave: "mouseout"
543 }, function( orig, fix ) {
544         jQuery.event.special[ orig ] = {
545                 setup: function(data){
546                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
547                 },
548                 teardown: function(data){
549                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
550                 }
551         };
552 });
553
554 // submit delegation
555 jQuery.event.special.submit = {
556         setup: function( data, namespaces, fn ) {
557                 if ( !jQuery.support.submitBubbles && this.nodeName.toLowerCase() !== "form" ) {
558                         jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
559                                 var elem = e.target, type = elem.type;
560
561                                 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
562                                         return trigger( "submit", this, arguments );
563                                 }
564                         });
565          
566                         jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
567                                 var elem = e.target, type = elem.type;
568
569                                 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
570                                         return trigger( "submit", this, arguments );
571                                 }
572                         });
573                 }
574
575                 return false;
576         },
577
578         remove: function( namespaces, fn ) {
579                 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
580                 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
581         }
582 };
583
584 // change delegation, happens here so we have bind.
585 jQuery.event.special.change = {
586         filters: {
587                 click: function( e ) { 
588                         var elem = e.target;
589
590                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
591                                 return trigger( "change", this, arguments );
592                         }
593
594                         return changeFilters.keyup.call( this, e );
595                 }, 
596                 keyup: function( e ) { 
597                         var elem = e.target, data, index = elem.selectedIndex + "";
598
599                         if ( elem.nodeName.toLowerCase() === "select" ) {
600                                 data = jQuery.data( elem, "_change_data" );
601                                 jQuery.data( elem, "_change_data", index );
602
603                                 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
604                                         return trigger( "change", this, arguments );
605                                 }
606                         }
607                 },
608                 beforeactivate: function( e ) {
609                         var elem = e.target;
610
611                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
612                                 return trigger( "change", this, arguments );
613                         }
614                 },
615                 blur: function( e ) {
616                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
617
618                         if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
619                                 && jQuery.data(elem, "_change_data") !== elem.value ) {
620
621                                 return trigger( "change", this, arguments );
622                         }
623                 },
624                 focus: function( e ) {
625                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
626
627                         if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
628                                 jQuery.data( elem, "_change_data", elem.value );
629                         }
630                 }
631         },
632         setup: function( data, namespaces, fn ) {
633                 // return false if we bubble
634                 if ( !jQuery.support.changeBubbles ) {
635                         for ( var type in changeFilters ) {
636                                 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
637                         }
638                 }
639                 
640                 // always want to listen for change for trigger
641                 return false;
642         },
643         remove: function( namespaces, fn ) {
644                 if ( !jQuery.support.changeBubbles ) {
645                         for ( var type in changeFilters ) {
646                                 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
647                         }
648                 }
649         }
650 };
651
652 var changeFilters = jQuery.event.special.change.filters;
653
654 function trigger( type, elem, args ) {
655         args[0].type = type;
656         return jQuery.event.handle.apply( elem, args );
657 }
658
659 // Create "bubbling" focus and blur events
660 jQuery.each({
661         focus: "focusin",
662         blur: "focusout"
663 }, function( orig, fix ){
664         var event = jQuery.event,
665                 handle = event.handle;
666         
667         function ieHandler() { 
668                 arguments[0].type = orig;
669                 return handle.apply(this, arguments);
670         }
671
672         event.special[orig] = {
673                 setup:function() {
674                         if ( this.addEventListener ) {
675                                 this.addEventListener( orig, handle, true );
676                         } else {
677                                 event.add( this, fix, ieHandler );
678                         }
679                 }, 
680                 teardown:function() { 
681                         if ( this.removeEventListener ) {
682                                 this.removeEventListener( orig, handle, true );
683                         } else {
684                                 event.remove( this, fix, ieHandler );
685                         }
686                 }
687         };
688 });
689
690 jQuery.fn.extend({
691         // TODO: make bind(), unbind() and one() DRY!
692         bind: function( type, data, fn, thisObject ) {
693                 // Handle object literals
694                 if ( typeof type === "object" ) {
695                         for ( var key in type ) {
696                                 this.bind(key, data, type[key], fn);
697                         }
698                         return this;
699                 }
700                 
701                 if ( jQuery.isFunction( data ) ) {
702                         thisObject = fn;
703                         fn = data;
704                         data = undefined;
705                 }
706                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
707                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
708                         jQuery.event.add( this, type, fn, data );
709                 });
710         },
711
712         one: function( type, data, fn, thisObject ) {
713                 // Handle object literals
714                 if ( typeof type === "object" ) {
715                         for ( var key in type ) {
716                                 this.one(key, data, type[key], fn);
717                         }
718                         return this;
719                 }
720                 
721                 if ( jQuery.isFunction( data ) ) {
722                         thisObject = fn;
723                         fn = data;
724                         data = undefined;
725                 }
726                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
727                 var one = jQuery.event.proxy( fn, function( event ) {
728                         jQuery( this ).unbind( event, one );
729                         return fn.apply( this, arguments );
730                 });
731                 return this.each(function() {
732                         jQuery.event.add( this, type, one, data );
733                 });
734         },
735
736         unbind: function( type, fn ) {
737                 // Handle object literals
738                 if ( typeof type === "object" && !type.preventDefault ) {
739                         for ( var key in type ) {
740                                 this.unbind(key, type[key]);
741                         }
742                         return this;
743                 }
744                 
745                 return this.each(function() {
746                         jQuery.event.remove( this, type, fn );
747                 });
748         },
749
750         trigger: function( type, data ) {
751                 return this.each(function() {
752                         jQuery.event.trigger( type, data, this );
753                 });
754         },
755
756         triggerHandler: function( type, data ) {
757                 if ( this[0] ) {
758                         var event = jQuery.Event( type );
759                         event.preventDefault();
760                         event.stopPropagation();
761                         jQuery.event.trigger( event, data, this[0] );
762                         return event.result;
763                 }
764         },
765
766         toggle: function( fn ) {
767                 // Save reference to arguments for access in closure
768                 var args = arguments, i = 1;
769
770                 // link all the functions, so any of them can unbind this click handler
771                 while( i < args.length ) {
772                         jQuery.event.proxy( fn, args[ i++ ] );
773                 }
774
775                 return this.click( jQuery.event.proxy( fn, function( event ) {
776                         // Figure out which function to execute
777                         var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
778                         jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
779
780                         // Make sure that clicks stop
781                         event.preventDefault();
782
783                         // and execute the function
784                         return args[ lastToggle ].apply( this, arguments ) || false;
785                 }));
786         },
787
788         hover: function( fnOver, fnOut ) {
789                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
790         },
791
792         ready: function( fn ) {
793                 // Attach the listeners
794                 bindReady();
795
796                 // If the DOM is already ready
797                 if ( jQuery.isReady ) {
798                         // Execute the function immediately
799                         fn.call( document, jQuery );
800
801                 // Otherwise, remember the function for later
802                 } else {
803                         // Add the function to the wait list
804                         jQuery.readyList.push( fn );
805                 }
806
807                 return this;
808         },
809
810         live: function( type, data, fn, thisObject ) {
811                 if ( jQuery.isFunction( data ) ) {
812                         if ( fn !== undefined ) {
813                                 thisObject = fn;
814                         }
815                         fn = data;
816                         data = undefined;
817                 }
818                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
819                         data: data, selector: this.selector, live: type
820                 }, fn, thisObject );
821                 return this;
822         },
823
824         die: function( type, fn ) {
825                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
826                 return this;
827         }
828 });
829
830 function liveHandler( event ) {
831         var stop = true, elems = [], selectors = [], args = arguments,
832                 related, match, fn, elem, j, i, data,
833                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
834
835         for ( j in live ) {
836                 fn = live[j];
837                 if ( fn.live === event.type ||
838                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
839
840                         data = fn.data;
841                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
842                                         !data.beforeFilter[event.type](event)) ) {
843                                 selectors.push( fn.selector );
844                         }
845                 } else {
846                         delete live[j];
847                 }
848         }
849
850         match = jQuery( event.target ).closest( selectors, event.currentTarget );
851
852         for ( i = 0, l = match.length; i < l; i++ ) {
853                 for ( j in live ) {
854                         fn = live[j];
855                         elem = match[i].elem;
856                         related = null;
857
858                         if ( match[i].selector === fn.selector ) {
859                                 // Those two events require additional checking
860                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
861                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
862                                 }
863
864                                 if ( !related || related !== elem ) {
865                                         elems.push({ elem: elem, fn: fn });
866                                 }
867                         }
868                 }
869         }
870
871         for ( i = 0, l = elems.length; i < l; i++ ) {
872                 match = elems[i];
873                 event.currentTarget = match.elem;
874                 event.data = match.fn.data;
875                 if ( match.fn.apply( match.elem, args ) === false ) {
876                         stop = false;
877                         break;
878                 }
879         }
880
881         return stop;
882 }
883
884 function liveConvert( type, selector ) {
885         return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
886                                                                   .replace(/\./g, "`")
887                                                                   .replace(/ /g, "|")].join(".");
888 }
889
890 jQuery.extend({
891         isReady: false,
892         readyList: [],
893         // Handle when the DOM is ready
894         ready: function() {
895                 // Make sure that the DOM is not already loaded
896                 if ( !jQuery.isReady ) {
897                         if ( !document.body ) {
898                                 return setTimeout( jQuery.ready, 13 );
899                         }
900
901                         // Remember that the DOM is ready
902                         jQuery.isReady = true;
903
904                         // If there are functions bound, to execute
905                         if ( jQuery.readyList ) {
906                                 // Execute all of them
907                                 var fn, i = 0;
908                                 while ( (fn = jQuery.readyList[ i++ ]) ) {
909                                         fn.call( document, jQuery );
910                                 }
911
912                                 // Reset the list of functions
913                                 jQuery.readyList = null;
914                         }
915
916                         // Trigger any bound ready events
917                         jQuery( document ).triggerHandler( "ready" );
918                 }
919         }
920 });
921
922 var readyBound = false;
923
924 function bindReady() {
925         if ( readyBound ) { return; }
926         readyBound = true;
927
928         // Catch cases where $(document).ready() is called after the
929         // browser event has already occurred.
930         if ( document.readyState === "complete" ) {
931                 return jQuery.ready();
932         }
933
934         // Mozilla, Opera and webkit nightlies currently support this event
935         if ( document.addEventListener ) {
936                 // Use the handy event callback
937                 document.addEventListener( "DOMContentLoaded", function() {
938                         document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
939                         jQuery.ready();
940                 }, false );
941
942         // If IE event model is used
943         } else if ( document.attachEvent ) {
944                 // ensure firing before onload,
945                 // maybe late but safe also for iframes
946                 document.attachEvent("onreadystatechange", function() {
947                         // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
948                         if ( document.readyState === "complete" ) {
949                                 document.detachEvent( "onreadystatechange", arguments.callee );
950                                 jQuery.ready();
951                         }
952                 });
953
954                 // If IE and not a frame
955                 // continually check to see if the document is ready
956                 var toplevel = false;
957
958                 try {
959                         toplevel = window.frameElement == null;
960                 } catch(e){}
961
962                 if ( document.documentElement.doScroll && toplevel ) {
963                         (function() {
964                                 if ( jQuery.isReady ) {
965                                         return;
966                                 }
967
968                                 try {
969                                         // If IE is used, use the trick by Diego Perini
970                                         // http://javascript.nwbox.com/IEContentLoaded/
971                                         document.documentElement.doScroll("left");
972                                 } catch( error ) {
973                                         setTimeout( arguments.callee, 0 );
974                                         return;
975                                 }
976
977                                 // and execute any waiting functions
978                                 jQuery.ready();
979                         })();
980                 }
981         }
982
983         // A fallback to window.onload, that will always work
984         jQuery.event.add( window, "load", jQuery.ready );
985 }
986
987 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
988         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
989         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
990
991         // Handle event binding
992         jQuery.fn[ name ] = function( fn ) {
993                 return fn ? this.bind( name, fn ) : this.trigger( name );
994         };
995 });
996
997 // Prevent memory leaks in IE
998 // Window isn't included so as not to unbind existing unload events
999 // More info:
1000 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1001 /*@cc_on
1002 jQuery( window ).bind( 'unload', function() {
1003         for ( var id in jQuery.cache ) {
1004                 // Skip the window
1005                 if ( id != 1 && jQuery.cache[ id ].handle ) {
1006                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1007                 }
1008         }
1009 });
1010 @*/