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