Stop trying to emulate the focus/blur event in IE, doesn't work as one might expect...
[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 // Checks if an event happened on an element within another element
536 // Used in jQuery.event.special.mouseenter and mouseleave handlers
537 var withinElement = function( event ) {
538         // Check if mouse(over|out) are still within the same parent element
539         var parent = event.relatedTarget;
540         // Traverse up the tree
541         while ( parent && parent != this ) {
542                 // Firefox sometimes assigns relatedTarget a XUL element
543                 // which we cannot access the parentNode property of
544                 try { parent = parent.parentNode; }
545                 // assuming we've left the element since we most likely mousedover a xul element
546                 catch(e) { break; }
547         }
548
549         if ( parent != this ) {
550                 // set the correct event type
551                 event.type = event.data;
552                 // handle event if we actually just moused on to a non sub-element
553                 jQuery.event.handle.apply( this, arguments );
554         }
555
556 },
557
558 // In case of event delegation, we only need to rename the event.type,
559 // liveHandler will take care of the rest.
560 delegate = function( event ) {
561         event.type = event.data;
562         jQuery.event.handle.apply( this, arguments );
563 };
564
565 // Create mouseenter and mouseleave events
566 jQuery.each({
567         mouseenter: "mouseover",
568         mouseleave: "mouseout"
569 }, function( orig, fix ) {
570         jQuery.event.special[ orig ] = {
571                 setup: function(data){
572                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
573                 },
574                 teardown: function(data){
575                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
576                 }
577         };
578 });
579
580 // submit delegation
581 if ( !jQuery.support.submitBubbles ) {
582
583 jQuery.event.special.submit = {
584         setup: function( data, namespaces, fn ) {
585                 if ( this.nodeName.toLowerCase() !== "form" ) {
586                         jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
587                                 var elem = e.target, type = elem.type;
588
589                                 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
590                                         return trigger( "submit", this, arguments );
591                                 }
592                         });
593          
594                         jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
595                                 var elem = e.target, type = elem.type;
596
597                                 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
598                                         return trigger( "submit", this, arguments );
599                                 }
600                         });
601                 }
602         },
603
604         remove: function( namespaces, fn ) {
605                 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
606                 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
607         }
608 };
609
610 }
611
612 // change delegation, happens here so we have bind.
613 if ( !jQuery.support.changeBubbles ) {
614
615 var formElems = /textarea|input|select/i;
616
617 function getVal( elem ) {
618         var type = elem.type, val = elem.value;
619
620         if ( type === "radio" || type === "checkbox" ) {
621                 val = elem.checked;
622
623         } else if ( type === "select-multiple" ) {
624                 val = elem.selectedIndex > -1 ?
625                         jQuery.map( elem.options, function( elem ) {
626                                 return elem.selected;
627                         }).join("-") :
628                         "";
629
630         } else if ( elem.nodeName.toLowerCase() === "select" ) {
631                 val = elem.selectedIndex;
632         }
633
634         return val;
635 }
636
637 function testChange( e ) {
638                 var elem = e.target, data, val;
639
640                 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
641                         return;
642                 }
643
644                 data = jQuery.data( elem, "_change_data" );
645                 val = getVal(elem);
646
647                 if ( val === data ) {
648                         return;
649                 }
650
651                 // the current data will be also retrieved by beforeactivate
652                 if ( e.type !== "focusout" || elem.type !== "radio" ) {
653                         jQuery.data( elem, "_change_data", val );
654                 }
655
656                 if ( elem.type !== "select" && (data != null || val) ) {
657                         e.type = "change";
658                         return jQuery.event.trigger( e, arguments[1], this );
659                 }
660 }
661
662 jQuery.event.special.change = {
663         filters: {
664                 focusout: testChange, 
665
666                 click: function( e ) {
667                         var elem = e.target, type = elem.type;
668
669                         if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
670                                 return testChange.call( this, e );
671                         }
672                 },
673
674                 // Change has to be called before submit
675                 // Keydown will be called before keypress, wich is used in submit-event delegation
676                 keydown: function( e ) {
677                         var elem = e.target, type = elem.type;
678
679                         if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
680                                 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
681                                 type === "select-multiple" ) {
682                                 return testChange.call( this, e );
683                         }
684                 },
685
686                 // Beforeactivate happens also before the previous element is blurred
687                 // with this event you can't trigger a change event, but you can store
688                 // information/focus[in] is not needed anymore
689                 beforeactivate: function( e ) {
690                         var elem = e.target;
691
692                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) {
693                                 return jQuery.data( elem, "_change_data", getVal(elem) );
694                         }
695                 }
696         },
697         setup: function( data, namespaces, fn ) {
698                 for ( var type in changeFilters ) {
699                         jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
700                 }
701
702                 return formElems.test( this.nodeName );
703         },
704         remove: function( namespaces, fn ) {
705                 for ( var type in changeFilters ) {
706                         jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
707                 }
708
709                 return formElems.test( this.nodeName );
710         }
711 };
712
713 var changeFilters = jQuery.event.special.change.filters;
714
715 }
716
717 function trigger( type, elem, args ) {
718         args[0].type = type;
719         return jQuery.event.handle.apply( elem, args );
720 }
721
722 // Create "bubbling" focus and blur events
723 if ( document.addEventListener ) {
724         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
725                 jQuery.event.special[ fix ] = {
726                         setup: function() {
727                                 this.addEventListener( orig, handler, true );
728                         }, 
729                         teardown: function() { 
730                                 this.removeEventListener( orig, handler, true );
731                         }
732                 };
733
734                 function handler( e ) { 
735                         e = jQuery.event.fix( e );
736                         e.type = fix;
737                         return jQuery.event.handle.call( this, e );
738                 }
739         });
740 }
741
742 jQuery.each(["bind", "one"], function(i, name) {
743         jQuery.fn[ name ] = function( type, data, fn, thisObject ) {
744                 // Handle object literals
745                 if ( typeof type === "object" ) {
746                         for ( var key in type ) {
747                                 this[ name ](key, data, type[key], fn);
748                         }
749                         return this;
750                 }
751                 
752                 if ( jQuery.isFunction( data ) ) {
753                         thisObject = fn;
754                         fn = data;
755                         data = undefined;
756                 }
757                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
758                 var handler = name == "one" ? jQuery.event.proxy( fn, function( event ) {
759                         jQuery( this ).unbind( event, handler );
760                         return fn.apply( this, arguments );
761                 }) : fn;
762                 return type === "unload" ? this.one(type, data, handler, thisObject) : this.each(function() {
763                         jQuery.event.add( this, type, handler, data );
764                 });
765         };
766 });
767
768 jQuery.fn.extend({
769         unbind: function( type, fn ) {
770                 // Handle object literals
771                 if ( typeof type === "object" && !type.preventDefault ) {
772                         for ( var key in type ) {
773                                 this.unbind(key, type[key]);
774                         }
775                         return this;
776                 }
777
778                 return this.each(function() {
779                         jQuery.event.remove( this, type, fn );
780                 });
781         },
782         trigger: function( type, data ) {
783                 return this.each(function() {
784                         jQuery.event.trigger( type, data, this );
785                 });
786         },
787
788         triggerHandler: function( type, data ) {
789                 if ( this[0] ) {
790                         var event = jQuery.Event( type );
791                         event.preventDefault();
792                         event.stopPropagation();
793                         jQuery.event.trigger( event, data, this[0] );
794                         return event.result;
795                 }
796         },
797
798         toggle: function( fn ) {
799                 // Save reference to arguments for access in closure
800                 var args = arguments, i = 1;
801
802                 // link all the functions, so any of them can unbind this click handler
803                 while( i < args.length ) {
804                         jQuery.event.proxy( fn, args[ i++ ] );
805                 }
806
807                 return this.click( jQuery.event.proxy( fn, function( event ) {
808                         // Figure out which function to execute
809                         var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
810                         jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
811
812                         // Make sure that clicks stop
813                         event.preventDefault();
814
815                         // and execute the function
816                         return args[ lastToggle ].apply( this, arguments ) || false;
817                 }));
818         },
819
820         hover: function( fnOver, fnOut ) {
821                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
822         },
823
824         live: function( type, data, fn, thisObject ) {
825                 if ( jQuery.isFunction( data ) ) {
826                         if ( fn !== undefined ) {
827                                 thisObject = fn;
828                         }
829                         fn = data;
830                         data = undefined;
831                 }
832                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
833                         data: data, selector: this.selector, live: type
834                 }, fn, thisObject );
835                 return this;
836         },
837
838         die: function( type, fn ) {
839                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
840                 return this;
841         }
842 });
843
844 function liveHandler( event ) {
845         var stop = true, elems = [], selectors = [], args = arguments,
846                 related, match, fn, elem, j, i, data,
847                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
848
849         for ( j in live ) {
850                 fn = live[j];
851                 if ( fn.live === event.type ||
852                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
853
854                         data = fn.data;
855                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
856                                         !data.beforeFilter[event.type](event)) ) {
857                                 selectors.push( fn.selector );
858                         }
859                 } else {
860                         delete live[j];
861                 }
862         }
863
864         match = jQuery( event.target ).closest( selectors, event.currentTarget );
865
866         for ( i = 0, l = match.length; i < l; i++ ) {
867                 for ( j in live ) {
868                         fn = live[j];
869                         elem = match[i].elem;
870                         related = null;
871
872                         if ( match[i].selector === fn.selector ) {
873                                 // Those two events require additional checking
874                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
875                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
876                                 }
877
878                                 if ( !related || related !== elem ) {
879                                         elems.push({ elem: elem, fn: fn });
880                                 }
881                         }
882                 }
883         }
884
885         for ( i = 0, l = elems.length; i < l; i++ ) {
886                 match = elems[i];
887                 event.currentTarget = match.elem;
888                 event.data = match.fn.data;
889                 if ( match.fn.apply( match.elem, args ) === false ) {
890                         stop = false;
891                         break;
892                 }
893         }
894
895         return stop;
896 }
897
898 function liveConvert( type, selector ) {
899         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "&")].join(".");
900 }
901
902 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
903         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
904         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
905
906         // Handle event binding
907         jQuery.fn[ name ] = function( fn ) {
908                 return fn ? this.bind( name, fn ) : this.trigger( name );
909         };
910
911         if ( jQuery.fnAttr ) {
912                 jQuery.fnAttr[ name ] = true;
913         }
914 });
915
916 // Prevent memory leaks in IE
917 // Window isn't included so as not to unbind existing unload events
918 // More info:
919 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
920 if ( window.attachEvent && !window.addEventListener ) {
921         window.attachEvent("onunload", function() {
922                 for ( var id in jQuery.cache ) {
923                         if ( jQuery.cache[ id ].handle ) {
924                                 // Try/Catch is to handle iframes being unloaded, see #4280
925                                 try {
926                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
927                                 } catch(e) {}
928                         }
929                 }
930         });
931 }