/** * @class Ext.EventManager * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides * several useful events directly. * See {@link Ext.EventObject} for more details on normalized event objects. * @singleton */ Ext.EventManager = { optionsRe: /^(?:capture|scope|delay|buffer|single|stopEvent|disableLocking|preventDefault|stopPropagation|normalized|args|delegate|horizontal|vertical|dragThreshold|holdThreshold|doubleTapThreshold|cancelThreshold|singleTapThreshold|fireClickEvent)$/, touchRe: /^(?:pinch|pinchstart|pinchend|tap|singletap|doubletap|swipe|swipeleft|swiperight|drag|dragstart|dragend|touchdown|touchstart|touchmove|touchend|taphold|tapstart|tapcancel)$/i,
/** * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The html element or id to assign the event handler to. * @param {String} eventName The name of the event to listen for. * @param {Function} handler The handler function the event invokes. This function is passed * the following parameters: * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. Defaults to the Element. * @param {Object} options (optional) An object containing handler configuration properties. * This may contain any of the following properties:
*

See {@link Ext.Element#addListener} for examples of how to use these options.

*/ addListener : function(element, eventName, fn, scope, o){ // handle our listener config object syntax if (Ext.isObject(eventName)) { this.handleListenerConfig(element, eventName); return; } var dom = Ext.getDom(element); // if the element doesnt exist throw an error if (!dom){ throw "Error listening for \"" + eventName + '\". Element "' + element + '" doesn\'t exist.'; } if (!fn) { throw 'Error listening for "' + eventName + '". No handler function specified'; } var touch = this.touchRe.test(eventName); // create the wrapper function var wrap = this.createListenerWrap(dom, eventName, fn, scope, o, touch); // add all required data into the event cache this.getEventListenerCache(dom, eventName).push({ fn: fn, wrap: wrap, scope: scope }); if (touch) { Ext.gesture.Manager.addEventListener(dom, eventName, wrap, o); } else { // now add the event listener to the actual element! o = o || {}; dom.addEventListener(eventName, wrap, o.capture || false); } },
/** * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The id or html element from which to remove the listener. * @param {String} eventName The name of the event. * @param {Function} fn The handler function to remove. This must be a reference to the function passed into the {@link #addListener} call. * @param {Object} scope If a scope (this reference) was specified when the listener was added, * then this must refer to the same object. */ removeListener : function(element, eventName, fn, scope) { // handle our listener config object syntax if (Ext.isObject(eventName)) { this.handleListenerConfig(element, eventName, true); return; } var dom = Ext.getDom(element), cache = this.getEventListenerCache(dom, eventName), i = cache.length, j, listener, wrap, tasks; while (i--) { listener = cache[i]; if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) { wrap = listener.wrap; // clear buffered calls if (wrap.task) { clearTimeout(wrap.task); delete wrap.task; } // clear delayed calls j = wrap.tasks && wrap.tasks.length; if (j) { while (j--) { clearTimeout(wrap.tasks[j]); } delete wrap.tasks; } if (this.touchRe.test(eventName)) { Ext.gesture.Manager.removeEventListener(dom, eventName, wrap); } else { // now add the event listener to the actual element! dom.removeEventListener(eventName, wrap, false); } // remove listener from cache cache.splice(i, 1); } } },
/** * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners} * directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The id or html element from which to remove all event handlers. */ removeAll : function(element){ var dom = Ext.getDom(element), cache = this.getElementEventCache(dom), ev; for (ev in cache) { if (!cache.hasOwnProperty(ev)) { continue; } this.removeListener(dom, ev); } Ext.cache[dom.id].events = {}; }, purgeElement : function(element, recurse, eventName) { var dom = Ext.getDom(element), i = 0, len; if(eventName) { this.removeListener(dom, eventName); } else { this.removeAll(dom); } if(recurse && dom && dom.childNodes) { for(len = element.childNodes.length; i < len; i++) { this.purgeElement(element.childNodes[i], recurse, eventName); } } }, handleListenerConfig : function(element, config, remove) { var key, value; // loop over all the keys in the object for (key in config) { if (!config.hasOwnProperty(key)) { continue; } // if the key is something else then an event option if (!this.optionsRe.test(key)) { value = config[key]; // if the value is a function it must be something like click: function(){}, scope: this // which means that there might be multiple event listeners with shared options if (Ext.isFunction(value)) { // shared options this[(remove ? 'remove' : 'add') + 'Listener'](element, key, value, config.scope, config); } // if its not a function, it must be an object like click: {fn: function(){}, scope: this} else { // individual options this[(remove ? 'remove' : 'add') + 'Listener'](element, key, config.fn, config.scope, config); } } } }, getId : function(element) { // if we bind listeners to either the document or the window // we have to put them in their own id cache since they both // can't get id's on the actual element var skip = false, id; element = Ext.getDom(element); if (element === document || element === window) { skip = true; } id = Ext.id(element); if (!Ext.cache[id]){ Ext.Element.addToCache(new Ext.Element(element), id); if(skip){ Ext.cache[id].skipGarbageCollection = true; } } return id; }, // private createListenerWrap : function(dom, ename, fn, scope, o, touch) { o = !Ext.isObject(o) ? {} : o; var f = ["if(!window.Ext) {return;}"]; if (touch) { f.push('e = new Ext.TouchEventObjectImpl(e, args);'); } else { if(o.buffer || o.delay) { f.push('e = new Ext.EventObjectImpl(e);'); } else { f.push('e = Ext.EventObject.setEvent(e);'); } } if (o.delegate) { f.push('var t = e.getTarget("' + o.delegate + '", this);'); f.push('if(!t) {return;}'); } else { f.push('var t = e.target;'); } if (o.target) { f.push('if(e.target !== o.target) {return;}'); } if(o.stopEvent) { f.push('e.stopEvent();'); } else { if(o.preventDefault) { f.push('e.preventDefault();'); } if(o.stopPropagation) { f.push('e.stopPropagation();'); } } if(o.normalized === false) { f.push('e = e.browserEvent;'); } if(o.buffer) { f.push('(wrap.task && clearTimeout(wrap.task));'); f.push('wrap.task = setTimeout(function(){'); } if(o.delay) { f.push('wrap.tasks = wrap.tasks || [];'); f.push('wrap.tasks.push(setTimeout(function(){'); } // finally call the actual handler fn f.push('fn.call(scope || dom, e, t, o);'); if(o.single) { f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);'); } if(o.delay) { f.push('}, ' + o.delay + '));'); } if(o.buffer) { f.push('}, ' + o.buffer + ');'); } var gen = new Function('e', 'o', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join("\n")); return function(e, args) { gen.call(dom, e, o, fn, scope, ename, dom, arguments.callee, args); }; }, getEventListenerCache : function(element, eventName) { var eventCache = this.getElementEventCache(element); return eventCache[eventName] || (eventCache[eventName] = []); }, getElementEventCache : function(element) { var elementCache = Ext.cache[this.getId(element)]; return elementCache.events || (elementCache.events = {}); },
/** * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be * accessed shorthanded as Ext.onReady(). * @param {Function} fn The method the event invokes. * @param {Object} scope (optional) The scope (this reference) in which the handler function executes. Defaults to the browser window. * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options * {single: true} be used so that the handler is removed on first invocation. */ onDocumentReady : function(fn, scope, options){ var me = this, readyEvent = me.readyEvent, intervalId; if(Ext.isReady){ // if it already fired readyEvent || (readyEvent = new Ext.util.Event()); readyEvent.addListener(fn, scope, options); readyEvent.fire(); readyEvent.listeners = []; // clearListeners no longer compatible. Force single: true? } else { if(!readyEvent) { readyEvent = me.readyEvent = new Ext.util.Event(); // the method that will actually fire the event and clean up any listeners and intervals var fireReady = function() { Ext.isReady = true; //document.removeEventListener('DOMContentLoaded', arguments.callee, false); window.removeEventListener('load', arguments.callee, false); // remove interval if there is one if (intervalId) { clearInterval(intervalId); } // Put this in a timeout to give the browser a chance to hide address // bars or do other things that would screw up viewport measurements setTimeout(function() { Ext.supports.init(); //Ext.TouchEventManager.init(); Ext.gesture.Manager.init(); Ext.orientation = Ext.Element.getOrientation(); // fire the ready event!! readyEvent.fire({ orientation: Ext.orientation, width: Ext.Element.getViewportWidth(), height: Ext.Element.getViewportHeight() }); readyEvent.listeners = []; }, 50); }; // for browsers that support DOMContentLoaded //document.addEventListener('DOMContentLoaded', fireReady, false); // // even though newer versions support DOMContentLoaded, we have to be sure intervalId = setInterval(function(){ if(/loaded|complete/.test(document.readyState)) { clearInterval(intervalId); intervalId = null; fireReady(); } }, 10); // final fallback method window.addEventListener('load', fireReady, false); } options = options || {}; options.delay = options.delay || 1; readyEvent.addListener(fn, scope, options); } },
/** * Adds a listener to be notified when the browser window is resized and provides resize event buffering (50 milliseconds), * passes new viewport width and height to handlers. * @param {Function} fn The handler function the window resize event invokes. * @param {Object} scope The scope (this reference) in which the handler function executes. Defaults to the browser window. * @param {boolean} options Options object as passed to {@link Ext.Element#addListener} */ onWindowResize : function(fn, scope, options) { var me = this, resizeEvent = me.resizeEvent; if(!resizeEvent){ me.resizeEvent = resizeEvent = new Ext.util.Event(); var onResize = function() { resizeEvent.fire(Ext.Element.getViewportWidth(), Ext.Element.getViewportHeight()); }; this.addListener(window, 'resize', onResize, this); } resizeEvent.addListener(fn, scope, options); }, onOrientationChange : function(fn, scope, options) { var me = this, orientationEvent = me.orientationEvent; if (!orientationEvent) { me.orientationEvent = orientationEvent = new Ext.util.Event(); var onOrientationChange = function(viewport, size) { Ext.orientation = Ext.Viewport.getOrientation(); orientationEvent.fire(Ext.orientation, size.width, size.height); }; Ext.Viewport.on('resize', onOrientationChange, this); } orientationEvent.addListener(fn, scope, options); }, unOrientationChange : function(fn, scope, options) { var me = this, orientationEvent = me.orientationEvent; if (orientationEvent) { orientationEvent.removeListener(fn, scope, options); } } };
/** * Appends an event handler to an element. Shorthand for {@link #addListener}. * @param {String/HTMLElement} el The html element or id to assign the event handler to * @param {String} eventName The name of the event to listen for. * @param {Function} handler The handler function the event invokes. * @param {Object} scope (optional) (this reference) in which the handler function executes. Defaults to the Element. * @param {Object} options (optional) An object containing standard {@link #addListener} options * @member Ext.EventManager * @method on */ Ext.EventManager.on = Ext.EventManager.addListener;
/** * Removes an event handler from an element. Shorthand for {@link #removeListener}. * @param {String/HTMLElement} el The id or html element from which to remove the listener. * @param {String} eventName The name of the event. * @param {Function} fn The handler function to remove. This must be a reference to the function passed into the {@link #on} call. * @param {Object} scope If a scope (this reference) was specified when the listener was added, * then this must refer to the same object. * @member Ext.EventManager * @method un */ Ext.EventManager.un = Ext.EventManager.removeListener;
/** * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}. * @param {Function} fn The method the event invokes. * @param {Object} scope (optional) The scope (this reference) in which the handler function executes. Defaults to the browser window. * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options * {single: true} be used so that the handler is removed on first invocation. * @member Ext * @method onReady */ Ext.onReady = Ext.EventManager.onDocumentReady; Ext.EventObjectImpl = Ext.extend(Object, { constructor : function(e) { if (e) { this.setEvent(e.browserEvent || e); } }, /** @private */ setEvent : function(e){ var me = this; if (e == me || (e && e.browserEvent)){ // already wrapped return e; } me.browserEvent = e; if(e) { me.type = e.type; // cache the target for the delayed and or buffered events var node = e.target; me.target = node && node.nodeType == 3 ? node.parentNode : node; // same for XY me.xy = [e.pageX, e.pageY]; me.timestamp = e.timeStamp; } else { me.target = null; me.xy = [0, 0]; } return me; },
/** * Stop the event (preventDefault and stopPropagation) */ stopEvent : function(){ this.stopPropagation(); this.preventDefault(); },
/** * Prevents the browsers default handling of the event. */ preventDefault : function(){ if(this.browserEvent) { this.browserEvent.preventDefault(); } },
/** * Cancels bubbling of the event. */ stopPropagation : function() { if(this.browserEvent) { this.browserEvent.stopPropagation(); } },
/** * Gets the x coordinate of the event. * @return {Number} */ getPageX : function(){ return this.xy[0]; },
/** * Gets the y coordinate of the event. * @return {Number} */ getPageY : function(){ return this.xy[1]; },
/** * Gets the page coordinates of the event. * @return {Array} The xy values like [x, y] */ getXY : function(){ return this.xy; },
/** * Gets the target for the event. * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body) * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node * @return {HTMLelement} */ getTarget : function(selector, maxDepth, returnEl) { return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target); }, getTime : function() { return this.timestamp; } });
/** * @class Ext.EventObject * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject * wraps the browser's native event-object normalizing cross-browser differences, * such as which mouse button is clicked, keys pressed, mechanisms to stop * event-propagation along with a method to prevent default actions from taking place. *

For example:

*

function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
    e.preventDefault();
    var target = e.getTarget(); // same as t (the target HTMLElement)
    ...
}
var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
myDiv.on(         // 'on' is shorthand for addListener
    "click",      // perform an action on click of myDiv
    handleClick   // reference to the action handler
);
// other methods to do the same:
Ext.EventManager.on("myDiv", 'click', handleClick);
Ext.EventManager.addListener("myDiv", 'click', handleClick);
 
* @singleton */ Ext.EventObject = new Ext.EventObjectImpl();