beforedeactivate will fire before focusout - use that to our advantage when working...
[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                         beforedeactivate: testChange,
796
797                         click: function( e ) {
798                                 var elem = e.target, type = elem.type;
799
800                                 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
801                                         return testChange.call( this, e );
802                                 }
803                         },
804
805                         // Change has to be called before submit
806                         // Keydown will be called before keypress, which is used in submit-event delegation
807                         keydown: function( e ) {
808                                 var elem = e.target, type = elem.type;
809
810                                 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
811                                         (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
812                                         type === "select-multiple" ) {
813                                         return testChange.call( this, e );
814                                 }
815                         },
816
817                         // Beforeactivate happens also before the previous element is blurred
818                         // with this event you can't trigger a change event, but you can store
819                         // information
820                         beforeactivate: function( e ) {
821                                 var elem = e.target;
822                                 jQuery.data( elem, "_change_data", getVal(elem) );
823                         }
824                 },
825
826                 setup: function( data, namespaces ) {
827                         if ( this.type === "file" ) {
828                                 return false;
829                         }
830
831                         for ( var type in changeFilters ) {
832                                 jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
833                         }
834
835                         return rformElems.test( this.nodeName );
836                 },
837
838                 teardown: function( namespaces ) {
839                         jQuery.event.remove( this, ".specialChange" );
840
841                         return rformElems.test( this.nodeName );
842                 }
843         };
844
845         changeFilters = jQuery.event.special.change.filters;
846
847         // Handle when the input is .focus()'d
848         changeFilters.focus = changeFilters.beforeactivate;
849 }
850
851 function trigger( type, elem, args ) {
852         args[0].type = type;
853         return jQuery.event.handle.apply( elem, args );
854 }
855
856 // Create "bubbling" focus and blur events
857 if ( document.addEventListener ) {
858         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
859                 jQuery.event.special[ fix ] = {
860                         setup: function() {
861                                 if ( focusCounts[fix]++ === 0 ) {
862                                         document.addEventListener( orig, handler, true );
863                                 }
864                         }, 
865                         teardown: function() { 
866                                 if ( --focusCounts[fix] === 0 ) {
867                                         document.removeEventListener( orig, handler, true );
868                                 }
869                         }
870                 };
871
872                 function handler( e ) { 
873                         e = jQuery.event.fix( e );
874                         e.type = fix;
875                         return jQuery.event.trigger( e, null, e.target );
876                 }
877         });
878 }
879
880 jQuery.each(["bind", "one"], function( i, name ) {
881         jQuery.fn[ name ] = function( type, data, fn ) {
882                 // Handle object literals
883                 if ( typeof type === "object" ) {
884                         for ( var key in type ) {
885                                 this[ name ](key, data, type[key], fn);
886                         }
887                         return this;
888                 }
889                 
890                 if ( jQuery.isFunction( data ) || data === false ) {
891                         fn = data;
892                         data = undefined;
893                 }
894
895                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
896                         jQuery( this ).unbind( event, handler );
897                         return fn.apply( this, arguments );
898                 }) : fn;
899
900                 if ( type === "unload" && name !== "one" ) {
901                         this.one( type, data, fn );
902
903                 } else {
904                         for ( var i = 0, l = this.length; i < l; i++ ) {
905                                 jQuery.event.add( this[i], type, handler, data );
906                         }
907                 }
908
909                 return this;
910         };
911 });
912
913 jQuery.fn.extend({
914         unbind: function( type, fn ) {
915                 // Handle object literals
916                 if ( typeof type === "object" && !type.preventDefault ) {
917                         for ( var key in type ) {
918                                 this.unbind(key, type[key]);
919                         }
920
921                 } else {
922                         for ( var i = 0, l = this.length; i < l; i++ ) {
923                                 jQuery.event.remove( this[i], type, fn );
924                         }
925                 }
926
927                 return this;
928         },
929         
930         delegate: function( selector, types, data, fn ) {
931                 return this.live( types, data, fn, selector );
932         },
933         
934         undelegate: function( selector, types, fn ) {
935                 if ( arguments.length === 0 ) {
936                                 return this.unbind( "live" );
937                 
938                 } else {
939                         return this.die( types, null, fn, selector );
940                 }
941         },
942         
943         trigger: function( type, data ) {
944                 return this.each(function() {
945                         jQuery.event.trigger( type, data, this );
946                 });
947         },
948
949         triggerHandler: function( type, data ) {
950                 if ( this[0] ) {
951                         var event = jQuery.Event( type );
952                         event.preventDefault();
953                         event.stopPropagation();
954                         jQuery.event.trigger( event, data, this[0] );
955                         return event.result;
956                 }
957         },
958
959         toggle: function( fn ) {
960                 // Save reference to arguments for access in closure
961                 var args = arguments, i = 1;
962
963                 // link all the functions, so any of them can unbind this click handler
964                 while ( i < args.length ) {
965                         jQuery.proxy( fn, args[ i++ ] );
966                 }
967
968                 return this.click( jQuery.proxy( fn, function( event ) {
969                         // Figure out which function to execute
970                         var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
971                         jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
972
973                         // Make sure that clicks stop
974                         event.preventDefault();
975
976                         // and execute the function
977                         return args[ lastToggle ].apply( this, arguments ) || false;
978                 }));
979         },
980
981         hover: function( fnOver, fnOut ) {
982                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
983         }
984 });
985
986 var liveMap = {
987         focus: "focusin",
988         blur: "focusout",
989         mouseenter: "mouseover",
990         mouseleave: "mouseout"
991 };
992
993 jQuery.each(["live", "die"], function( i, name ) {
994         jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
995                 var type, i = 0, match, namespaces, preType,
996                         selector = origSelector || this.selector,
997                         context = origSelector ? this : jQuery( this.context );
998                 
999                 if ( typeof types === "object" && !types.preventDefault ) {
1000                         for ( var key in types ) {
1001                                 context[ name ]( key, data, types[key], selector );
1002                         }
1003                         
1004                         return this;
1005                 }
1006
1007                 if ( jQuery.isFunction( data ) ) {
1008                         fn = data;
1009                         data = undefined;
1010                 }
1011
1012                 types = (types || "").split(" ");
1013
1014                 while ( (type = types[ i++ ]) != null ) {
1015                         match = rnamespaces.exec( type );
1016                         namespaces = "";
1017
1018                         if ( match )  {
1019                                 namespaces = match[0];
1020                                 type = type.replace( rnamespaces, "" );
1021                         }
1022
1023                         if ( type === "hover" ) {
1024                                 types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
1025                                 continue;
1026                         }
1027
1028                         preType = type;
1029
1030                         if ( type === "focus" || type === "blur" ) {
1031                                 types.push( liveMap[ type ] + namespaces );
1032                                 type = type + namespaces;
1033
1034                         } else {
1035                                 type = (liveMap[ type ] || type) + namespaces;
1036                         }
1037
1038                         if ( name === "live" ) {
1039                                 // bind live handler
1040                                 for ( var j = 0, l = context.length; j < l; j++ ) {
1041                                         jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
1042                                                 { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
1043                                 }
1044
1045                         } else {
1046                                 // unbind live handler
1047                                 context.unbind( "live." + liveConvert( type, selector ), fn );
1048                         }
1049                 }
1050                 
1051                 return this;
1052         };
1053 });
1054
1055 function liveHandler( event ) {
1056         var stop, maxLevel, elems = [], selectors = [],
1057                 related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
1058                 events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
1059
1060         if ( typeof events === "function" ) {
1061                 events = events.events;
1062         }
1063
1064         // Make sure we avoid non-left-click bubbling in Firefox (#3861)
1065         if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
1066                 return;
1067         }
1068
1069         if ( event.namespace ) {
1070                 namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
1071         }
1072
1073         event.liveFired = this;
1074
1075         var live = events.live.slice(0);
1076
1077         for ( j = 0; j < live.length; j++ ) {
1078                 handleObj = live[j];
1079
1080                 if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
1081                         selectors.push( handleObj.selector );
1082
1083                 } else {
1084                         live.splice( j--, 1 );
1085                 }
1086         }
1087
1088         match = jQuery( event.target ).closest( selectors, event.currentTarget );
1089
1090         for ( i = 0, l = match.length; i < l; i++ ) {
1091                 close = match[i];
1092
1093                 for ( j = 0; j < live.length; j++ ) {
1094                         handleObj = live[j];
1095
1096                         if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
1097                                 elem = close.elem;
1098                                 related = null;
1099
1100                                 // Those two events require additional checking
1101                                 if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
1102                                         event.type = handleObj.preType;
1103                                         related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
1104                                 }
1105
1106                                 if ( !related || related !== elem ) {
1107                                         elems.push({ elem: elem, handleObj: handleObj, level: close.level });
1108                                 }
1109                         }
1110                 }
1111         }
1112
1113         for ( i = 0, l = elems.length; i < l; i++ ) {
1114                 match = elems[i];
1115
1116                 if ( maxLevel && match.level > maxLevel ) {
1117                         break;
1118                 }
1119
1120                 event.currentTarget = match.elem;
1121                 event.data = match.handleObj.data;
1122                 event.handleObj = match.handleObj;
1123
1124                 ret = match.handleObj.origHandler.apply( match.elem, arguments );
1125
1126                 if ( ret === false || event.isPropagationStopped() ) {
1127                         maxLevel = match.level;
1128
1129                         if ( ret === false ) {
1130                                 stop = false;
1131                         }
1132                 }
1133         }
1134
1135         return stop;
1136 }
1137
1138 function liveConvert( type, selector ) {
1139         return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
1140 }
1141
1142 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1143         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1144         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1145
1146         // Handle event binding
1147         jQuery.fn[ name ] = function( data, fn ) {
1148                 if ( fn == null ) {
1149                         fn = data;
1150                         data = null;
1151                 }
1152
1153                 return arguments.length > 0 ?
1154                         this.bind( name, data, fn ) :
1155                         this.trigger( name );
1156         };
1157
1158         if ( jQuery.attrFn ) {
1159                 jQuery.attrFn[ name ] = true;
1160         }
1161 });
1162
1163 // Prevent memory leaks in IE
1164 // Window isn't included so as not to unbind existing unload events
1165 // More info:
1166 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1167 if ( window.attachEvent && !window.addEventListener ) {
1168         jQuery(window).bind("unload", function() {
1169                 for ( var id in jQuery.cache ) {
1170                         if ( jQuery.cache[ id ].handle ) {
1171                                 // Try/Catch is to handle iframes being unloaded, see #4280
1172                                 try {
1173                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1174                                 } catch(e) {}
1175                         }
1176                 }
1177         });
1178 }
1179
1180 })( jQuery );