Make sure namespaces is always an array, when passed to the remove special. Follow...
[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                 // if data is passed, bind to handler
33                 if ( data !== undefined ) {
34                         // Create temporary function pointer to original handler
35                         var fn = handler;
36
37                         // Create unique handler function, wrapped around original handler
38                         handler = jQuery.proxy( fn );
39
40                         // Store data in unique handler
41                         handler.data = data;
42                 }
43
44                 // Init the element's event structure
45                 var elemData = jQuery.data( elem );
46
47                 // If no elemData is found then we must be trying to bind to one of the
48                 // banned noData elements
49                 if ( !elemData ) {
50                         return;
51                 }
52
53                 var events = elemData.events || (elemData.events = {}),
54                         handle = elemData.handle, eventHandle;
55
56                 if ( !handle ) {
57                         eventHandle = function() {
58                                 // Handle the second event of a trigger and when
59                                 // an event is called after a page has unloaded
60                                 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
61                                         jQuery.event.handle.apply( eventHandle.elem, arguments ) :
62                                         undefined;
63                         };
64
65                         handle = elemData.handle = eventHandle;
66                 }
67
68                 // Add elem as a property of the handle function
69                 // This is to prevent a memory leak with non-native
70                 // event in IE.
71                 handle.elem = elem;
72
73                 // Handle multiple events separated by a space
74                 // jQuery(...).bind("mouseover mouseout", fn);
75                 types = types.split(" ");
76
77                 var type, i = 0, namespaces;
78
79                 while ( (type = types[ i++ ]) ) {
80                         if ( i > 1 ) {
81                                 handler = jQuery.proxy( handler );
82
83                                 if ( data !== undefined ) {
84                                         handler.data = data;
85                                 }
86                         }
87
88                         // Namespaced event handlers
89                         if ( type.indexOf(".") > -1 ) {
90                                 namespaces = type.split(".");
91                                 type = namespaces.shift();
92                                 handler.type = namespaces.slice(0).sort().join(".");
93
94                         } else {
95                                 namespaces = [];
96                                 handler.type = "";
97                         }
98
99                         // Get the current list of functions bound to this event
100                         var handlers = events[ type ],
101                                 special = this.special[ type ] || {};
102
103                         // Init the event handler queue
104                         if ( !handlers ) {
105                                 handlers = events[ type ] = {};
106
107                                 // Check for a special event handler
108                                 // Only use addEventListener/attachEvent if the special
109                                 // events handler returns false
110                                 if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
111                                         // Bind the global event handler to the element
112                                         if ( elem.addEventListener ) {
113                                                 elem.addEventListener( type, handle, false );
114
115                                         } else if ( elem.attachEvent ) {
116                                                 elem.attachEvent( "on" + type, handle );
117                                         }
118                                 }
119                         }
120                         
121                         if ( special.add ) { 
122                                 var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers ); 
123                                 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) { 
124                                         modifiedHandler.guid = modifiedHandler.guid || handler.guid; 
125                                         modifiedHandler.data = modifiedHandler.data || handler.data; 
126                                         modifiedHandler.type = modifiedHandler.type || handler.type; 
127                                         handler = modifiedHandler; 
128                                 } 
129                         } 
130                         
131                         // Add the function to the element's handler list
132                         handlers[ handler.guid ] = handler;
133
134                         // Keep track of which events have been used, for global triggering
135                         this.global[ type ] = true;
136                 }
137
138                 // Nullify elem to prevent memory leaks in IE
139                 elem = null;
140         },
141
142         global: {},
143
144         // Detach an event or set of events from an element
145         remove: function( elem, types, handler ) {
146                 // don't do events on text and comment nodes
147                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
148                         return;
149                 }
150
151                 var elemData = jQuery.data( elem );
152
153                 if ( !elemData ) {
154                         return;
155                 }
156
157                 var events = elemData.events, ret, type, fn;
158
159                 if ( events ) {
160                         // Unbind all events for the element
161                         if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
162                                 for ( type in events ) {
163                                         this.remove( elem, type + (types || "") );
164                                 }
165
166                         } else {
167                                 // types is actually an event object here
168                                 if ( types.type ) {
169                                         handler = types.handler;
170                                         types = types.type;
171                                 }
172
173                                 // Handle multiple events separated by a space
174                                 // jQuery(...).unbind("mouseover mouseout", fn);
175                                 types = types.split(" ");
176
177                                 var i = 0, all, namespaces, namespace;
178
179                                 while ( (type = types[ i++ ]) ) {
180                                         all = type.indexOf(".") < 0;
181                                         namespaces = null;
182
183                                         if ( !all ) {
184                                                 // Namespaced event handlers
185                                                 namespaces = type.split(".");
186                                                 type = namespaces.shift();
187
188                                                 namespace = new RegExp("(^|\\.)" + 
189                                                         jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
190
191                                         } else {
192                                                 namespaces = [];
193                                         }
194
195                                         var special = this.special[ type ] || {};
196
197                                         if ( events[ type ] ) {
198                                                 // remove the given handler for the given type
199                                                 if ( handler ) {
200                                                         fn = events[ type ][ handler.guid ];
201                                                         delete events[ type ][ handler.guid ];
202
203                                                 // remove all handlers for the given type
204                                                 } else {
205                                                         for ( var handle in events[ type ] ) {
206                                                                 // Handle the removal of namespaced events
207                                                                 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
208                                                                         delete events[ type ][ handle ];
209                                                                 }
210                                                         }
211                                                 }
212
213                                                 if ( special.remove ) {
214                                                         special.remove.call( elem, namespaces, fn);
215                                                 }
216
217                                                 // remove generic event handler if no more handlers exist
218                                                 for ( ret in events[ type ] ) {
219                                                         break;
220                                                 }
221
222                                                 if ( !ret ) {
223                                                         if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
224                                                                 if ( elem.removeEventListener ) {
225                                                                         elem.removeEventListener( type, elemData.handle, false );
226                                                                 } else if ( elem.detachEvent ) {
227                                                                         elem.detachEvent( "on" + type, elemData.handle );
228                                                                 }
229                                                         }
230
231                                                         ret = null;
232                                                         delete events[ type ];
233                                                 }
234                                         }
235                                 }
236                         }
237
238                         // Remove the expando if it's no longer used
239                         for ( ret in events ) {
240                                 break;
241                         }
242
243                         if ( !ret ) {
244                                 var handle = elemData.handle;
245                                 if ( handle ) {
246                                         handle.elem = null;
247                                 }
248
249                                 delete elemData.events;
250                                 delete elemData.handle;
251
252                                 if ( jQuery.isEmptyObject( elemData ) ) {
253                                         jQuery.removeData( elem );
254                                 }
255                         }
256                 }
257         },
258
259         // bubbling is internal
260         trigger: function( event, data, elem /*, bubbling */ ) {
261                 // Event object or event type
262                 var type = event.type || event,
263                         bubbling = arguments[3];
264
265                 if ( !bubbling ) {
266                         event = typeof event === "object" ?
267                                 // jQuery.Event object
268                                 event[expando] ? event :
269                                 // Object literal
270                                 jQuery.extend( jQuery.Event(type), event ) :
271                                 // Just the event type (string)
272                                 jQuery.Event(type);
273
274                         if ( type.indexOf("!") >= 0 ) {
275                                 event.type = type = type.slice(0, -1);
276                                 event.exclusive = true;
277                         }
278
279                         // Handle a global trigger
280                         if ( !elem ) {
281                                 // Don't bubble custom events when global (to avoid too much overhead)
282                                 event.stopPropagation();
283
284                                 // Only trigger if we've ever bound an event for it
285                                 if ( this.global[ type ] ) {
286                                         jQuery.each( jQuery.cache, function() {
287                                                 if ( this.events && this.events[type] ) {
288                                                         jQuery.event.trigger( event, data, this.handle.elem );
289                                                 }
290                                         });
291                                 }
292                         }
293
294                         // Handle triggering a single element
295
296                         // don't do events on text and comment nodes
297                         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
298                                 return undefined;
299                         }
300
301                         // Clean up in case it is reused
302                         event.result = undefined;
303                         event.target = elem;
304
305                         // Clone the incoming data, if any
306                         data = jQuery.makeArray( data );
307                         data.unshift( event );
308                 }
309
310                 event.currentTarget = elem;
311
312                 // Trigger the event, it is assumed that "handle" is a function
313                 var handle = jQuery.data( elem, "handle" );
314                 if ( handle ) {
315                         handle.apply( elem, data );
316                 }
317
318                 var parent = elem.parentNode || elem.ownerDocument;
319
320                 // Trigger an inline bound script
321                 try {
322                         if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
323                                 if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
324                                         event.result = false;
325                                 }
326                         }
327
328                 // prevent IE from throwing an error for some elements with some event types, see #3533
329                 } catch (e) {}
330
331                 if ( !event.isPropagationStopped() && parent ) {
332                         jQuery.event.trigger( event, data, parent, true );
333
334                 } else if ( !event.isDefaultPrevented() ) {
335                         var target = event.target, old,
336                                 isClick = jQuery.nodeName(target, "a") && type === "click";
337
338                         if ( !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
339                                 try {
340                                         if ( target[ type ] ) {
341                                                 // Make sure that we don't accidentally re-trigger the onFOO events
342                                                 old = target[ "on" + type ];
343
344                                                 if ( old ) {
345                                                         target[ "on" + type ] = null;
346                                                 }
347
348                                                 this.triggered = true;
349                                                 target[ type ]();
350                                         }
351
352                                 // prevent IE from throwing an error for some elements with some event types, see #3533
353                                 } catch (e) {}
354
355                                 if ( old ) {
356                                         target[ "on" + type ] = old;
357                                 }
358
359                                 this.triggered = false;
360                         }
361                 }
362         },
363
364         handle: function( event ) {
365                 // returned undefined or false
366                 var all, handlers;
367
368                 event = arguments[0] = jQuery.event.fix( event || window.event );
369                 event.currentTarget = this;
370
371                 // Namespaced event handlers
372                 var namespaces = event.type.split(".");
373                 event.type = namespaces.shift();
374
375                 // Cache this now, all = true means, any handler
376                 all = !namespaces.length && !event.exclusive;
377
378                 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
379
380                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
381
382                 for ( var j in handlers ) {
383                         var handler = handlers[ j ];
384
385                         // Filter the functions by class
386                         if ( all || namespace.test(handler.type) ) {
387                                 // Pass in a reference to the handler function itself
388                                 // So that we can later remove it
389                                 event.handler = handler;
390                                 event.data = handler.data;
391
392                                 var ret = handler.apply( this, arguments );
393
394                                 if ( ret !== undefined ) {
395                                         event.result = ret;
396                                         if ( ret === false ) {
397                                                 event.preventDefault();
398                                                 event.stopPropagation();
399                                         }
400                                 }
401
402                                 if ( event.isImmediatePropagationStopped() ) {
403                                         break;
404                                 }
405
406                         }
407                 }
408
409                 return event.result;
410         },
411
412         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(" "),
413
414         fix: function( event ) {
415                 if ( event[ expando ] ) {
416                         return event;
417                 }
418
419                 // store a copy of the original event object
420                 // and "clone" to set read-only properties
421                 var originalEvent = event;
422                 event = jQuery.Event( originalEvent );
423
424                 for ( var i = this.props.length, prop; i; ) {
425                         prop = this.props[ --i ];
426                         event[ prop ] = originalEvent[ prop ];
427                 }
428
429                 // Fix target property, if necessary
430                 if ( !event.target ) {
431                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
432                 }
433
434                 // check if target is a textnode (safari)
435                 if ( event.target.nodeType === 3 ) {
436                         event.target = event.target.parentNode;
437                 }
438
439                 // Add relatedTarget, if necessary
440                 if ( !event.relatedTarget && event.fromElement ) {
441                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
442                 }
443
444                 // Calculate pageX/Y if missing and clientX/Y available
445                 if ( event.pageX == null && event.clientX != null ) {
446                         var doc = document.documentElement, body = document.body;
447                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
448                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
449                 }
450
451                 // Add which for key events
452                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
453                         event.which = event.charCode || event.keyCode;
454                 }
455
456                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
457                 if ( !event.metaKey && event.ctrlKey ) {
458                         event.metaKey = event.ctrlKey;
459                 }
460
461                 // Add which for click: 1 === left; 2 === middle; 3 === right
462                 // Note: button is not normalized, so don't use it
463                 if ( !event.which && event.button !== undefined ) {
464                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
465                 }
466
467                 return event;
468         },
469
470         // Deprecated, use jQuery.guid instead
471         guid: 1E8,
472
473         // Deprecated, use jQuery.proxy instead
474         proxy: jQuery.proxy,
475
476         special: {
477                 ready: {
478                         // Make sure the ready event is setup
479                         setup: jQuery.bindReady,
480                         teardown: jQuery.noop
481                 },
482
483                 live: {
484                         add: function( proxy, data, namespaces, live ) {
485                                 jQuery.extend( proxy, data || {} );
486
487                                 proxy.guid += data.selector + data.live; 
488                                 data.liveProxy = proxy;
489
490                                 jQuery.event.add( this, data.live, liveHandler, data ); 
491                                 
492                         },
493
494                         remove: function( namespaces ) {
495                                 if ( namespaces.length ) {
496                                         var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
497
498                                         jQuery.each( (jQuery.data(this, "events").live || {}), function() {
499                                                 if ( name.test(this.type) ) {
500                                                         remove++;
501                                                 }
502                                         });
503
504                                         if ( remove < 1 ) {
505                                                 jQuery.event.remove( this, namespaces[0], liveHandler );
506                                         }
507                                 }
508                         },
509                         special: {}
510                 },
511                 beforeunload: {
512                         setup: function( data, namespaces, fn ) {
513                                 // We only want to do this special case on windows
514                                 if ( this.setInterval ) {
515                                         this.onbeforeunload = fn;
516                                 }
517
518                                 return false;
519                         },
520                         teardown: function( namespaces, fn ) {
521                                 if ( this.onbeforeunload === fn ) {
522                                         this.onbeforeunload = null;
523                                 }
524                         }
525                 }
526         }
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         trigger: function( type, data ) {
863                 return this.each(function() {
864                         jQuery.event.trigger( type, data, this );
865                 });
866         },
867
868         triggerHandler: function( type, data ) {
869                 if ( this[0] ) {
870                         var event = jQuery.Event( type );
871                         event.preventDefault();
872                         event.stopPropagation();
873                         jQuery.event.trigger( event, data, this[0] );
874                         return event.result;
875                 }
876         },
877
878         toggle: function( fn ) {
879                 // Save reference to arguments for access in closure
880                 var args = arguments, i = 1;
881
882                 // link all the functions, so any of them can unbind this click handler
883                 while ( i < args.length ) {
884                         jQuery.proxy( fn, args[ i++ ] );
885                 }
886
887                 return this.click( jQuery.proxy( fn, function( event ) {
888                         // Figure out which function to execute
889                         var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
890                         jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
891
892                         // Make sure that clicks stop
893                         event.preventDefault();
894
895                         // and execute the function
896                         return args[ lastToggle ].apply( this, arguments ) || false;
897                 }));
898         },
899
900         hover: function( fnOver, fnOut ) {
901                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
902         }
903 });
904
905 jQuery.each(["live", "die"], function( i, name ) {
906         jQuery.fn[ name ] = function( types, data, fn ) {
907                 var type, i = 0;
908
909                 if ( jQuery.isFunction( data ) ) {
910                         fn = data;
911                         data = undefined;
912                 }
913
914                 types = (types || "").split( /\s+/ );
915
916                 while ( (type = types[ i++ ]) != null ) {
917                         type = type === "focus" ? "focusin" : // focus --> focusin
918                                         type === "blur" ? "focusout" : // blur --> focusout
919                                         type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
920                                         type;
921                         
922                         if ( name === "live" ) {
923                                 // bind live handler
924                                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
925                                         data: data, selector: this.selector, live: type
926                                 }, fn );
927
928                         } else {
929                                 // unbind live handler
930                                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
931                         }
932                 }
933                 
934                 return this;
935         }
936 });
937
938 function liveHandler( event ) {
939         var stop, elems = [], selectors = [], args = arguments,
940                 related, match, fn, elem, j, i, l, data,
941                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
942
943         // Make sure we avoid non-left-click bubbling in Firefox (#3861)
944         if ( event.button && event.type === "click" ) {
945                 return;
946         }
947
948         for ( j in live ) {
949                 fn = live[j];
950                 if ( fn.live === event.type ||
951                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
952
953                         data = fn.data;
954                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
955                                         !data.beforeFilter[event.type](event)) ) {
956                                 selectors.push( fn.selector );
957                         }
958                 } else {
959                         delete live[j];
960                 }
961         }
962
963         match = jQuery( event.target ).closest( selectors, event.currentTarget );
964
965         for ( i = 0, l = match.length; i < l; i++ ) {
966                 for ( j in live ) {
967                         fn = live[j];
968                         elem = match[i].elem;
969                         related = null;
970
971                         if ( match[i].selector === fn.selector ) {
972                                 // Those two events require additional checking
973                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
974                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
975                                 }
976
977                                 if ( !related || related !== elem ) {
978                                         elems.push({ elem: elem, fn: fn });
979                                 }
980                         }
981                 }
982         }
983
984         for ( i = 0, l = elems.length; i < l; i++ ) {
985                 match = elems[i];
986                 event.currentTarget = match.elem;
987                 event.data = match.fn.data;
988                 if ( match.fn.apply( match.elem, args ) === false ) {
989                         stop = false;
990                         break;
991                 }
992         }
993
994         return stop;
995 }
996
997 function liveConvert( type, selector ) {
998         return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
999 }
1000
1001 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1002         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1003         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1004
1005         // Handle event binding
1006         jQuery.fn[ name ] = function( fn ) {
1007                 return fn ? this.bind( name, fn ) : this.trigger( name );
1008         };
1009
1010         if ( jQuery.attrFn ) {
1011                 jQuery.attrFn[ name ] = true;
1012         }
1013 });
1014
1015 // Prevent memory leaks in IE
1016 // Window isn't included so as not to unbind existing unload events
1017 // More info:
1018 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1019 if ( window.attachEvent && !window.addEventListener ) {
1020         window.attachEvent("onunload", function() {
1021                 for ( var id in jQuery.cache ) {
1022                         if ( jQuery.cache[ id ].handle ) {
1023                                 // Try/Catch is to handle iframes being unloaded, see #4280
1024                                 try {
1025                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1026                                 } catch(e) {}
1027                         }
1028                 }
1029         });
1030 }