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