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