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