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