Tweak the logic for fixing event.which. Fixes #2338.
[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                                         event.preventDefault();
335                                 }
336                         }
337
338                 // prevent IE from throwing an error for some elements with some event types, see #3533
339                 } catch (inlineError) {}
340
341                 if ( !event.isPropagationStopped() && parent ) {
342                         jQuery.event.trigger( event, data, parent, true );
343
344                 } else if ( !event.isDefaultPrevented() ) {
345                         var target = event.target, old, targetType = type.replace(rnamespaces, ""),
346                                 isClick = jQuery.nodeName(target, "a") && targetType === "click",
347                                 special = jQuery.event.special[ targetType ] || {};
348
349                         if ( (!special._default || special._default.call( elem, event ) === false) && 
350                                 !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
351
352                                 try {
353                                         if ( target[ targetType ] ) {
354                                                 // Make sure that we don't accidentally re-trigger the onFOO events
355                                                 old = target[ "on" + targetType ];
356
357                                                 if ( old ) {
358                                                         target[ "on" + targetType ] = null;
359                                                 }
360
361                                                 jQuery.event.triggered = true;
362                                                 target[ targetType ]();
363                                         }
364
365                                 // prevent IE from throwing an error for some elements with some event types, see #3533
366                                 } catch (triggerError) {}
367
368                                 if ( old ) {
369                                         target[ "on" + targetType ] = old;
370                                 }
371
372                                 jQuery.event.triggered = false;
373                         }
374                 }
375         },
376
377         handle: function( event ) {
378                 var all, handlers, namespaces, namespace_sort = [], namespace_re, events, args = jQuery.makeArray( arguments );
379
380                 event = args[0] = jQuery.event.fix( event || window.event );
381                 event.currentTarget = this;
382
383                 // Namespaced event handlers
384                 all = event.type.indexOf(".") < 0 && !event.exclusive;
385
386                 if ( !all ) {
387                         namespaces = event.type.split(".");
388                         event.type = namespaces.shift();
389                         namespace_sort = namespaces.slice(0).sort();
390                         namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
391                 }
392
393                 event.namespace = event.namespace || namespace_sort.join(".");
394
395                 events = jQuery.data(this, "events");
396                 handlers = (events || {})[ event.type ];
397
398                 if ( events && handlers ) {
399                         // Clone the handlers to prevent manipulation
400                         handlers = handlers.slice(0);
401
402                         for ( var j = 0, l = handlers.length; j < l; j++ ) {
403                                 var handleObj = handlers[ j ];
404
405                                 // Filter the functions by class
406                                 if ( all || namespace_re.test( handleObj.namespace ) ) {
407                                         // Pass in a reference to the handler function itself
408                                         // So that we can later remove it
409                                         event.handler = handleObj.handler;
410                                         event.data = handleObj.data;
411                                         event.handleObj = handleObj;
412         
413                                         var ret = handleObj.handler.apply( this, args );
414
415                                         if ( ret !== undefined ) {
416                                                 event.result = ret;
417                                                 if ( ret === false ) {
418                                                         event.preventDefault();
419                                                         event.stopPropagation();
420                                                 }
421                                         }
422
423                                         if ( event.isImmediatePropagationStopped() ) {
424                                                 break;
425                                         }
426                                 }
427                         }
428                 }
429
430                 return event.result;
431         },
432
433         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(" "),
434
435         fix: function( event ) {
436                 if ( event[ jQuery.expando ] ) {
437                         return event;
438                 }
439
440                 // store a copy of the original event object
441                 // and "clone" to set read-only properties
442                 var originalEvent = event;
443                 event = jQuery.Event( originalEvent );
444
445                 for ( var i = this.props.length, prop; i; ) {
446                         prop = this.props[ --i ];
447                         event[ prop ] = originalEvent[ prop ];
448                 }
449
450                 // Fix target property, if necessary
451                 if ( !event.target ) {
452                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
453                 }
454
455                 // check if target is a textnode (safari)
456                 if ( event.target.nodeType === 3 ) {
457                         event.target = event.target.parentNode;
458                 }
459
460                 // Add relatedTarget, if necessary
461                 if ( !event.relatedTarget && event.fromElement ) {
462                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
463                 }
464
465                 // Calculate pageX/Y if missing and clientX/Y available
466                 if ( event.pageX == null && event.clientX != null ) {
467                         var doc = document.documentElement, body = document.body;
468                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
469                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
470                 }
471
472                 // Add which for key events
473                 if ( event.which == null && (event.charCode != null || event.charCode != null) ) {
474                         event.which = event.charCode != null ? event.charCode : event.keyCode;
475                 }
476
477                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
478                 if ( !event.metaKey && event.ctrlKey ) {
479                         event.metaKey = event.ctrlKey;
480                 }
481
482                 // Add which for click: 1 === left; 2 === middle; 3 === right
483                 // Note: button is not normalized, so don't use it
484                 if ( !event.which && event.button !== undefined ) {
485                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
486                 }
487
488                 return event;
489         },
490
491         // Deprecated, use jQuery.guid instead
492         guid: 1E8,
493
494         // Deprecated, use jQuery.proxy instead
495         proxy: jQuery.proxy,
496
497         special: {
498                 ready: {
499                         // Make sure the ready event is setup
500                         setup: jQuery.bindReady,
501                         teardown: jQuery.noop
502                 },
503
504                 live: {
505                         add: function( handleObj ) {
506                                 jQuery.event.add( this,
507                                         liveConvert( handleObj.origType, handleObj.selector ),
508                                         jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
509                         },
510
511                         remove: function( handleObj ) {
512                                 jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
513                         }
514                 },
515
516                 beforeunload: {
517                         setup: function( data, namespaces, eventHandle ) {
518                                 // We only want to do this special case on windows
519                                 if ( jQuery.isWindow( this ) ) {
520                                         this.onbeforeunload = eventHandle;
521                                 }
522                         },
523
524                         teardown: function( namespaces, eventHandle ) {
525                                 if ( this.onbeforeunload === eventHandle ) {
526                                         this.onbeforeunload = null;
527                                 }
528                         }
529                 }
530         }
531 };
532
533 jQuery.removeEvent = document.removeEventListener ?
534         function( elem, type, handle ) {
535                 if ( elem.removeEventListener ) {
536                         elem.removeEventListener( type, handle, false );
537                 }
538         } : 
539         function( elem, type, handle ) {
540                 if ( elem.detachEvent ) {
541                         elem.detachEvent( "on" + type, handle );
542                 }
543         };
544
545 jQuery.Event = function( src ) {
546         // Allow instantiation without the 'new' keyword
547         if ( !this.preventDefault ) {
548                 return new jQuery.Event( src );
549         }
550
551         // Event object
552         if ( src && src.type ) {
553                 this.originalEvent = src;
554                 this.type = src.type;
555         // Event type
556         } else {
557                 this.type = src;
558         }
559
560         // timeStamp is buggy for some events on Firefox(#3843)
561         // So we won't rely on the native value
562         this.timeStamp = jQuery.now();
563
564         // Mark it as fixed
565         this[ jQuery.expando ] = true;
566 };
567
568 function returnFalse() {
569         return false;
570 }
571 function returnTrue() {
572         return true;
573 }
574
575 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
576 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
577 jQuery.Event.prototype = {
578         preventDefault: function() {
579                 this.isDefaultPrevented = returnTrue;
580
581                 var e = this.originalEvent;
582                 if ( !e ) {
583                         return;
584                 }
585                 
586                 // if preventDefault exists run it on the original event
587                 if ( e.preventDefault ) {
588                         e.preventDefault();
589
590                 // otherwise set the returnValue property of the original event to false (IE)
591                 } else {
592                         e.returnValue = false;
593                 }
594         },
595         stopPropagation: function() {
596                 this.isPropagationStopped = returnTrue;
597
598                 var e = this.originalEvent;
599                 if ( !e ) {
600                         return;
601                 }
602                 // if stopPropagation exists run it on the original event
603                 if ( e.stopPropagation ) {
604                         e.stopPropagation();
605                 }
606                 // otherwise set the cancelBubble property of the original event to true (IE)
607                 e.cancelBubble = true;
608         },
609         stopImmediatePropagation: function() {
610                 this.isImmediatePropagationStopped = returnTrue;
611                 this.stopPropagation();
612         },
613         isDefaultPrevented: returnFalse,
614         isPropagationStopped: returnFalse,
615         isImmediatePropagationStopped: returnFalse
616 };
617
618 // Checks if an event happened on an element within another element
619 // Used in jQuery.event.special.mouseenter and mouseleave handlers
620 var withinElement = function( event ) {
621         // Check if mouse(over|out) are still within the same parent element
622         var parent = event.relatedTarget;
623
624         // Firefox sometimes assigns relatedTarget a XUL element
625         // which we cannot access the parentNode property of
626         try {
627                 // Traverse up the tree
628                 while ( parent && parent !== this ) {
629                         parent = parent.parentNode;
630                 }
631
632                 if ( parent !== this ) {
633                         // set the correct event type
634                         event.type = event.data;
635
636                         // handle event if we actually just moused on to a non sub-element
637                         jQuery.event.handle.apply( this, arguments );
638                 }
639
640         // assuming we've left the element since we most likely mousedover a xul element
641         } catch(e) { }
642 },
643
644 // In case of event delegation, we only need to rename the event.type,
645 // liveHandler will take care of the rest.
646 delegate = function( event ) {
647         event.type = event.data;
648         jQuery.event.handle.apply( this, arguments );
649 };
650
651 // Create mouseenter and mouseleave events
652 jQuery.each({
653         mouseenter: "mouseover",
654         mouseleave: "mouseout"
655 }, function( orig, fix ) {
656         jQuery.event.special[ orig ] = {
657                 setup: function( data ) {
658                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
659                 },
660                 teardown: function( data ) {
661                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
662                 }
663         };
664 });
665
666 // submit delegation
667 if ( !jQuery.support.submitBubbles ) {
668
669         jQuery.event.special.submit = {
670                 setup: function( data, namespaces ) {
671                         if ( this.nodeName.toLowerCase() !== "form" ) {
672                                 jQuery.event.add(this, "click.specialSubmit", function( e ) {
673                                         var elem = e.target, type = elem.type;
674
675                                         if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
676                                                 return trigger( "submit", this, arguments );
677                                         }
678                                 });
679          
680                                 jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
681                                         var elem = e.target, type = elem.type;
682
683                                         if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
684                                                 return trigger( "submit", this, arguments );
685                                         }
686                                 });
687
688                         } else {
689                                 return false;
690                         }
691                 },
692
693                 teardown: function( namespaces ) {
694                         jQuery.event.remove( this, ".specialSubmit" );
695                 }
696         };
697
698 }
699
700 // change delegation, happens here so we have bind.
701 if ( !jQuery.support.changeBubbles ) {
702
703         var changeFilters,
704
705         getVal = function( elem ) {
706                 var type = elem.type, val = elem.value;
707
708                 if ( type === "radio" || type === "checkbox" ) {
709                         val = elem.checked;
710
711                 } else if ( type === "select-multiple" ) {
712                         val = elem.selectedIndex > -1 ?
713                                 jQuery.map( elem.options, function( elem ) {
714                                         return elem.selected;
715                                 }).join("-") :
716                                 "";
717
718                 } else if ( elem.nodeName.toLowerCase() === "select" ) {
719                         val = elem.selectedIndex;
720                 }
721
722                 return val;
723         },
724
725         testChange = function testChange( e ) {
726                 var elem = e.target, data, val;
727
728                 if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
729                         return;
730                 }
731
732                 data = jQuery.data( elem, "_change_data" );
733                 val = getVal(elem);
734
735                 // the current data will be also retrieved by beforeactivate
736                 if ( e.type !== "focusout" || elem.type !== "radio" ) {
737                         jQuery.data( elem, "_change_data", val );
738                 }
739                 
740                 if ( data === undefined || val === data ) {
741                         return;
742                 }
743
744                 if ( data != null || val ) {
745                         e.type = "change";
746                         return jQuery.event.trigger( e, arguments[1], elem );
747                 }
748         };
749
750         jQuery.event.special.change = {
751                 filters: {
752                         focusout: testChange, 
753
754                         click: function( e ) {
755                                 var elem = e.target, type = elem.type;
756
757                                 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
758                                         return testChange.call( this, e );
759                                 }
760                         },
761
762                         // Change has to be called before submit
763                         // Keydown will be called before keypress, which is used in submit-event delegation
764                         keydown: function( e ) {
765                                 var elem = e.target, type = elem.type;
766
767                                 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
768                                         (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
769                                         type === "select-multiple" ) {
770                                         return testChange.call( this, e );
771                                 }
772                         },
773
774                         // Beforeactivate happens also before the previous element is blurred
775                         // with this event you can't trigger a change event, but you can store
776                         // information/focus[in] is not needed anymore
777                         beforeactivate: function( e ) {
778                                 var elem = e.target;
779                                 jQuery.data( elem, "_change_data", getVal(elem) );
780                         }
781                 },
782
783                 setup: function( data, namespaces ) {
784                         if ( this.type === "file" ) {
785                                 return false;
786                         }
787
788                         for ( var type in changeFilters ) {
789                                 jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
790                         }
791
792                         return rformElems.test( this.nodeName );
793                 },
794
795                 teardown: function( namespaces ) {
796                         jQuery.event.remove( this, ".specialChange" );
797
798                         return rformElems.test( this.nodeName );
799                 }
800         };
801
802         changeFilters = jQuery.event.special.change.filters;
803 }
804
805 function trigger( type, elem, args ) {
806         args[0].type = type;
807         return jQuery.event.handle.apply( elem, args );
808 }
809
810 // Create "bubbling" focus and blur events
811 if ( document.addEventListener ) {
812         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
813                 jQuery.event.special[ fix ] = {
814                         setup: function() {
815                                 this.addEventListener( orig, handler, true );
816                         }, 
817                         teardown: function() { 
818                                 this.removeEventListener( orig, handler, true );
819                         }
820                 };
821
822                 function handler( e ) { 
823                         e = jQuery.event.fix( e );
824                         e.type = fix;
825                         return jQuery.event.handle.call( this, e );
826                 }
827         });
828 }
829
830 jQuery.each(["bind", "one"], function( i, name ) {
831         jQuery.fn[ name ] = function( type, data, fn ) {
832                 // Handle object literals
833                 if ( typeof type === "object" ) {
834                         for ( var key in type ) {
835                                 this[ name ](key, data, type[key], fn);
836                         }
837                         return this;
838                 }
839                 
840                 if ( jQuery.isFunction( data ) || data === false ) {
841                         fn = data;
842                         data = undefined;
843                 }
844
845                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
846                         jQuery( this ).unbind( event, handler );
847                         return fn.apply( this, arguments );
848                 }) : fn;
849
850                 if ( type === "unload" && name !== "one" ) {
851                         this.one( type, data, fn );
852
853                 } else {
854                         for ( var i = 0, l = this.length; i < l; i++ ) {
855                                 jQuery.event.add( this[i], type, handler, data );
856                         }
857                 }
858
859                 return this;
860         };
861 });
862
863 jQuery.fn.extend({
864         unbind: function( type, fn ) {
865                 // Handle object literals
866                 if ( typeof type === "object" && !type.preventDefault ) {
867                         for ( var key in type ) {
868                                 this.unbind(key, type[key]);
869                         }
870
871                 } else {
872                         for ( var i = 0, l = this.length; i < l; i++ ) {
873                                 jQuery.event.remove( this[i], type, fn );
874                         }
875                 }
876
877                 return this;
878         },
879         
880         delegate: function( selector, types, data, fn ) {
881                 return this.live( types, data, fn, selector );
882         },
883         
884         undelegate: function( selector, types, fn ) {
885                 if ( arguments.length === 0 ) {
886                                 return this.unbind( "live" );
887                 
888                 } else {
889                         return this.die( types, null, fn, selector );
890                 }
891         },
892         
893         trigger: function( type, data ) {
894                 return this.each(function() {
895                         jQuery.event.trigger( type, data, this );
896                 });
897         },
898
899         triggerHandler: function( type, data ) {
900                 if ( this[0] ) {
901                         var event = jQuery.Event( type );
902                         event.preventDefault();
903                         event.stopPropagation();
904                         jQuery.event.trigger( event, data, this[0] );
905                         return event.result;
906                 }
907         },
908
909         toggle: function( fn ) {
910                 // Save reference to arguments for access in closure
911                 var args = arguments, i = 1;
912
913                 // link all the functions, so any of them can unbind this click handler
914                 while ( i < args.length ) {
915                         jQuery.proxy( fn, args[ i++ ] );
916                 }
917
918                 return this.click( jQuery.proxy( fn, function( event ) {
919                         // Figure out which function to execute
920                         var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
921                         jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
922
923                         // Make sure that clicks stop
924                         event.preventDefault();
925
926                         // and execute the function
927                         return args[ lastToggle ].apply( this, arguments ) || false;
928                 }));
929         },
930
931         hover: function( fnOver, fnOut ) {
932                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
933         }
934 });
935
936 var liveMap = {
937         focus: "focusin",
938         blur: "focusout",
939         mouseenter: "mouseover",
940         mouseleave: "mouseout"
941 };
942
943 jQuery.each(["live", "die"], function( i, name ) {
944         jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
945                 var type, i = 0, match, namespaces, preType,
946                         selector = origSelector || this.selector,
947                         context = origSelector ? this : jQuery( this.context );
948                 
949                 if ( typeof types === "object" && !types.preventDefault ) {
950                         for ( var key in types ) {
951                                 context[ name ]( key, data, types[key], selector );
952                         }
953                         
954                         return this;
955                 }
956
957                 if ( jQuery.isFunction( data ) ) {
958                         fn = data;
959                         data = undefined;
960                 }
961
962                 types = (types || "").split(" ");
963
964                 while ( (type = types[ i++ ]) != null ) {
965                         match = rnamespaces.exec( type );
966                         namespaces = "";
967
968                         if ( match )  {
969                                 namespaces = match[0];
970                                 type = type.replace( rnamespaces, "" );
971                         }
972
973                         if ( type === "hover" ) {
974                                 types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
975                                 continue;
976                         }
977
978                         preType = type;
979
980                         if ( type === "focus" || type === "blur" ) {
981                                 types.push( liveMap[ type ] + namespaces );
982                                 type = type + namespaces;
983
984                         } else {
985                                 type = (liveMap[ type ] || type) + namespaces;
986                         }
987
988                         if ( name === "live" ) {
989                                 // bind live handler
990                                 for ( var j = 0, l = context.length; j < l; j++ ) {
991                                         jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
992                                                 { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
993                                 }
994
995                         } else {
996                                 // unbind live handler
997                                 context.unbind( "live." + liveConvert( type, selector ), fn );
998                         }
999                 }
1000                 
1001                 return this;
1002         };
1003 });
1004
1005 function liveHandler( event ) {
1006         var stop, maxLevel, elems = [], selectors = [],
1007                 related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
1008                 events = jQuery.data( this, "events" );
1009
1010         // Make sure we avoid non-left-click bubbling in Firefox (#3861)
1011         if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
1012                 return;
1013         }
1014
1015         if ( event.namespace ) {
1016                 namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
1017         }
1018
1019         event.liveFired = this;
1020
1021         var live = events.live.slice(0);
1022
1023         for ( j = 0; j < live.length; j++ ) {
1024                 handleObj = live[j];
1025
1026                 if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
1027                         selectors.push( handleObj.selector );
1028
1029                 } else {
1030                         live.splice( j--, 1 );
1031                 }
1032         }
1033
1034         match = jQuery( event.target ).closest( selectors, event.currentTarget );
1035
1036         for ( i = 0, l = match.length; i < l; i++ ) {
1037                 close = match[i];
1038
1039                 for ( j = 0; j < live.length; j++ ) {
1040                         handleObj = live[j];
1041
1042                         if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
1043                                 elem = close.elem;
1044                                 related = null;
1045
1046                                 // Those two events require additional checking
1047                                 if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
1048                                         event.type = handleObj.preType;
1049                                         related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
1050                                 }
1051
1052                                 if ( !related || related !== elem ) {
1053                                         elems.push({ elem: elem, handleObj: handleObj, level: close.level });
1054                                 }
1055                         }
1056                 }
1057         }
1058
1059         for ( i = 0, l = elems.length; i < l; i++ ) {
1060                 match = elems[i];
1061
1062                 if ( maxLevel && match.level > maxLevel ) {
1063                         break;
1064                 }
1065
1066                 event.currentTarget = match.elem;
1067                 event.data = match.handleObj.data;
1068                 event.handleObj = match.handleObj;
1069
1070                 ret = match.handleObj.origHandler.apply( match.elem, arguments );
1071
1072                 if ( ret === false || event.isPropagationStopped() ) {
1073                         maxLevel = match.level;
1074
1075                         if ( ret === false ) {
1076                                 stop = false;
1077                         }
1078                 }
1079         }
1080
1081         return stop;
1082 }
1083
1084 function liveConvert( type, selector ) {
1085         return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
1086 }
1087
1088 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1089         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1090         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1091
1092         // Handle event binding
1093         jQuery.fn[ name ] = function( data, fn ) {
1094                 if ( fn == null ) {
1095                         fn = data;
1096                         data = null;
1097                 }
1098
1099                 return arguments.length > 0 ?
1100                         this.bind( name, data, fn ) :
1101                         this.trigger( name );
1102         };
1103
1104         if ( jQuery.attrFn ) {
1105                 jQuery.attrFn[ name ] = true;
1106         }
1107 });
1108
1109 // Prevent memory leaks in IE
1110 // Window isn't included so as not to unbind existing unload events
1111 // More info:
1112 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1113 if ( window.attachEvent && !window.addEventListener ) {
1114         window.attachEvent("onunload", function() {
1115                 for ( var id in jQuery.cache ) {
1116                         if ( jQuery.cache[ id ].handle ) {
1117                                 // Try/Catch is to handle iframes being unloaded, see #4280
1118                                 try {
1119                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1120                                 } catch(e) {}
1121                         }
1122                 }
1123         });
1124 }
1125
1126 })( jQuery );