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