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