Merge branch 'eventprops.1.6final' of https://github.com/rwldrn/jquery into rwldrn...
[jquery.git] / src / event.js
bloba4f02a7a0a900a16a25963e01ec7cf914be422e9
1 (function( jQuery ) {
3 var hasOwn = Object.prototype.hasOwnProperty,
4         rnamespaces = /\.(.*)$/,
5         rformElems = /^(?:textarea|input|select)$/i,
6         rperiod = /\./g,
7         rspace = / /g,
8         rescape = /[^\w\s.|`]/g,
9         fcleanup = function( nm ) {
10                 return nm.replace(rescape, "\\$&");
11         };
14  * A number of helper functions used for managing events.
15  * Many of the ideas behind this code originated from
16  * Dean Edwards' addEvent library.
17  */
18 jQuery.event = {
20         // Bind an event to an element
21         // Original by Dean Edwards
22         add: function( elem, types, handler, data ) {
23                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
24                         return;
25                 }
27                 // TODO :: Use a try/catch until it's safe to pull this out (likely 1.6)
28                 // Minor release fix for bug #8018
29                 try {
30                         // For whatever reason, IE has trouble passing the window object
31                         // around, causing it to be cloned in the process
32                         if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
33                                 elem = window;
34                         }
35                 }
36                 catch ( e ) {}
38                 if ( handler === false ) {
39                         handler = returnFalse;
40                 } else if ( !handler ) {
41                         // Fixes bug #7229. Fix recommended by jdalton
42                         return;
43                 }
45                 var handleObjIn, handleObj;
47                 if ( handler.handler ) {
48                         handleObjIn = handler;
49                         handler = handleObjIn.handler;
50                 }
52                 // Make sure that the function being executed has a unique ID
53                 if ( !handler.guid ) {
54                         handler.guid = jQuery.guid++;
55                 }
57                 // Init the element's event structure
58                 var elemData = jQuery._data( elem );
60                 // If no elemData is found then we must be trying to bind to one of the
61                 // banned noData elements
62                 if ( !elemData ) {
63                         return;
64                 }
66                 var events = elemData.events,
67                         eventHandle = elemData.handle;
69                 if ( !events ) {
70                         elemData.events = events = {};
71                 }
73                 if ( !eventHandle ) {
74                         elemData.handle = eventHandle = function( e ) {
75                                 // Discard the second event of a jQuery.event.trigger() and
76                                 // when an event is called after a page has unloaded
77                                 return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
78                                         jQuery.event.handle.apply( eventHandle.elem, arguments ) :
79                                         undefined;
80                         };
81                 }
83                 // Add elem as a property of the handle function
84                 // This is to prevent a memory leak with non-native events in IE.
85                 eventHandle.elem = elem;
87                 // Handle multiple events separated by a space
88                 // jQuery(...).bind("mouseover mouseout", fn);
89                 types = types.split(" ");
91                 var type, i = 0, namespaces;
93                 while ( (type = types[ i++ ]) ) {
94                         handleObj = handleObjIn ?
95                                 jQuery.extend({}, handleObjIn) :
96                                 { handler: handler, data: data };
98                         // Namespaced event handlers
99                         if ( type.indexOf(".") > -1 ) {
100                                 namespaces = type.split(".");
101                                 type = namespaces.shift();
102                                 handleObj.namespace = namespaces.slice(0).sort().join(".");
104                         } else {
105                                 namespaces = [];
106                                 handleObj.namespace = "";
107                         }
109                         handleObj.type = type;
110                         if ( !handleObj.guid ) {
111                                 handleObj.guid = handler.guid;
112                         }
114                         // Get the current list of functions bound to this event
115                         var handlers = events[ type ],
116                                 special = jQuery.event.special[ type ] || {};
118                         // Init the event handler queue
119                         if ( !handlers ) {
120                                 handlers = events[ type ] = [];
122                                 // Check for a special event handler
123                                 // Only use addEventListener/attachEvent if the special
124                                 // events handler returns false
125                                 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
126                                         // Bind the global event handler to the element
127                                         if ( elem.addEventListener ) {
128                                                 elem.addEventListener( type, eventHandle, false );
130                                         } else if ( elem.attachEvent ) {
131                                                 elem.attachEvent( "on" + type, eventHandle );
132                                         }
133                                 }
134                         }
136                         if ( special.add ) {
137                                 special.add.call( elem, handleObj );
139                                 if ( !handleObj.handler.guid ) {
140                                         handleObj.handler.guid = handler.guid;
141                                 }
142                         }
144                         // Add the function to the element's handler list
145                         handlers.push( handleObj );
147                         // Keep track of which events have been used, for event optimization
148                         jQuery.event.global[ type ] = true;
149                 }
151                 // Nullify elem to prevent memory leaks in IE
152                 elem = null;
153         },
155         global: {},
157         // Detach an event or set of events from an element
158         remove: function( elem, types, handler, pos ) {
159                 // don't do events on text and comment nodes
160                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
161                         return;
162                 }
164                 if ( handler === false ) {
165                         handler = returnFalse;
166                 }
168                 var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
169                         elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
170                         events = elemData && elemData.events;
172                 if ( !elemData || !events ) {
173                         return;
174                 }
176                 // types is actually an event object here
177                 if ( types && types.type ) {
178                         handler = types.handler;
179                         types = types.type;
180                 }
182                 // Unbind all events for the element
183                 if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
184                         types = types || "";
186                         for ( type in events ) {
187                                 jQuery.event.remove( elem, type + types );
188                         }
190                         return;
191                 }
193                 // Handle multiple events separated by a space
194                 // jQuery(...).unbind("mouseover mouseout", fn);
195                 types = types.split(" ");
197                 while ( (type = types[ i++ ]) ) {
198                         origType = type;
199                         handleObj = null;
200                         all = type.indexOf(".") < 0;
201                         namespaces = [];
203                         if ( !all ) {
204                                 // Namespaced event handlers
205                                 namespaces = type.split(".");
206                                 type = namespaces.shift();
208                                 namespace = new RegExp("(^|\\.)" +
209                                         jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
210                         }
212                         eventType = events[ type ];
214                         if ( !eventType ) {
215                                 continue;
216                         }
218                         if ( !handler ) {
219                                 for ( j = 0; j < eventType.length; j++ ) {
220                                         handleObj = eventType[ j ];
222                                         if ( all || namespace.test( handleObj.namespace ) ) {
223                                                 jQuery.event.remove( elem, origType, handleObj.handler, j );
224                                                 eventType.splice( j--, 1 );
225                                         }
226                                 }
228                                 continue;
229                         }
231                         special = jQuery.event.special[ type ] || {};
233                         for ( j = pos || 0; j < eventType.length; j++ ) {
234                                 handleObj = eventType[ j ];
236                                 if ( handler.guid === handleObj.guid ) {
237                                         // remove the given handler for the given type
238                                         if ( all || namespace.test( handleObj.namespace ) ) {
239                                                 if ( pos == null ) {
240                                                         eventType.splice( j--, 1 );
241                                                 }
243                                                 if ( special.remove ) {
244                                                         special.remove.call( elem, handleObj );
245                                                 }
246                                         }
248                                         if ( pos != null ) {
249                                                 break;
250                                         }
251                                 }
252                         }
254                         // remove generic event handler if no more handlers exist
255                         if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
256                                 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
257                                         jQuery.removeEvent( elem, type, elemData.handle );
258                                 }
260                                 ret = null;
261                                 delete events[ type ];
262                         }
263                 }
265                 // Remove the expando if it's no longer used
266                 if ( jQuery.isEmptyObject( events ) ) {
267                         var handle = elemData.handle;
268                         if ( handle ) {
269                                 handle.elem = null;
270                         }
272                         delete elemData.events;
273                         delete elemData.handle;
275                         if ( jQuery.isEmptyObject( elemData ) ) {
276                                 jQuery.removeData( elem, undefined, true );
277                         }
278                 }
279         },
281         trigger: function( event, data, elem ) {
282                 // Event object or event type
283                 var type = event.type || event,
284                         namespaces = [];
286                 event = typeof event === "object" ?
287                         // jQuery.Event object
288                         event[ jQuery.expando ] ? event :
289                         // Object literal
290                         jQuery.extend( jQuery.Event(type), event ) :
291                         // Just the event type (string)
292                         jQuery.Event(type);
294                 if ( type.indexOf("!") >= 0 ) {
295                 // Exclusive events trigger only for the bare event type (no namespaces)
296                         event.type = type = type.slice(0, -1);
297                         event.exclusive = true;
298                 }
299                 if ( type.indexOf(".") >= 0 ) {
300                         // Namespaced trigger; create a regexp to match event type in handle()
301                         namespaces = type.split(".");
302                         event.type = type = namespaces.shift();
303                         namespaces.sort();
304                 }
305                 event.namespace = namespaces.join(".");
306                 event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
307                 
308                 // Handle a global trigger
309                 if ( !elem ) {
310                         // Don't bubble custom events when global (to avoid too much overhead)
311                         event.stopPropagation();
313                         // Save some time, only trigger if we've ever bound an event for this type
314                         if ( jQuery.event.global[ type ] ) {
315                                 // XXX This code smells terrible. event.js should not be directly
316                                 // inspecting the data cache
317                                 jQuery.each( jQuery.cache, function() {
318                                         // internalKey variable is just used to make it easier to find
319                                         // and potentially change this stuff later; currently it just
320                                         // points to jQuery.expando
321                                         var internalKey = jQuery.expando,
322                                                 internalCache = this[ internalKey ];
323                                         if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
324                                                 jQuery.event.trigger( event, data, internalCache.handle.elem );
325                                         }
326                                 });
327                         }
328                         return;
329                 }
331                 // Don't do events on text and comment nodes
332                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
333                         return;
334                 }
336                 // Clean up the event in case it is being reused
337                 event.result = undefined;
338                 event.target = elem;
340                 // Clone any incoming data and prepend the event, creating the handler arg list
341                 data = jQuery.makeArray( data );
342                 data.unshift( event );
344                 var cur = elem,
345                         // IE doesn't like method names with a colon (#3533, #8272)
346                         ontype = type.indexOf(":") < 0? "on" + type : "";
348                 // Fire event on the current element, then bubble up the DOM tree
349                 do {
350                         var handle = jQuery._data( cur, "handle" );
352                         event.currentTarget = cur;
353                         if ( handle ) {
354                                 handle.apply( cur, data );
355                         }
357                         // Trigger an inline bound script
358                         if ( ontype &&jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
359                                 event.result = false;
360                                 event.preventDefault();
361                         }
363                         // Bubble up to document, then to window
364                         cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
365                 } while ( cur && !event.isPropagationStopped() );
367                 // If nobody prevented the default action, do it now
368                 if ( !event.isDefaultPrevented() ) {
369                         var old,
370                                 special = jQuery.event.special[ type ] || {};
372                         if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
373                                 !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
375                                 // Call a native DOM method on the target with the same name name as the event.
376                                 // Can't use an .isFunction)() check here because IE6/7 fails that test.
377                                 // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
378                                 try {
379                                         if ( ontype && elem[ type ] ) {
380                                                 // Don't re-trigger an onFOO event when we call its FOO() method
381                                                 old = elem[ ontype ];
383                                                 if ( old ) {
384                                                         elem[ ontype ] = null;
385                                                 }
387                                                 jQuery.event.triggered = type;
388                                                 elem[ type ]();
389                                         }
390                                 } catch ( ieError ) {}
392                                 if ( old ) {
393                                         elem[ ontype ] = old;
394                                 }
396                                 jQuery.event.triggered = undefined;
397                         }
398                 }
399         },
401         handle: function( event ) {
402                 event = jQuery.event.fix( event || window.event );
403                 // Snapshot the handlers list since a called handler may add/remove events.
404                 var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
405                         run_all = !event.exclusive && !event.namespace,
406                         args = jQuery.makeArray( arguments );
408                 // Use the fix-ed Event rather than the (read-only) native event
409                 args[0] = event;
410                 event.currentTarget = this;
412                 for ( var j = 0, l = handlers.length; j < l; j++ ) {
413                         var handleObj = handlers[ j ];
415                         // Triggered event must 1) be non-exclusive and have no namespace, or
416                         // 2) have namespace(s) a subset or equal to those in the bound event.
417                         if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
418                                 // Pass in a reference to the handler function itself
419                                 // So that we can later remove it
420                                 event.handler = handleObj.handler;
421                                 event.data = handleObj.data;
422                                 event.handleObj = handleObj;
424                                 var ret = handleObj.handler.apply( this, args );
426                                 if ( ret !== undefined ) {
427                                         event.result = ret;
428                                         if ( ret === false ) {
429                                                 event.preventDefault();
430                                                 event.stopPropagation();
431                                         }
432                                 }
434                                 if ( event.isImmediatePropagationStopped() ) {
435                                         break;
436                                 }
437                         }
438                 }
439                 return event.result;
440         },
442         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(" "),
444         fix: function( event ) {
445                 if ( event[ jQuery.expando ] ) {
446                         return event;
447                 }
449                 // store a copy of the original event object
450                 // and "clone" to set read-only properties
451                 var originalEvent = event;
452                 event = jQuery.Event( originalEvent );
454                 for ( var i = this.props.length, prop; i; ) {
455                         prop = this.props[ --i ];
456                         event[ prop ] = originalEvent[ prop ];
457                 }
459                 // Fix target property, if necessary
460                 if ( !event.target ) {
461                         // Fixes #1925 where srcElement might not be defined either
462                         event.target = event.srcElement || document;
463                 }
465                 // check if target is a textnode (safari)
466                 if ( event.target.nodeType === 3 ) {
467                         event.target = event.target.parentNode;
468                 }
470                 // Add relatedTarget, if necessary
471                 if ( !event.relatedTarget && event.fromElement ) {
472                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
473                 }
475                 // Calculate pageX/Y if missing and clientX/Y available
476                 if ( event.pageX == null && event.clientX != null ) {
477                         var eventDocument = event.target.ownerDocument || document,
478                                 doc = eventDocument.documentElement,
479                                 body = eventDocument.body;
481                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
482                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
483                 }
485                 // Add which for key events
486                 if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
487                         event.which = event.charCode != null ? event.charCode : event.keyCode;
488                 }
490                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
491                 if ( !event.metaKey && event.ctrlKey ) {
492                         event.metaKey = event.ctrlKey;
493                 }
495                 // Add which for click: 1 === left; 2 === middle; 3 === right
496                 // Note: button is not normalized, so don't use it
497                 if ( !event.which && event.button !== undefined ) {
498                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
499                 }
501                 return event;
502         },
504         // Deprecated, use jQuery.guid instead
505         guid: 1E8,
507         // Deprecated, use jQuery.proxy instead
508         proxy: jQuery.proxy,
510         special: {
511                 ready: {
512                         // Make sure the ready event is setup
513                         setup: jQuery.bindReady,
514                         teardown: jQuery.noop
515                 },
517                 live: {
518                         add: function( handleObj ) {
519                                 jQuery.event.add( this,
520                                         liveConvert( handleObj.origType, handleObj.selector ),
521                                         jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
522                         },
524                         remove: function( handleObj ) {
525                                 jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
526                         }
527                 },
529                 beforeunload: {
530                         setup: function( data, namespaces, eventHandle ) {
531                                 // We only want to do this special case on windows
532                                 if ( jQuery.isWindow( this ) ) {
533                                         this.onbeforeunload = eventHandle;
534                                 }
535                         },
537                         teardown: function( namespaces, eventHandle ) {
538                                 if ( this.onbeforeunload === eventHandle ) {
539                                         this.onbeforeunload = null;
540                                 }
541                         }
542                 }
543         }
546 jQuery.removeEvent = document.removeEventListener ?
547         function( elem, type, handle ) {
548                 if ( elem.removeEventListener ) {
549                         elem.removeEventListener( type, handle, false );
550                 }
551         } :
552         function( elem, type, handle ) {
553                 if ( elem.detachEvent ) {
554                         elem.detachEvent( "on" + type, handle );
555                 }
556         };
558 jQuery.Event = function( src ) {
559         // Allow instantiation without the 'new' keyword
560         if ( !this.preventDefault ) {
561                 return new jQuery.Event( src );
562         }
564         // Event object
565         if ( src && src.type ) {
566                 this.originalEvent = src;
568                 // Push explicitly provided properties onto the event object
569                 for ( var prop in src ) {
570                         //      Ensure we don't clobber jQuery.Event prototype
571                         //      with own properties.
572                         if ( hasOwn.call( src, prop ) ) {
573                                 this[ prop ] = src[ prop ];
574                         }
575                 }
577                 // Events bubbling up the document may have been marked as prevented
578                 // by a handler lower down the tree; reflect the correct value.
579                 this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
580                         src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
582         // Event type
583         } else {
584                 this.type = src;
585         }
587         // timeStamp is buggy for some events on Firefox(#3843)
588         // So we won't rely on the native value
589         this.timeStamp = jQuery.now();
591         // Mark it as fixed
592         this[ jQuery.expando ] = true;
595 function returnFalse() {
596         return false;
598 function returnTrue() {
599         return true;
602 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
603 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
604 jQuery.Event.prototype = {
605         preventDefault: function() {
606                 this.isDefaultPrevented = returnTrue;
608                 var e = this.originalEvent;
609                 if ( !e ) {
610                         return;
611                 }
613                 // if preventDefault exists run it on the original event
614                 if ( e.preventDefault ) {
615                         e.preventDefault();
617                 // otherwise set the returnValue property of the original event to false (IE)
618                 } else {
619                         e.returnValue = false;
620                 }
621         },
622         stopPropagation: function() {
623                 this.isPropagationStopped = returnTrue;
625                 var e = this.originalEvent;
626                 if ( !e ) {
627                         return;
628                 }
629                 // if stopPropagation exists run it on the original event
630                 if ( e.stopPropagation ) {
631                         e.stopPropagation();
632                 }
633                 // otherwise set the cancelBubble property of the original event to true (IE)
634                 e.cancelBubble = true;
635         },
636         stopImmediatePropagation: function() {
637                 this.isImmediatePropagationStopped = returnTrue;
638                 this.stopPropagation();
639         },
640         isDefaultPrevented: returnFalse,
641         isPropagationStopped: returnFalse,
642         isImmediatePropagationStopped: returnFalse
645 // Checks if an event happened on an element within another element
646 // Used in jQuery.event.special.mouseenter and mouseleave handlers
647 var withinElement = function( event ) {
648         // Check if mouse(over|out) are still within the same parent element
649         var parent = event.relatedTarget;
651         // Firefox sometimes assigns relatedTarget a XUL element
652         // which we cannot access the parentNode property of
653         try {
655                 // Chrome does something similar, the parentNode property
656                 // can be accessed but is null.
657                 if ( parent && parent !== document && !parent.parentNode ) {
658                         return;
659                 }
660                 // Traverse up the tree
661                 while ( parent && parent !== this ) {
662                         parent = parent.parentNode;
663                 }
665                 if ( parent !== this ) {
666                         // set the correct event type
667                         event.type = event.data;
669                         // handle event if we actually just moused on to a non sub-element
670                         jQuery.event.handle.apply( this, arguments );
671                 }
673         // assuming we've left the element since we most likely mousedover a xul element
674         } catch(e) { }
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 );
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         };
699 // submit delegation
700 if ( !jQuery.support.submitBubbles ) {
702         jQuery.event.special.submit = {
703                 setup: function( data, namespaces ) {
704                         if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
705                                 jQuery.event.add(this, "click.specialSubmit", function( e ) {
706                                         var elem = e.target,
707                                                 type = elem.type;
709                                         if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
710                                                 trigger( "submit", this, arguments );
711                                         }
712                                 });
714                                 jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
715                                         var elem = e.target,
716                                                 type = elem.type;
718                                         if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
719                                                 trigger( "submit", this, arguments );
720                                         }
721                                 });
723                         } else {
724                                 return false;
725                         }
726                 },
728                 teardown: function( namespaces ) {
729                         jQuery.event.remove( this, ".specialSubmit" );
730                 }
731         };
735 // change delegation, happens here so we have bind.
736 if ( !jQuery.support.changeBubbles ) {
738         var changeFilters,
740         getVal = function( elem ) {
741                 var type = elem.type, val = elem.value;
743                 if ( type === "radio" || type === "checkbox" ) {
744                         val = elem.checked;
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                                 "";
753                 } else if ( elem.nodeName.toLowerCase() === "select" ) {
754                         val = elem.selectedIndex;
755                 }
757                 return val;
758         },
760         testChange = function testChange( e ) {
761                 var elem = e.target, data, val;
763                 if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
764                         return;
765                 }
767                 data = jQuery._data( elem, "_change_data" );
768                 val = getVal(elem);
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                 }
775                 if ( data === undefined || val === data ) {
776                         return;
777                 }
779                 if ( data != null || val ) {
780                         e.type = "change";
781                         e.liveFired = undefined;
782                         jQuery.event.trigger( e, arguments[1], elem );
783                 }
784         };
786         jQuery.event.special.change = {
787                 filters: {
788                         focusout: testChange,
790                         beforedeactivate: testChange,
792                         click: function( e ) {
793                                 var elem = e.target, type = elem.type;
795                                 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
796                                         testChange.call( this, e );
797                                 }
798                         },
800                         // Change has to be called before submit
801                         // Keydown will be called before keypress, which is used in submit-event delegation
802                         keydown: function( e ) {
803                                 var elem = e.target, type = elem.type;
805                                 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
806                                         (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
807                                         type === "select-multiple" ) {
808                                         testChange.call( this, e );
809                                 }
810                         },
812                         // Beforeactivate happens also before the previous element is blurred
813                         // with this event you can't trigger a change event, but you can store
814                         // information
815                         beforeactivate: function( e ) {
816                                 var elem = e.target;
817                                 jQuery._data( elem, "_change_data", getVal(elem) );
818                         }
819                 },
821                 setup: function( data, namespaces ) {
822                         if ( this.type === "file" ) {
823                                 return false;
824                         }
826                         for ( var type in changeFilters ) {
827                                 jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
828                         }
830                         return rformElems.test( this.nodeName );
831                 },
833                 teardown: function( namespaces ) {
834                         jQuery.event.remove( this, ".specialChange" );
836                         return rformElems.test( this.nodeName );
837                 }
838         };
840         changeFilters = jQuery.event.special.change.filters;
842         // Handle when the input is .focus()'d
843         changeFilters.focus = changeFilters.beforeactivate;
846 function trigger( type, elem, args ) {
847         // Piggyback on a donor event to simulate a different one.
848         // Fake originalEvent to avoid donor's stopPropagation, but if the
849         // simulated event prevents default then we do the same on the donor.
850         // Don't pass args or remember liveFired; they apply to the donor event.
851         var event = jQuery.extend( {}, args[ 0 ] );
852         event.type = type;
853         event.originalEvent = {};
854         event.liveFired = undefined;
855         jQuery.event.handle.call( elem, event );
856         if ( event.isDefaultPrevented() ) {
857                 args[ 0 ].preventDefault();
858         }
861 // Create "bubbling" focus and blur events
862 if ( !jQuery.support.focusinBubbles ) {
863         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
865                 // Attach a single capturing handler while someone wants focusin/focusout
866                 var attaches = 0;
868                 jQuery.event.special[ fix ] = {
869                         setup: function() {
870                                 if ( attaches++ === 0 ) {
871                                         document.addEventListener( orig, handler, true );
872                                 }
873                         },
874                         teardown: function() {
875                                 if ( --attaches === 0 ) {
876                                         document.removeEventListener( orig, handler, true );
877                                 }
878                         }
879                 };
881                 function handler( donor ) {
882                         // Donor event is always a native one; fix it and switch its type.
883                         // Let focusin/out handler cancel the donor focus/blur event.
884                         var e = jQuery.event.fix( donor );
885                         e.type = fix;
886                         e.originalEvent = {};
887                         jQuery.event.trigger( e, null, e.target );
888                         if ( e.isDefaultPrevented() ) {
889                                 donor.preventDefault();
890                         }
891                 }
892         });
895 jQuery.each(["bind", "one"], function( i, name ) {
896         jQuery.fn[ name ] = function( type, data, fn ) {
897                 // Handle object literals
898                 if ( typeof type === "object" ) {
899                         for ( var key in type ) {
900                                 this[ name ](key, data, type[key], fn);
901                         }
902                         return this;
903                 }
905                 if ( jQuery.isFunction( data ) || data === false ) {
906                         fn = data;
907                         data = undefined;
908                 }
910                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
911                         jQuery( this ).unbind( event, handler );
912                         return fn.apply( this, arguments );
913                 }) : fn;
915                 if ( type === "unload" && name !== "one" ) {
916                         this.one( type, data, fn );
918                 } else {
919                         for ( var i = 0, l = this.length; i < l; i++ ) {
920                                 jQuery.event.add( this[i], type, handler, data );
921                         }
922                 }
924                 return this;
925         };
928 jQuery.fn.extend({
929         unbind: function( type, fn ) {
930                 // Handle object literals
931                 if ( typeof type === "object" && !type.preventDefault ) {
932                         for ( var key in type ) {
933                                 this.unbind(key, type[key]);
934                         }
936                 } else {
937                         for ( var i = 0, l = this.length; i < l; i++ ) {
938                                 jQuery.event.remove( this[i], type, fn );
939                         }
940                 }
942                 return this;
943         },
945         delegate: function( selector, types, data, fn ) {
946                 return this.live( types, data, fn, selector );
947         },
949         undelegate: function( selector, types, fn ) {
950                 if ( arguments.length === 0 ) {
951                                 return this.unbind( "live" );
953                 } else {
954                         return this.die( types, null, fn, selector );
955                 }
956         },
958         trigger: function( type, data ) {
959                 return this.each(function() {
960                         jQuery.event.trigger( type, data, this );
961                 });
962         },
964         triggerHandler: function( type, data ) {
965                 if ( this[0] ) {
966                         var event = jQuery.Event( type );
967                         event.preventDefault();
968                         event.stopPropagation();
969                         jQuery.event.trigger( event, data, this[0] );
970                         return event.result;
971                 }
972         },
974         toggle: function( fn ) {
975                 // Save reference to arguments for access in closure
976                 var args = arguments,
977                         i = 1;
979                 // link all the functions, so any of them can unbind this click handler
980                 while ( i < args.length ) {
981                         jQuery.proxy( fn, args[ i++ ] );
982                 }
984                 return this.click( jQuery.proxy( fn, function( event ) {
985                         // Figure out which function to execute
986                         var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
987                         jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
989                         // Make sure that clicks stop
990                         event.preventDefault();
992                         // and execute the function
993                         return args[ lastToggle ].apply( this, arguments ) || false;
994                 }));
995         },
997         hover: function( fnOver, fnOut ) {
998                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
999         }
1002 var liveMap = {
1003         focus: "focusin",
1004         blur: "focusout",
1005         mouseenter: "mouseover",
1006         mouseleave: "mouseout"
1009 jQuery.each(["live", "die"], function( i, name ) {
1010         jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
1011                 var type, i = 0, match, namespaces, preType,
1012                         selector = origSelector || this.selector,
1013                         context = origSelector ? this : jQuery( this.context );
1015                 if ( typeof types === "object" && !types.preventDefault ) {
1016                         for ( var key in types ) {
1017                                 context[ name ]( key, data, types[key], selector );
1018                         }
1020                         return this;
1021                 }
1023                 if ( data === false || jQuery.isFunction( data ) ) {
1024                         fn = data || returnFalse;
1025                         data = undefined;
1026                 }       
1028                 types = (types || "").split(" ");
1030                 while ( (type = types[ i++ ]) != null ) {
1031                         match = rnamespaces.exec( type );
1032                         namespaces = "";
1034                         if ( match )  {
1035                                 namespaces = match[0];
1036                                 type = type.replace( rnamespaces, "" );
1037                         }
1039                         if ( type === "hover" ) {
1040                                 types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
1041                                 continue;
1042                         }
1044                         preType = type;
1046                         if ( type === "focus" || type === "blur" ) {
1047                                 types.push( liveMap[ type ] + namespaces );
1048                                 type = type + namespaces;
1050                         } else {
1051                                 type = (liveMap[ type ] || type) + namespaces;
1052                         }
1054                         if ( name === "live" ) {
1055                                 // bind live handler
1056                                 for ( var j = 0, l = context.length; j < l; j++ ) {
1057                                         jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
1058                                                 { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
1059                                 }
1061                         } else {
1062                                 // unbind live handler
1063                                 context.unbind( "live." + liveConvert( type, selector ), fn );
1064                         }
1065                 }
1067                 return this;
1068         };
1071 function liveHandler( event ) {
1072         var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
1073                 elems = [],
1074                 selectors = [],
1075                 events = jQuery._data( this, "events" );
1077         // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
1078         if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
1079                 return;
1080         }
1082         if ( event.namespace ) {
1083                 namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
1084         }
1086         event.liveFired = this;
1088         var live = events.live.slice(0);
1090         for ( j = 0; j < live.length; j++ ) {
1091                 handleObj = live[j];
1093                 if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
1094                         selectors.push( handleObj.selector );
1096                 } else {
1097                         live.splice( j--, 1 );
1098                 }
1099         }
1101         match = jQuery( event.target ).closest( selectors, event.currentTarget );
1103         for ( i = 0, l = match.length; i < l; i++ ) {
1104                 close = match[i];
1106                 for ( j = 0; j < live.length; j++ ) {
1107                         handleObj = live[j];
1109                         if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
1110                                 elem = close.elem;
1111                                 related = null;
1113                                 // Those two events require additional checking
1114                                 if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
1115                                         event.type = handleObj.preType;
1116                                         related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
1117                                 }
1119                                 if ( !related || related !== elem ) {
1120                                         elems.push({ elem: elem, handleObj: handleObj, level: close.level });
1121                                 }
1122                         }
1123                 }
1124         }
1126         for ( i = 0, l = elems.length; i < l; i++ ) {
1127                 match = elems[i];
1129                 if ( maxLevel && match.level > maxLevel ) {
1130                         break;
1131                 }
1133                 event.currentTarget = match.elem;
1134                 event.data = match.handleObj.data;
1135                 event.handleObj = match.handleObj;
1137                 ret = match.handleObj.origHandler.apply( match.elem, arguments );
1139                 if ( ret === false || event.isPropagationStopped() ) {
1140                         maxLevel = match.level;
1142                         if ( ret === false ) {
1143                                 stop = false;
1144                         }
1145                         if ( event.isImmediatePropagationStopped() ) {
1146                                 break;
1147                         }
1148                 }
1149         }
1151         return stop;
1154 function liveConvert( type, selector ) {
1155         return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
1158 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1159         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1160         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1162         // Handle event binding
1163         jQuery.fn[ name ] = function( data, fn ) {
1164                 if ( fn == null ) {
1165                         fn = data;
1166                         data = null;
1167                 }
1169                 return arguments.length > 0 ?
1170                         this.bind( name, data, fn ) :
1171                         this.trigger( name );
1172         };
1174         if ( jQuery.attrFn ) {
1175                 jQuery.attrFn[ name ] = true;
1176         }
1179 })( jQuery );