Make sure that we don't try to trigger non-existant native events on applets, embed...
[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 eventHandle() {
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( eventHandle.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                         if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
257                                 nativeFn = elem[ type ];
258                                 nativeHandler = elem[ "on" + type ];
259                         }
260                 // prevent IE from throwing an error for some elements with some event types, see #3533
261                 } catch (e) {}
262
263                 var isClick = jQuery.nodeName(elem, "a") && type === "click";
264
265                 // Trigger the native events (except for clicks on links)
266                 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !isClick ) {
267                         this.triggered = true;
268                         try {
269                                 elem[ type ]();
270                         // prevent IE from throwing an error for some hidden elements
271                         } catch (e) {}
272
273                 // Handle triggering native .onfoo handlers
274                 } else if ( nativeHandler && elem[ "on" + type ].apply( elem, data ) === false ) {
275                         event.result = false;
276                 }
277
278                 this.triggered = false;
279
280                 if ( !event.isPropagationStopped() ) {
281                         var parent = elem.parentNode || elem.ownerDocument;
282                         if ( parent ) {
283                                 jQuery.event.trigger( event, data, parent, true );
284                         }
285                 }
286         },
287
288         handle: function( event ) {
289                 // returned undefined or false
290                 var all, handlers;
291
292                 event = arguments[0] = jQuery.event.fix( event || window.event );
293                 event.currentTarget = this;
294
295                 // Namespaced event handlers
296                 var namespaces = event.type.split(".");
297                 event.type = namespaces.shift();
298
299                 // Cache this now, all = true means, any handler
300                 all = !namespaces.length && !event.exclusive;
301
302                 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
303
304                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
305
306                 for ( var j in handlers ) {
307                         var handler = handlers[ j ];
308
309                         // Filter the functions by class
310                         if ( all || namespace.test(handler.type) ) {
311                                 // Pass in a reference to the handler function itself
312                                 // So that we can later remove it
313                                 event.handler = handler;
314                                 event.data = handler.data;
315
316                                 var ret = handler.apply( this, arguments );
317
318                                 if ( ret !== undefined ) {
319                                         event.result = ret;
320                                         if ( ret === false ) {
321                                                 event.preventDefault();
322                                                 event.stopPropagation();
323                                         }
324                                 }
325
326                                 if ( event.isImmediatePropagationStopped() ) {
327                                         break;
328                                 }
329
330                         }
331                 }
332
333                 return event.result;
334         },
335
336         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(" "),
337
338         fix: function( event ) {
339                 if ( event[ expando ] ) {
340                         return event;
341                 }
342
343                 // store a copy of the original event object
344                 // and "clone" to set read-only properties
345                 var originalEvent = event;
346                 event = jQuery.Event( originalEvent );
347
348                 for ( var i = this.props.length, prop; i; ) {
349                         prop = this.props[ --i ];
350                         event[ prop ] = originalEvent[ prop ];
351                 }
352
353                 // Fix target property, if necessary
354                 if ( !event.target ) {
355                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
356                 }
357
358                 // check if target is a textnode (safari)
359                 if ( event.target.nodeType === 3 ) {
360                         event.target = event.target.parentNode;
361                 }
362
363                 // Add relatedTarget, if necessary
364                 if ( !event.relatedTarget && event.fromElement ) {
365                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
366                 }
367
368                 // Calculate pageX/Y if missing and clientX/Y available
369                 if ( event.pageX == null && event.clientX != null ) {
370                         var doc = document.documentElement, body = document.body;
371                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
372                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
373                 }
374
375                 // Add which for key events
376                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
377                         event.which = event.charCode || event.keyCode;
378                 }
379
380                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
381                 if ( !event.metaKey && event.ctrlKey ) {
382                         event.metaKey = event.ctrlKey;
383                 }
384
385                 // Add which for click: 1 == left; 2 == middle; 3 == right
386                 // Note: button is not normalized, so don't use it
387                 if ( !event.which && event.button !== undefined ) {
388                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
389                 }
390
391                 return event;
392         },
393
394         proxy: function( fn, proxy, thisObject ) {
395                 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
396                         thisObject = proxy;
397                         proxy = undefined;
398                 }
399                 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
400                 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
401                 // Set the guid of unique handler to the same of original handler, so it can be removed
402                 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
403                 // So proxy can be declared as an argument
404                 return proxy;
405         },
406
407         special: {
408                 ready: {
409                         // Make sure the ready event is setup
410                         setup: jQuery.bindReady,
411                         teardown: function() {}
412                 },
413
414                 live: {
415                         add: function( proxy, data, namespaces, live ) {
416                                 jQuery.extend( proxy, data || {} );
417
418                                 proxy.guid += data.selector + data.live; 
419                                 jQuery.event.add( this, data.live, liveHandler, data ); 
420                                 
421                         },
422
423                         remove: function( namespaces ) {
424                                 if ( namespaces.length ) {
425                                         var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
426
427                                         jQuery.each( (jQuery.data(this, "events").live || {}), function() {
428                                                 if ( name.test(this.type) ) {
429                                                         remove++;
430                                                 }
431                                         });
432
433                                         if ( remove < 1 ) {
434                                                 jQuery.event.remove( this, namespaces[0], liveHandler );
435                                         }
436                                 }
437                         },
438                         special: {}
439                 },
440                 beforeunload: {
441                         setup: function( data, namespaces, fn ) {
442                                 // We only want to do this special case on windows
443                                 if ( this.setInterval ) {
444                                         this.onbeforeunload = fn;
445                                 }
446
447                                 return false;
448                         },
449                         teardown: function( namespaces, fn ) {
450                                 if ( this.onbeforeunload === fn ) {
451                                         this.onbeforeunload = null;
452                                 }
453                         }
454                 }
455         }
456 };
457
458 jQuery.Event = function( src ){
459         // Allow instantiation without the 'new' keyword
460         if ( !this.preventDefault ) {
461                 return new jQuery.Event( src );
462         }
463
464         // Event object
465         if ( src && src.type ) {
466                 this.originalEvent = src;
467                 this.type = src.type;
468         // Event type
469         } else {
470                 this.type = src;
471         }
472
473         // timeStamp is buggy for some events on Firefox(#3843)
474         // So we won't rely on the native value
475         this.timeStamp = now();
476
477         // Mark it as fixed
478         this[ expando ] = true;
479 };
480
481 function returnFalse() {
482         return false;
483 }
484 function returnTrue() {
485         return true;
486 }
487
488 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
489 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
490 jQuery.Event.prototype = {
491         preventDefault: function() {
492                 this.isDefaultPrevented = returnTrue;
493
494                 var e = this.originalEvent;
495                 if ( !e ) {
496                         return;
497                 }
498                 
499                 // if preventDefault exists run it on the original event
500                 if ( e.preventDefault ) {
501                         e.preventDefault();
502                 }
503                 // otherwise set the returnValue property of the original event to false (IE)
504                 e.returnValue = false;
505         },
506         stopPropagation: function() {
507                 this.isPropagationStopped = returnTrue;
508
509                 var e = this.originalEvent;
510                 if ( !e ) {
511                         return;
512                 }
513                 // if stopPropagation exists run it on the original event
514                 if ( e.stopPropagation ) {
515                         e.stopPropagation();
516                 }
517                 // otherwise set the cancelBubble property of the original event to true (IE)
518                 e.cancelBubble = true;
519         },
520         stopImmediatePropagation: function(){
521                 this.isImmediatePropagationStopped = returnTrue;
522                 this.stopPropagation();
523         },
524         isDefaultPrevented: returnFalse,
525         isPropagationStopped: returnFalse,
526         isImmediatePropagationStopped: returnFalse
527 };
528 // Checks if an event happened on an element within another element
529 // Used in jQuery.event.special.mouseenter and mouseleave handlers
530 var withinElement = function( event ) {
531         // Check if mouse(over|out) are still within the same parent element
532         var parent = event.relatedTarget;
533         // Traverse up the tree
534         while ( parent && parent != this ) {
535                 // Firefox sometimes assigns relatedTarget a XUL element
536                 // which we cannot access the parentNode property of
537                 try { parent = parent.parentNode; }
538                 // assuming we've left the element since we most likely mousedover a xul element
539                 catch(e) { break; }
540         }
541
542         if ( parent != this ) {
543                 // set the correct event type
544                 event.type = event.data;
545                 // handle event if we actually just moused on to a non sub-element
546                 jQuery.event.handle.apply( this, arguments );
547         }
548
549 },
550
551 // In case of event delegation, we only need to rename the event.type,
552 // liveHandler will take care of the rest.
553 delegate = function( event ) {
554         event.type = event.data;
555         jQuery.event.handle.apply( this, arguments );
556 };
557
558 // Create mouseenter and mouseleave events
559 jQuery.each({
560         mouseenter: "mouseover",
561         mouseleave: "mouseout"
562 }, function( orig, fix ) {
563         jQuery.event.special[ orig ] = {
564                 setup: function(data){
565                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
566                 },
567                 teardown: function(data){
568                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
569                 }
570         };
571 });
572
573 // submit delegation
574 if ( !jQuery.support.submitBubbles ) {
575
576 jQuery.event.special.submit = {
577         setup: function( data, namespaces, fn ) {
578                 if ( this.nodeName.toLowerCase() !== "form" ) {
579                         jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
580                                 var elem = e.target, type = elem.type;
581
582                                 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
583                                         return trigger( "submit", this, arguments );
584                                 }
585                         });
586          
587                         jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
588                                 var elem = e.target, type = elem.type;
589
590                                 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
591                                         return trigger( "submit", this, arguments );
592                                 }
593                         });
594                 }
595         },
596
597         remove: function( namespaces, fn ) {
598                 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
599                 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
600         }
601 };
602
603 }
604
605 // change delegation, happens here so we have bind.
606 if ( !jQuery.support.changeBubbles ) {
607
608 jQuery.event.special.change = {
609         filters: {
610                 click: function( e ) { 
611                         var elem = e.target;
612
613                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
614                                 return trigger( "change", this, arguments );
615                         }
616
617                         return changeFilters.keyup.call( this, e );
618                 }, 
619                 keyup: function( e ) { 
620                         var elem = e.target, data, index = elem.selectedIndex + "";
621
622                         if ( elem.nodeName.toLowerCase() === "select" ) {
623                                 data = jQuery.data( elem, "_change_data" );
624                                 jQuery.data( elem, "_change_data", index );
625
626                                 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
627                                         return trigger( "change", this, arguments );
628                                 }
629                         }
630                 },
631                 beforeactivate: function( e ) {
632                         var elem = e.target;
633
634                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
635                                 return trigger( "change", this, arguments );
636                         }
637                 },
638                 blur: function( e ) {
639                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
640
641                         if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
642                                 && jQuery.data(elem, "_change_data") !== elem.value ) {
643
644                                 return trigger( "change", this, arguments );
645                         }
646                 },
647                 focus: function( e ) {
648                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
649
650                         if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
651                                 jQuery.data( elem, "_change_data", elem.value );
652                         }
653                 }
654         },
655         setup: function( data, namespaces, fn ) {
656                 for ( var type in changeFilters ) {
657                         jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
658                 }
659                 
660                 // always want to listen for change for trigger
661                 return false;
662         },
663         remove: function( namespaces, fn ) {
664                 for ( var type in changeFilters ) {
665                         jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
666                 }
667         }
668 };
669
670 var changeFilters = jQuery.event.special.change.filters;
671
672 }
673
674 function trigger( type, elem, args ) {
675         args[0].type = type;
676         return jQuery.event.handle.apply( elem, args );
677 }
678
679 // Create "bubbling" focus and blur events
680 if ( !jQuery.support.focusBubbles ) {
681
682 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
683         event.special[ orig ] = {
684                 setup: function() {
685                         jQuery.event.add( this, fix, ieHandler );
686                 }, 
687                 teardown: function() { 
688                         jQuery.event.remove( this, fix, ieHandler );
689                 }
690         };
691
692         function ieHandler() { 
693                 arguments[0].type = orig;
694                 return jQuery.event.handle.apply(this, arguments);
695         }
696 });
697
698 }
699
700 jQuery.fn.extend({
701         // TODO: make bind(), unbind() and one() DRY!
702         bind: function( type, data, fn, thisObject ) {
703                 // Handle object literals
704                 if ( typeof type === "object" ) {
705                         for ( var key in type ) {
706                                 this.bind(key, data, type[key], fn);
707                         }
708                         return this;
709                 }
710                 
711                 if ( jQuery.isFunction( data ) ) {
712                         thisObject = fn;
713                         fn = data;
714                         data = undefined;
715                 }
716                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
717                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
718                         jQuery.event.add( this, type, fn, data );
719                 });
720         },
721
722         one: function( type, data, fn, thisObject ) {
723                 // Handle object literals
724                 if ( typeof type === "object" ) {
725                         for ( var key in type ) {
726                                 this.one(key, data, type[key], fn);
727                         }
728                         return this;
729                 }
730                 
731                 if ( jQuery.isFunction( data ) ) {
732                         thisObject = fn;
733                         fn = data;
734                         data = undefined;
735                 }
736                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
737                 var one = jQuery.event.proxy( fn, function( event ) {
738                         jQuery( this ).unbind( event, one );
739                         return fn.apply( this, arguments );
740                 });
741                 return this.each(function() {
742                         jQuery.event.add( this, type, one, data );
743                 });
744         },
745
746         unbind: function( type, fn ) {
747                 // Handle object literals
748                 if ( typeof type === "object" && !type.preventDefault ) {
749                         for ( var key in type ) {
750                                 this.unbind(key, type[key]);
751                         }
752                         return this;
753                 }
754                 
755                 return this.each(function() {
756                         jQuery.event.remove( this, type, fn );
757                 });
758         },
759
760         trigger: function( type, data ) {
761                 return this.each(function() {
762                         jQuery.event.trigger( type, data, this );
763                 });
764         },
765
766         triggerHandler: function( type, data ) {
767                 if ( this[0] ) {
768                         var event = jQuery.Event( type );
769                         event.preventDefault();
770                         event.stopPropagation();
771                         jQuery.event.trigger( event, data, this[0] );
772                         return event.result;
773                 }
774         },
775
776         toggle: function( fn ) {
777                 // Save reference to arguments for access in closure
778                 var args = arguments, i = 1;
779
780                 // link all the functions, so any of them can unbind this click handler
781                 while( i < args.length ) {
782                         jQuery.event.proxy( fn, args[ i++ ] );
783                 }
784
785                 return this.click( jQuery.event.proxy( fn, function( event ) {
786                         // Figure out which function to execute
787                         var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
788                         jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
789
790                         // Make sure that clicks stop
791                         event.preventDefault();
792
793                         // and execute the function
794                         return args[ lastToggle ].apply( this, arguments ) || false;
795                 }));
796         },
797
798         hover: function( fnOver, fnOut ) {
799                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
800         },
801
802         live: function( type, data, fn, thisObject ) {
803                 if ( jQuery.isFunction( data ) ) {
804                         if ( fn !== undefined ) {
805                                 thisObject = fn;
806                         }
807                         fn = data;
808                         data = undefined;
809                 }
810                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
811                         data: data, selector: this.selector, live: type
812                 }, fn, thisObject );
813                 return this;
814         },
815
816         die: function( type, fn ) {
817                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
818                 return this;
819         }
820 });
821
822 function liveHandler( event ) {
823         var stop = true, elems = [], selectors = [], args = arguments,
824                 related, match, fn, elem, j, i, data,
825                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
826
827         for ( j in live ) {
828                 fn = live[j];
829                 if ( fn.live === event.type ||
830                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
831
832                         data = fn.data;
833                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
834                                         !data.beforeFilter[event.type](event)) ) {
835                                 selectors.push( fn.selector );
836                         }
837                 } else {
838                         delete live[j];
839                 }
840         }
841
842         match = jQuery( event.target ).closest( selectors, event.currentTarget );
843
844         for ( i = 0, l = match.length; i < l; i++ ) {
845                 for ( j in live ) {
846                         fn = live[j];
847                         elem = match[i].elem;
848                         related = null;
849
850                         if ( match[i].selector === fn.selector ) {
851                                 // Those two events require additional checking
852                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
853                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
854                                 }
855
856                                 if ( !related || related !== elem ) {
857                                         elems.push({ elem: elem, fn: fn });
858                                 }
859                         }
860                 }
861         }
862
863         for ( i = 0, l = elems.length; i < l; i++ ) {
864                 match = elems[i];
865                 event.currentTarget = match.elem;
866                 event.data = match.fn.data;
867                 if ( match.fn.apply( match.elem, args ) === false ) {
868                         stop = false;
869                         break;
870                 }
871         }
872
873         return stop;
874 }
875
876 function liveConvert( type, selector ) {
877         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "&")].join(".");
878 }
879
880 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
881         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
882         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
883
884         // Handle event binding
885         jQuery.fn[ name ] = function( fn ) {
886                 return fn ? this.bind( name, fn ) : this.trigger( name );
887         };
888 });
889
890 // Prevent memory leaks in IE
891 // Window isn't included so as not to unbind existing unload events
892 // More info:
893 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
894 /*@cc_on
895 jQuery( window ).bind( 'unload', function() {
896         for ( var id in jQuery.cache ) {
897                 // Skip the window
898                 if ( id != 1 && jQuery.cache[ id ].handle ) {
899                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
900                 }
901         }
902 });
903 @*/