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