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