Adding in support for bubbling submit and change events, thanks to the patch by Justi...
[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() {
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( arguments.callee.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 seperated 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                 // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
261                 if ( (!nativeFn || (jQuery.nodeName(elem, 'a') && type === "click")) && nativeHandler && nativeHandler.apply( elem, data ) === false ) {
262                         event.result = false;
263                 }
264
265                 // Trigger the native events (except for clicks on links)
266                 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
267                         this.triggered = true;
268                         try {
269                                 nativeFn();
270                         // prevent IE from throwing an error for some hidden elements
271                         } catch (e) {}
272                 }
273
274                 this.triggered = false;
275
276                 if ( !event.isPropagationStopped() ) {
277                         var parent = elem.parentNode || elem.ownerDocument;
278                         if ( parent ) {
279                                 jQuery.event.trigger( event, data, parent, true );
280                         }
281                 }
282         },
283
284         handle: function( event ) {
285                 // returned undefined or false
286                 var all, handlers;
287
288                 event = arguments[0] = jQuery.event.fix( event || window.event );
289                 event.currentTarget = this;
290
291                 // Namespaced event handlers
292                 var namespaces = event.type.split(".");
293                 event.type = namespaces.shift();
294
295                 // Cache this now, all = true means, any handler
296                 all = !namespaces.length && !event.exclusive;
297
298                 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
299
300                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
301
302                 for ( var j in handlers ) {
303                         var handler = handlers[ j ];
304
305                         // Filter the functions by class
306                         if ( all || namespace.test(handler.type) ) {
307                                 // Pass in a reference to the handler function itself
308                                 // So that we can later remove it
309                                 event.handler = handler;
310                                 event.data = handler.data;
311
312                                 var ret = handler.apply( this, arguments );
313
314                                 if ( ret !== undefined ) {
315                                         event.result = ret;
316                                         if ( ret === false ) {
317                                                 event.preventDefault();
318                                                 event.stopPropagation();
319                                         }
320                                 }
321
322                                 if ( event.isImmediatePropagationStopped() ) {
323                                         break;
324                                 }
325
326                         }
327                 }
328         },
329
330         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(" "),
331
332         fix: function( event ) {
333                 if ( event[ expando ] ) {
334                         return event;
335                 }
336
337                 // store a copy of the original event object
338                 // and "clone" to set read-only properties
339                 var originalEvent = event;
340                 event = jQuery.Event( originalEvent );
341
342                 for ( var i = this.props.length, prop; i; ) {
343                         prop = this.props[ --i ];
344                         event[ prop ] = originalEvent[ prop ];
345                 }
346
347                 // Fix target property, if necessary
348                 if ( !event.target ) {
349                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
350                 }
351
352                 // check if target is a textnode (safari)
353                 if ( event.target.nodeType === 3 ) {
354                         event.target = event.target.parentNode;
355                 }
356
357                 // Add relatedTarget, if necessary
358                 if ( !event.relatedTarget && event.fromElement ) {
359                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
360                 }
361
362                 // Calculate pageX/Y if missing and clientX/Y available
363                 if ( event.pageX == null && event.clientX != null ) {
364                         var doc = document.documentElement, body = document.body;
365                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
366                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
367                 }
368
369                 // Add which for key events
370                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
371                         event.which = event.charCode || event.keyCode;
372                 }
373
374                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
375                 if ( !event.metaKey && event.ctrlKey ) {
376                         event.metaKey = event.ctrlKey;
377                 }
378
379                 // Add which for click: 1 == left; 2 == middle; 3 == right
380                 // Note: button is not normalized, so don't use it
381                 if ( !event.which && event.button !== undefined ) {
382                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
383                 }
384
385                 return event;
386         },
387
388         proxy: function( fn, proxy, thisObject ) {
389                 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
390                         thisObject = proxy;
391                         proxy = undefined;
392                 }
393                 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
394                 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
395                 // Set the guid of unique handler to the same of original handler, so it can be removed
396                 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
397                 // So proxy can be declared as an argument
398                 return proxy;
399         },
400
401         special: {
402                 ready: {
403                         // Make sure the ready event is setup
404                         setup: bindReady,
405                         teardown: function() {}
406                 },
407
408                 live: {
409                         add: function( proxy, data, namespaces, live ) {
410                                 jQuery.extend( proxy, data || {} );
411
412                                 proxy.guid += data.selector + data.live; 
413                                 jQuery.event.add( this, data.live, liveHandler, data ); 
414                                 
415                         },
416
417                         remove: function( namespaces ) {
418                                 if ( namespaces.length ) {
419                                         var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
420
421                                         jQuery.each( (jQuery.data(this, "events").live || {}), function() {
422                                                 if ( name.test(this.type) ) {
423                                                         remove++;
424                                                 }
425                                         });
426
427                                         if ( remove < 1 ) {
428                                                 jQuery.event.remove( this, namespaces[0], liveHandler );
429                                         }
430                                 }
431                         },
432                         special: {}
433                 }
434         }
435 };
436
437 jQuery.Event = function( src ){
438         // Allow instantiation without the 'new' keyword
439         if ( !this.preventDefault ) {
440                 return new jQuery.Event( src );
441         }
442
443         // Event object
444         if ( src && src.type ) {
445                 this.originalEvent = src;
446                 this.type = src.type;
447         // Event type
448         } else {
449                 this.type = src;
450         }
451
452         // timeStamp is buggy for some events on Firefox(#3843)
453         // So we won't rely on the native value
454         this.timeStamp = now();
455
456         // Mark it as fixed
457         this[ expando ] = true;
458 };
459
460 function returnFalse() {
461         return false;
462 }
463 function returnTrue() {
464         return true;
465 }
466
467 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
468 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
469 jQuery.Event.prototype = {
470         preventDefault: function() {
471                 this.isDefaultPrevented = returnTrue;
472
473                 var e = this.originalEvent;
474                 if ( !e ) {
475                         return;
476                 }
477                 
478                 // if preventDefault exists run it on the original event
479                 if ( e.preventDefault ) {
480                         e.preventDefault();
481                 }
482                 // otherwise set the returnValue property of the original event to false (IE)
483                 e.returnValue = false;
484         },
485         stopPropagation: function() {
486                 this.isPropagationStopped = returnTrue;
487
488                 var e = this.originalEvent;
489                 if ( !e ) {
490                         return;
491                 }
492                 // if stopPropagation exists run it on the original event
493                 if ( e.stopPropagation ) {
494                         e.stopPropagation();
495                 }
496                 // otherwise set the cancelBubble property of the original event to true (IE)
497                 e.cancelBubble = true;
498         },
499         stopImmediatePropagation: function(){
500                 this.isImmediatePropagationStopped = returnTrue;
501                 this.stopPropagation();
502         },
503         isDefaultPrevented: returnFalse,
504         isPropagationStopped: returnFalse,
505         isImmediatePropagationStopped: returnFalse
506 };
507 // Checks if an event happened on an element within another element
508 // Used in jQuery.event.special.mouseenter and mouseleave handlers
509 var withinElement = function( event ) {
510         // Check if mouse(over|out) are still within the same parent element
511         var parent = event.relatedTarget;
512         // Traverse up the tree
513         while ( parent && parent != this ) {
514                 // Firefox sometimes assigns relatedTarget a XUL element
515                 // which we cannot access the parentNode property of
516                 try { parent = parent.parentNode; }
517                 // assuming we've left the element since we most likely mousedover a xul element
518                 catch(e) { break; }
519         }
520
521         if ( parent != this ) {
522                 // set the correct event type
523                 event.type = event.data;
524                 // handle event if we actually just moused on to a non sub-element
525                 jQuery.event.handle.apply( this, arguments );
526         }
527
528 },
529
530 // In case of event delegation, we only need to rename the event.type,
531 // liveHandler will take care of the rest.
532 delegate = function( event ) {
533         event.type = event.data;
534         jQuery.event.handle.apply( this, arguments );
535 };
536
537 // Create mouseenter and mouseleave events
538 jQuery.each({
539         mouseover: "mouseenter",
540         mouseout: "mouseleave"
541 }, function( orig, fix ) {
542         jQuery.event.special[ fix ] = {
543                 setup: function(data){
544                         jQuery.event.add( this, orig, data && data.selector ? delegate : withinElement, fix );
545                 },
546                 teardown: function(data){
547                         jQuery.event.remove( this, orig, data && data.selector ? delegate : withinElement );
548                 }
549         };
550 });
551
552 // submit delegation
553 jQuery.event.special.submit = {
554         setup: function( data, namespaces, fn ) {
555                 if ( !jQuery.support.submitBubbles && this.nodeName.toLowerCase() !== "form" ) {
556                         jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
557                                 var elem = e.target, type = elem.type;
558
559                                 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
560                                         return trigger( "submit", this, arguments );
561                                 }
562                         });
563          
564                         jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
565                                 var elem = e.target, type = elem.type;
566
567                                 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
568                                         return trigger( "submit", this, arguments );
569                                 }
570                         });
571                 }
572
573                 return false;
574         },
575
576         remove: function( namespaces, fn ) {
577                 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
578                 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
579         }
580 };
581
582 // change delegation, happens here so we have bind.
583 jQuery.event.special.change = {
584         filters: {
585                 click: function( e ) { 
586                         var elem = e.target;
587
588                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
589                                 return trigger( "change", this, arguments );
590                         }
591
592                         return changeFilters.keyup.call( this, e );
593                 }, 
594                 keyup: function( e ) { 
595                         var elem = e.target, data, index = elem.selectedIndex + "";
596
597                         if ( elem.nodeName.toLowerCase() === "select" ) {
598                                 data = jQuery.data( elem, "_change_data" );
599                                 jQuery.data( elem, "_change_data", index );
600
601                                 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
602                                         return trigger( "change", this, arguments );
603                                 }
604                         }
605                 },
606                 beforeactivate: function( e ) {
607                         var elem = e.target;
608
609                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
610                                 return trigger( "change", this, arguments );
611                         }
612                 },
613                 blur: function( e ) {
614                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
615
616                         if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
617                                 && jQuery.data(elem, "_change_data") !== elem.value ) {
618
619                                 return trigger( "change", this, arguments );
620                         }
621                 },
622                 focus: function( e ) {
623                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
624
625                         if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
626                                 jQuery.data( elem, "_change_data", elem.value );
627                         }
628                 }
629         },
630         setup: function( data, namespaces, fn ) {
631                 // return false if we bubble
632                 if ( !jQuery.support.changeBubbles ) {
633                         for ( var type in changeFilters ) {
634                                 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
635                         }
636                 }
637                 
638                 // always want to listen for change for trigger
639                 return false;
640         },
641         remove: function( namespaces, fn ) {
642                 if ( !jQuery.support.changeBubbles ) {
643                         for ( var type in changeFilters ) {
644                                 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
645                         }
646                 }
647         }
648 };
649
650 var changeFilters = jQuery.event.special.change.filters;
651
652 function trigger( type, elem, args ) {
653         args[0].type = type;
654         return jQuery.event.handle.apply( elem, args );
655 }
656
657 // Create "bubbling" focus and blur events
658 jQuery.each({
659         focus: "focusin",
660         blur: "focusout"
661 }, function( orig, fix ){
662         var event = jQuery.event,
663                 handle = event.handle;
664         
665         function ieHandler() { 
666                 arguments[0].type = orig;
667                 return handle.apply(this, arguments);
668         }
669
670         event.special[orig] = {
671                 setup:function() {
672                         if ( this.addEventListener ) {
673                                 this.addEventListener( orig, handle, true );
674                         } else {
675                                 event.add( this, fix, ieHandler );
676                         }
677                 }, 
678                 teardown:function() { 
679                         if ( this.removeEventListener ) {
680                                 this.removeEventListener( orig, handle, true );
681                         } else {
682                                 event.remove( this, fix, ieHandler );
683                         }
684                 }
685         };
686 });
687
688 jQuery.fn.extend({
689         // TODO: make bind(), unbind() and one() DRY!
690         bind: function( type, data, fn, thisObject ) {
691                 // Handle object literals
692                 if ( typeof type === "object" ) {
693                         for ( var key in type ) {
694                                 this.bind(key, data, type[key], fn);
695                         }
696                         return this;
697                 }
698                 
699                 if ( jQuery.isFunction( data ) ) {
700                         thisObject = fn;
701                         fn = data;
702                         data = undefined;
703                 }
704                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
705                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
706                         jQuery.event.add( this, type, fn, data );
707                 });
708         },
709
710         one: function( type, data, fn, thisObject ) {
711                 // Handle object literals
712                 if ( typeof type === "object" ) {
713                         for ( var key in type ) {
714                                 this.one(key, data, type[key], fn);
715                         }
716                         return this;
717                 }
718                 
719                 if ( jQuery.isFunction( data ) ) {
720                         thisObject = fn;
721                         fn = data;
722                         data = undefined;
723                 }
724                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
725                 var one = jQuery.event.proxy( fn, function( event ) {
726                         jQuery( this ).unbind( event, one );
727                         return fn.apply( this, arguments );
728                 });
729                 return this.each(function() {
730                         jQuery.event.add( this, type, one, data );
731                 });
732         },
733
734         unbind: function( type, fn ) {
735                 // Handle object literals
736                 if ( typeof type === "object" && !type.preventDefault ) {
737                         for ( var key in type ) {
738                                 this.unbind(key, type[key]);
739                         }
740                         return this;
741                 }
742                 
743                 return this.each(function() {
744                         jQuery.event.remove( this, type, fn );
745                 });
746         },
747
748         trigger: function( type, data ) {
749                 return this.each(function() {
750                         jQuery.event.trigger( type, data, this );
751                 });
752         },
753
754         triggerHandler: function( type, data ) {
755                 if ( this[0] ) {
756                         var event = jQuery.Event( type );
757                         event.preventDefault();
758                         event.stopPropagation();
759                         jQuery.event.trigger( event, data, this[0] );
760                         return event.result;
761                 }
762         },
763
764         toggle: function( fn ) {
765                 // Save reference to arguments for access in closure
766                 var args = arguments, i = 1;
767
768                 // link all the functions, so any of them can unbind this click handler
769                 while( i < args.length ) {
770                         jQuery.event.proxy( fn, args[ i++ ] );
771                 }
772
773                 return this.click( jQuery.event.proxy( fn, function( event ) {
774                         // Figure out which function to execute
775                         var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
776                         jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
777
778                         // Make sure that clicks stop
779                         event.preventDefault();
780
781                         // and execute the function
782                         return args[ lastToggle ].apply( this, arguments ) || false;
783                 }));
784         },
785
786         hover: function( fnOver, fnOut ) {
787                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
788         },
789
790         ready: function( fn ) {
791                 // Attach the listeners
792                 bindReady();
793
794                 // If the DOM is already ready
795                 if ( jQuery.isReady ) {
796                         // Execute the function immediately
797                         fn.call( document, jQuery );
798
799                 // Otherwise, remember the function for later
800                 } else {
801                         // Add the function to the wait list
802                         jQuery.readyList.push( fn );
803                 }
804
805                 return this;
806         },
807
808         live: function( type, data, fn, thisObject ) {
809                 if ( jQuery.isFunction( data ) ) {
810                         if ( fn !== undefined ) {
811                                 thisObject = fn;
812                         }
813                         fn = data;
814                         data = undefined;
815                 }
816                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
817                         data: data, selector: this.selector, live: type
818                 }, fn, thisObject );
819                 return this;
820         },
821
822         die: function( type, fn ) {
823                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
824                 return this;
825         }
826 });
827
828 function liveHandler( event ) {
829         var stop = true, elems = [], selectors = [], args = arguments,
830                 related, match, fn, elem, j, i, data,
831                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
832
833         for ( j in live ) {
834                 fn = live[j];
835                 if ( fn.live === event.type ||
836                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
837
838                         data = fn.data;
839                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
840                                         !data.beforeFilter[event.type](event)) ) {
841                                 selectors.push( fn.selector );
842                         }
843                 } else {
844                         delete live[j];
845                 }
846         }
847
848         match = jQuery( event.target ).closest( selectors, event.currentTarget );
849
850         for ( i = 0, l = match.length; i < l; i++ ) {
851                 for ( j in live ) {
852                         fn = live[j];
853                         elem = match[i].elem;
854                         related = null;
855
856                         if ( match[i].selector === fn.selector ) {
857                                 // Those two events require additional checking
858                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
859                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
860                                 }
861
862                                 if ( !related || related !== elem ) {
863                                         elems.push({ elem: elem, fn: fn });
864                                 }
865                         }
866                 }
867         }
868
869         for ( i = 0, l = elems.length; i < l; i++ ) {
870                 match = elems[i];
871                 event.currentTarget = match.elem;
872                 event.data = match.fn.data;
873                 if ( match.fn.apply( match.elem, args ) === false ) {
874                         stop = false;
875                         break;
876                 }
877         }
878
879         return stop;
880 }
881
882 function liveConvert( type, selector ) {
883         return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
884                                                                   .replace(/\./g, "`")
885                                                                   .replace(/ /g, "|")].join(".");
886 }
887
888 jQuery.extend({
889         isReady: false,
890         readyList: [],
891         // Handle when the DOM is ready
892         ready: function() {
893                 // Make sure that the DOM is not already loaded
894                 if ( !jQuery.isReady ) {
895                         if ( !document.body ) {
896                                 return setTimeout( jQuery.ready, 13 );
897                         }
898
899                         // Remember that the DOM is ready
900                         jQuery.isReady = true;
901
902                         // If there are functions bound, to execute
903                         if ( jQuery.readyList ) {
904                                 // Execute all of them
905                                 var fn, i = 0;
906                                 while ( (fn = jQuery.readyList[ i++ ]) ) {
907                                         fn.call( document, jQuery );
908                                 }
909
910                                 // Reset the list of functions
911                                 jQuery.readyList = null;
912                         }
913
914                         // Trigger any bound ready events
915                         jQuery( document ).triggerHandler( "ready" );
916                 }
917         }
918 });
919
920 var readyBound = false;
921
922 function bindReady() {
923         if ( readyBound ) { return; }
924         readyBound = true;
925
926         // Catch cases where $(document).ready() is called after the
927         // browser event has already occurred.
928         if ( document.readyState === "complete" ) {
929                 return jQuery.ready();
930         }
931
932         // Mozilla, Opera and webkit nightlies currently support this event
933         if ( document.addEventListener ) {
934                 // Use the handy event callback
935                 document.addEventListener( "DOMContentLoaded", function() {
936                         document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
937                         jQuery.ready();
938                 }, false );
939
940         // If IE event model is used
941         } else if ( document.attachEvent ) {
942                 // ensure firing before onload,
943                 // maybe late but safe also for iframes
944                 document.attachEvent("onreadystatechange", function() {
945                         // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
946                         if ( document.readyState === "complete" ) {
947                                 document.detachEvent( "onreadystatechange", arguments.callee );
948                                 jQuery.ready();
949                         }
950                 });
951
952                 // If IE and not a frame
953                 // continually check to see if the document is ready
954                 var toplevel = false;
955
956                 try {
957                         toplevel = window.frameElement == null;
958                 } catch(e){}
959
960                 if ( document.documentElement.doScroll && toplevel ) {
961                         (function() {
962                                 if ( jQuery.isReady ) {
963                                         return;
964                                 }
965
966                                 try {
967                                         // If IE is used, use the trick by Diego Perini
968                                         // http://javascript.nwbox.com/IEContentLoaded/
969                                         document.documentElement.doScroll("left");
970                                 } catch( error ) {
971                                         setTimeout( arguments.callee, 0 );
972                                         return;
973                                 }
974
975                                 // and execute any waiting functions
976                                 jQuery.ready();
977                         })();
978                 }
979         }
980
981         // A fallback to window.onload, that will always work
982         jQuery.event.add( window, "load", jQuery.ready );
983 }
984
985 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
986         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
987         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
988
989         // Handle event binding
990         jQuery.fn[ name ] = function( fn ) {
991                 return fn ? this.bind( name, fn ) : this.trigger( name );
992         };
993 });
994
995 // Prevent memory leaks in IE
996 // Window isn't included so as not to unbind existing unload events
997 // More info:
998 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
999 /*@cc_on
1000 jQuery( window ).bind( 'unload', function() {
1001         for ( var id in jQuery.cache ) {
1002                 // Skip the window
1003                 if ( id != 1 && jQuery.cache[ id ].handle ) {
1004                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1005                 }
1006         }
1007 });
1008 @*/