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