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