Fixed typo in logic, also disabled function setters in this case to allow the functi...
[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         // Deprecated, use jQuery.guid instead
408         guid: 1E8,
409
410         // Deprecated, use jQuery.proxy instead
411         proxy: jQuery.proxy,
412
413         special: {
414                 ready: {
415                         // Make sure the ready event is setup
416                         setup: jQuery.bindReady,
417                         teardown: jQuery.noop
418                 },
419
420                 live: {
421                         add: function( proxy, data, namespaces, live ) {
422                                 jQuery.extend( proxy, data || {} );
423
424                                 proxy.guid += data.selector + data.live; 
425                                 jQuery.event.add( this, data.live, liveHandler, data ); 
426                                 
427                         },
428
429                         remove: function( namespaces ) {
430                                 if ( namespaces.length ) {
431                                         var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
432
433                                         jQuery.each( (jQuery.data(this, "events").live || {}), function() {
434                                                 if ( name.test(this.type) ) {
435                                                         remove++;
436                                                 }
437                                         });
438
439                                         if ( remove < 1 ) {
440                                                 jQuery.event.remove( this, namespaces[0], liveHandler );
441                                         }
442                                 }
443                         },
444                         special: {}
445                 },
446                 beforeunload: {
447                         setup: function( data, namespaces, fn ) {
448                                 // We only want to do this special case on windows
449                                 if ( this.setInterval ) {
450                                         this.onbeforeunload = fn;
451                                 }
452
453                                 return false;
454                         },
455                         teardown: function( namespaces, fn ) {
456                                 if ( this.onbeforeunload === fn ) {
457                                         this.onbeforeunload = null;
458                                 }
459                         }
460                 }
461         }
462 };
463
464 jQuery.Event = function( src ) {
465         // Allow instantiation without the 'new' keyword
466         if ( !this.preventDefault ) {
467                 return new jQuery.Event( src );
468         }
469
470         // Event object
471         if ( src && src.type ) {
472                 this.originalEvent = src;
473                 this.type = src.type;
474         // Event type
475         } else {
476                 this.type = src;
477         }
478
479         // timeStamp is buggy for some events on Firefox(#3843)
480         // So we won't rely on the native value
481         this.timeStamp = now();
482
483         // Mark it as fixed
484         this[ expando ] = true;
485 };
486
487 function returnFalse() {
488         return false;
489 }
490 function returnTrue() {
491         return true;
492 }
493
494 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
495 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
496 jQuery.Event.prototype = {
497         preventDefault: function() {
498                 this.isDefaultPrevented = returnTrue;
499
500                 var e = this.originalEvent;
501                 if ( !e ) {
502                         return;
503                 }
504                 
505                 // if preventDefault exists run it on the original event
506                 if ( e.preventDefault ) {
507                         e.preventDefault();
508                 }
509                 // otherwise set the returnValue property of the original event to false (IE)
510                 e.returnValue = false;
511         },
512         stopPropagation: function() {
513                 this.isPropagationStopped = returnTrue;
514
515                 var e = this.originalEvent;
516                 if ( !e ) {
517                         return;
518                 }
519                 // if stopPropagation exists run it on the original event
520                 if ( e.stopPropagation ) {
521                         e.stopPropagation();
522                 }
523                 // otherwise set the cancelBubble property of the original event to true (IE)
524                 e.cancelBubble = true;
525         },
526         stopImmediatePropagation: function() {
527                 this.isImmediatePropagationStopped = returnTrue;
528                 this.stopPropagation();
529         },
530         isDefaultPrevented: returnFalse,
531         isPropagationStopped: returnFalse,
532         isImmediatePropagationStopped: returnFalse
533 };
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
541         // Traverse up the tree
542         while ( parent && parent !== this ) {
543                 // Firefox sometimes assigns relatedTarget a XUL element
544                 // which we cannot access the parentNode property of
545                 try {
546                         parent = parent.parentNode;
547
548                 // assuming we've left the element since we most likely mousedover a xul element
549                 } catch(e) {
550                         break;
551                 }
552         }
553
554         if ( parent !== this ) {
555                 // set the correct event type
556                 event.type = event.data;
557
558                 // handle event if we actually just moused on to a non sub-element
559                 jQuery.event.handle.apply( this, arguments );
560         }
561
562 },
563
564 // In case of event delegation, we only need to rename the event.type,
565 // liveHandler will take care of the rest.
566 delegate = function( event ) {
567         event.type = event.data;
568         jQuery.event.handle.apply( this, arguments );
569 };
570
571 // Create mouseenter and mouseleave events
572 jQuery.each({
573         mouseenter: "mouseover",
574         mouseleave: "mouseout"
575 }, function( orig, fix ) {
576         jQuery.event.special[ orig ] = {
577                 setup: function( data ) {
578                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
579                 },
580                 teardown: function( data ) {
581                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
582                 }
583         };
584 });
585
586 // submit delegation
587 if ( !jQuery.support.submitBubbles ) {
588
589 jQuery.event.special.submit = {
590         setup: function( data, namespaces, fn ) {
591                 if ( this.nodeName.toLowerCase() !== "form" ) {
592                         jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
593                                 var elem = e.target, type = elem.type;
594
595                                 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
596                                         return trigger( "submit", this, arguments );
597                                 }
598                         });
599          
600                         jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
601                                 var elem = e.target, type = elem.type;
602
603                                 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
604                                         return trigger( "submit", this, arguments );
605                                 }
606                         });
607
608                 } else {
609                         return false;
610                 }
611         },
612
613         remove: function( namespaces, fn ) {
614                 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
615                 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
616         }
617 };
618
619 }
620
621 // change delegation, happens here so we have bind.
622 if ( !jQuery.support.changeBubbles ) {
623
624 var formElems = /textarea|input|select/i;
625
626 function getVal( elem ) {
627         var type = elem.type, val = elem.value;
628
629         if ( type === "radio" || type === "checkbox" ) {
630                 val = elem.checked;
631
632         } else if ( type === "select-multiple" ) {
633                 val = elem.selectedIndex > -1 ?
634                         jQuery.map( elem.options, function( elem ) {
635                                 return elem.selected;
636                         }).join("-") :
637                         "";
638
639         } else if ( elem.nodeName.toLowerCase() === "select" ) {
640                 val = elem.selectedIndex;
641         }
642
643         return val;
644 }
645
646 function testChange( e ) {
647                 var elem = e.target, data, val;
648
649                 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
650                         return;
651                 }
652
653                 data = jQuery.data( elem, "_change_data" );
654                 val = getVal(elem);
655
656                 if ( val === data ) {
657                         return;
658                 }
659
660                 // the current data will be also retrieved by beforeactivate
661                 if ( e.type !== "focusout" || elem.type !== "radio" ) {
662                         jQuery.data( elem, "_change_data", val );
663                 }
664
665                 if ( elem.type !== "select" && (data != null || val) ) {
666                         e.type = "change";
667                         return jQuery.event.trigger( e, arguments[1], this );
668                 }
669 }
670
671 jQuery.event.special.change = {
672         filters: {
673                 focusout: testChange, 
674
675                 click: function( e ) {
676                         var elem = e.target, type = elem.type;
677
678                         if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
679                                 return testChange.call( this, e );
680                         }
681                 },
682
683                 // Change has to be called before submit
684                 // Keydown will be called before keypress, which is used in submit-event delegation
685                 keydown: function( e ) {
686                         var elem = e.target, type = elem.type;
687
688                         if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
689                                 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
690                                 type === "select-multiple" ) {
691                                 return testChange.call( this, e );
692                         }
693                 },
694
695                 // Beforeactivate happens also before the previous element is blurred
696                 // with this event you can't trigger a change event, but you can store
697                 // information/focus[in] is not needed anymore
698                 beforeactivate: function( e ) {
699                         var elem = e.target;
700
701                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) {
702                                 jQuery.data( elem, "_change_data", getVal(elem) );
703                         }
704                 }
705         },
706         setup: function( data, namespaces, fn ) {
707                 for ( var type in changeFilters ) {
708                         jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
709                 }
710
711                 return formElems.test( this.nodeName );
712         },
713         remove: function( namespaces, fn ) {
714                 for ( var type in changeFilters ) {
715                         jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
716                 }
717
718                 return formElems.test( this.nodeName );
719         }
720 };
721
722 var changeFilters = jQuery.event.special.change.filters;
723
724 }
725
726 function trigger( type, elem, args ) {
727         args[0].type = type;
728         return jQuery.event.handle.apply( elem, args );
729 }
730
731 // Create "bubbling" focus and blur events
732 if ( document.addEventListener ) {
733         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
734                 jQuery.event.special[ fix ] = {
735                         setup: function() {
736                                 this.addEventListener( orig, handler, true );
737                         }, 
738                         teardown: function() { 
739                                 this.removeEventListener( orig, handler, true );
740                         }
741                 };
742
743                 function handler( e ) { 
744                         e = jQuery.event.fix( e );
745                         e.type = fix;
746                         return jQuery.event.handle.call( this, e );
747                 }
748         });
749 }
750
751 jQuery.each(["bind", "one"], function( i, name ) {
752         jQuery.fn[ name ] = function( type, data, fn ) {
753                 // Handle object literals
754                 if ( typeof type === "object" ) {
755                         for ( var key in type ) {
756                                 this[ name ](key, data, type[key], fn);
757                         }
758                         return this;
759                 }
760                 
761                 if ( jQuery.isFunction( data ) ) {
762                         thisObject = fn;
763                         fn = data;
764                         data = undefined;
765                 }
766
767                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
768                         jQuery( this ).unbind( event, handler );
769                         return fn.apply( this, arguments );
770                 }) : fn;
771
772                 return type === "unload" && name !== "one" ?
773                         this.one( type, data, fn, thisObject ) :
774                         this.each(function() {
775                                 jQuery.event.add( this, type, handler, data );
776                         });
777         };
778 });
779
780 jQuery.fn.extend({
781         unbind: function( type, fn ) {
782                 // Handle object literals
783                 if ( typeof type === "object" && !type.preventDefault ) {
784                         for ( var key in type ) {
785                                 this.unbind(key, type[key]);
786                         }
787                         return this;
788                 }
789
790                 return this.each(function() {
791                         jQuery.event.remove( this, type, fn );
792                 });
793         },
794         trigger: function( type, data ) {
795                 return this.each(function() {
796                         jQuery.event.trigger( type, data, this );
797                 });
798         },
799
800         triggerHandler: function( type, data ) {
801                 if ( this[0] ) {
802                         var event = jQuery.Event( type );
803                         event.preventDefault();
804                         event.stopPropagation();
805                         jQuery.event.trigger( event, data, this[0] );
806                         return event.result;
807                 }
808         },
809
810         toggle: function( fn ) {
811                 // Save reference to arguments for access in closure
812                 var args = arguments, i = 1;
813
814                 // link all the functions, so any of them can unbind this click handler
815                 while ( i < args.length ) {
816                         jQuery.proxy( fn, args[ i++ ] );
817                 }
818
819                 return this.click( jQuery.proxy( fn, function( event ) {
820                         // Figure out which function to execute
821                         var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
822                         jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
823
824                         // Make sure that clicks stop
825                         event.preventDefault();
826
827                         // and execute the function
828                         return args[ lastToggle ].apply( this, arguments ) || false;
829                 }));
830         },
831
832         hover: function( fnOver, fnOut ) {
833                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
834         },
835
836         live: function( type, data, fn ) {
837                 if ( jQuery.isFunction( data ) ) {
838                         fn = data;
839                         data = undefined;
840                 }
841
842                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
843                         data: data, selector: this.selector, live: type
844                 }, fn );
845
846                 return this;
847         },
848
849         die: function( type, fn ) {
850                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
851                 return this;
852         }
853 });
854
855 function liveHandler( event ) {
856         var stop = true, elems = [], selectors = [], args = arguments,
857                 related, match, fn, elem, j, i, data,
858                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
859
860         for ( j in live ) {
861                 fn = live[j];
862                 if ( fn.live === event.type ||
863                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
864
865                         data = fn.data;
866                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
867                                         !data.beforeFilter[event.type](event)) ) {
868                                 selectors.push( fn.selector );
869                         }
870                 } else {
871                         delete live[j];
872                 }
873         }
874
875         match = jQuery( event.target ).closest( selectors, event.currentTarget );
876
877         for ( i = 0, l = match.length; i < l; i++ ) {
878                 for ( j in live ) {
879                         fn = live[j];
880                         elem = match[i].elem;
881                         related = null;
882
883                         if ( match[i].selector === fn.selector ) {
884                                 // Those two events require additional checking
885                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
886                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
887                                 }
888
889                                 if ( !related || related !== elem ) {
890                                         elems.push({ elem: elem, fn: fn });
891                                 }
892                         }
893                 }
894         }
895
896         for ( i = 0, l = elems.length; i < l; i++ ) {
897                 match = elems[i];
898                 event.currentTarget = match.elem;
899                 event.data = match.fn.data;
900                 if ( match.fn.apply( match.elem, args ) === false ) {
901                         stop = false;
902                         break;
903                 }
904         }
905
906         return stop;
907 }
908
909 function liveConvert( type, selector ) {
910         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "&")].join(".");
911 }
912
913 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
914         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
915         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
916
917         // Handle event binding
918         jQuery.fn[ name ] = function( fn ) {
919                 return fn ? this.bind( name, fn ) : this.trigger( name );
920         };
921
922         if ( jQuery.attrFn ) {
923                 jQuery.attrFn[ name ] = true;
924         }
925 });
926
927 // Prevent memory leaks in IE
928 // Window isn't included so as not to unbind existing unload events
929 // More info:
930 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
931 if ( window.attachEvent && !window.addEventListener ) {
932         window.attachEvent("onunload", function() {
933                 for ( var id in jQuery.cache ) {
934                         if ( jQuery.cache[ id ].handle ) {
935                                 // Try/Catch is to handle iframes being unloaded, see #4280
936                                 try {
937                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
938                                 } catch(e) {}
939                         }
940                 }
941         });
942 }