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