Make sure that we're doing proper focus bubble testing. Also simplified the logic...
[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 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                                 nativeFn();
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 && nativeHandler.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: 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 jQuery.event.special.submit = {
573         setup: function( data, namespaces, fn ) {
574                 if ( !jQuery.support.submitBubbles && this.nodeName.toLowerCase() !== "form" ) {
575                         jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
576                                 var elem = e.target, type = elem.type;
577
578                                 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
579                                         return trigger( "submit", this, arguments );
580                                 }
581                         });
582          
583                         jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
584                                 var elem = e.target, type = elem.type;
585
586                                 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
587                                         return trigger( "submit", this, arguments );
588                                 }
589                         });
590                 }
591
592                 return false;
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 // change delegation, happens here so we have bind.
602 jQuery.event.special.change = {
603         filters: {
604                 click: function( e ) { 
605                         var elem = e.target;
606
607                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
608                                 return trigger( "change", this, arguments );
609                         }
610
611                         return changeFilters.keyup.call( this, e );
612                 }, 
613                 keyup: function( e ) { 
614                         var elem = e.target, data, index = elem.selectedIndex + "";
615
616                         if ( elem.nodeName.toLowerCase() === "select" ) {
617                                 data = jQuery.data( elem, "_change_data" );
618                                 jQuery.data( elem, "_change_data", index );
619
620                                 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
621                                         return trigger( "change", this, arguments );
622                                 }
623                         }
624                 },
625                 beforeactivate: function( e ) {
626                         var elem = e.target;
627
628                         if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
629                                 return trigger( "change", this, arguments );
630                         }
631                 },
632                 blur: function( e ) {
633                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
634
635                         if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
636                                 && jQuery.data(elem, "_change_data") !== elem.value ) {
637
638                                 return trigger( "change", this, arguments );
639                         }
640                 },
641                 focus: function( e ) {
642                         var elem = e.target, nodeName = elem.nodeName.toLowerCase();
643
644                         if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
645                                 jQuery.data( elem, "_change_data", elem.value );
646                         }
647                 }
648         },
649         setup: function( data, namespaces, fn ) {
650                 // return false if we bubble
651                 if ( !jQuery.support.changeBubbles ) {
652                         for ( var type in changeFilters ) {
653                                 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
654                         }
655                 }
656                 
657                 // always want to listen for change for trigger
658                 return false;
659         },
660         remove: function( namespaces, fn ) {
661                 if ( !jQuery.support.changeBubbles ) {
662                         for ( var type in changeFilters ) {
663                                 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
664                         }
665                 }
666         }
667 };
668
669 var changeFilters = jQuery.event.special.change.filters;
670
671 function trigger( type, elem, args ) {
672         args[0].type = type;
673         return jQuery.event.handle.apply( elem, args );
674 }
675
676 // Create "bubbling" focus and blur events
677 if ( !jQuery.support.focusBubbles ) {
678
679 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
680         event.special[ orig ] = {
681                 setup: function() {
682                         jQuery.event.add( this, fix, ieHandler );
683                 }, 
684                 teardown: function() { 
685                         jQuery.event.remove( this, fix, ieHandler );
686                 }
687         };
688
689         function ieHandler() { 
690                 arguments[0].type = orig;
691                 return jQuery.event.handle.apply(this, arguments);
692         }
693 });
694
695 }
696
697 jQuery.fn.extend({
698         // TODO: make bind(), unbind() and one() DRY!
699         bind: function( type, data, fn, thisObject ) {
700                 // Handle object literals
701                 if ( typeof type === "object" ) {
702                         for ( var key in type ) {
703                                 this.bind(key, data, type[key], fn);
704                         }
705                         return this;
706                 }
707                 
708                 if ( jQuery.isFunction( data ) ) {
709                         thisObject = fn;
710                         fn = data;
711                         data = undefined;
712                 }
713                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
714                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
715                         jQuery.event.add( this, type, fn, data );
716                 });
717         },
718
719         one: function( type, data, fn, thisObject ) {
720                 // Handle object literals
721                 if ( typeof type === "object" ) {
722                         for ( var key in type ) {
723                                 this.one(key, data, type[key], fn);
724                         }
725                         return this;
726                 }
727                 
728                 if ( jQuery.isFunction( data ) ) {
729                         thisObject = fn;
730                         fn = data;
731                         data = undefined;
732                 }
733                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
734                 var one = jQuery.event.proxy( fn, function( event ) {
735                         jQuery( this ).unbind( event, one );
736                         return fn.apply( this, arguments );
737                 });
738                 return this.each(function() {
739                         jQuery.event.add( this, type, one, data );
740                 });
741         },
742
743         unbind: function( type, fn ) {
744                 // Handle object literals
745                 if ( typeof type === "object" && !type.preventDefault ) {
746                         for ( var key in type ) {
747                                 this.unbind(key, type[key]);
748                         }
749                         return this;
750                 }
751                 
752                 return this.each(function() {
753                         jQuery.event.remove( this, type, fn );
754                 });
755         },
756
757         trigger: function( type, data ) {
758                 return this.each(function() {
759                         jQuery.event.trigger( type, data, this );
760                 });
761         },
762
763         triggerHandler: function( type, data ) {
764                 if ( this[0] ) {
765                         var event = jQuery.Event( type );
766                         event.preventDefault();
767                         event.stopPropagation();
768                         jQuery.event.trigger( event, data, this[0] );
769                         return event.result;
770                 }
771         },
772
773         toggle: function( fn ) {
774                 // Save reference to arguments for access in closure
775                 var args = arguments, i = 1;
776
777                 // link all the functions, so any of them can unbind this click handler
778                 while( i < args.length ) {
779                         jQuery.event.proxy( fn, args[ i++ ] );
780                 }
781
782                 return this.click( jQuery.event.proxy( fn, function( event ) {
783                         // Figure out which function to execute
784                         var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
785                         jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
786
787                         // Make sure that clicks stop
788                         event.preventDefault();
789
790                         // and execute the function
791                         return args[ lastToggle ].apply( this, arguments ) || false;
792                 }));
793         },
794
795         hover: function( fnOver, fnOut ) {
796                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
797         },
798
799         ready: function( fn ) {
800                 // Attach the listeners
801                 bindReady();
802
803                 // If the DOM is already ready
804                 if ( jQuery.isReady ) {
805                         // Execute the function immediately
806                         fn.call( document, jQuery );
807
808                 // Otherwise, remember the function for later
809                 } else {
810                         // Add the function to the wait list
811                         jQuery.readyList.push( fn );
812                 }
813
814                 return this;
815         },
816
817         live: function( type, data, fn, thisObject ) {
818                 if ( jQuery.isFunction( data ) ) {
819                         if ( fn !== undefined ) {
820                                 thisObject = fn;
821                         }
822                         fn = data;
823                         data = undefined;
824                 }
825                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
826                         data: data, selector: this.selector, live: type
827                 }, fn, thisObject );
828                 return this;
829         },
830
831         die: function( type, fn ) {
832                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
833                 return this;
834         }
835 });
836
837 function liveHandler( event ) {
838         var stop = true, elems = [], selectors = [], args = arguments,
839                 related, match, fn, elem, j, i, data,
840                 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
841
842         for ( j in live ) {
843                 fn = live[j];
844                 if ( fn.live === event.type ||
845                                 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
846
847                         data = fn.data;
848                         if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
849                                         !data.beforeFilter[event.type](event)) ) {
850                                 selectors.push( fn.selector );
851                         }
852                 } else {
853                         delete live[j];
854                 }
855         }
856
857         match = jQuery( event.target ).closest( selectors, event.currentTarget );
858
859         for ( i = 0, l = match.length; i < l; i++ ) {
860                 for ( j in live ) {
861                         fn = live[j];
862                         elem = match[i].elem;
863                         related = null;
864
865                         if ( match[i].selector === fn.selector ) {
866                                 // Those two events require additional checking
867                                 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
868                                         related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
869                                 }
870
871                                 if ( !related || related !== elem ) {
872                                         elems.push({ elem: elem, fn: fn });
873                                 }
874                         }
875                 }
876         }
877
878         for ( i = 0, l = elems.length; i < l; i++ ) {
879                 match = elems[i];
880                 event.currentTarget = match.elem;
881                 event.data = match.fn.data;
882                 if ( match.fn.apply( match.elem, args ) === false ) {
883                         stop = false;
884                         break;
885                 }
886         }
887
888         return stop;
889 }
890
891 function liveConvert( type, selector ) {
892         return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
893                                                                   .replace(/\./g, "`")
894                                                                   .replace(/ /g, "|")].join(".");
895 }
896
897 jQuery.extend({
898         isReady: false,
899         readyList: [],
900         // Handle when the DOM is ready
901         ready: function() {
902                 // Make sure that the DOM is not already loaded
903                 if ( !jQuery.isReady ) {
904                         if ( !document.body ) {
905                                 return setTimeout( jQuery.ready, 13 );
906                         }
907
908                         // Remember that the DOM is ready
909                         jQuery.isReady = true;
910
911                         // If there are functions bound, to execute
912                         if ( jQuery.readyList ) {
913                                 // Execute all of them
914                                 var fn, i = 0;
915                                 while ( (fn = jQuery.readyList[ i++ ]) ) {
916                                         fn.call( document, jQuery );
917                                 }
918
919                                 // Reset the list of functions
920                                 jQuery.readyList = null;
921                         }
922
923                         // Trigger any bound ready events
924                         jQuery( document ).triggerHandler( "ready" );
925                 }
926         }
927 });
928
929 var readyBound = false;
930
931 function bindReady() {
932         if ( readyBound ) { return; }
933         readyBound = true;
934
935         // Catch cases where $(document).ready() is called after the
936         // browser event has already occurred.
937         if ( document.readyState === "complete" ) {
938                 return jQuery.ready();
939         }
940
941         // Mozilla, Opera and webkit nightlies currently support this event
942         if ( document.addEventListener ) {
943                 // Use the handy event callback
944                 document.addEventListener( "DOMContentLoaded", function() {
945                         document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
946                         jQuery.ready();
947                 }, false );
948
949         // If IE event model is used
950         } else if ( document.attachEvent ) {
951                 // ensure firing before onload,
952                 // maybe late but safe also for iframes
953                 document.attachEvent("onreadystatechange", function() {
954                         // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
955                         if ( document.readyState === "complete" ) {
956                                 document.detachEvent( "onreadystatechange", arguments.callee );
957                                 jQuery.ready();
958                         }
959                 });
960
961                 // If IE and not a frame
962                 // continually check to see if the document is ready
963                 var toplevel = false;
964
965                 try {
966                         toplevel = window.frameElement == null;
967                 } catch(e){}
968
969                 if ( document.documentElement.doScroll && toplevel ) {
970                         (function() {
971                                 if ( jQuery.isReady ) {
972                                         return;
973                                 }
974
975                                 try {
976                                         // If IE is used, use the trick by Diego Perini
977                                         // http://javascript.nwbox.com/IEContentLoaded/
978                                         document.documentElement.doScroll("left");
979                                 } catch( error ) {
980                                         setTimeout( arguments.callee, 0 );
981                                         return;
982                                 }
983
984                                 // and execute any waiting functions
985                                 jQuery.ready();
986                         })();
987                 }
988         }
989
990         // A fallback to window.onload, that will always work
991         jQuery.event.add( window, "load", jQuery.ready );
992 }
993
994 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
995         "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
996         "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
997
998         // Handle event binding
999         jQuery.fn[ name ] = function( fn ) {
1000                 return fn ? this.bind( name, fn ) : this.trigger( name );
1001         };
1002 });
1003
1004 // Prevent memory leaks in IE
1005 // Window isn't included so as not to unbind existing unload events
1006 // More info:
1007 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1008 /*@cc_on
1009 jQuery( window ).bind( 'unload', function() {
1010         for ( var id in jQuery.cache ) {
1011                 // Skip the window
1012                 if ( id != 1 && jQuery.cache[ id ].handle ) {
1013                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
1014                 }
1015         }
1016 });
1017 @*/