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