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