fix regression in removing live events caused by r6324
[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().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().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                 // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
251                 if ( (!elem[ type ] || (jQuery.nodeName(elem, 'a') && type === "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) {
252                         event.result = false;
253                 }
254
255                 // Trigger the native events (except for clicks on links)
256                 if ( !bubbling && elem[ type ] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
257                         this.triggered = true;
258                         try {
259                                 elem[ type ]();
260                         // prevent IE from throwing an error for some hidden elements
261                         } catch (e) {}
262                 }
263
264                 this.triggered = false;
265
266                 if ( !event.isPropagationStopped() ) {
267                         var parent = elem.parentNode || elem.ownerDocument;
268                         if ( parent ) {
269                                 jQuery.event.trigger( event, data, parent, true );
270                         }
271                 }
272         },
273
274         handle: function( event ) {
275                 // returned undefined or false
276                 var all, handlers;
277
278                 event = arguments[0] = jQuery.event.fix( event || window.event );
279                 event.currentTarget = this;
280
281                 // Namespaced event handlers
282                 var namespaces = event.type.split(".");
283                 event.type = namespaces.shift();
284
285                 // Cache this now, all = true means, any handler
286                 all = !namespaces.length && !event.exclusive;
287
288                 var namespace = new RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
289
290                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
291
292                 for ( var j in handlers ) {
293                         var handler = handlers[ j ];
294
295                         // Filter the functions by class
296                         if ( all || namespace.test(handler.type) ) {
297                                 // Pass in a reference to the handler function itself
298                                 // So that we can later remove it
299                                 event.handler = handler;
300                                 event.data = handler.data;
301
302                                 var ret = handler.apply( this, arguments );
303
304                                 if ( ret !== undefined ) {
305                                         event.result = ret;
306                                         if ( ret === false ) {
307                                                 event.preventDefault();
308                                                 event.stopPropagation();
309                                         }
310                                 }
311
312                                 if ( event.isImmediatePropagationStopped() ) {
313                                         break;
314                                 }
315
316                         }
317                 }
318         },
319
320         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(" "),
321
322         fix: function( event ) {
323                 if ( event[ expando ] ) {
324                         return event;
325                 }
326
327                 // store a copy of the original event object
328                 // and "clone" to set read-only properties
329                 var originalEvent = event;
330                 event = jQuery.Event( originalEvent );
331
332                 for ( var i = this.props.length, prop; i; ) {
333                         prop = this.props[ --i ];
334                         event[ prop ] = originalEvent[ prop ];
335                 }
336
337                 // Fix target property, if necessary
338                 if ( !event.target ) {
339                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
340                 }
341
342                 // check if target is a textnode (safari)
343                 if ( event.target.nodeType === 3 ) {
344                         event.target = event.target.parentNode;
345                 }
346
347                 // Add relatedTarget, if necessary
348                 if ( !event.relatedTarget && event.fromElement ) {
349                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
350                 }
351
352                 // Calculate pageX/Y if missing and clientX/Y available
353                 if ( event.pageX == null && event.clientX != null ) {
354                         var doc = document.documentElement, body = document.body;
355                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
356                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
357                 }
358
359                 // Add which for key events
360                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
361                         event.which = event.charCode || event.keyCode;
362                 }
363
364                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
365                 if ( !event.metaKey && event.ctrlKey ) {
366                         event.metaKey = event.ctrlKey;
367                 }
368
369                 // Add which for click: 1 == left; 2 == middle; 3 == right
370                 // Note: button is not normalized, so don't use it
371                 if ( !event.which && event.button ) {
372                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
373                 }
374
375                 return event;
376         },
377
378         proxy: function( fn, proxy, thisObject ) {
379                 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
380                         thisObject = proxy;
381                         proxy = undefined;
382                 }
383                 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
384                 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
385                 // Set the guid of unique handler to the same of original handler, so it can be removed
386                 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
387                 // So proxy can be declared as an argument
388                 return proxy;
389         },
390
391         special: {
392                 ready: {
393                         // Make sure the ready event is setup
394                         setup: bindReady,
395                         teardown: function() {}
396                 },
397
398                 live: {
399                         add: function( proxy, data, namespaces ) {
400                                 jQuery.extend( proxy, data || {} );
401                                 proxy.guid += data.selector + data.live;
402                                 jQuery.event.add( this, data.live, liveHandler );
403                         },
404
405                         remove: function( namespaces ) {
406                                 if ( namespaces.length ) {
407                                         var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
408
409                                         jQuery.each( (jQuery.data(this, "events").live || {}), function() {
410                                                 if ( name.test(this.type) ) {
411                                                         remove++;
412                                                 }
413                                         });
414
415                                         if ( remove < 1 ) {
416                                                 jQuery.event.remove( this, namespaces[0], liveHandler );
417                                         }
418                                 }
419                         }
420                 }
421         }
422 };
423
424 jQuery.Event = function( src ){
425         // Allow instantiation without the 'new' keyword
426         if ( !this.preventDefault ) {
427                 return new jQuery.Event( src );
428         }
429
430         // Event object
431         if ( src && src.type ) {
432                 this.originalEvent = src;
433                 this.type = src.type;
434         // Event type
435         } else {
436                 this.type = src;
437         }
438
439         // timeStamp is buggy for some events on Firefox(#3843)
440         // So we won't rely on the native value
441         this.timeStamp = now();
442
443         // Mark it as fixed
444         this[ expando ] = true;
445 };
446
447 function returnFalse() {
448         return false;
449 }
450 function returnTrue() {
451         return true;
452 }
453
454 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
455 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
456 jQuery.Event.prototype = {
457         preventDefault: function() {
458                 this.isDefaultPrevented = returnTrue;
459
460                 var e = this.originalEvent;
461                 if ( !e ) {
462                         return;
463                 }
464                 // if preventDefault exists run it on the original event
465                 if ( e.preventDefault ) {
466                         e.preventDefault();
467                 }
468                 // otherwise set the returnValue property of the original event to false (IE)
469                 e.returnValue = false;
470         },
471         stopPropagation: function() {
472                 this.isPropagationStopped = returnTrue;
473
474                 var e = this.originalEvent;
475                 if ( !e ) {
476                         return;
477                 }
478                 // if stopPropagation exists run it on the original event
479                 if ( e.stopPropagation ) {
480                         e.stopPropagation();
481                 }
482                 // otherwise set the cancelBubble property of the original event to true (IE)
483                 e.cancelBubble = true;
484         },
485         stopImmediatePropagation: function(){
486                 this.isImmediatePropagationStopped = returnTrue;
487                 this.stopPropagation();
488         },
489         isDefaultPrevented: returnFalse,
490         isPropagationStopped: returnFalse,
491         isImmediatePropagationStopped: returnFalse
492 };
493 // Checks if an event happened on an element within another element
494 // Used in jQuery.event.special.mouseenter and mouseleave handlers
495 var withinElement = function( event ) {
496         // Check if mouse(over|out) are still within the same parent element
497         var parent = event.relatedTarget;
498         // Traverse up the tree
499         while ( parent && parent != this ) {
500                 // Firefox sometimes assigns relatedTarget a XUL element
501                 // which we cannot access the parentNode property of
502                 try { parent = parent.parentNode; }
503                 // assuming we've left the element since we most likely mousedover a xul element
504                 catch(e) { break; }
505         }
506
507         if ( parent != this ) {
508                 // set the correct event type
509                 event.type = event.data;
510                 // handle event if we actually just moused on to a non sub-element
511                 jQuery.event.handle.apply( this, arguments );
512         }
513 };
514
515 jQuery.each({
516         mouseover: 'mouseenter',
517         mouseout: 'mouseleave'
518 }, function( orig, fix ) {
519         jQuery.event.special[ fix ] = {
520                 setup: function(){
521                         jQuery.event.add( this, orig, withinElement, fix );
522                 },
523                 teardown: function(){
524                         jQuery.event.remove( this, orig, withinElement );
525                 }
526         };
527 });
528
529 jQuery.fn.extend({
530         bind: function( type, data, fn, thisObject ) {
531                 if ( jQuery.isFunction( data ) ) {
532                         if ( fn !== undefined ) {
533                                 thisObject = fn;
534                         }
535                         fn = data;
536                         data = undefined;
537                 }
538                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
539                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
540                         jQuery.event.add( this, type, fn, data );
541                 });
542         },
543
544         one: function( type, data, fn, thisObject ) {
545                 if ( jQuery.isFunction( data ) ) {
546                         if ( fn !== undefined ) {
547                                 thisObject = fn;
548                         }
549                         fn = data;
550                         data = undefined;
551                 }
552                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
553                 var one = jQuery.event.proxy( fn, function( event ) {
554                         jQuery( this ).unbind( event, one );
555                         return fn.apply( this, arguments );
556                 });
557                 return this.each(function() {
558                         jQuery.event.add( this, type, one, data );
559                 });
560         },
561
562         unbind: function( type, fn ) {
563                 return this.each(function() {
564                         jQuery.event.remove( this, type, fn );
565                 });
566         },
567
568         trigger: function( type, data ) {
569                 return this.each(function() {
570                         jQuery.event.trigger( type, data, this );
571                 });
572         },
573
574         triggerHandler: function( type, data ) {
575                 if ( this[0] ) {
576                         var event = jQuery.Event( type );
577                         event.preventDefault();
578                         event.stopPropagation();
579                         jQuery.event.trigger( event, data, this[0] );
580                         return event.result;
581                 }
582         },
583
584         toggle: function( fn ) {
585                 // Save reference to arguments for access in closure
586                 var args = arguments, i = 1;
587
588                 // link all the functions, so any of them can unbind this click handler
589                 while( i < args.length ) {
590                         jQuery.event.proxy( fn, args[ i++ ] );
591                 }
592
593                 return this.click( jQuery.event.proxy( fn, function( event ) {
594                         // Figure out which function to execute
595                         this.lastToggle = ( this.lastToggle || 0 ) % i;
596
597                         // Make sure that clicks stop
598                         event.preventDefault();
599
600                         // and execute the function
601                         return args[ this.lastToggle++ ].apply( this, arguments ) || false;
602                 }));
603         },
604
605         hover: function( fnOver, fnOut ) {
606                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
607         },
608
609         ready: function( fn ) {
610                 // Attach the listeners
611                 bindReady();
612
613                 // If the DOM is already ready
614                 if ( jQuery.isReady ) {
615                         // Execute the function immediately
616                         fn.call( document, jQuery );
617
618                 // Otherwise, remember the function for later
619                 } else {
620                         // Add the function to the wait list
621                         jQuery.readyList.push( fn );
622                 }
623
624                 return this;
625         },
626
627         live: function( type, data, fn, thisObject ) {
628                 if ( jQuery.isFunction( data ) ) {
629                         if ( fn !== undefined ) {
630                                 thisObject = fn;
631                         }
632                         fn = data;
633                         data = undefined;
634                 }
635                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
636                         data: data, selector: this.selector, live: type
637                 }, fn, thisObject );
638                 return this;
639         },
640
641         die: function( type, fn ) {
642                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
643                 return this;
644         }
645 });
646
647 function liveHandler( event ) {
648         var stop = true, elems = [], args = arguments;
649
650         jQuery.each( jQuery.data( this, "events" ).live || [], function( i, fn ) {
651                 if ( fn.live === event.type ) {
652                         var elem = jQuery( event.target ).closest( fn.selector )[0];
653                         if ( elem ) {
654                                 elems.push({ elem: elem, fn: fn });
655                         }
656                 }
657         });
658
659         elems.sort(function( a, b ) {
660                 return jQuery.data( a.elem, "closest" ) - jQuery.data( b.elem, "closest" );
661         });
662
663         jQuery.each(elems, function() {
664                 event.currentTarget = this.elem;
665                 event.data = this.fn.data;
666                 if ( this.fn.apply( this.elem, args ) === false ) {
667                         return (stop = false);
668                 }
669         });
670
671         return stop;
672 }
673
674 function liveConvert( type, selector ) {
675         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
676 }
677
678 jQuery.extend({
679         isReady: false,
680         readyList: [],
681         // Handle when the DOM is ready
682         ready: function() {
683                 // Make sure that the DOM is not already loaded
684                 if ( !jQuery.isReady ) {
685                         // Remember that the DOM is ready
686                         jQuery.isReady = true;
687
688                         // If there are functions bound, to execute
689                         if ( jQuery.readyList ) {
690                                 // Execute all of them
691                                 var fn, i = 0;
692                                 while ( (fn = jQuery.readyList[ i++ ]) ) {
693                                         fn.call( document, jQuery );
694                                 }
695
696                                 // Reset the list of functions
697                                 jQuery.readyList = null;
698                         }
699
700                         // Trigger any bound ready events
701                         jQuery( document ).triggerHandler( "ready" );
702                 }
703         }
704 });
705
706 var readyBound = false;
707
708 function bindReady() {
709         if ( readyBound ) return;
710         readyBound = true;
711
712         // Mozilla, Opera and webkit nightlies currently support this event
713         if ( document.addEventListener ) {
714                 // Use the handy event callback
715                 document.addEventListener( "DOMContentLoaded", function() {
716                         document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
717                         jQuery.ready();
718                 }, false );
719
720         // If IE event model is used
721         } else if ( document.attachEvent ) {
722                 // ensure firing before onload,
723                 // maybe late but safe also for iframes
724                 document.attachEvent("onreadystatechange", function() {
725                         if ( document.readyState === "complete" ) {
726                                 document.detachEvent( "onreadystatechange", arguments.callee );
727                                 jQuery.ready();
728                         }
729                 });
730
731                 // If IE and not an iframe
732                 // continually check to see if the document is ready
733                 if ( document.documentElement.doScroll && window === window.top ) (function() {
734                         if ( jQuery.isReady ) {
735                                 return;
736                         }
737
738                         try {
739                                 // If IE is used, use the trick by Diego Perini
740                                 // http://javascript.nwbox.com/IEContentLoaded/
741                                 document.documentElement.doScroll("left");
742                         } catch( error ) {
743                                 setTimeout( arguments.callee, 0 );
744                                 return;
745                         }
746
747                         // and execute any waiting functions
748                         jQuery.ready();
749                 })();
750         }
751
752         // A fallback to window.onload, that will always work
753         jQuery.event.add( window, "load", jQuery.ready );
754 }
755
756 jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
757         "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
758         "change,select,submit,keydown,keypress,keyup,error").split(","), function( i, name ) {
759
760         // Handle event binding
761         jQuery.fn[ name ] = function( fn ) {
762                 return fn ? this.bind( name, fn ) : this.trigger( name );
763         };
764 });
765
766 // Prevent memory leaks in IE
767 // And prevent errors on refresh with events like mouseover in other browsers
768 // Window isn't included so as not to unbind existing unload events
769 // More info:
770 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
771 //  - https://bugzilla.mozilla.org/show_bug.cgi?id=252542
772 jQuery( window ).bind( 'unload', function() {
773         for ( var id in jQuery.cache ) {
774                 // Skip the window
775                 if ( id != 1 && jQuery.cache[ id ].handle ) {
776                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
777                 }
778         }
779 });