Added in Ben Alman's proposed event.namespace property (the property holds the namesp...
[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                         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, j, 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 ( 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 ( 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 (inlineError) {}
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 (triggerError) {}
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_sort = [], namespace_re, events, args = jQuery.makeArray( arguments );
372
373                 event = args[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_sort = namespaces.slice(0).sort();
383                         namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
384                         event.namespace = namespace_sort.join(".");
385                 }
386
387                 events = jQuery.data(this, "events");
388                 handlers = (events || {})[ event.type ];
389
390                 if ( events && handlers ) {
391                         // Clone the handlers to prevent manipulation
392                         handlers = handlers.slice(0);
393
394                         for ( var j = 0, l = handlers.length; j < l; j++ ) {
395                                 var handleObj = handlers[ j ];
396
397                                 // Filter the functions by class
398                                 if ( all || namespace_re.test( handleObj.namespace ) ) {
399                                         // Pass in a reference to the handler function itself
400                                         // So that we can later remove it
401                                         event.handler = handleObj.handler;
402                                         event.data = handleObj.data;
403                                         event.handleObj = handleObj;
404         
405                                         var ret = handleObj.handler.apply( this, args );
406
407                                         if ( ret !== undefined ) {
408                                                 event.result = ret;
409                                                 if ( ret === false ) {
410                                                         event.preventDefault();
411                                                         event.stopPropagation();
412                                                 }
413                                         }
414
415                                         if ( event.isImmediatePropagationStopped() ) {
416                                                 break;
417                                         }
418                                 }
419                         }
420                 }
421
422                 return event.result;
423         },
424
425         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(" "),
426
427         fix: function( event ) {
428                 if ( event[ expando ] ) {
429                         return event;
430                 }
431
432                 // store a copy of the original event object
433                 // and "clone" to set read-only properties
434                 var originalEvent = event;
435                 event = jQuery.Event( originalEvent );
436
437                 for ( var i = this.props.length, prop; i; ) {
438                         prop = this.props[ --i ];
439                         event[ prop ] = originalEvent[ prop ];
440                 }
441
442                 // Fix target property, if necessary
443                 if ( !event.target ) {
444                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
445                 }
446
447                 // check if target is a textnode (safari)
448                 if ( event.target.nodeType === 3 ) {
449                         event.target = event.target.parentNode;
450                 }
451
452                 // Add relatedTarget, if necessary
453                 if ( !event.relatedTarget && event.fromElement ) {
454                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
455                 }
456
457                 // Calculate pageX/Y if missing and clientX/Y available
458                 if ( event.pageX == null && event.clientX != null ) {
459                         var doc = document.documentElement, body = document.body;
460                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
461                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
462                 }
463
464                 // Add which for key events
465                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
466                         event.which = event.charCode || event.keyCode;
467                 }
468
469                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
470                 if ( !event.metaKey && event.ctrlKey ) {
471                         event.metaKey = event.ctrlKey;
472                 }
473
474                 // Add which for click: 1 === left; 2 === middle; 3 === right
475                 // Note: button is not normalized, so don't use it
476                 if ( !event.which && event.button !== undefined ) {
477                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
478                 }
479
480                 return event;
481         },
482
483         // Deprecated, use jQuery.guid instead
484         guid: 1E8,
485
486         // Deprecated, use jQuery.proxy instead
487         proxy: jQuery.proxy,
488
489         special: {
490                 ready: {
491                         // Make sure the ready event is setup
492                         setup: jQuery.bindReady,
493                         teardown: jQuery.noop
494                 },
495
496                 live: {
497                         add: function( handleObj ) {
498                                 jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); 
499                         },
500
501                         remove: function( handleObj ) {
502                                 var remove = true,
503                                         type = handleObj.origType.replace(rnamespaces, "");
504                                 
505                                 jQuery.each( jQuery.data(this, "events").live || [], function() {
506                                         if ( type === this.origType.replace(rnamespaces, "") ) {
507                                                 remove = false;
508                                                 return false;
509                                         }
510                                 });
511
512                                 if ( remove ) {
513                                         jQuery.event.remove( this, handleObj.origType, liveHandler );
514                                 }
515                         }
516
517                 },
518
519                 beforeunload: {
520                         setup: function( data, namespaces, eventHandle ) {
521                                 // We only want to do this special case on windows
522                                 if ( this.setInterval ) {
523                                         this.onbeforeunload = eventHandle;
524                                 }
525                         },
526
527                         teardown: function( namespaces, eventHandle ) {
528                                 if ( this.onbeforeunload === eventHandle ) {
529                                         this.onbeforeunload = null;
530                                 }
531                         }
532                 }
533         }
534 };
535
536 var removeEvent = document.removeEventListener ?
537         function( elem, type, handle ) {
538                 if ( elem.removeEventListener ) {
539                         elem.removeEventListener( type, handle, false );
540                 }
541         } : 
542         function( elem, type, handle ) {
543                 if ( elem.detachEvent ) {
544                         elem.detachEvent( "on" + type, handle );
545                 }
546         };
547
548 jQuery.Event = function( src ) {
549         // Allow instantiation without the 'new' keyword
550         if ( !this.preventDefault ) {
551                 return new jQuery.Event( src );
552         }
553
554         // Event object
555         if ( src && src.type ) {
556                 this.originalEvent = src;
557                 this.type = src.type;
558         // Event type
559         } else {
560                 this.type = src;
561         }
562
563         // timeStamp is buggy for some events on Firefox(#3843)
564         // So we won't rely on the native value
565         this.timeStamp = now();
566
567         // Mark it as fixed
568         this[ expando ] = true;
569 };
570
571 function returnFalse() {
572         return false;
573 }
574 function returnTrue() {
575         return true;
576 }
577
578 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
579 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
580 jQuery.Event.prototype = {
581         preventDefault: function() {
582                 this.isDefaultPrevented = returnTrue;
583
584                 var e = this.originalEvent;
585                 if ( !e ) {
586                         return;
587                 }
588                 
589                 // if preventDefault exists run it on the original event
590                 if ( e.preventDefault ) {
591                         e.preventDefault();
592                 }
593                 // otherwise set the returnValue property of the original event to false (IE)
594                 e.returnValue = false;
595         },
596         stopPropagation: function() {
597                 this.isPropagationStopped = returnTrue;
598
599                 var e = this.originalEvent;
600                 if ( !e ) {
601                         return;
602                 }
603                 // if stopPropagation exists run it on the original event
604                 if ( e.stopPropagation ) {
605                         e.stopPropagation();
606                 }
607                 // otherwise set the cancelBubble property of the original event to true (IE)
608                 e.cancelBubble = true;
609         },
610         stopImmediatePropagation: function() {
611                 this.isImmediatePropagationStopped = returnTrue;
612                 this.stopPropagation();
613         },
614         isDefaultPrevented: returnFalse,
615         isPropagationStopped: returnFalse,
616         isImmediatePropagationStopped: returnFalse
617 };
618
619 // Checks if an event happened on an element within another element
620 // Used in jQuery.event.special.mouseenter and mouseleave handlers
621 var withinElement = function( event ) {
622         // Check if mouse(over|out) are still within the same parent element
623         var parent = event.relatedTarget;
624
625         // Firefox sometimes assigns relatedTarget a XUL element
626         // which we cannot access the parentNode property of
627         try {
628                 // Traverse up the tree
629                 while ( parent && parent !== this ) {
630                         parent = parent.parentNode;
631                 }
632
633                 if ( parent !== this ) {
634                         // set the correct event type
635                         event.type = event.data;
636
637                         // handle event if we actually just moused on to a non sub-element
638                         jQuery.event.handle.apply( this, arguments );
639                 }
640
641         // assuming we've left the element since we most likely mousedover a xul element
642         } catch(e) { }
643 },
644
645 // In case of event delegation, we only need to rename the event.type,
646 // liveHandler will take care of the rest.
647 delegate = function( event ) {
648         event.type = event.data;
649         jQuery.event.handle.apply( this, arguments );
650 };
651
652 // Create mouseenter and mouseleave events
653 jQuery.each({
654         mouseenter: "mouseover",
655         mouseleave: "mouseout"
656 }, function( orig, fix ) {
657         jQuery.event.special[ orig ] = {
658                 setup: function( data ) {
659                         jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
660                 },
661                 teardown: function( data ) {
662                         jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
663                 }
664         };
665 });
666
667 // submit delegation
668 if ( !jQuery.support.submitBubbles ) {
669
670         jQuery.event.special.submit = {
671                 setup: function( data, namespaces ) {
672                         if ( this.nodeName.toLowerCase() !== "form" ) {
673                                 jQuery.event.add(this, "click.specialSubmit", function( e ) {
674                                         var elem = e.target, type = elem.type;
675
676                                         if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
677                                                 return trigger( "submit", this, arguments );
678                                         }
679                                 });
680          
681                                 jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
682                                         var elem = e.target, type = elem.type;
683
684                                         if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
685                                                 return trigger( "submit", this, arguments );
686                                         }
687                                 });
688
689                         } else {
690                                 return false;
691                         }
692                 },
693
694                 teardown: function( namespaces ) {
695                         jQuery.event.remove( this, ".specialSubmit" );
696                 }
697         };
698
699 }
700
701 // change delegation, happens here so we have bind.
702 if ( !jQuery.support.changeBubbles ) {
703
704         var formElems = /textarea|input|select/i,
705
706         changeFilters,
707
708         getVal = function( elem ) {
709                 var type = elem.type, val = elem.value;
710
711                 if ( type === "radio" || type === "checkbox" ) {
712                         val = elem.checked;
713
714                 } else if ( type === "select-multiple" ) {
715                         val = elem.selectedIndex > -1 ?
716                                 jQuery.map( elem.options, function( elem ) {
717                                         return elem.selected;
718                                 }).join("-") :
719                                 "";
720
721                 } else if ( elem.nodeName.toLowerCase() === "select" ) {
722                         val = elem.selectedIndex;
723                 }
724
725                 return val;
726         },
727
728         testChange = function testChange( e ) {
729                 var elem = e.target, data, val;
730
731                 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
732                         return;
733                 }
734
735                 data = jQuery.data( elem, "_change_data" );
736                 val = getVal(elem);
737
738                 // the current data will be also retrieved by beforeactivate
739                 if ( e.type !== "focusout" || elem.type !== "radio" ) {
740                         jQuery.data( elem, "_change_data", val );
741                 }
742                 
743                 if ( data === undefined || val === data ) {
744                         return;
745                 }
746
747                 if ( data != null || val ) {
748                         e.type = "change";
749                         return jQuery.event.trigger( e, arguments[1], elem );
750                 }
751         };
752
753         jQuery.event.special.change = {
754                 filters: {
755                         focusout: testChange, 
756
757                         click: function( e ) {
758                                 var elem = e.target, type = elem.type;
759
760                                 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
761                                         return testChange.call( this, e );
762                                 }
763                         },
764
765                         // Change has to be called before submit
766                         // Keydown will be called before keypress, which is used in submit-event delegation
767                         keydown: function( e ) {
768                                 var elem = e.target, type = elem.type;
769
770                                 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
771                                         (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
772                                         type === "select-multiple" ) {
773                                         return testChange.call( this, e );
774                                 }
775                         },
776
777                         // Beforeactivate happens also before the previous element is blurred
778                         // with this event you can't trigger a change event, but you can store
779                         // information/focus[in] is not needed anymore
780                         beforeactivate: function( e ) {
781                                 var elem = e.target;
782                                 jQuery.data( elem, "_change_data", getVal(elem) );
783                         }
784                 },
785
786                 setup: function( data, namespaces ) {
787                         if ( this.type === "file" ) {
788                                 return false;
789                         }
790
791                         for ( var type in changeFilters ) {
792                                 jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
793                         }
794
795                         return formElems.test( this.nodeName );
796                 },
797
798                 teardown: function( namespaces ) {
799                         jQuery.event.remove( this, ".specialChange" );
800
801                         return formElems.test( this.nodeName );
802                 }
803         };
804
805         changeFilters = jQuery.event.special.change.filters;
806 }
807
808 function trigger( type, elem, args ) {
809         args[0].type = type;
810         return jQuery.event.handle.apply( elem, args );
811 }
812
813 // Create "bubbling" focus and blur events
814 if ( document.addEventListener ) {
815         jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
816                 jQuery.event.special[ fix ] = {
817                         setup: function() {
818                                 this.addEventListener( orig, handler, true );
819                         }, 
820                         teardown: function() { 
821                                 this.removeEventListener( orig, handler, true );
822                         }
823                 };
824
825                 function handler( e ) { 
826                         e = jQuery.event.fix( e );
827                         e.type = fix;
828                         return jQuery.event.handle.call( this, e );
829                 }
830         });
831 }
832
833 jQuery.each(["bind", "one"], function( i, name ) {
834         jQuery.fn[ name ] = function( type, data, fn ) {
835                 // Handle object literals
836                 if ( typeof type === "object" ) {
837                         for ( var key in type ) {
838                                 this[ name ](key, data, type[key], fn);
839                         }
840                         return this;
841                 }
842                 
843                 if ( jQuery.isFunction( data ) || data === false ) {
844                         fn = data;
845                         data = undefined;
846                 }
847
848                 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
849                         jQuery( this ).unbind( event, handler );
850                         return fn.apply( this, arguments );
851                 }) : fn;
852
853                 if ( type === "unload" && name !== "one" ) {
854                         this.one( type, data, fn );
855
856                 } else {
857                         for ( var i = 0, l = this.length; i < l; i++ ) {
858                                 jQuery.event.add( this[i], type, handler, data );
859                         }
860                 }
861
862                 return this;
863         };
864 });
865
866 jQuery.fn.extend({
867         unbind: function( type, fn ) {
868                 // Handle object literals
869                 if ( typeof type === "object" && !type.preventDefault ) {
870                         for ( var key in type ) {
871                                 this.unbind(key, type[key]);
872                         }
873
874                 } else {
875                         for ( var i = 0, l = this.length; i < l; i++ ) {
876                                 jQuery.event.remove( this[i], type, fn );
877                         }
878                 }
879
880                 return this;
881         },
882         
883         delegate: function( selector, types, data, fn ) {
884                 return this.live( types, data, fn, selector );
885         },
886         
887         undelegate: function( selector, types, fn ) {
888                 if ( arguments.length === 0 ) {
889                                 return this.unbind( "live" );
890                 
891                 } else {
892                         return this.die( types, null, fn, selector );
893                 }
894         },
895         
896         trigger: function( type, data ) {
897                 return this.each(function() {
898                         jQuery.event.trigger( type, data, this );
899                 });
900         },
901
902         triggerHandler: function( type, data ) {
903                 if ( this[0] ) {
904                         var event = jQuery.Event( type );
905                         event.preventDefault();
906                         event.stopPropagation();
907                         jQuery.event.trigger( event, data, this[0] );
908                         return event.result;
909                 }
910         },
911
912         toggle: function( fn ) {
913                 // Save reference to arguments for access in closure
914                 var args = arguments, i = 1;
915
916                 // link all the functions, so any of them can unbind this click handler
917                 while ( i < args.length ) {
918                         jQuery.proxy( fn, args[ i++ ] );
919                 }
920
921                 return this.click( jQuery.proxy( fn, function( event ) {
922                         // Figure out which function to execute
923                         var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
924                         jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
925
926                         // Make sure that clicks stop
927                         event.preventDefault();
928
929                         // and execute the function
930                         return args[ lastToggle ].apply( this, arguments ) || false;
931                 }));
932         },
933
934         hover: function( fnOver, fnOut ) {
935                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
936         }
937 });
938
939 var liveMap = {
940         focus: "focusin",
941         blur: "focusout",
942         mouseenter: "mouseover",
943         mouseleave: "mouseout"
944 };
945
946 jQuery.each(["live", "die"], function( i, name ) {
947         jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
948                 var type, i = 0, match, namespaces, preType,
949                         selector = origSelector || this.selector,
950                         context = origSelector ? this : jQuery( this.context );
951
952                 if ( jQuery.isFunction( data ) ) {
953                         fn = data;
954                         data = undefined;
955                 }
956
957                 types = (types || "").split(" ");
958
959                 while ( (type = types[ i++ ]) != null ) {
960                         match = rnamespaces.exec( type );
961                         namespaces = "";
962
963                         if ( match )  {
964                                 namespaces = match[0];
965                                 type = type.replace( rnamespaces, "" );
966                         }
967
968                         if ( type === "hover" ) {
969                                 types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
970                                 continue;
971                         }
972
973                         preType = type;
974
975                         if ( type === "focus" || type === "blur" ) {
976                                 types.push( liveMap[ type ] + namespaces );
977                                 type = type + namespaces;
978
979                         } else {
980                                 type = (liveMap[ type ] || type) + namespaces;
981                         }
982
983                         if ( name === "live" ) {
984                                 // bind live handler
985                                 for ( var j = 0, l = context.length; j < l; j++ ) {
986                                         jQuery.event.add( context[j], liveConvert( type, selector ),
987                                                 { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
988                                 }
989
990                         } else {
991                                 // unbind live handler
992                                 context.unbind( liveConvert( type, selector ), fn );
993                         }
994                 }
995                 
996                 return this;
997         };
998 });
999
1000 function liveHandler( event ) {
1001         var stop, maxLevel, elems = [], selectors = [],
1002                 related, match, handleObj, elem, j, i, l, data, close, namespace,
1003                 events = jQuery.data( this, "events" );
1004
1005         // Make sure we avoid non-left-click bubbling in Firefox (#3861)
1006         if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
1007                 return;
1008         }
1009
1010         if ( event.namespace ) {
1011                 namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
1012         }
1013
1014         event.liveFired = this;
1015
1016         var live = events.live.slice(0);
1017
1018         for ( j = 0; j < live.length; j++ ) {
1019                 handleObj = live[j];
1020
1021                 if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
1022                         selectors.push( handleObj.selector );
1023
1024                 } else {
1025                         live.splice( j--, 1 );
1026                 }
1027         }
1028
1029         match = jQuery( event.target ).closest( selectors, event.currentTarget );
1030
1031         for ( i = 0, l = match.length; i < l; i++ ) {
1032                 close = match[i];
1033
1034                 for ( j = 0; j < live.length; j++ ) {
1035                         handleObj = live[j];
1036
1037                         if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
1038                                 elem = close.elem;
1039                                 related = null;
1040
1041                                 // Those two events require additional checking
1042                                 if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
1043                                         event.type = handleObj.preType;
1044                                         related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
1045                                 }
1046
1047                                 if ( !related || related !== elem ) {
1048                                         elems.push({ elem: elem, handleObj: handleObj, level: close.level });
1049                                 }
1050                         }
1051                 }
1052         }
1053
1054         for ( i = 0, l = elems.length; i < l; i++ ) {
1055                 match = elems[i];
1056
1057                 if ( maxLevel && match.level > maxLevel ) {
1058                         break;
1059                 }
1060
1061                 event.currentTarget = match.elem;
1062                 event.data = match.handleObj.data;
1063                 event.handleObj = match.handleObj;
1064
1065                 ret = match.handleObj.origHandler.apply( match.elem, arguments );
1066
1067                 if ( ret === false || event.isPropagationStopped() ) {
1068                         maxLevel = match.level;
1069
1070                         if ( ret === false ) {
1071                                 stop = false;
1072                         }
1073                 }
1074         }
1075
1076         return stop;
1077 }
1078
1079 function liveConvert( type, selector ) {
1080         return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
1081 }
1082
1083 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1084         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1085         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1086
1087         // Handle event binding
1088         jQuery.fn[ name ] = function( data, fn ) {
1089                 if ( fn == null ) {
1090                         fn = data;
1091                         data = null;
1092                 }
1093
1094                 return arguments.length > 0 ?
1095                         this.bind( name, data, fn ) :
1096                         this.trigger( name );
1097         };
1098
1099         if ( jQuery.attrFn ) {
1100                 jQuery.attrFn[ name ] = true;
1101         }
1102 });
1103
1104 // Prevent memory leaks in IE
1105 // Window isn't included so as not to unbind existing unload events
1106 // More info:
1107 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1108 if ( window.attachEvent && !window.addEventListener ) {
1109         window.attachEvent("onunload", function() {
1110                 for ( var id in jQuery.cache ) {
1111                         if ( jQuery.cache[ id ].handle ) {
1112                                 // Try/Catch is to handle iframes being unloaded, see #4280
1113                                 try {
1114                                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1115                                 } catch(e) {}
1116                         }
1117                 }
1118         });
1119 }