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