cf00e18630e5e651d99cef95ae18000b0316ade0
[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 jQuery.event.special.change = {
616         filters: {
617                 click: function( e ) { 
618                         var elem = e.target;
619
620                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
621                                 return trigger( "change", this, arguments );
622                         }
623
624                         return changeFilters.keyup.call( this, e );
625                 }, 
626                 keyup: function( e ) { 
627                         var elem = e.target, data, index = elem.selectedIndex + "";
628
629                         if ( elem.nodeName.toLowerCase() === "select" ) {
630                                 data = jQuery.data( elem, "_change_data" );
631                                 jQuery.data( elem, "_change_data", index );
632
633                                 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
634                                         return trigger( "change", this, arguments );
635                                 }
636                         }
637                 },
638                 beforeactivate: function( e ) {
639                         var elem = e.target;
640
641                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
642                                 return trigger( "change", this, arguments );
643                         }
644                 },
645                 blur: function( e ) {
646                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
647
648                         if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
649                                 && jQuery.data(elem, "_change_data") !== elem.value ) {
650
651                                 return trigger( "change", this, arguments );
652                         }
653                 },
654                 focus: function( e ) {
655                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
656
657                         if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
658                                 jQuery.data( elem, "_change_data", elem.value );
659                         }
660                 }
661         },
662         setup: function( data, namespaces, fn ) {
663                 for ( var type in changeFilters ) {
664                         jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
665                 }
666                 
667                 // always want to listen for change for trigger
668                 return false;
669         },
670         remove: function( namespaces, fn ) {
671                 for ( var type in changeFilters ) {
672                         jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
673                 }
674         }
675 };
676
677 var changeFilters = jQuery.event.special.change.filters;
678
679 }
680
681 function trigger( type, elem, args ) {
682         args[0].type = type;
683         return jQuery.event.handle.apply( elem, args );
684 }
685
686 // Create "bubbling" focus and blur events
687 if ( !jQuery.support.focusBubbles ) {
688
689 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
690         jQuery.event.special[ orig ] = {
691                 setup: function() {
692                         jQuery.event.add( this, fix, ieHandler );
693                 }, 
694                 teardown: function() { 
695                         jQuery.event.remove( this, fix, ieHandler );
696                 }
697         };
698
699         function ieHandler() { 
700                 arguments[0].type = orig;
701                 return jQuery.event.handle.apply(this, arguments);
702         }
703 });
704
705 }
706
707 jQuery.each(["bind", "one"], function(i, name) {
708         jQuery.fn[ name ] = function( type, data, fn, thisObject ) {
709                 // Handle object literals
710                 if ( typeof type === "object" ) {
711                         for ( var key in type ) {
712                                 this[ name ](key, data, type[key], fn);
713                         }
714                         return this;
715                 }
716                 
717                 if ( jQuery.isFunction( data ) ) {
718                         thisObject = fn;
719                         fn = data;
720                         data = undefined;
721                 }
722                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
723                 var handler = name == "one" ? jQuery.event.proxy( fn, function( event ) {
724                         jQuery( this ).unbind( event, handler );
725                         return fn.apply( this, arguments );
726                 }) : fn;
727                 return type === "unload" ? this.one(type, data, handler, thisObject) : this.each(function() {
728                         jQuery.event.add( this, type, handler, data );
729                 });
730         };
731 });
732
733 jQuery.fn.extend({
734         unbind: function( type, fn ) {
735                 // Handle object literals
736                 if ( typeof type === "object" && !type.preventDefault ) {
737                         for ( var key in type ) {
738                                 this.unbind(key, type[key]);
739                         }
740                         return this;
741                 }
742
743                 return this.each(function() {
744                         jQuery.event.remove( this, type, fn );
745                 });
746         },
747         trigger: function( type, data ) {
748                 return this.each(function() {
749                         jQuery.event.trigger( type, data, this );
750                 });
751         },
752
753         triggerHandler: function( type, data ) {
754                 if ( this[0] ) {
755                         var event = jQuery.Event( type );
756                         event.preventDefault();
757                         event.stopPropagation();
758                         jQuery.event.trigger( event, data, this[0] );
759                         return event.result;
760                 }
761         },
762
763         toggle: function( fn ) {
764                 // Save reference to arguments for access in closure
765                 var args = arguments, i = 1;
766
767                 // link all the functions, so any of them can unbind this click handler
768                 while( i < args.length ) {
769                         jQuery.event.proxy( fn, args[ i++ ] );
770                 }
771
772                 return this.click( jQuery.event.proxy( fn, function( event ) {
773                         // Figure out which function to execute
774                         var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
775                         jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
776
777                         // Make sure that clicks stop
778                         event.preventDefault();
779
780                         // and execute the function
781                         return args[ lastToggle ].apply( this, arguments ) || false;
782                 }));
783         },
784
785         hover: function( fnOver, fnOut ) {
786                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
787         },
788
789         live: function( type, data, fn, thisObject ) {
790                 if ( jQuery.isFunction( data ) ) {
791                         if ( fn !== undefined ) {
792                                 thisObject = fn;
793                         }
794                         fn = data;
795                         data = undefined;
796                 }
797                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
798                         data: data, selector: this.selector, live: type
799                 }, fn, thisObject );
800                 return this;
801         },
802
803         die: function( type, fn ) {
804                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
805                 return this;
806         }
807 });
808
809 function liveHandler( event ) {
810         var stop = true, elems = [], selectors = [], args = arguments,
811                 related, match, fn, elem, j, i, data,
812                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
813
814         for ( j in live ) {
815                 fn = live[j];
816                 if ( fn.live === event.type ||
817                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
818
819                         data = fn.data;
820                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
821                                         !data.beforeFilter[event.type](event)) ) {
822                                 selectors.push( fn.selector );
823                         }
824                 } else {
825                         delete live[j];
826                 }
827         }
828
829         match = jQuery( event.target ).closest( selectors, event.currentTarget );
830
831         for ( i = 0, l = match.length; i < l; i++ ) {
832                 for ( j in live ) {
833                         fn = live[j];
834                         elem = match[i].elem;
835                         related = null;
836
837                         if ( match[i].selector === fn.selector ) {
838                                 // Those two events require additional checking
839                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
840                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
841                                 }
842
843                                 if ( !related || related !== elem ) {
844                                         elems.push({ elem: elem, fn: fn });
845                                 }
846                         }
847                 }
848         }
849
850         for ( i = 0, l = elems.length; i < l; i++ ) {
851                 match = elems[i];
852                 event.currentTarget = match.elem;
853                 event.data = match.fn.data;
854                 if ( match.fn.apply( match.elem, args ) === false ) {
855                         stop = false;
856                         break;
857                 }
858         }
859
860         return stop;
861 }
862
863 function liveConvert( type, selector ) {
864         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "&")].join(".");
865 }
866
867 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
868         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
869         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
870
871         // Handle event binding
872         jQuery.fn[ name ] = function( fn ) {
873                 return fn ? this.bind( name, fn ) : this.trigger( name );
874         };
875
876         if ( jQuery.fnAttr ) {
877                 jQuery.fnAttr[ name ] = true;
878         }
879 });
880
881 // Prevent memory leaks in IE
882 // Window isn't included so as not to unbind existing unload events
883 // More info:
884 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
885 if ( window.attachEvent && !window.addEventListener ) {
886         window.attachEvent("onunload", function() {
887                 for ( var id in jQuery.cache ) {
888                         if ( jQuery.cache[ id ].handle ) {
889                                 // Try/Catch is to handle iframes being unloaded, see #4280
890                                 try {
891                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
892                                 } catch(e) {}
893                         }
894                 }
895         });
896 }