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