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