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