Fix patch for #7150, which was not deleting the events object properly on plain JS...
[jquery.git] / src / event.js
1 (function( jQuery ) {
2
3 var rnamespaces = /\.(.*)$/,
4         rformElems = /^(?:textarea|input|select)$/i,
5         rperiod = /\./g,
6         rspace = / /g,
7         rescape = /[^\w\s.|`]/g,
8         fcleanup = function( nm ) {
9                 return nm.replace(rescape, "\\$&");
10         };
11
12 /*
13  * A number of helper functions used for managing events.
14  * Many of the ideas behind this code originated from
15  * Dean Edwards' addEvent library.
16  */
17 jQuery.event = {
18
19         // Bind an event to an element
20         // Original by Dean Edwards
21         add: function( elem, types, handler, data ) {
22                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
23                         return;
24                 }
25
26                 // For whatever reason, IE has trouble passing the window object
27                 // around, causing it to be cloned in the process
28                 if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
29                         elem = window;
30                 }
31
32                 if ( handler === false ) {
33                         handler = returnFalse;
34                 }
35
36                 var handleObjIn, handleObj;
37
38                 if ( handler.handler ) {
39                         handleObjIn = handler;
40                         handler = handleObjIn.handler;
41                 }
42
43                 // Make sure that the function being executed has a unique ID
44                 if ( !handler.guid ) {
45                         handler.guid = jQuery.guid++;
46                 }
47
48                 // Init the element's event structure
49                 var elemData = jQuery.data( elem );
50
51                 // If no elemData is found then we must be trying to bind to one of the
52                 // banned noData elements
53                 if ( !elemData ) {
54                         return;
55                 }
56
57                 // 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;
69
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                         }
76
77                         elemData.events = events = {};
78                 }
79
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                 }
89
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;
93
94                 // Handle multiple events separated by a space
95                 // jQuery(...).bind("mouseover mouseout", fn);
96                 types = types.split(" ");
97
98                 var type, i = 0, namespaces;
99
100                 while ( (type = types[ i++ ]) ) {
101                         handleObj = handleObjIn ?
102                                 jQuery.extend({}, handleObjIn) :
103                                 { handler: handler, data: data };
104
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(".");
110
111                         } else {
112                                 namespaces = [];
113                                 handleObj.namespace = "";
114                         }
115
116                         handleObj.type = type;
117                         if ( !handleObj.guid ) {
118                                 handleObj.guid = handler.guid;
119                         }
120
121                         // Get the current list of functions bound to this event
122                         var handlers = events[ type ],
123                                 special = jQuery.event.special[ type ] || {};
124
125                         // Init the event handler queue
126                         if ( !handlers ) {
127                                 handlers = events[ type ] = [];
128
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 );
136
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 ); 
145
146                                 if ( !handleObj.handler.guid ) {
147                                         handleObj.handler.guid = handler.guid;
148                                 }
149                         }
150
151                         // Add the function to the element's handler list
152                         handlers.push( handleObj );
153
154                         // Keep track of which events have been used, for global triggering
155                         jQuery.event.global[ type ] = true;
156                 }
157
158                 // Nullify elem to prevent memory leaks in IE
159                 elem = null;
160         },
161
162         global: {},
163
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                 }
170
171                 if ( handler === false ) {
172                         handler = returnFalse;
173                 }
174
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 ];
179
180                 if ( !elemData || !events ) {
181                         return;
182                 }
183                 
184                 if ( typeof events === "function" ) {
185                         elemData = events;
186                         events = events.events;
187                 }
188
189                 // types is actually an event object here
190                 if ( types && types.type ) {
191                         handler = types.handler;
192                         types = types.type;
193                 }
194
195                 // Unbind all events for the element
196                 if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
197                         types = types || "";
198
199                         for ( type in events ) {
200                                 jQuery.event.remove( elem, type + types );
201                         }
202
203                         return;
204                 }
205
206                 // Handle multiple events separated by a space
207                 // jQuery(...).unbind("mouseover mouseout", fn);
208                 types = types.split(" ");
209
210                 while ( (type = types[ i++ ]) ) {
211                         origType = type;
212                         handleObj = null;
213                         all = type.indexOf(".") < 0;
214                         namespaces = [];
215
216                         if ( !all ) {
217                                 // Namespaced event handlers
218                                 namespaces = type.split(".");
219                                 type = namespaces.shift();
220
221                                 namespace = new RegExp("(^|\\.)" + 
222                                         jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
223                         }
224
225                         eventType = events[ type ];
226
227                         if ( !eventType ) {
228                                 continue;
229                         }
230
231                         if ( !handler ) {
232                                 for ( j = 0; j < eventType.length; j++ ) {
233                                         handleObj = eventType[ j ];
234
235                                         if ( all || namespace.test( handleObj.namespace ) ) {
236                                                 jQuery.event.remove( elem, origType, handleObj.handler, j );
237                                                 eventType.splice( j--, 1 );
238                                         }
239                                 }
240
241                                 continue;
242                         }
243
244                         special = jQuery.event.special[ type ] || {};
245
246                         for ( j = pos || 0; j < eventType.length; j++ ) {
247                                 handleObj = eventType[ j ];
248
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                                                 }
255
256                                                 if ( special.remove ) {
257                                                         special.remove.call( elem, handleObj );
258                                                 }
259                                         }
260
261                                         if ( pos != null ) {
262                                                 break;
263                                         }
264                                 }
265                         }
266
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                                 }
272
273                                 ret = null;
274                                 delete events[ type ];
275                         }
276                 }
277
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                         }
284
285                         delete elemData.events;
286                         delete elemData.handle;
287
288                         if ( typeof elemData === "function" ) {
289                                 jQuery.removeData( elem, eventKey );
290
291                         } else if ( jQuery.isEmptyObject( elemData ) ) {
292                                 jQuery.removeData( elem );
293                         }
294                 }
295         },
296
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];
302
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);
311
312                         if ( type.indexOf("!") >= 0 ) {
313                                 event.type = type = type.slice(0, -1);
314                                 event.exclusive = true;
315                         }
316
317                         // Handle a global trigger
318                         if ( !elem ) {
319                                 // Don't bubble custom events when global (to avoid too much overhead)
320                                 event.stopPropagation();
321
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                         }
331
332                         // Handle triggering a single element
333
334                         // don't do events on text and comment nodes
335                         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
336                                 return undefined;
337                         }
338
339                         // Clean up in case it is reused
340                         event.result = undefined;
341                         event.target = elem;
342
343                         // Clone the incoming data, if any
344                         data = jQuery.makeArray( data );
345                         data.unshift( event );
346                 }
347
348                 event.currentTarget = elem;
349
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;
354
355                 if ( handle ) {
356                         handle.apply( elem, data );
357                 }
358
359                 var parent = elem.parentNode || elem.ownerDocument;
360
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                         }
369
370                 // prevent IE from throwing an error for some elements with some event types, see #3533
371                 } catch (inlineError) {}
372
373                 if ( !event.isPropagationStopped() && parent ) {
374                         jQuery.event.trigger( event, data, parent, true );
375
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 ] || {};
380
381                         if ( (!special._default || special._default.call( elem, event ) === false) && 
382                                 !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
383
384                                 try {
385                                         if ( target[ targetType ] ) {
386                                                 // Make sure that we don't accidentally re-trigger the onFOO events
387                                                 old = target[ "on" + targetType ];
388
389                                                 if ( old ) {
390                                                         target[ "on" + targetType ] = null;
391                                                 }
392
393                                                 jQuery.event.triggered = true;
394                                                 target[ targetType ]();
395                                         }
396
397                                 // prevent IE from throwing an error for some elements with some event types, see #3533
398                                 } catch (triggerError) {}
399
400                                 if ( old ) {
401                                         target[ "on" + targetType ] = old;
402                                 }
403
404                                 jQuery.event.triggered = false;
405                         }
406                 }
407         },
408
409         handle: function( event ) {
410                 var all, handlers, namespaces, namespace_sort = [], namespace_re, events, args = jQuery.makeArray( arguments );
411
412                 event = args[0] = jQuery.event.fix( event || window.event );
413                 event.currentTarget = this;
414
415                 // Namespaced event handlers
416                 all = event.type.indexOf(".") < 0 && !event.exclusive;
417
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                 }
424
425                 event.namespace = event.namespace || namespace_sort.join(".");
426
427                 events = jQuery.data(this, this.nodeType ? "events" : "__events__");
428
429                 if ( typeof events === "function" ) {
430                         events = events.events;
431                 }
432
433                 handlers = (events || {})[ event.type ];
434
435                 if ( events && handlers ) {
436                         // Clone the handlers to prevent manipulation
437                         handlers = handlers.slice(0);
438
439                         for ( var j = 0, l = handlers.length; j < l; j++ ) {
440                                 var handleObj = handlers[ j ];
441
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 );
451
452                                         if ( ret !== undefined ) {
453                                                 event.result = ret;
454                                                 if ( ret === false ) {
455                                                         event.preventDefault();
456                                                         event.stopPropagation();
457                                                 }
458                                         }
459
460                                         if ( event.isImmediatePropagationStopped() ) {
461                                                 break;
462                                         }
463                                 }
464                         }
465                 }
466
467                 return event.result;
468         },
469
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(" "),
471
472         fix: function( event ) {
473                 if ( event[ jQuery.expando ] ) {
474                         return event;
475                 }
476
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 );
481
482                 for ( var i = this.props.length, prop; i; ) {
483                         prop = this.props[ --i ];
484                         event[ prop ] = originalEvent[ prop ];
485                 }
486
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                 }
491
492                 // check if target is a textnode (safari)
493                 if ( event.target.nodeType === 3 ) {
494                         event.target = event.target.parentNode;
495                 }
496
497                 // Add relatedTarget, if necessary
498                 if ( !event.relatedTarget && event.fromElement ) {
499                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
500                 }
501
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                 }
508
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                 }
513
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                 }
518
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                 }
524
525                 return event;
526         },
527
528         // Deprecated, use jQuery.guid instead
529         guid: 1E8,
530
531         // Deprecated, use jQuery.proxy instead
532         proxy: jQuery.proxy,
533
534         special: {
535                 ready: {
536                         // Make sure the ready event is setup
537                         setup: jQuery.bindReady,
538                         teardown: jQuery.noop
539                 },
540
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                         },
547
548                         remove: function( handleObj ) {
549                                 jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
550                         }
551                 },
552
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                         },
560
561                         teardown: function( namespaces, eventHandle ) {
562                                 if ( this.onbeforeunload === eventHandle ) {
563                                         this.onbeforeunload = null;
564                                 }
565                         }
566                 }
567         }
568 };
569
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         };
581
582 jQuery.Event = function( src ) {
583         // Allow instantiation without the 'new' keyword
584         if ( !this.preventDefault ) {
585                 return new jQuery.Event( src );
586         }
587
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         }
596
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();
600
601         // Mark it as fixed
602         this[ jQuery.expando ] = true;
603 };
604
605 function returnFalse() {
606         return false;
607 }
608 function returnTrue() {
609         return true;
610 }
611
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;
617
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();
626
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;
634
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
653 };
654
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;
660
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                 }
668
669                 if ( parent !== this ) {
670                         // set the correct event type
671                         event.type = event.data;
672
673                         // handle event if we actually just moused on to a non sub-element
674                         jQuery.event.handle.apply( this, arguments );
675                 }
676
677         // assuming we've left the element since we most likely mousedover a xul element
678         } catch(e) { }
679 },
680
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 );
686 };
687
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         };
701 });
702
703 // submit delegation
704 if ( !jQuery.support.submitBubbles ) {
705
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;
711
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;
720
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                                 });
726
727                         } else {
728                                 return false;
729                         }
730                 },
731
732                 teardown: function( namespaces ) {
733                         jQuery.event.remove( this, ".specialSubmit" );
734                 }
735         };
736
737 }
738
739 // change delegation, happens here so we have bind.
740 if ( !jQuery.support.changeBubbles ) {
741
742         var changeFilters,
743
744         getVal = function( elem ) {
745                 var type = elem.type, val = elem.value;
746
747                 if ( type === "radio" || type === "checkbox" ) {
748                         val = elem.checked;
749
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                                 "";
756
757                 } else if ( elem.nodeName.toLowerCase() === "select" ) {
758                         val = elem.selectedIndex;
759                 }
760
761                 return val;
762         },
763
764         testChange = function testChange( e ) {
765                 var elem = e.target, data, val;
766
767                 if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
768                         return;
769                 }
770
771                 data = jQuery.data( elem, "_change_data" );
772                 val = getVal(elem);
773
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                 }
782
783                 if ( data != null || val ) {
784                         e.type = "change";
785                         e.liveFired = undefined;
786                         return jQuery.event.trigger( e, arguments[1], elem );
787                 }
788         };
789
790         jQuery.event.special.change = {
791                 filters: {
792                         focusout: testChange, 
793
794                         click: function( e ) {
795                                 var elem = e.target, type = elem.type;
796
797                                 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
798                                         return testChange.call( this, e );
799                                 }
800                         },
801
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;
806
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                         },
813
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                 },
822
823                 setup: function( data, namespaces ) {
824                         if ( this.type === "file" ) {
825                                 return false;
826                         }
827
828                         for ( var type in changeFilters ) {
829                                 jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
830                         }
831
832                         return rformElems.test( this.nodeName );
833                 },
834
835                 teardown: function( namespaces ) {
836                         jQuery.event.remove( this, ".specialChange" );
837
838                         return rformElems.test( this.nodeName );
839                 }
840         };
841
842         changeFilters = jQuery.event.special.change.filters;
843
844         // Handle when the input is .focus()'d
845         changeFilters.focus = changeFilters.beforeactivate;
846 }
847
848 function trigger( type, elem, args ) {
849         args[0].type = type;
850         return jQuery.event.handle.apply( elem, args );
851 }
852
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                 };
864
865                 function handler( e ) { 
866                         e = jQuery.event.fix( e );
867                         e.type = fix;
868                         return jQuery.event.handle.call( this, e );
869                 }
870         });
871 }
872
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                 }
887
888                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
889                         jQuery( this ).unbind( event, handler );
890                         return fn.apply( this, arguments );
891                 }) : fn;
892
893                 if ( type === "unload" && name !== "one" ) {
894                         this.one( type, data, fn );
895
896                 } else {
897                         for ( var i = 0, l = this.length; i < l; i++ ) {
898                                 jQuery.event.add( this[i], type, handler, data );
899                         }
900                 }
901
902                 return this;
903         };
904 });
905
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                         }
913
914                 } else {
915                         for ( var i = 0, l = this.length; i < l; i++ ) {
916                                 jQuery.event.remove( this[i], type, fn );
917                         }
918                 }
919
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         },
941
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         },
951
952         toggle: function( fn ) {
953                 // Save reference to arguments for access in closure
954                 var args = arguments, i = 1;
955
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                 }
960
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 );
965
966                         // Make sure that clicks stop
967                         event.preventDefault();
968
969                         // and execute the function
970                         return args[ lastToggle ].apply( this, arguments ) || false;
971                 }));
972         },
973
974         hover: function( fnOver, fnOut ) {
975                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
976         }
977 });
978
979 var liveMap = {
980         focus: "focusin",
981         blur: "focusout",
982         mouseenter: "mouseover",
983         mouseleave: "mouseout"
984 };
985
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                 }
999
1000                 if ( jQuery.isFunction( data ) ) {
1001                         fn = data;
1002                         data = undefined;
1003                 }
1004
1005                 types = (types || "").split(" ");
1006
1007                 while ( (type = types[ i++ ]) != null ) {
1008                         match = rnamespaces.exec( type );
1009                         namespaces = "";
1010
1011                         if ( match )  {
1012                                 namespaces = match[0];
1013                                 type = type.replace( rnamespaces, "" );
1014                         }
1015
1016                         if ( type === "hover" ) {
1017                                 types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
1018                                 continue;
1019                         }
1020
1021                         preType = type;
1022
1023                         if ( type === "focus" || type === "blur" ) {
1024                                 types.push( liveMap[ type ] + namespaces );
1025                                 type = type + namespaces;
1026
1027                         } else {
1028                                 type = (liveMap[ type ] || type) + namespaces;
1029                         }
1030
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                                 }
1037
1038                         } else {
1039                                 // unbind live handler
1040                                 context.unbind( "live." + liveConvert( type, selector ), fn );
1041                         }
1042                 }
1043                 
1044                 return this;
1045         };
1046 });
1047
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__" );
1052
1053         if ( typeof events === "function" ) {
1054                 events = events.events;
1055         }
1056
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         }
1061
1062         if ( event.namespace ) {
1063                 namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
1064         }
1065
1066         event.liveFired = this;
1067
1068         var live = events.live.slice(0);
1069
1070         for ( j = 0; j < live.length; j++ ) {
1071                 handleObj = live[j];
1072
1073                 if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
1074                         selectors.push( handleObj.selector );
1075
1076                 } else {
1077                         live.splice( j--, 1 );
1078                 }
1079         }
1080
1081         match = jQuery( event.target ).closest( selectors, event.currentTarget );
1082
1083         for ( i = 0, l = match.length; i < l; i++ ) {
1084                 close = match[i];
1085
1086                 for ( j = 0; j < live.length; j++ ) {
1087                         handleObj = live[j];
1088
1089                         if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
1090                                 elem = close.elem;
1091                                 related = null;
1092
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                                 }
1098
1099                                 if ( !related || related !== elem ) {
1100                                         elems.push({ elem: elem, handleObj: handleObj, level: close.level });
1101                                 }
1102                         }
1103                 }
1104         }
1105
1106         for ( i = 0, l = elems.length; i < l; i++ ) {
1107                 match = elems[i];
1108
1109                 if ( maxLevel && match.level > maxLevel ) {
1110                         break;
1111                 }
1112
1113                 event.currentTarget = match.elem;
1114                 event.data = match.handleObj.data;
1115                 event.handleObj = match.handleObj;
1116
1117                 ret = match.handleObj.origHandler.apply( match.elem, arguments );
1118
1119                 if ( ret === false || event.isPropagationStopped() ) {
1120                         maxLevel = match.level;
1121
1122                         if ( ret === false ) {
1123                                 stop = false;
1124                         }
1125                 }
1126         }
1127
1128         return stop;
1129 }
1130
1131 function liveConvert( type, selector ) {
1132         return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
1133 }
1134
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 ) {
1138
1139         // Handle event binding
1140         jQuery.fn[ name ] = function( data, fn ) {
1141                 if ( fn == null ) {
1142                         fn = data;
1143                         data = null;
1144                 }
1145
1146                 return arguments.length > 0 ?
1147                         this.bind( name, data, fn ) :
1148                         this.trigger( name );
1149         };
1150
1151         if ( jQuery.attrFn ) {
1152                 jQuery.attrFn[ name ] = true;
1153         }
1154 });
1155
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         });
1171 }
1172
1173 })( jQuery );