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