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