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