/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 3.0.0 build: 1549 */ /* * DOM event listener abstraction layer * @module event * @submodule event-base */ (function() { // Unlike most of the library, this code has to be executed as soon as it is // introduced into the page -- and it should only be executed one time // regardless of the number of instances that use it. var GLOBAL_ENV = YUI.Env, C = YUI.config, D = C.doc, POLL_INTERVAL = C.pollInterval || 40, _ready = function(e) { GLOBAL_ENV._ready(); }; if (!GLOBAL_ENV._ready) { GLOBAL_ENV._ready = function() { if (!GLOBAL_ENV.DOMReady) { GLOBAL_ENV.DOMReady=true; // Remove the DOMContentLoaded (FF/Opera/Safari) if (D.removeEventListener) { D.removeEventListener("DOMContentLoaded", _ready, false); } } }; // create custom event /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */ // Internet Explorer: use the readyState of a defered script. // This isolates what appears to be a safe moment to manipulate // the DOM prior to when the document's readyState suggests // it is safe to do so. if (navigator.userAgent.match(/MSIE/)) { if (self !== self.top) { document.onreadystatechange = function() { if (document.readyState == 'complete') { document.onreadystatechange = null; _ready(); } }; } else { GLOBAL_ENV._dri = setInterval(function() { try { // throws an error if doc is not ready document.documentElement.doScroll('left'); clearInterval(GLOBAL_ENV._dri); GLOBAL_ENV._dri = null; _ready(); } catch (ex) { } }, POLL_INTERVAL); } // FireFox, Opera, Safari 3+: These browsers provide a event for this // moment. } else { D.addEventListener("DOMContentLoaded", _ready, false); } ///////////////////////////////////////////////////////////// } })(); YUI.add('event-base', function(Y) { (function() { /* * DOM event listener abstraction layer * @module event * @submodule event-base */ var GLOBAL_ENV = YUI.Env, yready = function() { Y.fire('domready'); }; Y.publish('domready', { fireOnce: true }); if (GLOBAL_ENV.DOMReady) { // console.log('DOMReady already fired', 'info', 'event'); yready(); } else { // console.log('setting up before listener', 'info', 'event'); // console.log('env: ' + YUI.Env.windowLoaded, 'info', 'event'); Y.before(yready, GLOBAL_ENV, "_ready"); } })(); (function() { /** * Custom event engine, DOM event listener abstraction layer, synthetic DOM * events. * @module event * @submodule event-base */ /** * Wraps a DOM event, properties requiring browser abstraction are * fixed here. Provids a security layer when required. * @class DOMEventFacade * @param ev {Event} the DOM event * @param currentTarget {HTMLElement} the element the listener was attached to * @param wrapper {Event.Custom} the custom event wrapper for this DOM event */ /* * @TODO constants? LEFTBUTTON, MIDDLEBUTTON, RIGHTBUTTON, keys */ /* var whitelist = { altKey : 1, // "button" : 1, // we supply // "bubbles" : 1, // needed? // "cancelable" : 1, // needed? // "charCode" : 1, // we supply cancelBubble : 1, // "currentTarget" : 1, // we supply ctrlKey : 1, clientX : 1, // needed? clientY : 1, // needed? detail : 1, // not fully implemented // "fromElement" : 1, keyCode : 1, // "height" : 1, // needed? // "initEvent" : 1, // need the init events? // "initMouseEvent" : 1, // "initUIEvent" : 1, // "layerX" : 1, // needed? // "layerY" : 1, // needed? metaKey : 1, // "modifiers" : 1, // needed? // "offsetX" : 1, // needed? // "offsetY" : 1, // needed? // "preventDefault" : 1, // we supply // "reason" : 1, // IE proprietary // "relatedTarget" : 1, // "returnValue" : 1, // needed? shiftKey : 1, // "srcUrn" : 1, // IE proprietary // "srcElement" : 1, // "srcFilter" : 1, IE proprietary // "stopPropagation" : 1, // we supply // "target" : 1, // "timeStamp" : 1, // needed? // "toElement" : 1, type : 1, // "view" : 1, // "which" : 1, // we supply // "width" : 1, // needed? x : 1, y : 1 }, */ var ua = Y.UA, /** * webkit key remapping required for Safari < 3.1 * @property webkitKeymap * @private */ webkitKeymap = { 63232: 38, // up 63233: 40, // down 63234: 37, // left 63235: 39, // right 63276: 33, // page up 63277: 34, // page down 25: 9, // SHIFT-TAB (Safari provides a different key code in // this case, even though the shiftKey modifier is set) 63272: 46, // delete 63273: 36, // home 63275: 35 // end }, /** * Returns a wrapped node. Intended to be used on event targets, * so it will return the node's parent if the target is a text * node. * * If accessing a property of the node throws an error, this is * probably the anonymous div wrapper Gecko adds inside text * nodes. This likely will only occur when attempting to access * the relatedTarget. In this case, we now return null because * the anonymous div is completely useless and we do not know * what the related target was because we can't even get to * the element's parent node. * * @method resolve * @private */ resolve = function(n) { try { if (n && 3 == n.nodeType) { n = n.parentNode; } } catch(e) { return null; } return Y.one(n); }; // provide a single event with browser abstractions resolved // // include all properties for both browers? // include only DOM2 spec properties? // provide browser-specific facade? Y.DOMEventFacade = function(ev, currentTarget, wrapper) { wrapper = wrapper || {}; var e = ev, ot = currentTarget, d = Y.config.doc, b = d.body, x = e.pageX, y = e.pageY, c, t; this.altKey = e.altKey; this.ctrlKey = e.ctrlKey; this.metaKey = e.metaKey; this.shiftKey = e.shiftKey; this.type = e.type; this.clientX = e.clientX; this.clientY = e.clientY; ////////////////////////////////////////////////////// if (!x && 0 !== x) { x = e.clientX || 0; y = e.clientY || 0; if (ua.ie) { x += Math.max(d.documentElement.scrollLeft, b.scrollLeft); y += Math.max(d.documentElement.scrollTop, b.scrollTop); } } this._yuifacade = true; /** * The native event * @property _event */ this._event = e; /** * The X location of the event on the page (including scroll) * @property pageX * @type int */ this.pageX = x; /** * The Y location of the event on the page (including scroll) * @property pageY * @type int */ this.pageY = y; ////////////////////////////////////////////////////// c = e.keyCode || e.charCode || 0; if (ua.webkit && (c in webkitKeymap)) { c = webkitKeymap[c]; } /** * The keyCode for key events. Uses charCode if keyCode is not available * @property keyCode * @type int */ this.keyCode = c; /** * The charCode for key events. Same as keyCode * @property charCode * @type int */ this.charCode = c; ////////////////////////////////////////////////////// /** * The button that was pushed. * @property button * @type int */ this.button = e.which || e.button; /** * The button that was pushed. Same as button. * @property which * @type int */ this.which = this.button; ////////////////////////////////////////////////////// /** * Node reference for the targeted element * @propery target * @type Node */ this.target = resolve(e.target || e.srcElement); /** * Node reference for the element that the listener was attached to. * @propery currentTarget * @type Node */ this.currentTarget = resolve(ot); t = e.relatedTarget; if (!t) { if (e.type == "mouseout") { t = e.toElement; } else if (e.type == "mouseover") { t = e.fromElement; } } /** * Node reference to the relatedTarget * @propery relatedTarget * @type Node */ this.relatedTarget = resolve(t); /** * Number representing the direction and velocity of the movement of the mousewheel. * Negative is down, the higher the number, the faster. Applies to the mousewheel event. * @property wheelDelta * @type int */ if (e.type == "mousewheel" || e.type == "DOMMouseScroll") { this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1); } ////////////////////////////////////////////////////// // methods /** * Stops the propagation to the next bubble target * @method stopPropagation */ this.stopPropagation = function() { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } wrapper.stopped = 1; }; /** * Stops the propagation to the next bubble target and * prevents any additional listeners from being exectued * on the current target. * @method stopImmediatePropagation */ this.stopImmediatePropagation = function() { if (e.stopImmediatePropagation) { e.stopImmediatePropagation(); } else { this.stopPropagation(); } wrapper.stopped = 2; }; /** * Prevents the event's default behavior * @method preventDefault * @param returnValue {string} sets the returnValue of the event to this value * (rather than the default false value). This can be used to add a customized * confirmation query to the beforeunload event). */ this.preventDefault = function(returnValue) { if (e.preventDefault) { e.preventDefault(); } e.returnValue = returnValue || false; wrapper.prevented = 1; }; /** * Stops the event propagation and prevents the default * event behavior. * @method halt * @param immediate {boolean} if true additional listeners * on the current target will not be executed */ this.halt = function(immediate) { if (immediate) { this.stopImmediatePropagation(); } else { this.stopPropagation(); } this.preventDefault(); }; }; })(); (function() { /** * DOM event listener abstraction layer * @module event * @submodule event-base */ /** * The event utility provides functions to add and remove event listeners, * event cleansing. It also tries to automatically remove listeners it * registers during the unload event. * * @class Event * @static */ Y.Env.evt.dom_wrappers = {}; Y.Env.evt.dom_map = {}; var _eventenv = Y.Env.evt, add = YUI.Env.add, remove = YUI.Env.remove, onLoad = function() { YUI.Env.windowLoaded = true; Y.Event._load(); remove(window, "load", onLoad); }, onUnload = function() { Y.Event._unload(); remove(window, "unload", onUnload); }, EVENT_READY = 'domready', COMPAT_ARG = '~yui|2|compat~', shouldIterate = function(o) { try { return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !o.alert); } catch(ex) { return false; } }, Event = function() { /** * True after the onload event has fired * @property _loadComplete * @type boolean * @static * @private */ var _loadComplete = false, /** * The number of times to poll after window.onload. This number is * increased if additional late-bound handlers are requested after * the page load. * @property _retryCount * @static * @private */ _retryCount = 0, /** * onAvailable listeners * @property _avail * @static * @private */ _avail = [], /** * Custom event wrappers for DOM events. Key is * 'event:' + Element uid stamp + event type * @property _wrappers * @type Y.Event.Custom * @static * @private */ _wrappers = _eventenv.dom_wrappers, _windowLoadKey = null, /** * Custom event wrapper map DOM events. Key is * Element uid stamp. Each item is a hash of custom event * wrappers as provided in the _wrappers collection. This * provides the infrastructure for getListeners. * @property _el_events * @static * @private */ _el_events = _eventenv.dom_map; return { /** * The number of times we should look for elements that are not * in the DOM at the time the event is requested after the document * has been loaded. The default is 1000@amp;40 ms, so it will poll * for 40 seconds or until all outstanding handlers are bound * (whichever comes first). * @property POLL_RETRYS * @type int * @static * @final */ POLL_RETRYS: 1000, /** * The poll interval in milliseconds * @property POLL_INTERVAL * @type int * @static * @final */ POLL_INTERVAL: 40, /** * addListener/removeListener can throw errors in unexpected scenarios. * These errors are suppressed, the method returns false, and this property * is set * @property lastError * @static * @type Error */ lastError: null, /** * poll handle * @property _interval * @static * @private */ _interval: null, /** * document readystate poll handle * @property _dri * @static * @private */ _dri: null, /** * True when the document is initially usable * @property DOMReady * @type boolean * @static */ DOMReady: false, /** * @method startInterval * @static * @private */ startInterval: function() { var E = Y.Event; if (!E._interval) { E._interval = setInterval(Y.bind(E._poll, E), E.POLL_INTERVAL); } }, /** * Executes the supplied callback when the item with the supplied * id is found. This is meant to be used to execute behavior as * soon as possible as the page loads. If you use this after the * initial page load it will poll for a fixed time for the element. * The number of times it will poll and the frequency are * configurable. By default it will poll for 10 seconds. * *
The callback is executed with a single parameter: * the custom object parameter, if provided.
* * @method onAvailable * * @param {string||string[]} id the id of the element, or an array * of ids to look for. * @param {function} fn what to execute when the element is found. * @param {object} p_obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} p_override If set to true, fn will execute * in the context of p_obj, if set to an object it * will execute in the context of that object * @param checkContent {boolean} check child node readiness (onContentReady) * @static * @deprecated Use Y.on("available") */ // @TODO fix arguments onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) { var a = Y.Array(id), i, availHandle; for (i=0; i