Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / EventManager.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-EventManager'>/**
19 </span> * @class Ext.EventManager
20  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
21  * several useful events directly.
22  * See {@link Ext.EventObject} for more details on normalized event objects.
23  * @singleton
24  */
25 Ext.EventManager = {
26
27     // --------------------- onReady ---------------------
28
29 <span id='Ext-EventManager-property-hasBoundOnReady'>    /**
30 </span>     * Check if we have bound our global onReady listener
31      * @private
32      */
33     hasBoundOnReady: false,
34
35 <span id='Ext-EventManager-property-hasFiredReady'>    /**
36 </span>     * Check if fireDocReady has been called
37      * @private
38      */
39     hasFiredReady: false,
40
41 <span id='Ext-EventManager-property-readyTimeout'>    /**
42 </span>     * Timer for the document ready event in old IE versions
43      * @private
44      */
45     readyTimeout: null,
46
47 <span id='Ext-EventManager-property-hasOnReadyStateChange'>    /**
48 </span>     * Checks if we have bound an onreadystatechange event
49      * @private
50      */
51     hasOnReadyStateChange: false,
52
53 <span id='Ext-EventManager-property-readyEvent'>    /**
54 </span>     * Holds references to any onReady functions
55      * @private
56      */
57     readyEvent: new Ext.util.Event(),
58
59 <span id='Ext-EventManager-method-checkReadyState'>    /**
60 </span>     * Check the ready state for old IE versions
61      * @private
62      * @return {Boolean} True if the document is ready
63      */
64     checkReadyState: function(){
65         var me = Ext.EventManager;
66
67         if(window.attachEvent){
68             // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
69             if (window != top) {
70                 return false;
71             }
72             try{
73                 document.documentElement.doScroll('left');
74             }catch(e){
75                 return false;
76             }
77             me.fireDocReady();
78             return true;
79         }
80         if (document.readyState == 'complete') {
81             me.fireDocReady();
82             return true;
83         }
84         me.readyTimeout = setTimeout(arguments.callee, 2);
85         return false;
86     },
87
88 <span id='Ext-EventManager-method-bindReadyEvent'>    /**
89 </span>     * Binds the appropriate browser event for checking if the DOM has loaded.
90      * @private
91      */
92     bindReadyEvent: function(){
93         var me = Ext.EventManager;
94         if (me.hasBoundOnReady) {
95             return;
96         }
97
98         if (document.addEventListener) {
99             document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
100             // fallback, load will ~always~ fire
101             window.addEventListener('load', me.fireDocReady, false);
102         } else {
103             // check if the document is ready, this will also kick off the scroll checking timer
104             if (!me.checkReadyState()) {
105                 document.attachEvent('onreadystatechange', me.checkReadyState);
106                 me.hasOnReadyStateChange = true;
107             }
108             // fallback, onload will ~always~ fire
109             window.attachEvent('onload', me.fireDocReady, false);
110         }
111         me.hasBoundOnReady = true;
112     },
113
114 <span id='Ext-EventManager-method-fireDocReady'>    /**
115 </span>     * We know the document is loaded, so trigger any onReady events.
116      * @private
117      */
118     fireDocReady: function(){
119         var me = Ext.EventManager;
120
121         // only unbind these events once
122         if (!me.hasFiredReady) {
123             me.hasFiredReady = true;
124
125             if (document.addEventListener) {
126                 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
127                 window.removeEventListener('load', me.fireDocReady, false);
128             } else {
129                 if (me.readyTimeout !== null) {
130                     clearTimeout(me.readyTimeout);
131                 }
132                 if (me.hasOnReadyStateChange) {
133                     document.detachEvent('onreadystatechange', me.checkReadyState);
134                 }
135                 window.detachEvent('onload', me.fireDocReady);
136             }
137             Ext.supports.init();
138         }
139         if (!Ext.isReady) {
140             Ext.isReady = true;
141             me.onWindowUnload();
142             me.readyEvent.fire();
143         }
144     },
145
146 <span id='Ext-EventManager-method-onDocumentReady'>    /**
147 </span>     * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
148      * accessed shorthanded as Ext.onReady().
149      * @param {Function} fn The method the event invokes.
150      * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the handler function executes. Defaults to the browser window.
151      * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
152      */
153     onDocumentReady: function(fn, scope, options){
154         options = options || {};
155         var me = Ext.EventManager,
156             readyEvent = me.readyEvent;
157
158         // force single to be true so our event is only ever fired once.
159         options.single = true;
160
161         // Document already loaded, let's just fire it
162         if (Ext.isReady) {
163             readyEvent.addListener(fn, scope, options);
164             readyEvent.fire();
165         } else {
166             options.delay = options.delay || 1;
167             readyEvent.addListener(fn, scope, options);
168             me.bindReadyEvent();
169         }
170     },
171
172
173     // --------------------- event binding ---------------------
174
175 <span id='Ext-EventManager-property-stoppedMouseDownEvent'>    /**
176 </span>     * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
177      * @private
178      */
179     stoppedMouseDownEvent: new Ext.util.Event(),
180
181 <span id='Ext-EventManager-property-propRe'>    /**
182 </span>     * Options to parse for the 4th argument to addListener.
183      * @private
184      */
185     propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
186
187 <span id='Ext-EventManager-method-getId'>    /**
188 </span>     * Get the id of the element. If one has not been assigned, automatically assign it.
189      * @param {Mixed} element The element to get the id for.
190      * @return {String} id
191      */
192     getId : function(element) {
193         var skipGarbageCollection = false,
194             id;
195     
196         element = Ext.getDom(element);
197     
198         if (element === document || element === window) {
199             id = element === document ? Ext.documentId : Ext.windowId;
200         }
201         else {
202             id = Ext.id(element);
203         }
204         // skip garbage collection for special elements (window, document, iframes)
205         if (element &amp;&amp; (element.getElementById || element.navigator)) {
206             skipGarbageCollection = true;
207         }
208     
209         if (!Ext.cache[id]){
210             Ext.core.Element.addToCache(new Ext.core.Element(element), id);
211             if (skipGarbageCollection) {
212                 Ext.cache[id].skipGarbageCollection = true;
213             }
214         }
215         return id;
216     },
217
218 <span id='Ext-EventManager-method-prepareListenerConfig'>    /**
219 </span>     * Convert a &quot;config style&quot; listener into a set of flat arguments so they can be passed to addListener
220      * @private
221      * @param {Object} element The element the event is for
222      * @param {Object} event The event configuration
223      * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
224      */
225     prepareListenerConfig: function(element, config, isRemove){
226         var me = this,
227             propRe = me.propRe,
228             key, value, args;
229
230         // loop over all the keys in the object
231         for (key in config) {
232             if (config.hasOwnProperty(key)) {
233                 // if the key is something else then an event option
234                 if (!propRe.test(key)) {
235                     value = config[key];
236                     // if the value is a function it must be something like click: function(){}, scope: this
237                     // which means that there might be multiple event listeners with shared options
238                     if (Ext.isFunction(value)) {
239                         // shared options
240                         args = [element, key, value, config.scope, config];
241                     } else {
242                         // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
243                         args = [element, key, value.fn, value.scope, value];
244                     }
245
246                     if (isRemove === true) {
247                         me.removeListener.apply(this, args);
248                     } else {
249                         me.addListener.apply(me, args);
250                     }
251                 }
252             }
253         }
254     },
255
256 <span id='Ext-EventManager-method-normalizeEvent'>    /**
257 </span>     * Normalize cross browser event differences
258      * @private
259      * @param {Object} eventName The event name
260      * @param {Object} fn The function to execute
261      * @return {Object} The new event name/function
262      */
263     normalizeEvent: function(eventName, fn){
264         if (/mouseenter|mouseleave/.test(eventName) &amp;&amp; !Ext.supports.MouseEnterLeave) {
265             if (fn) {
266                 fn = Ext.Function.createInterceptor(fn, this.contains, this);
267             }
268             eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
269         } else if (eventName == 'mousewheel' &amp;&amp; !Ext.supports.MouseWheel &amp;&amp; !Ext.isOpera){
270             eventName = 'DOMMouseScroll';
271         }
272         return {
273             eventName: eventName,
274             fn: fn
275         };
276     },
277
278 <span id='Ext-EventManager-method-contains'>    /**
279 </span>     * Checks whether the event's relatedTarget is contained inside (or &lt;b&gt;is&lt;/b&gt;) the element.
280      * @private
281      * @param {Object} event
282      */
283     contains: function(event){
284         var parent = event.browserEvent.currentTarget,
285             child = this.getRelatedTarget(event);
286
287         if (parent &amp;&amp; parent.firstChild) {
288             while (child) {
289                 if (child === parent) {
290                     return false;
291                 }
292                 child = child.parentNode;
293                 if (child &amp;&amp; (child.nodeType != 1)) {
294                     child = null;
295                 }
296             }
297         }
298         return true;
299     },
300
301 <span id='Ext-EventManager-method-addListener'>    /**
302 </span>    * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
303     * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
304     * @param {String/HTMLElement} el The html element or id to assign the event handler to.
305     * @param {String} eventName The name of the event to listen for.
306     * @param {Function} handler The handler function the event invokes. This function is passed
307     * the following parameters:&lt;ul&gt;
308     * &lt;li&gt;evt : EventObject&lt;div class=&quot;sub-desc&quot;&gt;The {@link Ext.EventObject EventObject} describing the event.&lt;/div&gt;&lt;/li&gt;
309     * &lt;li&gt;t : Element&lt;div class=&quot;sub-desc&quot;&gt;The {@link Ext.core.Element Element} which was the target of the event.
310     * Note that this may be filtered by using the &lt;tt&gt;delegate&lt;/tt&gt; option.&lt;/div&gt;&lt;/li&gt;
311     * &lt;li&gt;o : Object&lt;div class=&quot;sub-desc&quot;&gt;The options object from the addListener call.&lt;/div&gt;&lt;/li&gt;
312     * &lt;/ul&gt;
313     * @param {Object} scope (optional) The scope (&lt;b&gt;&lt;code&gt;this&lt;/code&gt;&lt;/b&gt; reference) in which the handler function is executed. &lt;b&gt;Defaults to the Element&lt;/b&gt;.
314     * @param {Object} options (optional) An object containing handler configuration properties.
315     * This may contain any of the following properties:&lt;ul&gt;
316     * &lt;li&gt;scope : Object&lt;div class=&quot;sub-desc&quot;&gt;The scope (&lt;b&gt;&lt;code&gt;this&lt;/code&gt;&lt;/b&gt; reference) in which the handler function is executed. &lt;b&gt;Defaults to the Element&lt;/b&gt;.&lt;/div&gt;&lt;/li&gt;
317     * &lt;li&gt;delegate : String&lt;div class=&quot;sub-desc&quot;&gt;A simple selector to filter the target or look for a descendant of the target&lt;/div&gt;&lt;/li&gt;
318     * &lt;li&gt;stopEvent : Boolean&lt;div class=&quot;sub-desc&quot;&gt;True to stop the event. That is stop propagation, and prevent the default action.&lt;/div&gt;&lt;/li&gt;
319     * &lt;li&gt;preventDefault : Boolean&lt;div class=&quot;sub-desc&quot;&gt;True to prevent the default action&lt;/div&gt;&lt;/li&gt;
320     * &lt;li&gt;stopPropagation : Boolean&lt;div class=&quot;sub-desc&quot;&gt;True to prevent event propagation&lt;/div&gt;&lt;/li&gt;
321     * &lt;li&gt;normalized : Boolean&lt;div class=&quot;sub-desc&quot;&gt;False to pass a browser event to the handler function instead of an Ext.EventObject&lt;/div&gt;&lt;/li&gt;
322     * &lt;li&gt;delay : Number&lt;div class=&quot;sub-desc&quot;&gt;The number of milliseconds to delay the invocation of the handler after te event fires.&lt;/div&gt;&lt;/li&gt;
323     * &lt;li&gt;single : Boolean&lt;div class=&quot;sub-desc&quot;&gt;True to add a handler to handle just the next firing of the event, and then remove itself.&lt;/div&gt;&lt;/li&gt;
324     * &lt;li&gt;buffer : Number&lt;div class=&quot;sub-desc&quot;&gt;Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
325     * by the specified number of milliseconds. If the event fires again within that time, the original
326     * handler is &lt;em&gt;not&lt;/em&gt; invoked, but the new handler is scheduled in its place.&lt;/div&gt;&lt;/li&gt;
327     * &lt;li&gt;target : Element&lt;div class=&quot;sub-desc&quot;&gt;Only call the handler if the event was fired on the target Element, &lt;i&gt;not&lt;/i&gt; if the event was bubbled up from a child node.&lt;/div&gt;&lt;/li&gt;
328     * &lt;/ul&gt;&lt;br&gt;
329     * &lt;p&gt;See {@link Ext.core.Element#addListener} for examples of how to use these options.&lt;/p&gt;
330     */
331     addListener: function(element, eventName, fn, scope, options){
332         // Check if we've been passed a &quot;config style&quot; event.
333         if (typeof eventName !== 'string') {
334             this.prepareListenerConfig(element, eventName);
335             return;
336         }
337
338         var dom = Ext.getDom(element),
339             bind,
340             wrap;
341
342         //&lt;debug&gt;
343         if (!dom){
344             Ext.Error.raise({
345                 sourceClass: 'Ext.EventManager',
346                 sourceMethod: 'addListener',
347                 targetElement: element,
348                 eventName: eventName,
349                 msg: 'Error adding &quot;' + eventName + '\&quot; listener for nonexistent element &quot;' + element + '&quot;'
350             });
351         }
352         if (!fn) {
353             Ext.Error.raise({
354                 sourceClass: 'Ext.EventManager',
355                 sourceMethod: 'addListener',
356                 targetElement: element,
357                 eventName: eventName,
358                 msg: 'Error adding &quot;' + eventName + '\&quot; listener. The handler function is undefined.'
359             });
360         }
361         //&lt;/debug&gt;
362
363         // create the wrapper function
364         options = options || {};
365
366         bind = this.normalizeEvent(eventName, fn);
367         wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
368
369
370         if (dom.attachEvent) {
371             dom.attachEvent('on' + bind.eventName, wrap);
372         } else {
373             dom.addEventListener(bind.eventName, wrap, options.capture || false);
374         }
375
376         if (dom == document &amp;&amp; eventName == 'mousedown') {
377             this.stoppedMouseDownEvent.addListener(wrap);
378         }
379
380         // add all required data into the event cache
381         this.getEventListenerCache(dom, eventName).push({
382             fn: fn,
383             wrap: wrap,
384             scope: scope
385         });
386     },
387
388 <span id='Ext-EventManager-method-removeListener'>    /**
389 </span>    * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
390     * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
391     * @param {String/HTMLElement} el The id or html element from which to remove the listener.
392     * @param {String} eventName The name of the event.
393     * @param {Function} fn The handler function to remove. &lt;b&gt;This must be a reference to the function passed into the {@link #addListener} call.&lt;/b&gt;
394     * @param {Object} scope If a scope (&lt;b&gt;&lt;code&gt;this&lt;/code&gt;&lt;/b&gt; reference) was specified when the listener was added,
395     * then this must refer to the same object.
396     */
397     removeListener : function(element, eventName, fn, scope) {
398         // handle our listener config object syntax
399         if (typeof eventName !== 'string') {
400             this.prepareListenerConfig(element, eventName, true);
401             return;
402         }
403
404         var dom = Ext.getDom(element),
405             cache = this.getEventListenerCache(dom, eventName),
406             bindName = this.normalizeEvent(eventName).eventName,
407             i = cache.length, j,
408             listener, wrap, tasks;
409
410
411         while (i--) {
412             listener = cache[i];
413
414             if (listener &amp;&amp; (!fn || listener.fn == fn) &amp;&amp; (!scope || listener.scope === scope)) {
415                 wrap = listener.wrap;
416
417                 // clear buffered calls
418                 if (wrap.task) {
419                     clearTimeout(wrap.task);
420                     delete wrap.task;
421                 }
422
423                 // clear delayed calls
424                 j = wrap.tasks &amp;&amp; wrap.tasks.length;
425                 if (j) {
426                     while (j--) {
427                         clearTimeout(wrap.tasks[j]);
428                     }
429                     delete wrap.tasks;
430                 }
431
432                 if (dom.detachEvent) {
433                     dom.detachEvent('on' + bindName, wrap);
434                 } else {
435                     dom.removeEventListener(bindName, wrap, false);
436                 }
437
438                 if (wrap &amp;&amp; dom == document &amp;&amp; eventName == 'mousedown') {
439                     this.stoppedMouseDownEvent.removeListener(wrap);
440                 }
441
442                 // remove listener from cache
443                 Ext.Array.erase(cache, i, 1);
444             }
445         }
446     },
447
448 <span id='Ext-EventManager-method-removeAll'>    /**
449 </span>    * Removes all event handers from an element.  Typically you will use {@link Ext.core.Element#removeAllListeners}
450     * directly on an Element in favor of calling this version.
451     * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
452     */
453     removeAll : function(element){
454         var dom = Ext.getDom(element),
455             cache, ev;
456         if (!dom) {
457             return;
458         }
459         cache = this.getElementEventCache(dom);
460
461         for (ev in cache) {
462             if (cache.hasOwnProperty(ev)) {
463                 this.removeListener(dom, ev);
464             }
465         }
466         Ext.cache[dom.id].events = {};
467     },
468
469 <span id='Ext-EventManager-method-purgeElement'>    /**
470 </span>     * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
471      * directly on an Element in favor of calling this version.
472      * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
473      * @param {String} eventName (optional) The name of the event.
474      */
475     purgeElement : function(element, eventName) {
476         var dom = Ext.getDom(element),
477             i = 0, len;
478
479         if(eventName) {
480             this.removeListener(dom, eventName);
481         }
482         else {
483             this.removeAll(dom);
484         }
485
486         if(dom &amp;&amp; dom.childNodes) {
487             for(len = element.childNodes.length; i &lt; len; i++) {
488                 this.purgeElement(element.childNodes[i], eventName);
489             }
490         }
491     },
492
493 <span id='Ext-EventManager-method-createListenerWrap'>    /**
494 </span>     * Create the wrapper function for the event
495      * @private
496      * @param {HTMLElement} dom The dom element
497      * @param {String} ename The event name
498      * @param {Function} fn The function to execute
499      * @param {Object} scope The scope to execute callback in
500      * @param {Object} options The options
501      * @return {Function} the wrapper function
502      */
503     createListenerWrap : function(dom, ename, fn, scope, options) {
504         options = options || {};
505
506         var f, gen;
507
508         return function wrap(e, args) {
509             // Compile the implementation upon first firing
510             if (!gen) {
511                 f = ['if(!Ext) {return;}'];
512
513                 if(options.buffer || options.delay || options.freezeEvent) {
514                     f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
515                 } else {
516                     f.push('e = Ext.EventObject.setEvent(e);');
517                 }
518
519                 if (options.delegate) {
520                     f.push('var t = e.getTarget(&quot;' + options.delegate + '&quot;, this);');
521                     f.push('if(!t) {return;}');
522                 } else {
523                     f.push('var t = e.target;');
524                 }
525
526                 if (options.target) {
527                     f.push('if(e.target !== options.target) {return;}');
528                 }
529
530                 if(options.stopEvent) {
531                     f.push('e.stopEvent();');
532                 } else {
533                     if(options.preventDefault) {
534                         f.push('e.preventDefault();');
535                     }
536                     if(options.stopPropagation) {
537                         f.push('e.stopPropagation();');
538                     }
539                 }
540
541                 if(options.normalized === false) {
542                     f.push('e = e.browserEvent;');
543                 }
544
545                 if(options.buffer) {
546                     f.push('(wrap.task &amp;&amp; clearTimeout(wrap.task));');
547                     f.push('wrap.task = setTimeout(function(){');
548                 }
549
550                 if(options.delay) {
551                     f.push('wrap.tasks = wrap.tasks || [];');
552                     f.push('wrap.tasks.push(setTimeout(function(){');
553                 }
554
555                 // finally call the actual handler fn
556                 f.push('fn.call(scope || dom, e, t, options);');
557
558                 if(options.single) {
559                     f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
560                 }
561
562                 if(options.delay) {
563                     f.push('}, ' + options.delay + '));');
564                 }
565
566                 if(options.buffer) {
567                     f.push('}, ' + options.buffer + ');');
568                 }
569
570                 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
571             }
572
573             gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
574         };
575     },
576
577 <span id='Ext-EventManager-method-getEventListenerCache'>    /**
578 </span>     * Get the event cache for a particular element for a particular event
579      * @private
580      * @param {HTMLElement} element The element
581      * @param {Object} eventName The event name
582      * @return {Array} The events for the element
583      */
584     getEventListenerCache : function(element, eventName) {
585         if (!element) {
586             return [];
587         }
588         
589         var eventCache = this.getElementEventCache(element);
590         return eventCache[eventName] || (eventCache[eventName] = []);
591     },
592
593 <span id='Ext-EventManager-method-getElementEventCache'>    /**
594 </span>     * Gets the event cache for the object
595      * @private
596      * @param {HTMLElement} element The element
597      * @return {Object} The event cache for the object
598      */
599     getElementEventCache : function(element) {
600         if (!element) {
601             return {};
602         }
603         var elementCache = Ext.cache[this.getId(element)];
604         return elementCache.events || (elementCache.events = {});
605     },
606
607     // --------------------- utility methods ---------------------
608     mouseLeaveRe: /(mouseout|mouseleave)/,
609     mouseEnterRe: /(mouseover|mouseenter)/,
610
611 <span id='Ext-EventManager-method-stopEvent'>    /**
612 </span>     * Stop the event (preventDefault and stopPropagation)
613      * @param {Event} The event to stop
614      */
615     stopEvent: function(event) {
616         this.stopPropagation(event);
617         this.preventDefault(event);
618     },
619
620 <span id='Ext-EventManager-method-stopPropagation'>    /**
621 </span>     * Cancels bubbling of the event.
622      * @param {Event} The event to stop bubbling.
623      */
624     stopPropagation: function(event) {
625         event = event.browserEvent || event;
626         if (event.stopPropagation) {
627             event.stopPropagation();
628         } else {
629             event.cancelBubble = true;
630         }
631     },
632
633 <span id='Ext-EventManager-method-preventDefault'>    /**
634 </span>     * Prevents the browsers default handling of the event.
635      * @param {Event} The event to prevent the default
636      */
637     preventDefault: function(event) {
638         event = event.browserEvent || event;
639         if (event.preventDefault) {
640             event.preventDefault();
641         } else {
642             event.returnValue = false;
643             // Some keys events require setting the keyCode to -1 to be prevented
644             try {
645               // all ctrl + X and F1 -&gt; F12
646               if (event.ctrlKey || event.keyCode &gt; 111 &amp;&amp; event.keyCode &lt; 124) {
647                   event.keyCode = -1;
648               }
649             } catch (e) {
650                 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
651             }
652         }
653     },
654
655 <span id='Ext-EventManager-method-getRelatedTarget'>    /**
656 </span>     * Gets the related target from the event.
657      * @param {Object} event The event
658      * @return {HTMLElement} The related target.
659      */
660     getRelatedTarget: function(event) {
661         event = event.browserEvent || event;
662         var target = event.relatedTarget;
663         if (!target) {
664             if (this.mouseLeaveRe.test(event.type)) {
665                 target = event.toElement;
666             } else if (this.mouseEnterRe.test(event.type)) {
667                 target = event.fromElement;
668             }
669         }
670         return this.resolveTextNode(target);
671     },
672
673 <span id='Ext-EventManager-method-getPageX'>    /**
674 </span>     * Gets the x coordinate from the event
675      * @param {Object} event The event
676      * @return {Number} The x coordinate
677      */
678     getPageX: function(event) {
679         return this.getXY(event)[0];
680     },
681
682 <span id='Ext-EventManager-method-getPageY'>    /**
683 </span>     * Gets the y coordinate from the event
684      * @param {Object} event The event
685      * @return {Number} The y coordinate
686      */
687     getPageY: function(event) {
688         return this.getXY(event)[1];
689     },
690
691 <span id='Ext-EventManager-method-getPageXY'>    /**
692 </span>     * Gets the x &amp; ycoordinate from the event
693      * @param {Object} event The event
694      * @return {Array} The x/y coordinate
695      */
696     getPageXY: function(event) {
697         event = event.browserEvent || event;
698         var x = event.pageX,
699             y = event.pageY,
700             doc = document.documentElement,
701             body = document.body;
702
703         // pageX/pageY not available (undefined, not null), use clientX/clientY instead
704         if (!x &amp;&amp; x !== 0) {
705             x = event.clientX + (doc &amp;&amp; doc.scrollLeft || body &amp;&amp; body.scrollLeft || 0) - (doc &amp;&amp; doc.clientLeft || body &amp;&amp; body.clientLeft || 0);
706             y = event.clientY + (doc &amp;&amp; doc.scrollTop  || body &amp;&amp; body.scrollTop  || 0) - (doc &amp;&amp; doc.clientTop  || body &amp;&amp; body.clientTop  || 0);
707         }
708         return [x, y];
709     },
710
711 <span id='Ext-EventManager-method-getTarget'>    /**
712 </span>     * Gets the target of the event.
713      * @param {Object} event The event
714      * @return {HTMLElement} target
715      */
716     getTarget: function(event) {
717         event = event.browserEvent || event;
718         return this.resolveTextNode(event.target || event.srcElement);
719     },
720
721 <span id='Ext-EventManager-property-resolveTextNode'>    /**
722 </span>     * Resolve any text nodes accounting for browser differences.
723      * @private
724      * @param {HTMLElement} node The node
725      * @return {HTMLElement} The resolved node
726      */
727     // technically no need to browser sniff this, however it makes no sense to check this every time, for every event, whether the string is equal.
728     resolveTextNode: Ext.isGecko ?
729         function(node) {
730             if (!node) {
731                 return;
732             }
733             // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
734             var s = HTMLElement.prototype.toString.call(node);
735             if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
736                 return;
737             }
738                 return node.nodeType == 3 ? node.parentNode: node;
739             }: function(node) {
740                 return node &amp;&amp; node.nodeType == 3 ? node.parentNode: node;
741             },
742
743     // --------------------- custom event binding ---------------------
744
745     // Keep track of the current width/height
746     curWidth: 0,
747     curHeight: 0,
748
749 <span id='Ext-EventManager-method-onWindowResize'>    /**
750 </span>     * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
751      * passes new viewport width and height to handlers.
752      * @param {Function} fn      The handler function the window resize event invokes.
753      * @param {Object}   scope   The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the handler function executes. Defaults to the browser window.
754      * @param {boolean}  options Options object as passed to {@link Ext.core.Element#addListener}
755      */
756     onWindowResize: function(fn, scope, options){
757         var resize = this.resizeEvent;
758         if(!resize){
759             this.resizeEvent = resize = new Ext.util.Event();
760             this.on(window, 'resize', this.fireResize, this, {buffer: 100});
761         }
762         resize.addListener(fn, scope, options);
763     },
764
765 <span id='Ext-EventManager-method-fireResize'>    /**
766 </span>     * Fire the resize event.
767      * @private
768      */
769     fireResize: function(){
770         var me = this,
771             w = Ext.core.Element.getViewWidth(),
772             h = Ext.core.Element.getViewHeight();
773
774          //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
775          if(me.curHeight != h || me.curWidth != w){
776              me.curHeight = h;
777              me.curWidth = w;
778              me.resizeEvent.fire(w, h);
779          }
780     },
781
782 <span id='Ext-EventManager-method-removeResizeListener'>    /**
783 </span>     * Removes the passed window resize listener.
784      * @param {Function} fn        The method the event invokes
785      * @param {Object}   scope    The scope of handler
786      */
787     removeResizeListener: function(fn, scope){
788         if (this.resizeEvent) {
789             this.resizeEvent.removeListener(fn, scope);
790         }
791     },
792
793     onWindowUnload: function() {
794         var unload = this.unloadEvent;
795         if (!unload) {
796             this.unloadEvent = unload = new Ext.util.Event();
797             this.addListener(window, 'unload', this.fireUnload, this);
798         }
799     },
800
801 <span id='Ext-EventManager-method-fireUnload'>    /**
802 </span>     * Fires the unload event for items bound with onWindowUnload
803      * @private
804      */
805     fireUnload: function() {
806         // wrap in a try catch, could have some problems during unload
807         try {
808             this.removeUnloadListener();
809             // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
810             if (Ext.isGecko3) {
811                 var gridviews = Ext.ComponentQuery.query('gridview'),
812                     i = 0,
813                     ln = gridviews.length;
814                 for (; i &lt; ln; i++) {
815                     gridviews[i].scrollToTop();
816                 }
817             }
818             // Purge all elements in the cache
819             var el,
820                 cache = Ext.cache;
821             for (el in cache) {
822                 if (cache.hasOwnProperty(el)) {
823                     Ext.EventManager.removeAll(el);
824                 }
825             }
826         } catch(e) {
827         }
828     },
829
830 <span id='Ext-EventManager-method-removeUnloadListener'>    /**
831 </span>     * Removes the passed window unload listener.
832      * @param {Function} fn        The method the event invokes
833      * @param {Object}   scope    The scope of handler
834      */
835     removeUnloadListener: function(){
836         if (this.unloadEvent) {
837             this.removeListener(window, 'unload', this.fireUnload);
838         }
839     },
840
841 <span id='Ext-EventManager-property-useKeyDown'>    /**
842 </span>     * note 1: IE fires ONLY the keydown event on specialkey autorepeat
843      * note 2: Safari &lt; 3.1, Gecko (Mac/Linux) &amp; Opera fire only the keypress event on specialkey autorepeat
844      * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
845      * @private
846      */
847     useKeyDown: Ext.isWebKit ?
848                    parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) &gt;= 525 :
849                    !((Ext.isGecko &amp;&amp; !Ext.isWindows) || Ext.isOpera),
850
851 <span id='Ext-EventManager-method-getKeyEvent'>    /**
852 </span>     * Indicates which event to use for getting key presses.
853      * @return {String} The appropriate event name.
854      */
855     getKeyEvent: function(){
856         return this.useKeyDown ? 'keydown' : 'keypress';
857     }
858 };
859
860 <span id='Ext-method-onReady'>/**
861 </span> * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
862  * @member Ext
863  * @method onReady
864  */
865 Ext.onReady = function(fn, scope, options) {
866     Ext.Loader.onReady(fn, scope, true, options);
867 };
868
869 <span id='Ext-method-onDocumentReady'>/**
870 </span> * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
871  * @member Ext
872  * @method onDocumentReady
873  */
874 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
875
876 <span id='Ext-EventManager-method-on'>/**
877 </span> * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
878  * @member Ext.EventManager
879  * @method on
880  */
881 Ext.EventManager.on = Ext.EventManager.addListener;
882
883 <span id='Ext-EventManager-method-un'>/**
884 </span> * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
885  * @member Ext.EventManager
886  * @method un
887  */
888 Ext.EventManager.un = Ext.EventManager.removeListener;
889
890 (function(){
891     var initExtCss = function() {
892         // find the body element
893         var bd = document.body || document.getElementsByTagName('body')[0],
894             baseCSSPrefix = Ext.baseCSSPrefix,
895             cls = [baseCSSPrefix + 'body'],
896             htmlCls = [],
897             html;
898
899         if (!bd) {
900             return false;
901         }
902
903         html = bd.parentNode;
904
905         //Let's keep this human readable!
906         if (Ext.isIE) {
907             cls.push(baseCSSPrefix + 'ie');
908         }
909         if (Ext.isIE6) {
910             cls.push(baseCSSPrefix + 'ie6');
911         }
912         if (Ext.isIE7) {
913             cls.push(baseCSSPrefix + 'ie7');
914         }
915         if (Ext.isIE8) {
916             cls.push(baseCSSPrefix + 'ie8');
917         }
918         if (Ext.isIE9) {
919             cls.push(baseCSSPrefix + 'ie9');
920         }
921         if (Ext.isGecko) {
922             cls.push(baseCSSPrefix + 'gecko');
923         }
924         if (Ext.isGecko3) {
925             cls.push(baseCSSPrefix + 'gecko3');
926         }
927         if (Ext.isGecko4) {
928             cls.push(baseCSSPrefix + 'gecko4');
929         }
930         if (Ext.isOpera) {
931             cls.push(baseCSSPrefix + 'opera');
932         }
933         if (Ext.isWebKit) {
934             cls.push(baseCSSPrefix + 'webkit');
935         }
936         if (Ext.isSafari) {
937             cls.push(baseCSSPrefix + 'safari');
938         }
939         if (Ext.isSafari2) {
940             cls.push(baseCSSPrefix + 'safari2');
941         }
942         if (Ext.isSafari3) {
943             cls.push(baseCSSPrefix + 'safari3');
944         }
945         if (Ext.isSafari4) {
946             cls.push(baseCSSPrefix + 'safari4');
947         }
948         if (Ext.isChrome) {
949             cls.push(baseCSSPrefix + 'chrome');
950         }
951         if (Ext.isMac) {
952             cls.push(baseCSSPrefix + 'mac');
953         }
954         if (Ext.isLinux) {
955             cls.push(baseCSSPrefix + 'linux');
956         }
957         if (!Ext.supports.CSS3BorderRadius) {
958             cls.push(baseCSSPrefix + 'nbr');
959         }
960         if (!Ext.supports.CSS3LinearGradient) {
961             cls.push(baseCSSPrefix + 'nlg');
962         }
963         if (!Ext.scopeResetCSS) {
964             cls.push(baseCSSPrefix + 'reset');
965         }
966
967         // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
968         if (html) {
969             if (Ext.isStrict &amp;&amp; (Ext.isIE6 || Ext.isIE7)) {
970                 Ext.isBorderBox = false;
971             }
972             else {
973                 Ext.isBorderBox = true;
974             }
975
976             htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
977             if (!Ext.isStrict) {
978                 htmlCls.push(baseCSSPrefix + 'quirks');
979                 if (Ext.isIE &amp;&amp; !Ext.isStrict) {
980                     Ext.isIEQuirks = true;
981                 }
982             }
983             Ext.fly(html, '_internal').addCls(htmlCls);
984         }
985
986         Ext.fly(bd, '_internal').addCls(cls);
987         return true;
988     };
989
990     Ext.onReady(initExtCss);
991 })();
992 </pre>
993 </body>
994 </html>