/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.7.0 */ (function () { /** * Config is a utility used within an Object to allow the implementer to * maintain a list of local configuration properties and listen for changes * to those properties dynamically using CustomEvent. The initial values are * also maintained so that the configuration can be reset at any given point * to its initial state. * @namespace YAHOO.util * @class Config * @constructor * @param {Object} owner The owner Object to which this Config Object belongs */ YAHOO.util.Config = function (owner) { if (owner) { this.init(owner); } }; var Lang = YAHOO.lang, CustomEvent = YAHOO.util.CustomEvent, Config = YAHOO.util.Config; /** * Constant representing the CustomEvent type for the config changed event. * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT * @private * @static * @final */ Config.CONFIG_CHANGED_EVENT = "configChanged"; /** * Constant representing the boolean type string * @property YAHOO.util.Config.BOOLEAN_TYPE * @private * @static * @final */ Config.BOOLEAN_TYPE = "boolean"; Config.prototype = { /** * Object reference to the owner of this Config Object * @property owner * @type Object */ owner: null, /** * Boolean flag that specifies whether a queue is currently * being executed * @property queueInProgress * @type Boolean */ queueInProgress: false, /** * Maintains the local collection of configuration property objects and * their specified values * @property config * @private * @type Object */ config: null, /** * Maintains the local collection of configuration property objects as * they were initially applied. * This object is used when resetting a property. * @property initialConfig * @private * @type Object */ initialConfig: null, /** * Maintains the local, normalized CustomEvent queue * @property eventQueue * @private * @type Object */ eventQueue: null, /** * Custom Event, notifying subscribers when Config properties are set * (setProperty is called without the silent flag * @event configChangedEvent */ configChangedEvent: null, /** * Initializes the configuration Object and all of its local members. * @method init * @param {Object} owner The owner Object to which this Config * Object belongs */ init: function (owner) { this.owner = owner; this.configChangedEvent = this.createEvent(Config.CONFIG_CHANGED_EVENT); this.configChangedEvent.signature = CustomEvent.LIST; this.queueInProgress = false; this.config = {}; this.initialConfig = {}; this.eventQueue = []; }, /** * Validates that the value passed in is a Boolean. * @method checkBoolean * @param {Object} val The value to validate * @return {Boolean} true, if the value is valid */ checkBoolean: function (val) { return (typeof val == Config.BOOLEAN_TYPE); }, /** * Validates that the value passed in is a number. * @method checkNumber * @param {Object} val The value to validate * @return {Boolean} true, if the value is valid */ checkNumber: function (val) { return (!isNaN(val)); }, /** * Fires a configuration property event using the specified value. * @method fireEvent * @private * @param {String} key The configuration property's name * @param {value} Object The value of the correct type for the property */ fireEvent: function ( key, value ) { var property = this.config[key]; if (property && property.event) { property.event.fire(value); } }, /** * Adds a property to the Config Object's private config hash. * @method addProperty * @param {String} key The configuration property's name * @param {Object} propertyObject The Object containing all of this * property's arguments */ addProperty: function ( key, propertyObject ) { key = key.toLowerCase(); this.config[key] = propertyObject; propertyObject.event = this.createEvent(key, { scope: this.owner }); propertyObject.event.signature = CustomEvent.LIST; propertyObject.key = key; if (propertyObject.handler) { propertyObject.event.subscribe(propertyObject.handler, this.owner); } this.setProperty(key, propertyObject.value, true); if (! propertyObject.suppressEvent) { this.queueProperty(key, propertyObject.value); } }, /** * Returns a key-value configuration map of the values currently set in * the Config Object. * @method getConfig * @return {Object} The current config, represented in a key-value map */ getConfig: function () { var cfg = {}, currCfg = this.config, prop, property; for (prop in currCfg) { if (Lang.hasOwnProperty(currCfg, prop)) { property = currCfg[prop]; if (property && property.event) { cfg[prop] = property.value; } } } return cfg; }, /** * Returns the value of specified property. * @method getProperty * @param {String} key The name of the property * @return {Object} The value of the specified property */ getProperty: function (key) { var property = this.config[key.toLowerCase()]; if (property && property.event) { return property.value; } else { return undefined; } }, /** * Resets the specified property's value to its initial value. * @method resetProperty * @param {String} key The name of the property * @return {Boolean} True is the property was reset, false if not */ resetProperty: function (key) { key = key.toLowerCase(); var property = this.config[key]; if (property && property.event) { if (this.initialConfig[key] && !Lang.isUndefined(this.initialConfig[key])) { this.setProperty(key, this.initialConfig[key]); return true; } } else { return false; } }, /** * Sets the value of a property. If the silent property is passed as * true, the property's event will not be fired. * @method setProperty * @param {String} key The name of the property * @param {String} value The value to set the property to * @param {Boolean} silent Whether the value should be set silently, * without firing the property event. * @return {Boolean} True, if the set was successful, false if it failed. */ setProperty: function (key, value, silent) { var property; key = key.toLowerCase(); if (this.queueInProgress && ! silent) { // Currently running through a queue... this.queueProperty(key,value); return true; } else { property = this.config[key]; if (property && property.event) { if (property.validator && !property.validator(value)) { return false; } else { property.value = value; if (! silent) { this.fireEvent(key, value); this.configChangedEvent.fire([key, value]); } return true; } } else { return false; } } }, /** * Sets the value of a property and queues its event to execute. If the * event is already scheduled to execute, it is * moved from its current position to the end of the queue. * @method queueProperty * @param {String} key The name of the property * @param {String} value The value to set the property to * @return {Boolean} true, if the set was successful, false if * it failed. */ queueProperty: function (key, value) { key = key.toLowerCase(); var property = this.config[key], foundDuplicate = false, iLen, queueItem, queueItemKey, queueItemValue, sLen, supercedesCheck, qLen, queueItemCheck, queueItemCheckKey, queueItemCheckValue, i, s, q; if (property && property.event) { if (!Lang.isUndefined(value) && property.validator && !property.validator(value)) { // validator return false; } else { if (!Lang.isUndefined(value)) { property.value = value; } else { value = property.value; } foundDuplicate = false; iLen = this.eventQueue.length; for (i = 0; i < iLen; i++) { queueItem = this.eventQueue[i]; if (queueItem) { queueItemKey = queueItem[0]; queueItemValue = queueItem[1]; if (queueItemKey == key) { /* found a dupe... push to end of queue, null current item, and break */ this.eventQueue[i] = null; this.eventQueue.push( [key, (!Lang.isUndefined(value) ? value : queueItemValue)]); foundDuplicate = true; break; } } } // this is a refire, or a new property in the queue if (! foundDuplicate && !Lang.isUndefined(value)) { this.eventQueue.push([key, value]); } } if (property.supercedes) { sLen = property.supercedes.length; for (s = 0; s < sLen; s++) { supercedesCheck = property.supercedes[s]; qLen = this.eventQueue.length; for (q = 0; q < qLen; q++) { queueItemCheck = this.eventQueue[q]; if (queueItemCheck) { queueItemCheckKey = queueItemCheck[0]; queueItemCheckValue = queueItemCheck[1]; if (queueItemCheckKey == supercedesCheck.toLowerCase() ) { this.eventQueue.push([queueItemCheckKey, queueItemCheckValue]); this.eventQueue[q] = null; break; } } } } } return true; } else { return false; } }, /** * Fires the event for a property using the property's current value. * @method refireEvent * @param {String} key The name of the property */ refireEvent: function (key) { key = key.toLowerCase(); var property = this.config[key]; if (property && property.event && !Lang.isUndefined(property.value)) { if (this.queueInProgress) { this.queueProperty(key); } else { this.fireEvent(key, property.value); } } }, /** * Applies a key-value Object literal to the configuration, replacing * any existing values, and queueing the property events. * Although the values will be set, fireQueue() must be called for their * associated events to execute. * @method applyConfig * @param {Object} userConfig The configuration Object literal * @param {Boolean} init When set to true, the initialConfig will * be set to the userConfig passed in, so that calling a reset will * reset the properties to the passed values. */ applyConfig: function (userConfig, init) { var sKey, oConfig; if (init) { oConfig = {}; for (sKey in userConfig) { if (Lang.hasOwnProperty(userConfig, sKey)) { oConfig[sKey.toLowerCase()] = userConfig[sKey]; } } this.initialConfig = oConfig; } for (sKey in userConfig) { if (Lang.hasOwnProperty(userConfig, sKey)) { this.queueProperty(sKey, userConfig[sKey]); } } }, /** * Refires the events for all configuration properties using their * current values. * @method refresh */ refresh: function () { var prop; for (prop in this.config) { if (Lang.hasOwnProperty(this.config, prop)) { this.refireEvent(prop); } } }, /** * Fires the normalized list of queued property change events * @method fireQueue */ fireQueue: function () { var i, queueItem, key, value, property; this.queueInProgress = true; for (i = 0;i < this.eventQueue.length; i++) { queueItem = this.eventQueue[i]; if (queueItem) { key = queueItem[0]; value = queueItem[1]; property = this.config[key]; property.value = value; // Clear out queue entry, to avoid it being // re-added to the queue by any queueProperty/supercedes // calls which are invoked during fireEvent this.eventQueue[i] = null; this.fireEvent(key,value); } } this.queueInProgress = false; this.eventQueue = []; }, /** * Subscribes an external handler to the change event for any * given property. * @method subscribeToConfigEvent * @param {String} key The property name * @param {Function} handler The handler function to use subscribe to * the property's event * @param {Object} obj The Object to use for scoping the event handler * (see CustomEvent documentation) * @param {Boolean} override Optional. If true, will override "this" * within the handler to map to the scope Object passed into the method. * @return {Boolean} True, if the subscription was successful, * otherwise false. */ subscribeToConfigEvent: function (key, handler, obj, override) { var property = this.config[key.toLowerCase()]; if (property && property.event) { if (!Config.alreadySubscribed(property.event, handler, obj)) { property.event.subscribe(handler, obj, override); } return true; } else { return false; } }, /** * Unsubscribes an external handler from the change event for any * given property. * @method unsubscribeFromConfigEvent * @param {String} key The property name * @param {Function} handler The handler function to use subscribe to * the property's event * @param {Object} obj The Object to use for scoping the event * handler (see CustomEvent documentation) * @return {Boolean} True, if the unsubscription was successful, * otherwise false. */ unsubscribeFromConfigEvent: function (key, handler, obj) { var property = this.config[key.toLowerCase()]; if (property && property.event) { return property.event.unsubscribe(handler, obj); } else { return false; } }, /** * Returns a string representation of the Config object * @method toString * @return {String} The Config object in string format. */ toString: function () { var output = "Config"; if (this.owner) { output += " [" + this.owner.toString() + "]"; } return output; }, /** * Returns a string representation of the Config object's current * CustomEvent queue * @method outputEventQueue * @return {String} The string list of CustomEvents currently queued * for execution */ outputEventQueue: function () { var output = "", queueItem, q, nQueue = this.eventQueue.length; for (q = 0; q < nQueue; q++) { queueItem = this.eventQueue[q]; if (queueItem) { output += queueItem[0] + "=" + queueItem[1] + ", "; } } return output; }, /** * Sets all properties to null, unsubscribes all listeners from each * property's change event and all listeners from the configChangedEvent. * @method destroy */ destroy: function () { var oConfig = this.config, sProperty, oProperty; for (sProperty in oConfig) { if (Lang.hasOwnProperty(oConfig, sProperty)) { oProperty = oConfig[sProperty]; oProperty.event.unsubscribeAll(); oProperty.event = null; } } this.configChangedEvent.unsubscribeAll(); this.configChangedEvent = null; this.owner = null; this.config = null; this.initialConfig = null; this.eventQueue = null; } }; /** * Checks to determine if a particular function/Object pair are already * subscribed to the specified CustomEvent * @method YAHOO.util.Config.alreadySubscribed * @static * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check * the subscriptions * @param {Function} fn The function to look for in the subscribers list * @param {Object} obj The execution scope Object for the subscription * @return {Boolean} true, if the function/Object pair is already subscribed * to the CustomEvent passed in */ Config.alreadySubscribed = function (evt, fn, obj) { var nSubscribers = evt.subscribers.length, subsc, i; if (nSubscribers > 0) { i = nSubscribers - 1; do { subsc = evt.subscribers[i]; if (subsc && subsc.obj == obj && subsc.fn == fn) { return true; } } while (i--); } return false; }; YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider); }()); (function () { /** * The Container family of components is designed to enable developers to * create different kinds of content-containing modules on the web. Module * and Overlay are the most basic containers, and they can be used directly * or extended to build custom containers. Also part of the Container family * are four UI controls that extend Module and Overlay: Tooltip, Panel, * Dialog, and SimpleDialog. * @module container * @title Container * @requires yahoo, dom, event * @optional dragdrop, animation, button */ /** * Module is a JavaScript representation of the Standard Module Format. * Standard Module Format is a simple standard for markup containers where * child nodes representing the header, body, and footer of the content are * denoted using the CSS classes "hd", "bd", and "ft" respectively. * Module is the base class for all other classes in the YUI * Container package. * @namespace YAHOO.widget * @class Module * @constructor * @param {String} el The element ID representing the Module <em>OR</em> * @param {HTMLElement} el The element representing the Module * @param {Object} userConfig The configuration Object literal containing * the configuration that should be set for this module. See configuration * documentation for more details. */ YAHOO.widget.Module = function (el, userConfig) { if (el) { this.init(el, userConfig); } else { } }; var Dom = YAHOO.util.Dom, Config = YAHOO.util.Config, Event = YAHOO.util.Event, CustomEvent = YAHOO.util.CustomEvent, Module = YAHOO.widget.Module, UA = YAHOO.env.ua, m_oModuleTemplate, m_oHeaderTemplate, m_oBodyTemplate, m_oFooterTemplate, /** * Constant representing the name of the Module's events * @property EVENT_TYPES * @private * @final * @type Object */ EVENT_TYPES = { "BEFORE_INIT": "beforeInit", "INIT": "init", "APPEND": "append", "BEFORE_RENDER": "beforeRender", "RENDER": "render", "CHANGE_HEADER": "changeHeader", "CHANGE_BODY": "changeBody", "CHANGE_FOOTER": "changeFooter", "CHANGE_CONTENT": "changeContent", "DESTORY": "destroy", "BEFORE_SHOW": "beforeShow", "SHOW": "show", "BEFORE_HIDE": "beforeHide", "HIDE": "hide" }, /** * Constant representing the Module's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "VISIBLE": { key: "visible", value: true, validator: YAHOO.lang.isBoolean }, "EFFECT": { key: "effect", suppressEvent: true, supercedes: ["visible"] }, "MONITOR_RESIZE": { key: "monitorresize", value: true }, "APPEND_TO_DOCUMENT_BODY": { key: "appendtodocumentbody", value: false } }; /** * Constant representing the prefix path to use for non-secure images * @property YAHOO.widget.Module.IMG_ROOT * @static * @final * @type String */ Module.IMG_ROOT = null; /** * Constant representing the prefix path to use for securely served images * @property YAHOO.widget.Module.IMG_ROOT_SSL * @static * @final * @type String */ Module.IMG_ROOT_SSL = null; /** * Constant for the default CSS class name that represents a Module * @property YAHOO.widget.Module.CSS_MODULE * @static * @final * @type String */ Module.CSS_MODULE = "yui-module"; /** * Constant representing the module header * @property YAHOO.widget.Module.CSS_HEADER * @static * @final * @type String */ Module.CSS_HEADER = "hd"; /** * Constant representing the module body * @property YAHOO.widget.Module.CSS_BODY * @static * @final * @type String */ Module.CSS_BODY = "bd"; /** * Constant representing the module footer * @property YAHOO.widget.Module.CSS_FOOTER * @static * @final * @type String */ Module.CSS_FOOTER = "ft"; /** * Constant representing the url for the "src" attribute of the iframe * used to monitor changes to the browser's base font size * @property YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL * @static * @final * @type String */ Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;"; /** * Constant representing the buffer amount (in pixels) to use when positioning * the text resize monitor offscreen. The resize monitor is positioned * offscreen by an amount eqaul to its offsetHeight + the buffer value. * * @property YAHOO.widget.Module.RESIZE_MONITOR_BUFFER * @static * @type Number */ // Set to 1, to work around pixel offset in IE8, which increases when zoom is used Module.RESIZE_MONITOR_BUFFER = 1; /** * Singleton CustomEvent fired when the font size is changed in the browser. * Opera's "zoom" functionality currently does not support text * size detection. * @event YAHOO.widget.Module.textResizeEvent */ Module.textResizeEvent = new CustomEvent("textResize"); /** * Helper utility method, which forces a document level * redraw for Opera, which can help remove repaint * irregularities after applying DOM changes. * * @method YAHOO.widget.Module.forceDocumentRedraw * @static */ Module.forceDocumentRedraw = function() { var docEl = document.documentElement; if (docEl) { docEl.className += " "; docEl.className = YAHOO.lang.trim(docEl.className); } }; function createModuleTemplate() { if (!m_oModuleTemplate) { m_oModuleTemplate = document.createElement("div"); m_oModuleTemplate.innerHTML = ("<div class=\"" + Module.CSS_HEADER + "\"></div>" + "<div class=\"" + Module.CSS_BODY + "\"></div><div class=\"" + Module.CSS_FOOTER + "\"></div>"); m_oHeaderTemplate = m_oModuleTemplate.firstChild; m_oBodyTemplate = m_oHeaderTemplate.nextSibling; m_oFooterTemplate = m_oBodyTemplate.nextSibling; } return m_oModuleTemplate; } function createHeader() { if (!m_oHeaderTemplate) { createModuleTemplate(); } return (m_oHeaderTemplate.cloneNode(false)); } function createBody() { if (!m_oBodyTemplate) { createModuleTemplate(); } return (m_oBodyTemplate.cloneNode(false)); } function createFooter() { if (!m_oFooterTemplate) { createModuleTemplate(); } return (m_oFooterTemplate.cloneNode(false)); } Module.prototype = { /** * The class's constructor function * @property contructor * @type Function */ constructor: Module, /** * The main module element that contains the header, body, and footer * @property element * @type HTMLElement */ element: null, /** * The header element, denoted with CSS class "hd" * @property header * @type HTMLElement */ header: null, /** * The body element, denoted with CSS class "bd" * @property body * @type HTMLElement */ body: null, /** * The footer element, denoted with CSS class "ft" * @property footer * @type HTMLElement */ footer: null, /** * The id of the element * @property id * @type String */ id: null, /** * A string representing the root path for all images created by * a Module instance. * @deprecated It is recommend that any images for a Module be applied * via CSS using the "background-image" property. * @property imageRoot * @type String */ imageRoot: Module.IMG_ROOT, /** * Initializes the custom events for Module which are fired * automatically at appropriate times by the Module class. * @method initEvents */ initEvents: function () { var SIGNATURE = CustomEvent.LIST; /** * CustomEvent fired prior to class initalization. * @event beforeInitEvent * @param {class} classRef class reference of the initializing * class, such as this.beforeInitEvent.fire(Module) */ this.beforeInitEvent = this.createEvent(EVENT_TYPES.BEFORE_INIT); this.beforeInitEvent.signature = SIGNATURE; /** * CustomEvent fired after class initalization. * @event initEvent * @param {class} classRef class reference of the initializing * class, such as this.beforeInitEvent.fire(Module) */ this.initEvent = this.createEvent(EVENT_TYPES.INIT); this.initEvent.signature = SIGNATURE; /** * CustomEvent fired when the Module is appended to the DOM * @event appendEvent */ this.appendEvent = this.createEvent(EVENT_TYPES.APPEND); this.appendEvent.signature = SIGNATURE; /** * CustomEvent fired before the Module is rendered * @event beforeRenderEvent */ this.beforeRenderEvent = this.createEvent(EVENT_TYPES.BEFORE_RENDER); this.beforeRenderEvent.signature = SIGNATURE; /** * CustomEvent fired after the Module is rendered * @event renderEvent */ this.renderEvent = this.createEvent(EVENT_TYPES.RENDER); this.renderEvent.signature = SIGNATURE; /** * CustomEvent fired when the header content of the Module * is modified * @event changeHeaderEvent * @param {String/HTMLElement} content String/element representing * the new header content */ this.changeHeaderEvent = this.createEvent(EVENT_TYPES.CHANGE_HEADER); this.changeHeaderEvent.signature = SIGNATURE; /** * CustomEvent fired when the body content of the Module is modified * @event changeBodyEvent * @param {String/HTMLElement} content String/element representing * the new body content */ this.changeBodyEvent = this.createEvent(EVENT_TYPES.CHANGE_BODY); this.changeBodyEvent.signature = SIGNATURE; /** * CustomEvent fired when the footer content of the Module * is modified * @event changeFooterEvent * @param {String/HTMLElement} content String/element representing * the new footer content */ this.changeFooterEvent = this.createEvent(EVENT_TYPES.CHANGE_FOOTER); this.changeFooterEvent.signature = SIGNATURE; /** * CustomEvent fired when the content of the Module is modified * @event changeContentEvent */ this.changeContentEvent = this.createEvent(EVENT_TYPES.CHANGE_CONTENT); this.changeContentEvent.signature = SIGNATURE; /** * CustomEvent fired when the Module is destroyed * @event destroyEvent */ this.destroyEvent = this.createEvent(EVENT_TYPES.DESTORY); this.destroyEvent.signature = SIGNATURE; /** * CustomEvent fired before the Module is shown * @event beforeShowEvent */ this.beforeShowEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW); this.beforeShowEvent.signature = SIGNATURE; /** * CustomEvent fired after the Module is shown * @event showEvent */ this.showEvent = this.createEvent(EVENT_TYPES.SHOW); this.showEvent.signature = SIGNATURE; /** * CustomEvent fired before the Module is hidden * @event beforeHideEvent */ this.beforeHideEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE); this.beforeHideEvent.signature = SIGNATURE; /** * CustomEvent fired after the Module is hidden * @event hideEvent */ this.hideEvent = this.createEvent(EVENT_TYPES.HIDE); this.hideEvent.signature = SIGNATURE; }, /** * String representing the current user-agent platform * @property platform * @type String */ platform: function () { var ua = navigator.userAgent.toLowerCase(); if (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1) { return "windows"; } else if (ua.indexOf("macintosh") != -1) { return "mac"; } else { return false; } }(), /** * String representing the user-agent of the browser * @deprecated Use YAHOO.env.ua * @property browser * @type String */ browser: function () { var ua = navigator.userAgent.toLowerCase(); /* Check Opera first in case of spoof and check Safari before Gecko since Safari's user agent string includes "like Gecko" */ if (ua.indexOf('opera') != -1) { return 'opera'; } else if (ua.indexOf('msie 7') != -1) { return 'ie7'; } else if (ua.indexOf('msie') != -1) { return 'ie'; } else if (ua.indexOf('safari') != -1) { return 'safari'; } else if (ua.indexOf('gecko') != -1) { return 'gecko'; } else { return false; } }(), /** * Boolean representing whether or not the current browsing context is * secure (https) * @property isSecure * @type Boolean */ isSecure: function () { if (window.location.href.toLowerCase().indexOf("https") === 0) { return true; } else { return false; } }(), /** * Initializes the custom events for Module which are fired * automatically at appropriate times by the Module class. */ initDefaultConfig: function () { // Add properties // /** * Specifies whether the Module is visible on the page. * @config visible * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.VISIBLE.key, { handler: this.configVisible, value: DEFAULT_CONFIG.VISIBLE.value, validator: DEFAULT_CONFIG.VISIBLE.validator }); /** * <p> * Object or array of objects representing the ContainerEffect * classes that are active for animating the container. * </p> * <p> * <strong>NOTE:</strong> Although this configuration * property is introduced at the Module level, an out of the box * implementation is not shipped for the Module class so setting * the proroperty on the Module class has no effect. The Overlay * class is the first class to provide out of the box ContainerEffect * support. * </p> * @config effect * @type Object * @default null */ this.cfg.addProperty(DEFAULT_CONFIG.EFFECT.key, { suppressEvent: DEFAULT_CONFIG.EFFECT.suppressEvent, supercedes: DEFAULT_CONFIG.EFFECT.supercedes }); /** * Specifies whether to create a special proxy iframe to monitor * for user font resizing in the document * @config monitorresize * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.MONITOR_RESIZE.key, { handler: this.configMonitorResize, value: DEFAULT_CONFIG.MONITOR_RESIZE.value }); /** * Specifies if the module should be rendered as the first child * of document.body or appended as the last child when render is called * with document.body as the "appendToNode". * <p> * Appending to the body while the DOM is still being constructed can * lead to Operation Aborted errors in IE hence this flag is set to * false by default. * </p> * * @config appendtodocumentbody * @type Boolean * @default false */ this.cfg.addProperty(DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.key, { value: DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.value }); }, /** * The Module class's initialization method, which is executed for * Module and all of its subclasses. This method is automatically * called by the constructor, and sets up all DOM references for * pre-existing markup, and creates required markup if it is not * already present. * <p> * If the element passed in does not have an id, one will be generated * for it. * </p> * @method init * @param {String} el The element ID representing the Module <em>OR</em> * @param {HTMLElement} el The element representing the Module * @param {Object} userConfig The configuration Object literal * containing the configuration that should be set for this module. * See configuration documentation for more details. */ init: function (el, userConfig) { var elId, child; this.initEvents(); this.beforeInitEvent.fire(Module); /** * The Module's Config object used for monitoring * configuration properties. * @property cfg * @type YAHOO.util.Config */ this.cfg = new Config(this); if (this.isSecure) { this.imageRoot = Module.IMG_ROOT_SSL; } if (typeof el == "string") { elId = el; el = document.getElementById(el); if (! el) { el = (createModuleTemplate()).cloneNode(false); el.id = elId; } } this.id = Dom.generateId(el); this.element = el; child = this.element.firstChild; if (child) { var fndHd = false, fndBd = false, fndFt = false; do { // We're looking for elements if (1 == child.nodeType) { if (!fndHd && Dom.hasClass(child, Module.CSS_HEADER)) { this.header = child; fndHd = true; } else if (!fndBd && Dom.hasClass(child, Module.CSS_BODY)) { this.body = child; fndBd = true; } else if (!fndFt && Dom.hasClass(child, Module.CSS_FOOTER)){ this.footer = child; fndFt = true; } } } while ((child = child.nextSibling)); } this.initDefaultConfig(); Dom.addClass(this.element, Module.CSS_MODULE); if (userConfig) { this.cfg.applyConfig(userConfig, true); } /* Subscribe to the fireQueue() method of Config so that any queued configuration changes are excecuted upon render of the Module */ if (!Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) { this.renderEvent.subscribe(this.cfg.fireQueue, this.cfg, true); } this.initEvent.fire(Module); }, /** * Initialize an empty IFRAME that is placed out of the visible area * that can be used to detect text resize. * @method initResizeMonitor */ initResizeMonitor: function () { var isGeckoWin = (UA.gecko && this.platform == "windows"); if (isGeckoWin) { // Help prevent spinning loading icon which // started with FireFox 2.0.0.8/Win var self = this; setTimeout(function(){self._initResizeMonitor();}, 0); } else { this._initResizeMonitor(); } }, /** * Create and initialize the text resize monitoring iframe. * * @protected * @method _initResizeMonitor */ _initResizeMonitor : function() { var oDoc, oIFrame, sHTML; function fireTextResize() { Module.textResizeEvent.fire(); } if (!UA.opera) { oIFrame = Dom.get("_yuiResizeMonitor"); var supportsCWResize = this._supportsCWResize(); if (!oIFrame) { oIFrame = document.createElement("iframe"); if (this.isSecure && Module.RESIZE_MONITOR_SECURE_URL && UA.ie) { oIFrame.src = Module.RESIZE_MONITOR_SECURE_URL; } if (!supportsCWResize) { // Can't monitor on contentWindow, so fire from inside iframe sHTML = ["<html><head><script ", "type=\"text/javascript\">", "window.onresize=function(){window.parent.", "YAHOO.widget.Module.textResizeEvent.", "fire();};<", "\/script></head>", "<body></body></html>"].join(''); oIFrame.src = "data:text/html;charset=utf-8," + encodeURIComponent(sHTML); } oIFrame.id = "_yuiResizeMonitor"; oIFrame.title = "Text Resize Monitor"; /* Need to set "position" property before inserting the iframe into the document or Safari's status bar will forever indicate the iframe is loading (See SourceForge bug #1723064) */ oIFrame.style.position = "absolute"; oIFrame.style.visibility = "hidden"; var db = document.body, fc = db.firstChild; if (fc) { db.insertBefore(oIFrame, fc); } else { db.appendChild(oIFrame); } oIFrame.style.width = "2em"; oIFrame.style.height = "2em"; oIFrame.style.top = (-1 * (oIFrame.offsetHeight + Module.RESIZE_MONITOR_BUFFER)) + "px"; oIFrame.style.left = "0"; oIFrame.style.borderWidth = "0"; oIFrame.style.visibility = "visible"; /* Don't open/close the document for Gecko like we used to, since it leads to duplicate cookies. (See SourceForge bug #1721755) */ if (UA.webkit) { oDoc = oIFrame.contentWindow.document; oDoc.open(); oDoc.close(); } } if (oIFrame && oIFrame.contentWindow) { Module.textResizeEvent.subscribe(this.onDomResize, this, true); if (!Module.textResizeInitialized) { if (supportsCWResize) { if (!Event.on(oIFrame.contentWindow, "resize", fireTextResize)) { /* This will fail in IE if document.domain has changed, so we must change the listener to use the oIFrame element instead */ Event.on(oIFrame, "resize", fireTextResize); } } Module.textResizeInitialized = true; } this.resizeMonitor = oIFrame; } } }, /** * Text resize monitor helper method. * Determines if the browser supports resize events on iframe content windows. * * @private * @method _supportsCWResize */ _supportsCWResize : function() { /* Gecko 1.8.0 (FF1.5), 1.8.1.0-5 (FF2) won't fire resize on contentWindow. Gecko 1.8.1.6+ (FF2.0.0.6+) and all other browsers will fire resize on contentWindow. We don't want to start sniffing for patch versions, so fire textResize the same way on all FF2 flavors */ var bSupported = true; if (UA.gecko && UA.gecko <= 1.8) { bSupported = false; } return bSupported; }, /** * Event handler fired when the resize monitor element is resized. * @method onDomResize * @param {DOMEvent} e The DOM resize event * @param {Object} obj The scope object passed to the handler */ onDomResize: function (e, obj) { var nTop = -1 * (this.resizeMonitor.offsetHeight + Module.RESIZE_MONITOR_BUFFER); this.resizeMonitor.style.top = nTop + "px"; this.resizeMonitor.style.left = "0"; }, /** * Sets the Module's header content to the string specified, or appends * the passed element to the header. If no header is present, one will * be automatically created. An empty string can be passed to the method * to clear the contents of the header. * * @method setHeader * @param {String} headerContent The string used to set the header. * As a convenience, non HTMLElement objects can also be passed into * the method, and will be treated as strings, with the header innerHTML * set to their default toString implementations. * <em>OR</em> * @param {HTMLElement} headerContent The HTMLElement to append to * <em>OR</em> * @param {DocumentFragment} headerContent The document fragment * containing elements which are to be added to the header */ setHeader: function (headerContent) { var oHeader = this.header || (this.header = createHeader()); if (headerContent.nodeName) { oHeader.innerHTML = ""; oHeader.appendChild(headerContent); } else { oHeader.innerHTML = headerContent; } this.changeHeaderEvent.fire(headerContent); this.changeContentEvent.fire(); }, /** * Appends the passed element to the header. If no header is present, * one will be automatically created. * @method appendToHeader * @param {HTMLElement | DocumentFragment} element The element to * append to the header. In the case of a document fragment, the * children of the fragment will be appended to the header. */ appendToHeader: function (element) { var oHeader = this.header || (this.header = createHeader()); oHeader.appendChild(element); this.changeHeaderEvent.fire(element); this.changeContentEvent.fire(); }, /** * Sets the Module's body content to the HTML specified. * * If no body is present, one will be automatically created. * * An empty string can be passed to the method to clear the contents of the body. * @method setBody * @param {String} bodyContent The HTML used to set the body. * As a convenience, non HTMLElement objects can also be passed into * the method, and will be treated as strings, with the body innerHTML * set to their default toString implementations. * <em>OR</em> * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only * child of the body element. * <em>OR</em> * @param {DocumentFragment} bodyContent The document fragment * containing elements which are to be added to the body */ setBody: function (bodyContent) { var oBody = this.body || (this.body = createBody()); if (bodyContent.nodeName) { oBody.innerHTML = ""; oBody.appendChild(bodyContent); } else { oBody.innerHTML = bodyContent; } this.changeBodyEvent.fire(bodyContent); this.changeContentEvent.fire(); }, /** * Appends the passed element to the body. If no body is present, one * will be automatically created. * @method appendToBody * @param {HTMLElement | DocumentFragment} element The element to * append to the body. In the case of a document fragment, the * children of the fragment will be appended to the body. * */ appendToBody: function (element) { var oBody = this.body || (this.body = createBody()); oBody.appendChild(element); this.changeBodyEvent.fire(element); this.changeContentEvent.fire(); }, /** * Sets the Module's footer content to the HTML specified, or appends * the passed element to the footer. If no footer is present, one will * be automatically created. An empty string can be passed to the method * to clear the contents of the footer. * @method setFooter * @param {String} footerContent The HTML used to set the footer * As a convenience, non HTMLElement objects can also be passed into * the method, and will be treated as strings, with the footer innerHTML * set to their default toString implementations. * <em>OR</em> * @param {HTMLElement} footerContent The HTMLElement to append to * the footer * <em>OR</em> * @param {DocumentFragment} footerContent The document fragment containing * elements which are to be added to the footer */ setFooter: function (footerContent) { var oFooter = this.footer || (this.footer = createFooter()); if (footerContent.nodeName) { oFooter.innerHTML = ""; oFooter.appendChild(footerContent); } else { oFooter.innerHTML = footerContent; } this.changeFooterEvent.fire(footerContent); this.changeContentEvent.fire(); }, /** * Appends the passed element to the footer. If no footer is present, * one will be automatically created. * @method appendToFooter * @param {HTMLElement | DocumentFragment} element The element to * append to the footer. In the case of a document fragment, the * children of the fragment will be appended to the footer */ appendToFooter: function (element) { var oFooter = this.footer || (this.footer = createFooter()); oFooter.appendChild(element); this.changeFooterEvent.fire(element); this.changeContentEvent.fire(); }, /** * Renders the Module by inserting the elements that are not already * in the main Module into their correct places. Optionally appends * the Module to the specified node prior to the render's execution. * <p> * For Modules without existing markup, the appendToNode argument * is REQUIRED. If this argument is ommitted and the current element is * not present in the document, the function will return false, * indicating that the render was a failure. * </p> * <p> * NOTE: As of 2.3.1, if the appendToNode is the document's body element * then the module is rendered as the first child of the body element, * and not appended to it, to avoid Operation Aborted errors in IE when * rendering the module before window's load event is fired. You can * use the appendtodocumentbody configuration property to change this * to append to document.body if required. * </p> * @method render * @param {String} appendToNode The element id to which the Module * should be appended to prior to rendering <em>OR</em> * @param {HTMLElement} appendToNode The element to which the Module * should be appended to prior to rendering * @param {HTMLElement} moduleElement OPTIONAL. The element that * represents the actual Standard Module container. * @return {Boolean} Success or failure of the render */ render: function (appendToNode, moduleElement) { var me = this, firstChild; function appendTo(parentNode) { if (typeof parentNode == "string") { parentNode = document.getElementById(parentNode); } if (parentNode) { me._addToParent(parentNode, me.element); me.appendEvent.fire(); } } this.beforeRenderEvent.fire(); if (! moduleElement) { moduleElement = this.element; } if (appendToNode) { appendTo(appendToNode); } else { // No node was passed in. If the element is not already in the Dom, this fails if (! Dom.inDocument(this.element)) { return false; } } // Need to get everything into the DOM if it isn't already if (this.header && ! Dom.inDocument(this.header)) { // There is a header, but it's not in the DOM yet. Need to add it. firstChild = moduleElement.firstChild; if (firstChild) { moduleElement.insertBefore(this.header, firstChild); } else { moduleElement.appendChild(this.header); } } if (this.body && ! Dom.inDocument(this.body)) { // There is a body, but it's not in the DOM yet. Need to add it. if (this.footer && Dom.isAncestor(this.moduleElement, this.footer)) { moduleElement.insertBefore(this.body, this.footer); } else { moduleElement.appendChild(this.body); } } if (this.footer && ! Dom.inDocument(this.footer)) { // There is a footer, but it's not in the DOM yet. Need to add it. moduleElement.appendChild(this.footer); } this.renderEvent.fire(); return true; }, /** * Removes the Module element from the DOM and sets all child elements * to null. * @method destroy */ destroy: function () { var parent; if (this.element) { Event.purgeElement(this.element, true); parent = this.element.parentNode; } if (parent) { parent.removeChild(this.element); } this.element = null; this.header = null; this.body = null; this.footer = null; Module.textResizeEvent.unsubscribe(this.onDomResize, this); this.cfg.destroy(); this.cfg = null; this.destroyEvent.fire(); }, /** * Shows the Module element by setting the visible configuration * property to true. Also fires two events: beforeShowEvent prior to * the visibility change, and showEvent after. * @method show */ show: function () { this.cfg.setProperty("visible", true); }, /** * Hides the Module element by setting the visible configuration * property to false. Also fires two events: beforeHideEvent prior to * the visibility change, and hideEvent after. * @method hide */ hide: function () { this.cfg.setProperty("visible", false); }, // BUILT-IN EVENT HANDLERS FOR MODULE // /** * Default event handler for changing the visibility property of a * Module. By default, this is achieved by switching the "display" style * between "block" and "none". * This method is responsible for firing showEvent and hideEvent. * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. * @method configVisible */ configVisible: function (type, args, obj) { var visible = args[0]; if (visible) { this.beforeShowEvent.fire(); Dom.setStyle(this.element, "display", "block"); this.showEvent.fire(); } else { this.beforeHideEvent.fire(); Dom.setStyle(this.element, "display", "none"); this.hideEvent.fire(); } }, /** * Default event handler for the "monitorresize" configuration property * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. * @method configMonitorResize */ configMonitorResize: function (type, args, obj) { var monitor = args[0]; if (monitor) { this.initResizeMonitor(); } else { Module.textResizeEvent.unsubscribe(this.onDomResize, this, true); this.resizeMonitor = null; } }, /** * This method is a protected helper, used when constructing the DOM structure for the module * to account for situations which may cause Operation Aborted errors in IE. It should not * be used for general DOM construction. * <p> * If the parentNode is not document.body, the element is appended as the last element. * </p> * <p> * If the parentNode is document.body the element is added as the first child to help * prevent Operation Aborted errors in IE. * </p> * * @param {parentNode} The HTML element to which the element will be added * @param {element} The HTML element to be added to parentNode's children * @method _addToParent * @protected */ _addToParent: function(parentNode, element) { if (!this.cfg.getProperty("appendtodocumentbody") && parentNode === document.body && parentNode.firstChild) { parentNode.insertBefore(element, parentNode.firstChild); } else { parentNode.appendChild(element); } }, /** * Returns a String representation of the Object. * @method toString * @return {String} The string representation of the Module */ toString: function () { return "Module " + this.id; } }; YAHOO.lang.augmentProto(Module, YAHOO.util.EventProvider); }()); (function () { /** * Overlay is a Module that is absolutely positioned above the page flow. It * has convenience methods for positioning and sizing, as well as options for * controlling zIndex and constraining the Overlay's position to the current * visible viewport. Overlay also contains a dynamicly generated IFRAME which * is placed beneath it for Internet Explorer 6 and 5.x so that it will be * properly rendered above SELECT elements. * @namespace YAHOO.widget * @class Overlay * @extends YAHOO.widget.Module * @param {String} el The element ID representing the Overlay <em>OR</em> * @param {HTMLElement} el The element representing the Overlay * @param {Object} userConfig The configuration object literal containing * the configuration that should be set for this Overlay. See configuration * documentation for more details. * @constructor */ YAHOO.widget.Overlay = function (el, userConfig) { YAHOO.widget.Overlay.superclass.constructor.call(this, el, userConfig); }; var Lang = YAHOO.lang, CustomEvent = YAHOO.util.CustomEvent, Module = YAHOO.widget.Module, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom, Config = YAHOO.util.Config, UA = YAHOO.env.ua, Overlay = YAHOO.widget.Overlay, _SUBSCRIBE = "subscribe", _UNSUBSCRIBE = "unsubscribe", _CONTAINED = "contained", m_oIFrameTemplate, /** * Constant representing the name of the Overlay's events * @property EVENT_TYPES * @private * @final * @type Object */ EVENT_TYPES = { "BEFORE_MOVE": "beforeMove", "MOVE": "move" }, /** * Constant representing the Overlay's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "X": { key: "x", validator: Lang.isNumber, suppressEvent: true, supercedes: ["iframe"] }, "Y": { key: "y", validator: Lang.isNumber, suppressEvent: true, supercedes: ["iframe"] }, "XY": { key: "xy", suppressEvent: true, supercedes: ["iframe"] }, "CONTEXT": { key: "context", suppressEvent: true, supercedes: ["iframe"] }, "FIXED_CENTER": { key: "fixedcenter", value: false, supercedes: ["iframe", "visible"] }, "WIDTH": { key: "width", suppressEvent: true, supercedes: ["context", "fixedcenter", "iframe"] }, "HEIGHT": { key: "height", suppressEvent: true, supercedes: ["context", "fixedcenter", "iframe"] }, "AUTO_FILL_HEIGHT" : { key: "autofillheight", supercedes: ["height"], value:"body" }, "ZINDEX": { key: "zindex", value: null }, "CONSTRAIN_TO_VIEWPORT": { key: "constraintoviewport", value: false, validator: Lang.isBoolean, supercedes: ["iframe", "x", "y", "xy"] }, "IFRAME": { key: "iframe", value: (UA.ie == 6 ? true : false), validator: Lang.isBoolean, supercedes: ["zindex"] }, "PREVENT_CONTEXT_OVERLAP": { key: "preventcontextoverlap", value: false, validator: Lang.isBoolean, supercedes: ["constraintoviewport"] } }; /** * The URL that will be placed in the iframe * @property YAHOO.widget.Overlay.IFRAME_SRC * @static * @final * @type String */ Overlay.IFRAME_SRC = "javascript:false;"; /** * Number representing how much the iframe shim should be offset from each * side of an Overlay instance, in pixels. * @property YAHOO.widget.Overlay.IFRAME_SRC * @default 3 * @static * @final * @type Number */ Overlay.IFRAME_OFFSET = 3; /** * Number representing the minimum distance an Overlay instance should be * positioned relative to the boundaries of the browser's viewport, in pixels. * @property YAHOO.widget.Overlay.VIEWPORT_OFFSET * @default 10 * @static * @final * @type Number */ Overlay.VIEWPORT_OFFSET = 10; /** * Constant representing the top left corner of an element, used for * configuring the context element alignment * @property YAHOO.widget.Overlay.TOP_LEFT * @static * @final * @type String */ Overlay.TOP_LEFT = "tl"; /** * Constant representing the top right corner of an element, used for * configuring the context element alignment * @property YAHOO.widget.Overlay.TOP_RIGHT * @static * @final * @type String */ Overlay.TOP_RIGHT = "tr"; /** * Constant representing the top bottom left corner of an element, used for * configuring the context element alignment * @property YAHOO.widget.Overlay.BOTTOM_LEFT * @static * @final * @type String */ Overlay.BOTTOM_LEFT = "bl"; /** * Constant representing the bottom right corner of an element, used for * configuring the context element alignment * @property YAHOO.widget.Overlay.BOTTOM_RIGHT * @static * @final * @type String */ Overlay.BOTTOM_RIGHT = "br"; /** * Constant representing the default CSS class used for an Overlay * @property YAHOO.widget.Overlay.CSS_OVERLAY * @static * @final * @type String */ Overlay.CSS_OVERLAY = "yui-overlay"; /** * Constant representing the names of the standard module elements * used in the overlay. * @property YAHOO.widget.Overlay.STD_MOD_RE * @static * @final * @type RegExp */ Overlay.STD_MOD_RE = /^\s*?(body|footer|header)\s*?$/i; /** * A singleton CustomEvent used for reacting to the DOM event for * window scroll * @event YAHOO.widget.Overlay.windowScrollEvent */ Overlay.windowScrollEvent = new CustomEvent("windowScroll"); /** * A singleton CustomEvent used for reacting to the DOM event for * window resize * @event YAHOO.widget.Overlay.windowResizeEvent */ Overlay.windowResizeEvent = new CustomEvent("windowResize"); /** * The DOM event handler used to fire the CustomEvent for window scroll * @method YAHOO.widget.Overlay.windowScrollHandler * @static * @param {DOMEvent} e The DOM scroll event */ Overlay.windowScrollHandler = function (e) { var t = Event.getTarget(e); // - Webkit (Safari 2/3) and Opera 9.2x bubble scroll events from elements to window // - FF2/3 and IE6/7, Opera 9.5x don't bubble scroll events from elements to window // - IE doesn't recognize scroll registered on the document. // // Also, when document view is scrolled, IE doesn't provide a target, // rest of the browsers set target to window.document, apart from opera // which sets target to window. if (!t || t === window || t === window.document) { if (UA.ie) { if (! window.scrollEnd) { window.scrollEnd = -1; } clearTimeout(window.scrollEnd); window.scrollEnd = setTimeout(function () { Overlay.windowScrollEvent.fire(); }, 1); } else { Overlay.windowScrollEvent.fire(); } } }; /** * The DOM event handler used to fire the CustomEvent for window resize * @method YAHOO.widget.Overlay.windowResizeHandler * @static * @param {DOMEvent} e The DOM resize event */ Overlay.windowResizeHandler = function (e) { if (UA.ie) { if (! window.resizeEnd) { window.resizeEnd = -1; } clearTimeout(window.resizeEnd); window.resizeEnd = setTimeout(function () { Overlay.windowResizeEvent.fire(); }, 100); } else { Overlay.windowResizeEvent.fire(); } }; /** * A boolean that indicated whether the window resize and scroll events have * already been subscribed to. * @property YAHOO.widget.Overlay._initialized * @private * @type Boolean */ Overlay._initialized = null; if (Overlay._initialized === null) { Event.on(window, "scroll", Overlay.windowScrollHandler); Event.on(window, "resize", Overlay.windowResizeHandler); Overlay._initialized = true; } /** * Internal map of special event types, which are provided * by the instance. It maps the event type to the custom event * instance. Contains entries for the "windowScroll", "windowResize" and * "textResize" static container events. * * @property YAHOO.widget.Overlay._TRIGGER_MAP * @type Object * @static * @private */ Overlay._TRIGGER_MAP = { "windowScroll" : Overlay.windowScrollEvent, "windowResize" : Overlay.windowResizeEvent, "textResize" : Module.textResizeEvent }; YAHOO.extend(Overlay, Module, { /** * <p> * Array of default event types which will trigger * context alignment for the Overlay class. * </p> * <p>The array is empty by default for Overlay, * but maybe populated in future releases, so classes extending * Overlay which need to define their own set of CONTEXT_TRIGGERS * should concatenate their super class's prototype.CONTEXT_TRIGGERS * value with their own array of values. * </p> * <p> * E.g.: * <code>CustomOverlay.prototype.CONTEXT_TRIGGERS = YAHOO.widget.Overlay.prototype.CONTEXT_TRIGGERS.concat(["windowScroll"]);</code> * </p> * * @property CONTEXT_TRIGGERS * @type Array * @final */ CONTEXT_TRIGGERS : [], /** * The Overlay initialization method, which is executed for Overlay and * all of its subclasses. This method is automatically called by the * constructor, and sets up all DOM references for pre-existing markup, * and creates required markup if it is not already present. * @method init * @param {String} el The element ID representing the Overlay <em>OR</em> * @param {HTMLElement} el The element representing the Overlay * @param {Object} userConfig The configuration object literal * containing the configuration that should be set for this Overlay. * See configuration documentation for more details. */ init: function (el, userConfig) { /* Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level */ Overlay.superclass.init.call(this, el/*, userConfig*/); this.beforeInitEvent.fire(Overlay); Dom.addClass(this.element, Overlay.CSS_OVERLAY); if (userConfig) { this.cfg.applyConfig(userConfig, true); } if (this.platform == "mac" && UA.gecko) { if (! Config.alreadySubscribed(this.showEvent, this.showMacGeckoScrollbars, this)) { this.showEvent.subscribe(this.showMacGeckoScrollbars, this, true); } if (! Config.alreadySubscribed(this.hideEvent, this.hideMacGeckoScrollbars, this)) { this.hideEvent.subscribe(this.hideMacGeckoScrollbars, this, true); } } this.initEvent.fire(Overlay); }, /** * Initializes the custom events for Overlay which are fired * automatically at appropriate times by the Overlay class. * @method initEvents */ initEvents: function () { Overlay.superclass.initEvents.call(this); var SIGNATURE = CustomEvent.LIST; /** * CustomEvent fired before the Overlay is moved. * @event beforeMoveEvent * @param {Number} x x coordinate * @param {Number} y y coordinate */ this.beforeMoveEvent = this.createEvent(EVENT_TYPES.BEFORE_MOVE); this.beforeMoveEvent.signature = SIGNATURE; /** * CustomEvent fired after the Overlay is moved. * @event moveEvent * @param {Number} x x coordinate * @param {Number} y y coordinate */ this.moveEvent = this.createEvent(EVENT_TYPES.MOVE); this.moveEvent.signature = SIGNATURE; }, /** * Initializes the class's configurable properties which can be changed * using the Overlay's Config object (cfg). * @method initDefaultConfig */ initDefaultConfig: function () { Overlay.superclass.initDefaultConfig.call(this); var cfg = this.cfg; // Add overlay config properties // /** * The absolute x-coordinate position of the Overlay * @config x * @type Number * @default null */ cfg.addProperty(DEFAULT_CONFIG.X.key, { handler: this.configX, validator: DEFAULT_CONFIG.X.validator, suppressEvent: DEFAULT_CONFIG.X.suppressEvent, supercedes: DEFAULT_CONFIG.X.supercedes }); /** * The absolute y-coordinate position of the Overlay * @config y * @type Number * @default null */ cfg.addProperty(DEFAULT_CONFIG.Y.key, { handler: this.configY, validator: DEFAULT_CONFIG.Y.validator, suppressEvent: DEFAULT_CONFIG.Y.suppressEvent, supercedes: DEFAULT_CONFIG.Y.supercedes }); /** * An array with the absolute x and y positions of the Overlay * @config xy * @type Number[] * @default null */ cfg.addProperty(DEFAULT_CONFIG.XY.key, { handler: this.configXY, suppressEvent: DEFAULT_CONFIG.XY.suppressEvent, supercedes: DEFAULT_CONFIG.XY.supercedes }); /** * <p> * The array of context arguments for context-sensitive positioning. * </p> * * <p> * The format of the array is: <code>[contextElementOrId, overlayCorner, contextCorner, arrayOfTriggerEvents (optional)]</code>, the * the 4 array elements described in detail below: * </p> * * <dl> * <dt>contextElementOrId <String|HTMLElement></dt> * <dd>A reference to the context element to which the overlay should be aligned (or it's id).</dd> * <dt>overlayCorner <String></dt> * <dd>The corner of the overlay which is to be used for alignment. This corner will be aligned to the * corner of the context element defined by the "contextCorner" entry which follows. Supported string values are: * "tr" (top right), "tl" (top left), "br" (bottom right), or "bl" (bottom left).</dd> * <dt>contextCorner <String></dt> * <dd>The corner of the context element which is to be used for alignment. Supported string values are the same ones listed for the "overlayCorner" entry above.</dd> * <dt>arrayOfTriggerEvents (optional) <Array[String|CustomEvent]></dt> * <dd> * <p> * By default, context alignment is a one time operation, aligning the Overlay to the context element when context configuration property is set, or when the <a href="#method_align">align</a> * method is invoked. However, you can use the optional "arrayOfTriggerEvents" entry to define the list of events which should force the overlay to re-align itself with the context element. * This is useful in situations where the layout of the document may change, resulting in the context element's position being modified. * </p> * <p> * The array can contain either event type strings for events the instance publishes (e.g. "beforeShow") or CustomEvent instances. Additionally the following * 3 static container event types are also currently supported : <code>"windowResize", "windowScroll", "textResize"</code> (defined in <a href="#property__TRIGGER_MAP">_TRIGGER_MAP</a> private property). * </p> * </dd> * </dl> * * <p> * For example, setting this property to <code>["img1", "tl", "bl"]</code> will * align the Overlay's top left corner to the bottom left corner of the * context element with id "img1". * </p> * <p> * Adding the optional trigger values: <code>["img1", "tl", "bl", ["beforeShow", "windowResize"]]</code>, * will re-align the overlay position, whenever the "beforeShow" or "windowResize" events are fired. * </p> * * @config context * @type Array * @default null */ cfg.addProperty(DEFAULT_CONFIG.CONTEXT.key, { handler: this.configContext, suppressEvent: DEFAULT_CONFIG.CONTEXT.suppressEvent, supercedes: DEFAULT_CONFIG.CONTEXT.supercedes }); /** * Determines whether or not the Overlay should be anchored * to the center of the viewport. * * <p>This property can be set to:</p> * * <dl> * <dt>true</dt> * <dd> * To enable fixed center positioning * <p> * When enabled, the overlay will * be positioned in the center of viewport when initially displayed, and * will remain in the center of the viewport whenever the window is * scrolled or resized. * </p> * <p> * If the overlay is too big for the viewport, * it's top left corner will be aligned with the top left corner of the viewport. * </p> * </dd> * <dt>false</dt> * <dd> * To disable fixed center positioning. * <p>In this case the overlay can still be * centered as a one-off operation, by invoking the <code>center()</code> method, * however it will not remain centered when the window is scrolled/resized. * </dd> * <dt>"contained"<dt> * <dd>To enable fixed center positioning, as with the <code>true</code> option. * <p>However, unlike setting the property to <code>true</code>, * when the property is set to <code>"contained"</code>, if the overlay is * too big for the viewport, it will not get automatically centered when the * user scrolls or resizes the window (until the window is large enough to contain the * overlay). This is useful in cases where the Overlay has both header and footer * UI controls which the user may need to access. * </p> * </dd> * </dl> * * @config fixedcenter * @type Boolean | String * @default false */ cfg.addProperty(DEFAULT_CONFIG.FIXED_CENTER.key, { handler: this.configFixedCenter, value: DEFAULT_CONFIG.FIXED_CENTER.value, validator: DEFAULT_CONFIG.FIXED_CENTER.validator, supercedes: DEFAULT_CONFIG.FIXED_CENTER.supercedes }); /** * CSS width of the Overlay. * @config width * @type String * @default null */ cfg.addProperty(DEFAULT_CONFIG.WIDTH.key, { handler: this.configWidth, suppressEvent: DEFAULT_CONFIG.WIDTH.suppressEvent, supercedes: DEFAULT_CONFIG.WIDTH.supercedes }); /** * CSS height of the Overlay. * @config height * @type String * @default null */ cfg.addProperty(DEFAULT_CONFIG.HEIGHT.key, { handler: this.configHeight, suppressEvent: DEFAULT_CONFIG.HEIGHT.suppressEvent, supercedes: DEFAULT_CONFIG.HEIGHT.supercedes }); /** * Standard module element which should auto fill out the height of the Overlay if the height config property is set. * Supported values are "header", "body", "footer". * * @config autofillheight * @type String * @default null */ cfg.addProperty(DEFAULT_CONFIG.AUTO_FILL_HEIGHT.key, { handler: this.configAutoFillHeight, value : DEFAULT_CONFIG.AUTO_FILL_HEIGHT.value, validator : this._validateAutoFill, supercedes: DEFAULT_CONFIG.AUTO_FILL_HEIGHT.supercedes }); /** * CSS z-index of the Overlay. * @config zIndex * @type Number * @default null */ cfg.addProperty(DEFAULT_CONFIG.ZINDEX.key, { handler: this.configzIndex, value: DEFAULT_CONFIG.ZINDEX.value }); /** * True if the Overlay should be prevented from being positioned * out of the viewport. * @config constraintoviewport * @type Boolean * @default false */ cfg.addProperty(DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.key, { handler: this.configConstrainToViewport, value: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.value, validator: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.validator, supercedes: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.supercedes }); /** * @config iframe * @description Boolean indicating whether or not the Overlay should * have an IFRAME shim; used to prevent SELECT elements from * poking through an Overlay instance in IE6. When set to "true", * the iframe shim is created when the Overlay instance is intially * made visible. * @type Boolean * @default true for IE6 and below, false for all other browsers. */ cfg.addProperty(DEFAULT_CONFIG.IFRAME.key, { handler: this.configIframe, value: DEFAULT_CONFIG.IFRAME.value, validator: DEFAULT_CONFIG.IFRAME.validator, supercedes: DEFAULT_CONFIG.IFRAME.supercedes }); /** * @config preventcontextoverlap * @description Boolean indicating whether or not the Overlay should overlap its * context element (defined using the "context" configuration property) when the * "constraintoviewport" configuration property is set to "true". * @type Boolean * @default false */ cfg.addProperty(DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.key, { value: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.value, validator: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.validator, supercedes: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.supercedes }); }, /** * Moves the Overlay to the specified position. This function is * identical to calling this.cfg.setProperty("xy", [x,y]); * @method moveTo * @param {Number} x The Overlay's new x position * @param {Number} y The Overlay's new y position */ moveTo: function (x, y) { this.cfg.setProperty("xy", [x, y]); }, /** * Adds a CSS class ("hide-scrollbars") and removes a CSS class * ("show-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435) * @method hideMacGeckoScrollbars */ hideMacGeckoScrollbars: function () { Dom.replaceClass(this.element, "show-scrollbars", "hide-scrollbars"); }, /** * Adds a CSS class ("show-scrollbars") and removes a CSS class * ("hide-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435) * @method showMacGeckoScrollbars */ showMacGeckoScrollbars: function () { Dom.replaceClass(this.element, "hide-scrollbars", "show-scrollbars"); }, /** * Internal implementation to set the visibility of the overlay in the DOM. * * @method _setDomVisibility * @param {boolean} visible Whether to show or hide the Overlay's outer element * @protected */ _setDomVisibility : function(show) { Dom.setStyle(this.element, "visibility", (show) ? "visible" : "hidden"); if (show) { Dom.removeClass(this.element, "yui-overlay-hidden"); } else { Dom.addClass(this.element, "yui-overlay-hidden"); } }, // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // /** * The default event handler fired when the "visible" property is * changed. This method is responsible for firing showEvent * and hideEvent. * @method configVisible * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configVisible: function (type, args, obj) { var visible = args[0], currentVis = Dom.getStyle(this.element, "visibility"), effect = this.cfg.getProperty("effect"), effectInstances = [], isMacGecko = (this.platform == "mac" && UA.gecko), alreadySubscribed = Config.alreadySubscribed, eff, ei, e, i, j, k, h, nEffects, nEffectInstances; if (currentVis == "inherit") { e = this.element.parentNode; while (e.nodeType != 9 && e.nodeType != 11) { currentVis = Dom.getStyle(e, "visibility"); if (currentVis != "inherit") { break; } e = e.parentNode; } if (currentVis == "inherit") { currentVis = "visible"; } } if (effect) { if (effect instanceof Array) { nEffects = effect.length; for (i = 0; i < nEffects; i++) { eff = effect[i]; effectInstances[effectInstances.length] = eff.effect(this, eff.duration); } } else { effectInstances[effectInstances.length] = effect.effect(this, effect.duration); } } if (visible) { // Show if (isMacGecko) { this.showMacGeckoScrollbars(); } if (effect) { // Animate in if (visible) { // Animate in if not showing if (currentVis != "visible" || currentVis === "") { this.beforeShowEvent.fire(); nEffectInstances = effectInstances.length; for (j = 0; j < nEffectInstances; j++) { ei = effectInstances[j]; if (j === 0 && !alreadySubscribed( ei.animateInCompleteEvent, this.showEvent.fire, this.showEvent)) { /* Delegate showEvent until end of animateInComplete */ ei.animateInCompleteEvent.subscribe( this.showEvent.fire, this.showEvent, true); } ei.animateIn(); } } } } else { // Show if (currentVis != "visible" || currentVis === "") { this.beforeShowEvent.fire(); this._setDomVisibility(true); this.cfg.refireEvent("iframe"); this.showEvent.fire(); } else { this._setDomVisibility(true); } } } else { // Hide if (isMacGecko) { this.hideMacGeckoScrollbars(); } if (effect) { // Animate out if showing if (currentVis == "visible") { this.beforeHideEvent.fire(); nEffectInstances = effectInstances.length; for (k = 0; k < nEffectInstances; k++) { h = effectInstances[k]; if (k === 0 && !alreadySubscribed( h.animateOutCompleteEvent, this.hideEvent.fire, this.hideEvent)) { /* Delegate hideEvent until end of animateOutComplete */ h.animateOutCompleteEvent.subscribe( this.hideEvent.fire, this.hideEvent, true); } h.animateOut(); } } else if (currentVis === "") { this._setDomVisibility(false); } } else { // Simple hide if (currentVis == "visible" || currentVis === "") { this.beforeHideEvent.fire(); this._setDomVisibility(false); this.hideEvent.fire(); } else { this._setDomVisibility(false); } } } }, /** * Fixed center event handler used for centering on scroll/resize, but only if * the overlay is visible and, if "fixedcenter" is set to "contained", only if * the overlay fits within the viewport. * * @method doCenterOnDOMEvent */ doCenterOnDOMEvent: function () { var cfg = this.cfg, fc = cfg.getProperty("fixedcenter"); if (cfg.getProperty("visible")) { if (fc && (fc !== _CONTAINED || this.fitsInViewport())) { this.center(); } } }, /** * Determines if the Overlay (including the offset value defined by Overlay.VIEWPORT_OFFSET) * will fit entirely inside the viewport, in both dimensions - width and height. * * @method fitsInViewport * @return boolean true if the Overlay will fit, false if not */ fitsInViewport : function() { var nViewportOffset = Overlay.VIEWPORT_OFFSET, element = this.element, elementWidth = element.offsetWidth, elementHeight = element.offsetHeight, viewportWidth = Dom.getViewportWidth(), viewportHeight = Dom.getViewportHeight(); return ((elementWidth + nViewportOffset < viewportWidth) && (elementHeight + nViewportOffset < viewportHeight)); }, /** * The default event handler fired when the "fixedcenter" property * is changed. * @method configFixedCenter * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configFixedCenter: function (type, args, obj) { var val = args[0], alreadySubscribed = Config.alreadySubscribed, windowResizeEvent = Overlay.windowResizeEvent, windowScrollEvent = Overlay.windowScrollEvent; if (val) { this.center(); if (!alreadySubscribed(this.beforeShowEvent, this.center)) { this.beforeShowEvent.subscribe(this.center); } if (!alreadySubscribed(windowResizeEvent, this.doCenterOnDOMEvent, this)) { windowResizeEvent.subscribe(this.doCenterOnDOMEvent, this, true); } if (!alreadySubscribed(windowScrollEvent, this.doCenterOnDOMEvent, this)) { windowScrollEvent.subscribe(this.doCenterOnDOMEvent, this, true); } } else { this.beforeShowEvent.unsubscribe(this.center); windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent, this); windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent, this); } }, /** * The default event handler fired when the "height" property is changed. * @method configHeight * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configHeight: function (type, args, obj) { var height = args[0], el = this.element; Dom.setStyle(el, "height", height); this.cfg.refireEvent("iframe"); }, /** * The default event handler fired when the "autofillheight" property is changed. * @method configAutoFillHeight * * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configAutoFillHeight: function (type, args, obj) { var fillEl = args[0], cfg = this.cfg, autoFillHeight = "autofillheight", height = "height", currEl = cfg.getProperty(autoFillHeight), autoFill = this._autoFillOnHeightChange; cfg.unsubscribeFromConfigEvent(height, autoFill); Module.textResizeEvent.unsubscribe(autoFill); this.changeContentEvent.unsubscribe(autoFill); if (currEl && fillEl !== currEl && this[currEl]) { Dom.setStyle(this[currEl], height, ""); } if (fillEl) { fillEl = Lang.trim(fillEl.toLowerCase()); cfg.subscribeToConfigEvent(height, autoFill, this[fillEl], this); Module.textResizeEvent.subscribe(autoFill, this[fillEl], this); this.changeContentEvent.subscribe(autoFill, this[fillEl], this); cfg.setProperty(autoFillHeight, fillEl, true); } }, /** * The default event handler fired when the "width" property is changed. * @method configWidth * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configWidth: function (type, args, obj) { var width = args[0], el = this.element; Dom.setStyle(el, "width", width); this.cfg.refireEvent("iframe"); }, /** * The default event handler fired when the "zIndex" property is changed. * @method configzIndex * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configzIndex: function (type, args, obj) { var zIndex = args[0], el = this.element; if (! zIndex) { zIndex = Dom.getStyle(el, "zIndex"); if (! zIndex || isNaN(zIndex)) { zIndex = 0; } } if (this.iframe || this.cfg.getProperty("iframe") === true) { if (zIndex <= 0) { zIndex = 1; } } Dom.setStyle(el, "zIndex", zIndex); this.cfg.setProperty("zIndex", zIndex, true); if (this.iframe) { this.stackIframe(); } }, /** * The default event handler fired when the "xy" property is changed. * @method configXY * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configXY: function (type, args, obj) { var pos = args[0], x = pos[0], y = pos[1]; this.cfg.setProperty("x", x); this.cfg.setProperty("y", y); this.beforeMoveEvent.fire([x, y]); x = this.cfg.getProperty("x"); y = this.cfg.getProperty("y"); this.cfg.refireEvent("iframe"); this.moveEvent.fire([x, y]); }, /** * The default event handler fired when the "x" property is changed. * @method configX * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configX: function (type, args, obj) { var x = args[0], y = this.cfg.getProperty("y"); this.cfg.setProperty("x", x, true); this.cfg.setProperty("y", y, true); this.beforeMoveEvent.fire([x, y]); x = this.cfg.getProperty("x"); y = this.cfg.getProperty("y"); Dom.setX(this.element, x, true); this.cfg.setProperty("xy", [x, y], true); this.cfg.refireEvent("iframe"); this.moveEvent.fire([x, y]); }, /** * The default event handler fired when the "y" property is changed. * @method configY * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configY: function (type, args, obj) { var x = this.cfg.getProperty("x"), y = args[0]; this.cfg.setProperty("x", x, true); this.cfg.setProperty("y", y, true); this.beforeMoveEvent.fire([x, y]); x = this.cfg.getProperty("x"); y = this.cfg.getProperty("y"); Dom.setY(this.element, y, true); this.cfg.setProperty("xy", [x, y], true); this.cfg.refireEvent("iframe"); this.moveEvent.fire([x, y]); }, /** * Shows the iframe shim, if it has been enabled. * @method showIframe */ showIframe: function () { var oIFrame = this.iframe, oParentNode; if (oIFrame) { oParentNode = this.element.parentNode; if (oParentNode != oIFrame.parentNode) { this._addToParent(oParentNode, oIFrame); } oIFrame.style.display = "block"; } }, /** * Hides the iframe shim, if it has been enabled. * @method hideIframe */ hideIframe: function () { if (this.iframe) { this.iframe.style.display = "none"; } }, /** * Syncronizes the size and position of iframe shim to that of its * corresponding Overlay instance. * @method syncIframe */ syncIframe: function () { var oIFrame = this.iframe, oElement = this.element, nOffset = Overlay.IFRAME_OFFSET, nDimensionOffset = (nOffset * 2), aXY; if (oIFrame) { // Size <iframe> oIFrame.style.width = (oElement.offsetWidth + nDimensionOffset + "px"); oIFrame.style.height = (oElement.offsetHeight + nDimensionOffset + "px"); // Position <iframe> aXY = this.cfg.getProperty("xy"); if (!Lang.isArray(aXY) || (isNaN(aXY[0]) || isNaN(aXY[1]))) { this.syncPosition(); aXY = this.cfg.getProperty("xy"); } Dom.setXY(oIFrame, [(aXY[0] - nOffset), (aXY[1] - nOffset)]); } }, /** * Sets the zindex of the iframe shim, if it exists, based on the zindex of * the Overlay element. The zindex of the iframe is set to be one less * than the Overlay element's zindex. * * <p>NOTE: This method will not bump up the zindex of the Overlay element * to ensure that the iframe shim has a non-negative zindex. * If you require the iframe zindex to be 0 or higher, the zindex of * the Overlay element should be set to a value greater than 0, before * this method is called. * </p> * @method stackIframe */ stackIframe: function () { if (this.iframe) { var overlayZ = Dom.getStyle(this.element, "zIndex"); if (!YAHOO.lang.isUndefined(overlayZ) && !isNaN(overlayZ)) { Dom.setStyle(this.iframe, "zIndex", (overlayZ - 1)); } } }, /** * The default event handler fired when the "iframe" property is changed. * @method configIframe * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configIframe: function (type, args, obj) { var bIFrame = args[0]; function createIFrame() { var oIFrame = this.iframe, oElement = this.element, oParent; if (!oIFrame) { if (!m_oIFrameTemplate) { m_oIFrameTemplate = document.createElement("iframe"); if (this.isSecure) { m_oIFrameTemplate.src = Overlay.IFRAME_SRC; } /* Set the opacity of the <iframe> to 0 so that it doesn't modify the opacity of any transparent elements that may be on top of it (like a shadow). */ if (UA.ie) { m_oIFrameTemplate.style.filter = "alpha(opacity=0)"; /* Need to set the "frameBorder" property to 0 supress the default <iframe> border in IE. Setting the CSS "border" property alone doesn't supress it. */ m_oIFrameTemplate.frameBorder = 0; } else { m_oIFrameTemplate.style.opacity = "0"; } m_oIFrameTemplate.style.position = "absolute"; m_oIFrameTemplate.style.border = "none"; m_oIFrameTemplate.style.margin = "0"; m_oIFrameTemplate.style.padding = "0"; m_oIFrameTemplate.style.display = "none"; m_oIFrameTemplate.tabIndex = -1; } oIFrame = m_oIFrameTemplate.cloneNode(false); oParent = oElement.parentNode; var parentNode = oParent || document.body; this._addToParent(parentNode, oIFrame); this.iframe = oIFrame; } /* Show the <iframe> before positioning it since the "setXY" method of DOM requires the element be in the document and visible. */ this.showIframe(); /* Syncronize the size and position of the <iframe> to that of the Overlay. */ this.syncIframe(); this.stackIframe(); // Add event listeners to update the <iframe> when necessary if (!this._hasIframeEventListeners) { this.showEvent.subscribe(this.showIframe); this.hideEvent.subscribe(this.hideIframe); this.changeContentEvent.subscribe(this.syncIframe); this._hasIframeEventListeners = true; } } function onBeforeShow() { createIFrame.call(this); this.beforeShowEvent.unsubscribe(onBeforeShow); this._iframeDeferred = false; } if (bIFrame) { // <iframe> shim is enabled if (this.cfg.getProperty("visible")) { createIFrame.call(this); } else { if (!this._iframeDeferred) { this.beforeShowEvent.subscribe(onBeforeShow); this._iframeDeferred = true; } } } else { // <iframe> shim is disabled this.hideIframe(); if (this._hasIframeEventListeners) { this.showEvent.unsubscribe(this.showIframe); this.hideEvent.unsubscribe(this.hideIframe); this.changeContentEvent.unsubscribe(this.syncIframe); this._hasIframeEventListeners = false; } } }, /** * Set's the container's XY value from DOM if not already set. * * Differs from syncPosition, in that the XY value is only sync'd with DOM if * not already set. The method also refire's the XY config property event, so any * beforeMove, Move event listeners are invoked. * * @method _primeXYFromDOM * @protected */ _primeXYFromDOM : function() { if (YAHOO.lang.isUndefined(this.cfg.getProperty("xy"))) { // Set CFG XY based on DOM XY this.syncPosition(); // Account for XY being set silently in syncPosition (no moveTo fired/called) this.cfg.refireEvent("xy"); this.beforeShowEvent.unsubscribe(this._primeXYFromDOM); } }, /** * The default event handler fired when the "constraintoviewport" * property is changed. * @method configConstrainToViewport * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for * the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configConstrainToViewport: function (type, args, obj) { var val = args[0]; if (val) { if (! Config.alreadySubscribed(this.beforeMoveEvent, this.enforceConstraints, this)) { this.beforeMoveEvent.subscribe(this.enforceConstraints, this, true); } if (! Config.alreadySubscribed(this.beforeShowEvent, this._primeXYFromDOM)) { this.beforeShowEvent.subscribe(this._primeXYFromDOM); } } else { this.beforeShowEvent.unsubscribe(this._primeXYFromDOM); this.beforeMoveEvent.unsubscribe(this.enforceConstraints, this); } }, /** * The default event handler fired when the "context" property * is changed. * * @method configContext * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configContext: function (type, args, obj) { var contextArgs = args[0], contextEl, elementMagnetCorner, contextMagnetCorner, triggers, defTriggers = this.CONTEXT_TRIGGERS; if (contextArgs) { contextEl = contextArgs[0]; elementMagnetCorner = contextArgs[1]; contextMagnetCorner = contextArgs[2]; triggers = contextArgs[3]; if (defTriggers && defTriggers.length > 0) { triggers = (triggers || []).concat(defTriggers); } if (contextEl) { if (typeof contextEl == "string") { this.cfg.setProperty("context", [ document.getElementById(contextEl), elementMagnetCorner, contextMagnetCorner, triggers ], true); } if (elementMagnetCorner && contextMagnetCorner) { this.align(elementMagnetCorner, contextMagnetCorner); } if (this._contextTriggers) { // Unsubscribe Old Set this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger); } if (triggers) { // Subscribe New Set this._processTriggers(triggers, _SUBSCRIBE, this._alignOnTrigger); this._contextTriggers = triggers; } } } }, /** * Custom Event handler for context alignment triggers. Invokes the align method * * @method _alignOnTrigger * @protected * * @param {String} type The event type (not used by the default implementation) * @param {Any[]} args The array of arguments for the trigger event (not used by the default implementation) */ _alignOnTrigger: function(type, args) { this.align(); }, /** * Helper method to locate the custom event instance for the event name string * passed in. As a convenience measure, any custom events passed in are returned. * * @method _findTriggerCE * @private * * @param {String|CustomEvent} t Either a CustomEvent, or event type (e.g. "windowScroll") for which a * custom event instance needs to be looked up from the Overlay._TRIGGER_MAP. */ _findTriggerCE : function(t) { var tce = null; if (t instanceof CustomEvent) { tce = t; } else if (Overlay._TRIGGER_MAP[t]) { tce = Overlay._TRIGGER_MAP[t]; } return tce; }, /** * Utility method that subscribes or unsubscribes the given * function from the list of trigger events provided. * * @method _processTriggers * @protected * * @param {Array[String|CustomEvent]} triggers An array of either CustomEvents, event type strings * (e.g. "beforeShow", "windowScroll") to/from which the provided function should be * subscribed/unsubscribed respectively. * * @param {String} mode Either "subscribe" or "unsubscribe", specifying whether or not * we are subscribing or unsubscribing trigger listeners * * @param {Function} fn The function to be subscribed/unsubscribed to/from the trigger event. * Context is always set to the overlay instance, and no additional object argument * get passed to the subscribed function. */ _processTriggers : function(triggers, mode, fn) { var t, tce; for (var i = 0, l = triggers.length; i < l; ++i) { t = triggers[i]; tce = this._findTriggerCE(t); if (tce) { tce[mode](fn, this, true); } else { this[mode](t, fn); } } }, // END BUILT-IN PROPERTY EVENT HANDLERS // /** * Aligns the Overlay to its context element using the specified corner * points (represented by the constants TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, * and BOTTOM_RIGHT. * @method align * @param {String} elementAlign The String representing the corner of * the Overlay that should be aligned to the context element * @param {String} contextAlign The corner of the context element * that the elementAlign corner should stick to. */ align: function (elementAlign, contextAlign) { var contextArgs = this.cfg.getProperty("context"), me = this, context, element, contextRegion; function doAlign(v, h) { switch (elementAlign) { case Overlay.TOP_LEFT: me.moveTo(h, v); break; case Overlay.TOP_RIGHT: me.moveTo((h - element.offsetWidth), v); break; case Overlay.BOTTOM_LEFT: me.moveTo(h, (v - element.offsetHeight)); break; case Overlay.BOTTOM_RIGHT: me.moveTo((h - element.offsetWidth), (v - element.offsetHeight)); break; } } if (contextArgs) { context = contextArgs[0]; element = this.element; me = this; if (! elementAlign) { elementAlign = contextArgs[1]; } if (! contextAlign) { contextAlign = contextArgs[2]; } if (element && context) { contextRegion = Dom.getRegion(context); switch (contextAlign) { case Overlay.TOP_LEFT: doAlign(contextRegion.top, contextRegion.left); break; case Overlay.TOP_RIGHT: doAlign(contextRegion.top, contextRegion.right); break; case Overlay.BOTTOM_LEFT: doAlign(contextRegion.bottom, contextRegion.left); break; case Overlay.BOTTOM_RIGHT: doAlign(contextRegion.bottom, contextRegion.right); break; } } } }, /** * The default event handler executed when the moveEvent is fired, if the * "constraintoviewport" is set to true. * @method enforceConstraints * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ enforceConstraints: function (type, args, obj) { var pos = args[0]; var cXY = this.getConstrainedXY(pos[0], pos[1]); this.cfg.setProperty("x", cXY[0], true); this.cfg.setProperty("y", cXY[1], true); this.cfg.setProperty("xy", cXY, true); }, /** * Given x coordinate value, returns the calculated x coordinate required to * position the Overlay if it is to be constrained to the viewport, based on the * current element size, viewport dimensions and scroll values. * * @param {Number} x The X coordinate value to be constrained * @return {Number} The constrained x coordinate */ getConstrainedX: function (x) { var oOverlay = this, oOverlayEl = oOverlay.element, nOverlayOffsetWidth = oOverlayEl.offsetWidth, nViewportOffset = Overlay.VIEWPORT_OFFSET, viewPortWidth = Dom.getViewportWidth(), scrollX = Dom.getDocumentScrollLeft(), bCanConstrain = (nOverlayOffsetWidth + nViewportOffset < viewPortWidth), aContext = this.cfg.getProperty("context"), oContextEl, nContextElX, nContextElWidth, bFlipped = false, nLeftRegionWidth, nRightRegionWidth, leftConstraint = scrollX + nViewportOffset, rightConstraint = scrollX + viewPortWidth - nOverlayOffsetWidth - nViewportOffset, xNew = x, oOverlapPositions = { "tltr": true, "blbr": true, "brbl": true, "trtl": true }; var flipHorizontal = function () { var nNewX; if ((oOverlay.cfg.getProperty("x") - scrollX) > nContextElX) { nNewX = (nContextElX - nOverlayOffsetWidth); } else { nNewX = (nContextElX + nContextElWidth); } oOverlay.cfg.setProperty("x", (nNewX + scrollX), true); return nNewX; }; /* Uses the context element's position to calculate the availble width to the right and left of it to display its corresponding Overlay. */ var getDisplayRegionWidth = function () { // The Overlay is to the right of the context element if ((oOverlay.cfg.getProperty("x") - scrollX) > nContextElX) { return (nRightRegionWidth - nViewportOffset); } else { // The Overlay is to the left of the context element return (nLeftRegionWidth - nViewportOffset); } }; /* Positions the Overlay to the left or right of the context element so that it remains inside the viewport. */ var setHorizontalPosition = function () { var nDisplayRegionWidth = getDisplayRegionWidth(), fnReturnVal; if (nOverlayOffsetWidth > nDisplayRegionWidth) { if (bFlipped) { /* All possible positions and values have been tried, but none were successful, so fall back to the original size and position. */ flipHorizontal(); } else { flipHorizontal(); bFlipped = true; fnReturnVal = setHorizontalPosition(); } } return fnReturnVal; }; // Determine if the current value for the Overlay's "x" configuration property will // result in the Overlay being positioned outside the boundaries of the viewport if (x < leftConstraint || x > rightConstraint) { // The current value for the Overlay's "x" configuration property WILL // result in the Overlay being positioned outside the boundaries of the viewport if (bCanConstrain) { // If the "preventcontextoverlap" configuration property is set to "true", // try to flip the Overlay to both keep it inside the boundaries of the // viewport AND from overlaping its context element. if (this.cfg.getProperty("preventcontextoverlap") && aContext && oOverlapPositions[(aContext[1] + aContext[2])]) { oContextEl = aContext[0]; nContextElX = Dom.getX(oContextEl) - scrollX; nContextElWidth = oContextEl.offsetWidth; nLeftRegionWidth = nContextElX; nRightRegionWidth = (viewPortWidth - (nContextElX + nContextElWidth)); setHorizontalPosition(); xNew = this.cfg.getProperty("x"); } else { if (x < leftConstraint) { xNew = leftConstraint; } else if (x > rightConstraint) { xNew = rightConstraint; } } } else { // The "x" configuration property cannot be set to a value that will keep // entire Overlay inside the boundary of the viewport. Therefore, set // the "x" configuration property to scrollY to keep as much of the // Overlay inside the viewport as possible. xNew = nViewportOffset + scrollX; } } return xNew; }, /** * Given y coordinate value, returns the calculated y coordinate required to * position the Overlay if it is to be constrained to the viewport, based on the * current element size, viewport dimensions and scroll values. * * @param {Number} y The Y coordinate value to be constrained * @return {Number} The constrained y coordinate */ getConstrainedY: function (y) { var oOverlay = this, oOverlayEl = oOverlay.element, nOverlayOffsetHeight = oOverlayEl.offsetHeight, nViewportOffset = Overlay.VIEWPORT_OFFSET, viewPortHeight = Dom.getViewportHeight(), scrollY = Dom.getDocumentScrollTop(), bCanConstrain = (nOverlayOffsetHeight + nViewportOffset < viewPortHeight), aContext = this.cfg.getProperty("context"), oContextEl, nContextElY, nContextElHeight, bFlipped = false, nTopRegionHeight, nBottomRegionHeight, topConstraint = scrollY + nViewportOffset, bottomConstraint = scrollY + viewPortHeight - nOverlayOffsetHeight - nViewportOffset, yNew = y, oOverlapPositions = { "trbr": true, "tlbl": true, "bltl": true, "brtr": true }; var flipVertical = function () { var nNewY; // The Overlay is below the context element, flip it above if ((oOverlay.cfg.getProperty("y") - scrollY) > nContextElY) { nNewY = (nContextElY - nOverlayOffsetHeight); } else { // The Overlay is above the context element, flip it below nNewY = (nContextElY + nContextElHeight); } oOverlay.cfg.setProperty("y", (nNewY + scrollY), true); return nNewY; }; /* Uses the context element's position to calculate the availble height above and below it to display its corresponding Overlay. */ var getDisplayRegionHeight = function () { // The Overlay is below the context element if ((oOverlay.cfg.getProperty("y") - scrollY) > nContextElY) { return (nBottomRegionHeight - nViewportOffset); } else { // The Overlay is above the context element return (nTopRegionHeight - nViewportOffset); } }; /* Trys to place the Overlay in the best possible position (either above or below its corresponding context element). */ var setVerticalPosition = function () { var nDisplayRegionHeight = getDisplayRegionHeight(), fnReturnVal; if (nOverlayOffsetHeight > nDisplayRegionHeight) { if (bFlipped) { /* All possible positions and values for the "maxheight" configuration property have been tried, but none were successful, so fall back to the original size and position. */ flipVertical(); } else { flipVertical(); bFlipped = true; fnReturnVal = setVerticalPosition(); } } return fnReturnVal; }; // Determine if the current value for the Overlay's "y" configuration property will // result in the Overlay being positioned outside the boundaries of the viewport if (y < topConstraint || y > bottomConstraint) { // The current value for the Overlay's "y" configuration property WILL // result in the Overlay being positioned outside the boundaries of the viewport if (bCanConstrain) { // If the "preventcontextoverlap" configuration property is set to "true", // try to flip the Overlay to both keep it inside the boundaries of the // viewport AND from overlaping its context element. if (this.cfg.getProperty("preventcontextoverlap") && aContext && oOverlapPositions[(aContext[1] + aContext[2])]) { oContextEl = aContext[0]; nContextElHeight = oContextEl.offsetHeight; nContextElY = (Dom.getY(oContextEl) - scrollY); nTopRegionHeight = nContextElY; nBottomRegionHeight = (viewPortHeight - (nContextElY + nContextElHeight)); setVerticalPosition(); yNew = oOverlay.cfg.getProperty("y"); } else { if (y < topConstraint) { yNew = topConstraint; } else if (y > bottomConstraint) { yNew = bottomConstraint; } } } else { // The "y" configuration property cannot be set to a value that will keep // entire Overlay inside the boundary of the viewport. Therefore, set // the "y" configuration property to scrollY to keep as much of the // Overlay inside the viewport as possible. yNew = nViewportOffset + scrollY; } } return yNew; }, /** * Given x, y coordinate values, returns the calculated coordinates required to * position the Overlay if it is to be constrained to the viewport, based on the * current element size, viewport dimensions and scroll values. * * @param {Number} x The X coordinate value to be constrained * @param {Number} y The Y coordinate value to be constrained * @return {Array} The constrained x and y coordinates at index 0 and 1 respectively; */ getConstrainedXY: function(x, y) { return [this.getConstrainedX(x), this.getConstrainedY(y)]; }, /** * Centers the container in the viewport. * @method center */ center: function () { var nViewportOffset = Overlay.VIEWPORT_OFFSET, elementWidth = this.element.offsetWidth, elementHeight = this.element.offsetHeight, viewPortWidth = Dom.getViewportWidth(), viewPortHeight = Dom.getViewportHeight(), x, y; if (elementWidth < viewPortWidth) { x = (viewPortWidth / 2) - (elementWidth / 2) + Dom.getDocumentScrollLeft(); } else { x = nViewportOffset + Dom.getDocumentScrollLeft(); } if (elementHeight < viewPortHeight) { y = (viewPortHeight / 2) - (elementHeight / 2) + Dom.getDocumentScrollTop(); } else { y = nViewportOffset + Dom.getDocumentScrollTop(); } this.cfg.setProperty("xy", [parseInt(x, 10), parseInt(y, 10)]); this.cfg.refireEvent("iframe"); if (UA.webkit) { this.forceContainerRedraw(); } }, /** * Synchronizes the Panel's "xy", "x", and "y" properties with the * Panel's position in the DOM. This is primarily used to update * position information during drag & drop. * @method syncPosition */ syncPosition: function () { var pos = Dom.getXY(this.element); this.cfg.setProperty("x", pos[0], true); this.cfg.setProperty("y", pos[1], true); this.cfg.setProperty("xy", pos, true); }, /** * Event handler fired when the resize monitor element is resized. * @method onDomResize * @param {DOMEvent} e The resize DOM event * @param {Object} obj The scope object */ onDomResize: function (e, obj) { var me = this; Overlay.superclass.onDomResize.call(this, e, obj); setTimeout(function () { me.syncPosition(); me.cfg.refireEvent("iframe"); me.cfg.refireEvent("context"); }, 0); }, /** * Determines the content box height of the given element (height of the element, without padding or borders) in pixels. * * @method _getComputedHeight * @private * @param {HTMLElement} el The element for which the content height needs to be determined * @return {Number} The content box height of the given element, or null if it could not be determined. */ _getComputedHeight : (function() { if (document.defaultView && document.defaultView.getComputedStyle) { return function(el) { var height = null; if (el.ownerDocument && el.ownerDocument.defaultView) { var computed = el.ownerDocument.defaultView.getComputedStyle(el, ''); if (computed) { height = parseInt(computed.height, 10); } } return (Lang.isNumber(height)) ? height : null; }; } else { return function(el) { var height = null; if (el.style.pixelHeight) { height = el.style.pixelHeight; } return (Lang.isNumber(height)) ? height : null; }; } })(), /** * autofillheight validator. Verifies that the autofill value is either null * or one of the strings : "body", "header" or "footer". * * @method _validateAutoFillHeight * @protected * @param {String} val * @return true, if valid, false otherwise */ _validateAutoFillHeight : function(val) { return (!val) || (Lang.isString(val) && Overlay.STD_MOD_RE.test(val)); }, /** * The default custom event handler executed when the overlay's height is changed, * if the autofillheight property has been set. * * @method _autoFillOnHeightChange * @protected * @param {String} type The event type * @param {Array} args The array of arguments passed to event subscribers * @param {HTMLElement} el The header, body or footer element which is to be resized to fill * out the containers height */ _autoFillOnHeightChange : function(type, args, el) { var height = this.cfg.getProperty("height"); if ((height && height !== "auto") || (height === 0)) { this.fillHeight(el); } }, /** * Returns the sub-pixel height of the el, using getBoundingClientRect, if available, * otherwise returns the offsetHeight * @method _getPreciseHeight * @private * @param {HTMLElement} el * @return {Float} The sub-pixel height if supported by the browser, else the rounded height. */ _getPreciseHeight : function(el) { var height = el.offsetHeight; if (el.getBoundingClientRect) { var rect = el.getBoundingClientRect(); height = rect.bottom - rect.top; } return height; }, /** * <p> * Sets the height on the provided header, body or footer element to * fill out the height of the container. It determines the height of the * containers content box, based on it's configured height value, and * sets the height of the autofillheight element to fill out any * space remaining after the other standard module element heights * have been accounted for. * </p> * <p><strong>NOTE:</strong> This method is not designed to work if an explicit * height has not been set on the container, since for an "auto" height container, * the heights of the header/body/footer will drive the height of the container.</p> * * @method fillHeight * @param {HTMLElement} el The element which should be resized to fill out the height * of the container element. */ fillHeight : function(el) { if (el) { var container = this.innerElement || this.element, containerEls = [this.header, this.body, this.footer], containerEl, total = 0, filled = 0, remaining = 0, validEl = false; for (var i = 0, l = containerEls.length; i < l; i++) { containerEl = containerEls[i]; if (containerEl) { if (el !== containerEl) { filled += this._getPreciseHeight(containerEl); } else { validEl = true; } } } if (validEl) { if (UA.ie || UA.opera) { // Need to set height to 0, to allow height to be reduced Dom.setStyle(el, 'height', 0 + 'px'); } total = this._getComputedHeight(container); // Fallback, if we can't get computed value for content height if (total === null) { Dom.addClass(container, "yui-override-padding"); total = container.clientHeight; // Content, No Border, 0 Padding (set by yui-override-padding) Dom.removeClass(container, "yui-override-padding"); } remaining = Math.max(total - filled, 0); Dom.setStyle(el, "height", remaining + "px"); // Re-adjust height if required, to account for el padding and border if (el.offsetHeight != remaining) { remaining = Math.max(remaining - (el.offsetHeight - remaining), 0); } Dom.setStyle(el, "height", remaining + "px"); } } }, /** * Places the Overlay on top of all other instances of * YAHOO.widget.Overlay. * @method bringToTop */ bringToTop: function () { var aOverlays = [], oElement = this.element; function compareZIndexDesc(p_oOverlay1, p_oOverlay2) { var sZIndex1 = Dom.getStyle(p_oOverlay1, "zIndex"), sZIndex2 = Dom.getStyle(p_oOverlay2, "zIndex"), nZIndex1 = (!sZIndex1 || isNaN(sZIndex1)) ? 0 : parseInt(sZIndex1, 10), nZIndex2 = (!sZIndex2 || isNaN(sZIndex2)) ? 0 : parseInt(sZIndex2, 10); if (nZIndex1 > nZIndex2) { return -1; } else if (nZIndex1 < nZIndex2) { return 1; } else { return 0; } } function isOverlayElement(p_oElement) { var isOverlay = Dom.hasClass(p_oElement, Overlay.CSS_OVERLAY), Panel = YAHOO.widget.Panel; if (isOverlay && !Dom.isAncestor(oElement, p_oElement)) { if (Panel && Dom.hasClass(p_oElement, Panel.CSS_PANEL)) { aOverlays[aOverlays.length] = p_oElement.parentNode; } else { aOverlays[aOverlays.length] = p_oElement; } } } Dom.getElementsBy(isOverlayElement, "DIV", document.body); aOverlays.sort(compareZIndexDesc); var oTopOverlay = aOverlays[0], nTopZIndex; if (oTopOverlay) { nTopZIndex = Dom.getStyle(oTopOverlay, "zIndex"); if (!isNaN(nTopZIndex)) { var bRequiresBump = false; if (oTopOverlay != oElement) { bRequiresBump = true; } else if (aOverlays.length > 1) { var nNextZIndex = Dom.getStyle(aOverlays[1], "zIndex"); // Don't rely on DOM order to stack if 2 overlays are at the same zindex. if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) { bRequiresBump = true; } } if (bRequiresBump) { this.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2)); } } } }, /** * Removes the Overlay element from the DOM and sets all child * elements to null. * @method destroy */ destroy: function () { if (this.iframe) { this.iframe.parentNode.removeChild(this.iframe); } this.iframe = null; Overlay.windowResizeEvent.unsubscribe( this.doCenterOnDOMEvent, this); Overlay.windowScrollEvent.unsubscribe( this.doCenterOnDOMEvent, this); Module.textResizeEvent.unsubscribe(this._autoFillOnHeightChange); Overlay.superclass.destroy.call(this); }, /** * Can be used to force the container to repaint/redraw it's contents. * <p> * By default applies and then removes a 1px bottom margin through the * application/removal of a "yui-force-redraw" class. * </p> * <p> * It is currently used by Overlay to force a repaint for webkit * browsers, when centering. * </p> * @method forceContainerRedraw */ forceContainerRedraw : function() { var c = this; Dom.addClass(c.element, "yui-force-redraw"); setTimeout(function() { Dom.removeClass(c.element, "yui-force-redraw"); }, 0); }, /** * Returns a String representation of the object. * @method toString * @return {String} The string representation of the Overlay. */ toString: function () { return "Overlay " + this.id; } }); }()); (function () { /** * OverlayManager is used for maintaining the focus status of * multiple Overlays. * @namespace YAHOO.widget * @namespace YAHOO.widget * @class OverlayManager * @constructor * @param {Array} overlays Optional. A collection of Overlays to register * with the manager. * @param {Object} userConfig The object literal representing the user * configuration of the OverlayManager */ YAHOO.widget.OverlayManager = function (userConfig) { this.init(userConfig); }; var Overlay = YAHOO.widget.Overlay, Event = YAHOO.util.Event, Dom = YAHOO.util.Dom, Config = YAHOO.util.Config, CustomEvent = YAHOO.util.CustomEvent, OverlayManager = YAHOO.widget.OverlayManager; /** * The CSS class representing a focused Overlay * @property OverlayManager.CSS_FOCUSED * @static * @final * @type String */ OverlayManager.CSS_FOCUSED = "focused"; OverlayManager.prototype = { /** * The class's constructor function * @property contructor * @type Function */ constructor: OverlayManager, /** * The array of Overlays that are currently registered * @property overlays * @type YAHOO.widget.Overlay[] */ overlays: null, /** * Initializes the default configuration of the OverlayManager * @method initDefaultConfig */ initDefaultConfig: function () { /** * The collection of registered Overlays in use by * the OverlayManager * @config overlays * @type YAHOO.widget.Overlay[] * @default null */ this.cfg.addProperty("overlays", { suppressEvent: true } ); /** * The default DOM event that should be used to focus an Overlay * @config focusevent * @type String * @default "mousedown" */ this.cfg.addProperty("focusevent", { value: "mousedown" } ); }, /** * Initializes the OverlayManager * @method init * @param {Overlay[]} overlays Optional. A collection of Overlays to * register with the manager. * @param {Object} userConfig The object literal representing the user * configuration of the OverlayManager */ init: function (userConfig) { /** * The OverlayManager's Config object used for monitoring * configuration properties. * @property cfg * @type Config */ this.cfg = new Config(this); this.initDefaultConfig(); if (userConfig) { this.cfg.applyConfig(userConfig, true); } this.cfg.fireQueue(); /** * The currently activated Overlay * @property activeOverlay * @private * @type YAHOO.widget.Overlay */ var activeOverlay = null; /** * Returns the currently focused Overlay * @method getActive * @return {Overlay} The currently focused Overlay */ this.getActive = function () { return activeOverlay; }; /** * Focuses the specified Overlay * @method focus * @param {Overlay} overlay The Overlay to focus * @param {String} overlay The id of the Overlay to focus */ this.focus = function (overlay) { var o = this.find(overlay); if (o) { o.focus(); } }; /** * Removes the specified Overlay from the manager * @method remove * @param {Overlay} overlay The Overlay to remove * @param {String} overlay The id of the Overlay to remove */ this.remove = function (overlay) { var o = this.find(overlay), originalZ; if (o) { if (activeOverlay == o) { activeOverlay = null; } var bDestroyed = (o.element === null && o.cfg === null) ? true : false; if (!bDestroyed) { // Set it's zindex so that it's sorted to the end. originalZ = Dom.getStyle(o.element, "zIndex"); o.cfg.setProperty("zIndex", -1000, true); } this.overlays.sort(this.compareZIndexDesc); this.overlays = this.overlays.slice(0, (this.overlays.length - 1)); o.hideEvent.unsubscribe(o.blur); o.destroyEvent.unsubscribe(this._onOverlayDestroy, o); o.focusEvent.unsubscribe(this._onOverlayFocusHandler, o); o.blurEvent.unsubscribe(this._onOverlayBlurHandler, o); if (!bDestroyed) { Event.removeListener(o.element, this.cfg.getProperty("focusevent"), this._onOverlayElementFocus); o.cfg.setProperty("zIndex", originalZ, true); o.cfg.setProperty("manager", null); } /* _managed Flag for custom or existing. Don't want to remove existing */ if (o.focusEvent._managed) { o.focusEvent = null; } if (o.blurEvent._managed) { o.blurEvent = null; } if (o.focus._managed) { o.focus = null; } if (o.blur._managed) { o.blur = null; } } }; /** * Removes focus from all registered Overlays in the manager * @method blurAll */ this.blurAll = function () { var nOverlays = this.overlays.length, i; if (nOverlays > 0) { i = nOverlays - 1; do { this.overlays[i].blur(); } while(i--); } }; /** * Updates the state of the OverlayManager and overlay, as a result of the overlay * being blurred. * * @method _manageBlur * @param {Overlay} overlay The overlay instance which got blurred. * @protected */ this._manageBlur = function (overlay) { var changed = false; if (activeOverlay == overlay) { Dom.removeClass(activeOverlay.element, OverlayManager.CSS_FOCUSED); activeOverlay = null; changed = true; } return changed; }; /** * Updates the state of the OverlayManager and overlay, as a result of the overlay * receiving focus. * * @method _manageFocus * @param {Overlay} overlay The overlay instance which got focus. * @protected */ this._manageFocus = function(overlay) { var changed = false; if (activeOverlay != overlay) { if (activeOverlay) { activeOverlay.blur(); } activeOverlay = overlay; this.bringToTop(activeOverlay); Dom.addClass(activeOverlay.element, OverlayManager.CSS_FOCUSED); changed = true; } return changed; }; var overlays = this.cfg.getProperty("overlays"); if (! this.overlays) { this.overlays = []; } if (overlays) { this.register(overlays); this.overlays.sort(this.compareZIndexDesc); } }, /** * @method _onOverlayElementFocus * @description Event handler for the DOM event that is used to focus * the Overlay instance as specified by the "focusevent" * configuration property. * @private * @param {Event} p_oEvent Object representing the DOM event * object passed back by the event utility (Event). */ _onOverlayElementFocus: function (p_oEvent) { var oTarget = Event.getTarget(p_oEvent), oClose = this.close; if (oClose && (oTarget == oClose || Dom.isAncestor(oClose, oTarget))) { this.blur(); } else { this.focus(); } }, /** * @method _onOverlayDestroy * @description "destroy" event handler for the Overlay. * @private * @param {String} p_sType String representing the name of the event * that was fired. * @param {Array} p_aArgs Array of arguments sent when the event * was fired. * @param {Overlay} p_oOverlay Object representing the overlay that * fired the event. */ _onOverlayDestroy: function (p_sType, p_aArgs, p_oOverlay) { this.remove(p_oOverlay); }, /** * @method _onOverlayFocusHandler * * focusEvent Handler, used to delegate to _manageFocus with the * correct arguments. * * @private * @param {String} p_sType String representing the name of the event * that was fired. * @param {Array} p_aArgs Array of arguments sent when the event * was fired. * @param {Overlay} p_oOverlay Object representing the overlay that * fired the event. */ _onOverlayFocusHandler: function(p_sType, p_aArgs, p_oOverlay) { this._manageFocus(p_oOverlay); }, /** * @method _onOverlayBlurHandler * * blurEvent Handler, used to delegate to _manageBlur with the * correct arguments. * * @private * @param {String} p_sType String representing the name of the event * that was fired. * @param {Array} p_aArgs Array of arguments sent when the event * was fired. * @param {Overlay} p_oOverlay Object representing the overlay that * fired the event. */ _onOverlayBlurHandler: function(p_sType, p_aArgs, p_oOverlay) { this._manageBlur(p_oOverlay); }, /** * Subscribes to the Overlay based instance focusEvent, to allow the OverlayManager to * monitor focus state. * * If the instance already has a focusEvent (e.g. Menu), OverlayManager will subscribe * to the existing focusEvent, however if a focusEvent or focus method does not exist * on the instance, the _bindFocus method will add them, and the focus method will * update the OverlayManager's state directly. * * @method _bindFocus * @param {Overlay} overlay The overlay for which focus needs to be managed * @protected */ _bindFocus : function(overlay) { var mgr = this; if (!overlay.focusEvent) { overlay.focusEvent = overlay.createEvent("focus"); overlay.focusEvent.signature = CustomEvent.LIST; overlay.focusEvent._managed = true; } else { overlay.focusEvent.subscribe(mgr._onOverlayFocusHandler, overlay, mgr); } if (!overlay.focus) { Event.on(overlay.element, mgr.cfg.getProperty("focusevent"), mgr._onOverlayElementFocus, null, overlay); overlay.focus = function () { if (mgr._manageFocus(this)) { // For Panel/Dialog if (this.cfg.getProperty("visible") && this.focusFirst) { this.focusFirst(); } this.focusEvent.fire(); } }; overlay.focus._managed = true; } }, /** * Subscribes to the Overlay based instance's blurEvent to allow the OverlayManager to * monitor blur state. * * If the instance already has a blurEvent (e.g. Menu), OverlayManager will subscribe * to the existing blurEvent, however if a blurEvent or blur method does not exist * on the instance, the _bindBlur method will add them, and the blur method * update the OverlayManager's state directly. * * @method _bindBlur * @param {Overlay} overlay The overlay for which blur needs to be managed * @protected */ _bindBlur : function(overlay) { var mgr = this; if (!overlay.blurEvent) { overlay.blurEvent = overlay.createEvent("blur"); overlay.blurEvent.signature = CustomEvent.LIST; overlay.focusEvent._managed = true; } else { overlay.blurEvent.subscribe(mgr._onOverlayBlurHandler, overlay, mgr); } if (!overlay.blur) { overlay.blur = function () { if (mgr._manageBlur(this)) { this.blurEvent.fire(); } }; overlay.blur._managed = true; } overlay.hideEvent.subscribe(overlay.blur); }, /** * Subscribes to the Overlay based instance's destroyEvent, to allow the Overlay * to be removed for the OverlayManager when destroyed. * * @method _bindDestroy * @param {Overlay} overlay The overlay instance being managed * @protected */ _bindDestroy : function(overlay) { var mgr = this; overlay.destroyEvent.subscribe(mgr._onOverlayDestroy, overlay, mgr); }, /** * Ensures the zIndex configuration property on the managed overlay based instance * is set to the computed zIndex value from the DOM (with "auto" translating to 0). * * @method _syncZIndex * @param {Overlay} overlay The overlay instance being managed * @protected */ _syncZIndex : function(overlay) { var zIndex = Dom.getStyle(overlay.element, "zIndex"); if (!isNaN(zIndex)) { overlay.cfg.setProperty("zIndex", parseInt(zIndex, 10)); } else { overlay.cfg.setProperty("zIndex", 0); } }, /** * Registers an Overlay or an array of Overlays with the manager. Upon * registration, the Overlay receives functions for focus and blur, * along with CustomEvents for each. * * @method register * @param {Overlay} overlay An Overlay to register with the manager. * @param {Overlay[]} overlay An array of Overlays to register with * the manager. * @return {boolean} true if any Overlays are registered. */ register: function (overlay) { var registered = false, i, n; if (overlay instanceof Overlay) { overlay.cfg.addProperty("manager", { value: this } ); this._bindFocus(overlay); this._bindBlur(overlay); this._bindDestroy(overlay); this._syncZIndex(overlay); this.overlays.push(overlay); this.bringToTop(overlay); registered = true; } else if (overlay instanceof Array) { for (i = 0, n = overlay.length; i < n; i++) { registered = this.register(overlay[i]) || registered; } } return registered; }, /** * Places the specified Overlay instance on top of all other * Overlay instances. * @method bringToTop * @param {YAHOO.widget.Overlay} p_oOverlay Object representing an * Overlay instance. * @param {String} p_oOverlay String representing the id of an * Overlay instance. */ bringToTop: function (p_oOverlay) { var oOverlay = this.find(p_oOverlay), nTopZIndex, oTopOverlay, aOverlays; if (oOverlay) { aOverlays = this.overlays; aOverlays.sort(this.compareZIndexDesc); oTopOverlay = aOverlays[0]; if (oTopOverlay) { nTopZIndex = Dom.getStyle(oTopOverlay.element, "zIndex"); if (!isNaN(nTopZIndex)) { var bRequiresBump = false; if (oTopOverlay !== oOverlay) { bRequiresBump = true; } else if (aOverlays.length > 1) { var nNextZIndex = Dom.getStyle(aOverlays[1].element, "zIndex"); // Don't rely on DOM order to stack if 2 overlays are at the same zindex. if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) { bRequiresBump = true; } } if (bRequiresBump) { oOverlay.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2)); } } aOverlays.sort(this.compareZIndexDesc); } } }, /** * Attempts to locate an Overlay by instance or ID. * @method find * @param {Overlay} overlay An Overlay to locate within the manager * @param {String} overlay An Overlay id to locate within the manager * @return {Overlay} The requested Overlay, if found, or null if it * cannot be located. */ find: function (overlay) { var isInstance = overlay instanceof Overlay, overlays = this.overlays, n = overlays.length, found = null, o, i; if (isInstance || typeof overlay == "string") { for (i = n-1; i >= 0; i--) { o = overlays[i]; if ((isInstance && (o === overlay)) || (o.id == overlay)) { found = o; break; } } } return found; }, /** * Used for sorting the manager's Overlays by z-index. * @method compareZIndexDesc * @private * @return {Number} 0, 1, or -1, depending on where the Overlay should * fall in the stacking order. */ compareZIndexDesc: function (o1, o2) { var zIndex1 = (o1.cfg) ? o1.cfg.getProperty("zIndex") : null, // Sort invalid (destroyed) zIndex2 = (o2.cfg) ? o2.cfg.getProperty("zIndex") : null; // objects at bottom. if (zIndex1 === null && zIndex2 === null) { return 0; } else if (zIndex1 === null){ return 1; } else if (zIndex2 === null) { return -1; } else if (zIndex1 > zIndex2) { return -1; } else if (zIndex1 < zIndex2) { return 1; } else { return 0; } }, /** * Shows all Overlays in the manager. * @method showAll */ showAll: function () { var overlays = this.overlays, n = overlays.length, i; for (i = n - 1; i >= 0; i--) { overlays[i].show(); } }, /** * Hides all Overlays in the manager. * @method hideAll */ hideAll: function () { var overlays = this.overlays, n = overlays.length, i; for (i = n - 1; i >= 0; i--) { overlays[i].hide(); } }, /** * Returns a string representation of the object. * @method toString * @return {String} The string representation of the OverlayManager */ toString: function () { return "OverlayManager"; } }; }()); (function () { /** * Tooltip is an implementation of Overlay that behaves like an OS tooltip, * displaying when the user mouses over a particular element, and * disappearing on mouse out. * @namespace YAHOO.widget * @class Tooltip * @extends YAHOO.widget.Overlay * @constructor * @param {String} el The element ID representing the Tooltip <em>OR</em> * @param {HTMLElement} el The element representing the Tooltip * @param {Object} userConfig The configuration object literal containing * the configuration that should be set for this Overlay. See configuration * documentation for more details. */ YAHOO.widget.Tooltip = function (el, userConfig) { YAHOO.widget.Tooltip.superclass.constructor.call(this, el, userConfig); }; var Lang = YAHOO.lang, Event = YAHOO.util.Event, CustomEvent = YAHOO.util.CustomEvent, Dom = YAHOO.util.Dom, Tooltip = YAHOO.widget.Tooltip, UA = YAHOO.env.ua, bIEQuirks = (UA.ie && (UA.ie <= 6 || document.compatMode == "BackCompat")), m_oShadowTemplate, /** * Constant representing the Tooltip's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "PREVENT_OVERLAP": { key: "preventoverlap", value: true, validator: Lang.isBoolean, supercedes: ["x", "y", "xy"] }, "SHOW_DELAY": { key: "showdelay", value: 200, validator: Lang.isNumber }, "AUTO_DISMISS_DELAY": { key: "autodismissdelay", value: 5000, validator: Lang.isNumber }, "HIDE_DELAY": { key: "hidedelay", value: 250, validator: Lang.isNumber }, "TEXT": { key: "text", suppressEvent: true }, "CONTAINER": { key: "container" }, "DISABLED": { key: "disabled", value: false, suppressEvent: true } }, /** * Constant representing the name of the Tooltip's events * @property EVENT_TYPES * @private * @final * @type Object */ EVENT_TYPES = { "CONTEXT_MOUSE_OVER": "contextMouseOver", "CONTEXT_MOUSE_OUT": "contextMouseOut", "CONTEXT_TRIGGER": "contextTrigger" }; /** * Constant representing the Tooltip CSS class * @property YAHOO.widget.Tooltip.CSS_TOOLTIP * @static * @final * @type String */ Tooltip.CSS_TOOLTIP = "yui-tt"; function restoreOriginalWidth(sOriginalWidth, sForcedWidth) { var oConfig = this.cfg, sCurrentWidth = oConfig.getProperty("width"); if (sCurrentWidth == sForcedWidth) { oConfig.setProperty("width", sOriginalWidth); } } /* changeContent event handler that sets a Tooltip instance's "width" configuration property to the value of its root HTML elements's offsetWidth if a specific width has not been set. */ function setWidthToOffsetWidth(p_sType, p_aArgs) { if ("_originalWidth" in this) { restoreOriginalWidth.call(this, this._originalWidth, this._forcedWidth); } var oBody = document.body, oConfig = this.cfg, sOriginalWidth = oConfig.getProperty("width"), sNewWidth, oClone; if ((!sOriginalWidth || sOriginalWidth == "auto") && (oConfig.getProperty("container") != oBody || oConfig.getProperty("x") >= Dom.getViewportWidth() || oConfig.getProperty("y") >= Dom.getViewportHeight())) { oClone = this.element.cloneNode(true); oClone.style.visibility = "hidden"; oClone.style.top = "0px"; oClone.style.left = "0px"; oBody.appendChild(oClone); sNewWidth = (oClone.offsetWidth + "px"); oBody.removeChild(oClone); oClone = null; oConfig.setProperty("width", sNewWidth); oConfig.refireEvent("xy"); this._originalWidth = sOriginalWidth || ""; this._forcedWidth = sNewWidth; } } // "onDOMReady" that renders the ToolTip function onDOMReady(p_sType, p_aArgs, p_oObject) { this.render(p_oObject); } // "init" event handler that automatically renders the Tooltip function onInit() { Event.onDOMReady(onDOMReady, this.cfg.getProperty("container"), this); } YAHOO.extend(Tooltip, YAHOO.widget.Overlay, { /** * The Tooltip initialization method. This method is automatically * called by the constructor. A Tooltip is automatically rendered by * the init method, and it also is set to be invisible by default, * and constrained to viewport by default as well. * @method init * @param {String} el The element ID representing the Tooltip <em>OR</em> * @param {HTMLElement} el The element representing the Tooltip * @param {Object} userConfig The configuration object literal * containing the configuration that should be set for this Tooltip. * See configuration documentation for more details. */ init: function (el, userConfig) { Tooltip.superclass.init.call(this, el); this.beforeInitEvent.fire(Tooltip); Dom.addClass(this.element, Tooltip.CSS_TOOLTIP); if (userConfig) { this.cfg.applyConfig(userConfig, true); } this.cfg.queueProperty("visible", false); this.cfg.queueProperty("constraintoviewport", true); this.setBody(""); this.subscribe("changeContent", setWidthToOffsetWidth); this.subscribe("init", onInit); this.subscribe("render", this.onRender); this.initEvent.fire(Tooltip); }, /** * Initializes the custom events for Tooltip * @method initEvents */ initEvents: function () { Tooltip.superclass.initEvents.call(this); var SIGNATURE = CustomEvent.LIST; /** * CustomEvent fired when user mouses over a context element. Returning false from * a subscriber to this event will prevent the tooltip from being displayed for * the current context element. * * @event contextMouseOverEvent * @param {HTMLElement} context The context element which the user just moused over * @param {DOMEvent} e The DOM event object, associated with the mouse over */ this.contextMouseOverEvent = this.createEvent(EVENT_TYPES.CONTEXT_MOUSE_OVER); this.contextMouseOverEvent.signature = SIGNATURE; /** * CustomEvent fired when the user mouses out of a context element. * * @event contextMouseOutEvent * @param {HTMLElement} context The context element which the user just moused out of * @param {DOMEvent} e The DOM event object, associated with the mouse out */ this.contextMouseOutEvent = this.createEvent(EVENT_TYPES.CONTEXT_MOUSE_OUT); this.contextMouseOutEvent.signature = SIGNATURE; /** * CustomEvent fired just before the tooltip is displayed for the current context. * <p> * You can subscribe to this event if you need to set up the text for the * tooltip based on the context element for which it is about to be displayed. * </p> * <p>This event differs from the beforeShow event in following respects:</p> * <ol> * <li> * When moving from one context element to another, if the tooltip is not * hidden (the <code>hidedelay</code> is not reached), the beforeShow and Show events will not * be fired when the tooltip is displayed for the new context since it is already visible. * However the contextTrigger event is always fired before displaying the tooltip for * a new context. * </li> * <li> * The trigger event provides access to the context element, allowing you to * set the text of the tooltip based on context element for which the tooltip is * triggered. * </li> * </ol> * <p> * It is not possible to prevent the tooltip from being displayed * using this event. You can use the contextMouseOverEvent if you need to prevent * the tooltip from being displayed. * </p> * @event contextTriggerEvent * @param {HTMLElement} context The context element for which the tooltip is triggered */ this.contextTriggerEvent = this.createEvent(EVENT_TYPES.CONTEXT_TRIGGER); this.contextTriggerEvent.signature = SIGNATURE; }, /** * Initializes the class's configurable properties which can be * changed using the Overlay's Config object (cfg). * @method initDefaultConfig */ initDefaultConfig: function () { Tooltip.superclass.initDefaultConfig.call(this); /** * Specifies whether the Tooltip should be kept from overlapping * its context element. * @config preventoverlap * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.PREVENT_OVERLAP.key, { value: DEFAULT_CONFIG.PREVENT_OVERLAP.value, validator: DEFAULT_CONFIG.PREVENT_OVERLAP.validator, supercedes: DEFAULT_CONFIG.PREVENT_OVERLAP.supercedes }); /** * The number of milliseconds to wait before showing a Tooltip * on mouseover. * @config showdelay * @type Number * @default 200 */ this.cfg.addProperty(DEFAULT_CONFIG.SHOW_DELAY.key, { handler: this.configShowDelay, value: 200, validator: DEFAULT_CONFIG.SHOW_DELAY.validator }); /** * The number of milliseconds to wait before automatically * dismissing a Tooltip after the mouse has been resting on the * context element. * @config autodismissdelay * @type Number * @default 5000 */ this.cfg.addProperty(DEFAULT_CONFIG.AUTO_DISMISS_DELAY.key, { handler: this.configAutoDismissDelay, value: DEFAULT_CONFIG.AUTO_DISMISS_DELAY.value, validator: DEFAULT_CONFIG.AUTO_DISMISS_DELAY.validator }); /** * The number of milliseconds to wait before hiding a Tooltip * after mouseout. * @config hidedelay * @type Number * @default 250 */ this.cfg.addProperty(DEFAULT_CONFIG.HIDE_DELAY.key, { handler: this.configHideDelay, value: DEFAULT_CONFIG.HIDE_DELAY.value, validator: DEFAULT_CONFIG.HIDE_DELAY.validator }); /** * Specifies the Tooltip's text. * @config text * @type String * @default null */ this.cfg.addProperty(DEFAULT_CONFIG.TEXT.key, { handler: this.configText, suppressEvent: DEFAULT_CONFIG.TEXT.suppressEvent }); /** * Specifies the container element that the Tooltip's markup * should be rendered into. * @config container * @type HTMLElement/String * @default document.body */ this.cfg.addProperty(DEFAULT_CONFIG.CONTAINER.key, { handler: this.configContainer, value: document.body }); /** * Specifies whether or not the tooltip is disabled. Disabled tooltips * will not be displayed. If the tooltip is driven by the title attribute * of the context element, the title attribute will still be removed for * disabled tooltips, to prevent default tooltip behavior. * * @config disabled * @type Boolean * @default false */ this.cfg.addProperty(DEFAULT_CONFIG.DISABLED.key, { handler: this.configContainer, value: DEFAULT_CONFIG.DISABLED.value, supressEvent: DEFAULT_CONFIG.DISABLED.suppressEvent }); /** * Specifies the element or elements that the Tooltip should be * anchored to on mouseover. * @config context * @type HTMLElement[]/String[] * @default null */ /** * String representing the width of the Tooltip. <em>Please note: * </em> As of version 2.3 if either no value or a value of "auto" * is specified, and the Toolip's "container" configuration property * is set to something other than <code>document.body</code> or * its "context" element resides outside the immediately visible * portion of the document, the width of the Tooltip will be * calculated based on the offsetWidth of its root HTML and set just * before it is made visible. The original value will be * restored when the Tooltip is hidden. This ensures the Tooltip is * rendered at a usable width. For more information see * SourceForge bug #1685496 and SourceForge * bug #1735423. * @config width * @type String * @default null */ }, // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // /** * The default event handler fired when the "text" property is changed. * @method configText * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configText: function (type, args, obj) { var text = args[0]; if (text) { this.setBody(text); } }, /** * The default event handler fired when the "container" property * is changed. * @method configContainer * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For * configuration handlers, args[0] will equal the newly applied value * for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configContainer: function (type, args, obj) { var container = args[0]; if (typeof container == 'string') { this.cfg.setProperty("container", document.getElementById(container), true); } }, /** * @method _removeEventListeners * @description Removes all of the DOM event handlers from the HTML * element(s) that trigger the display of the tooltip. * @protected */ _removeEventListeners: function () { var aElements = this._context, nElements, oElement, i; if (aElements) { nElements = aElements.length; if (nElements > 0) { i = nElements - 1; do { oElement = aElements[i]; Event.removeListener(oElement, "mouseover", this.onContextMouseOver); Event.removeListener(oElement, "mousemove", this.onContextMouseMove); Event.removeListener(oElement, "mouseout", this.onContextMouseOut); } while (i--); } } }, /** * The default event handler fired when the "context" property * is changed. * @method configContext * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configContext: function (type, args, obj) { var context = args[0], aElements, nElements, oElement, i; if (context) { // Normalize parameter into an array if (! (context instanceof Array)) { if (typeof context == "string") { this.cfg.setProperty("context", [document.getElementById(context)], true); } else { // Assuming this is an element this.cfg.setProperty("context", [context], true); } context = this.cfg.getProperty("context"); } // Remove any existing mouseover/mouseout listeners this._removeEventListeners(); // Add mouseover/mouseout listeners to context elements this._context = context; aElements = this._context; if (aElements) { nElements = aElements.length; if (nElements > 0) { i = nElements - 1; do { oElement = aElements[i]; Event.on(oElement, "mouseover", this.onContextMouseOver, this); Event.on(oElement, "mousemove", this.onContextMouseMove, this); Event.on(oElement, "mouseout", this.onContextMouseOut, this); } while (i--); } } } }, // END BUILT-IN PROPERTY EVENT HANDLERS // // BEGIN BUILT-IN DOM EVENT HANDLERS // /** * The default event handler fired when the user moves the mouse while * over the context element. * @method onContextMouseMove * @param {DOMEvent} e The current DOM event * @param {Object} obj The object argument */ onContextMouseMove: function (e, obj) { obj.pageX = Event.getPageX(e); obj.pageY = Event.getPageY(e); }, /** * The default event handler fired when the user mouses over the * context element. * @method onContextMouseOver * @param {DOMEvent} e The current DOM event * @param {Object} obj The object argument */ onContextMouseOver: function (e, obj) { var context = this; if (context.title) { obj._tempTitle = context.title; context.title = ""; } // Fire first, to honor disabled set in the listner if (obj.fireEvent("contextMouseOver", context, e) !== false && !obj.cfg.getProperty("disabled")) { // Stop the tooltip from being hidden (set on last mouseout) if (obj.hideProcId) { clearTimeout(obj.hideProcId); obj.hideProcId = null; } Event.on(context, "mousemove", obj.onContextMouseMove, obj); /** * The unique process ID associated with the thread responsible * for showing the Tooltip. * @type int */ obj.showProcId = obj.doShow(e, context); } }, /** * The default event handler fired when the user mouses out of * the context element. * @method onContextMouseOut * @param {DOMEvent} e The current DOM event * @param {Object} obj The object argument */ onContextMouseOut: function (e, obj) { var el = this; if (obj._tempTitle) { el.title = obj._tempTitle; obj._tempTitle = null; } if (obj.showProcId) { clearTimeout(obj.showProcId); obj.showProcId = null; } if (obj.hideProcId) { clearTimeout(obj.hideProcId); obj.hideProcId = null; } obj.fireEvent("contextMouseOut", el, e); obj.hideProcId = setTimeout(function () { obj.hide(); }, obj.cfg.getProperty("hidedelay")); }, // END BUILT-IN DOM EVENT HANDLERS // /** * Processes the showing of the Tooltip by setting the timeout delay * and offset of the Tooltip. * @method doShow * @param {DOMEvent} e The current DOM event * @param {HTMLElement} context The current context element * @return {Number} The process ID of the timeout function associated * with doShow */ doShow: function (e, context) { var yOffset = 25, me = this; if (UA.opera && context.tagName && context.tagName.toUpperCase() == "A") { yOffset += 12; } return setTimeout(function () { var txt = me.cfg.getProperty("text"); // title does not over-ride text if (me._tempTitle && (txt === "" || YAHOO.lang.isUndefined(txt) || YAHOO.lang.isNull(txt))) { me.setBody(me._tempTitle); } else { me.cfg.refireEvent("text"); } me.moveTo(me.pageX, me.pageY + yOffset); if (me.cfg.getProperty("preventoverlap")) { me.preventOverlap(me.pageX, me.pageY); } Event.removeListener(context, "mousemove", me.onContextMouseMove); me.contextTriggerEvent.fire(context); me.show(); me.hideProcId = me.doHide(); }, this.cfg.getProperty("showdelay")); }, /** * Sets the timeout for the auto-dismiss delay, which by default is 5 * seconds, meaning that a tooltip will automatically dismiss itself * after 5 seconds of being displayed. * @method doHide */ doHide: function () { var me = this; return setTimeout(function () { me.hide(); }, this.cfg.getProperty("autodismissdelay")); }, /** * Fired when the Tooltip is moved, this event handler is used to * prevent the Tooltip from overlapping with its context element. * @method preventOverlay * @param {Number} pageX The x coordinate position of the mouse pointer * @param {Number} pageY The y coordinate position of the mouse pointer */ preventOverlap: function (pageX, pageY) { var height = this.element.offsetHeight, mousePoint = new YAHOO.util.Point(pageX, pageY), elementRegion = Dom.getRegion(this.element); elementRegion.top -= 5; elementRegion.left -= 5; elementRegion.right += 5; elementRegion.bottom += 5; if (elementRegion.contains(mousePoint)) { this.cfg.setProperty("y", (pageY - height - 5)); } }, /** * @method onRender * @description "render" event handler for the Tooltip. * @param {String} p_sType String representing the name of the event * that was fired. * @param {Array} p_aArgs Array of arguments sent when the event * was fired. */ onRender: function (p_sType, p_aArgs) { function sizeShadow() { var oElement = this.element, oShadow = this.underlay; if (oShadow) { oShadow.style.width = (oElement.offsetWidth + 6) + "px"; oShadow.style.height = (oElement.offsetHeight + 1) + "px"; } } function addShadowVisibleClass() { Dom.addClass(this.underlay, "yui-tt-shadow-visible"); if (UA.ie) { this.forceUnderlayRedraw(); } } function removeShadowVisibleClass() { Dom.removeClass(this.underlay, "yui-tt-shadow-visible"); } function createShadow() { var oShadow = this.underlay, oElement, Module, nIE, me; if (!oShadow) { oElement = this.element; Module = YAHOO.widget.Module; nIE = UA.ie; me = this; if (!m_oShadowTemplate) { m_oShadowTemplate = document.createElement("div"); m_oShadowTemplate.className = "yui-tt-shadow"; } oShadow = m_oShadowTemplate.cloneNode(false); oElement.appendChild(oShadow); this.underlay = oShadow; // Backward compatibility, even though it's probably // intended to be "private", it isn't marked as such in the api docs this._shadow = this.underlay; addShadowVisibleClass.call(this); this.subscribe("beforeShow", addShadowVisibleClass); this.subscribe("hide", removeShadowVisibleClass); if (bIEQuirks) { window.setTimeout(function () { sizeShadow.call(me); }, 0); this.cfg.subscribeToConfigEvent("width", sizeShadow); this.cfg.subscribeToConfigEvent("height", sizeShadow); this.subscribe("changeContent", sizeShadow); Module.textResizeEvent.subscribe(sizeShadow, this, true); this.subscribe("destroy", function () { Module.textResizeEvent.unsubscribe(sizeShadow, this); }); } } } function onBeforeShow() { createShadow.call(this); this.unsubscribe("beforeShow", onBeforeShow); } if (this.cfg.getProperty("visible")) { createShadow.call(this); } else { this.subscribe("beforeShow", onBeforeShow); } }, /** * Forces the underlay element to be repainted, through the application/removal * of a yui-force-redraw class to the underlay element. * * @method forceUnderlayRedraw */ forceUnderlayRedraw : function() { var tt = this; Dom.addClass(tt.underlay, "yui-force-redraw"); setTimeout(function() {Dom.removeClass(tt.underlay, "yui-force-redraw");}, 0); }, /** * Removes the Tooltip element from the DOM and sets all child * elements to null. * @method destroy */ destroy: function () { // Remove any existing mouseover/mouseout listeners this._removeEventListeners(); Tooltip.superclass.destroy.call(this); }, /** * Returns a string representation of the object. * @method toString * @return {String} The string representation of the Tooltip */ toString: function () { return "Tooltip " + this.id; } }); }()); (function () { /** * Panel is an implementation of Overlay that behaves like an OS window, * with a draggable header and an optional close icon at the top right. * @namespace YAHOO.widget * @class Panel * @extends YAHOO.widget.Overlay * @constructor * @param {String} el The element ID representing the Panel <em>OR</em> * @param {HTMLElement} el The element representing the Panel * @param {Object} userConfig The configuration object literal containing * the configuration that should be set for this Panel. See configuration * documentation for more details. */ YAHOO.widget.Panel = function (el, userConfig) { YAHOO.widget.Panel.superclass.constructor.call(this, el, userConfig); }; var _currentModal = null; var Lang = YAHOO.lang, Util = YAHOO.util, Dom = Util.Dom, Event = Util.Event, CustomEvent = Util.CustomEvent, KeyListener = YAHOO.util.KeyListener, Config = Util.Config, Overlay = YAHOO.widget.Overlay, Panel = YAHOO.widget.Panel, UA = YAHOO.env.ua, bIEQuirks = (UA.ie && (UA.ie <= 6 || document.compatMode == "BackCompat")), m_oMaskTemplate, m_oUnderlayTemplate, m_oCloseIconTemplate, /** * Constant representing the name of the Panel's events * @property EVENT_TYPES * @private * @final * @type Object */ EVENT_TYPES = { "SHOW_MASK": "showMask", "HIDE_MASK": "hideMask", "DRAG": "drag" }, /** * Constant representing the Panel's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "CLOSE": { key: "close", value: true, validator: Lang.isBoolean, supercedes: ["visible"] }, "DRAGGABLE": { key: "draggable", value: (Util.DD ? true : false), validator: Lang.isBoolean, supercedes: ["visible"] }, "DRAG_ONLY" : { key: "dragonly", value: false, validator: Lang.isBoolean, supercedes: ["draggable"] }, "UNDERLAY": { key: "underlay", value: "shadow", supercedes: ["visible"] }, "MODAL": { key: "modal", value: false, validator: Lang.isBoolean, supercedes: ["visible", "zindex"] }, "KEY_LISTENERS": { key: "keylisteners", suppressEvent: true, supercedes: ["visible"] }, "STRINGS" : { key: "strings", supercedes: ["close"], validator: Lang.isObject, value: { close: "Close" } } }; /** * Constant representing the default CSS class used for a Panel * @property YAHOO.widget.Panel.CSS_PANEL * @static * @final * @type String */ Panel.CSS_PANEL = "yui-panel"; /** * Constant representing the default CSS class used for a Panel's * wrapping container * @property YAHOO.widget.Panel.CSS_PANEL_CONTAINER * @static * @final * @type String */ Panel.CSS_PANEL_CONTAINER = "yui-panel-container"; /** * Constant representing the default set of focusable elements * on the pagewhich Modal Panels will prevent access to, when * the modal mask is displayed * * @property YAHOO.widget.Panel.FOCUSABLE * @static * @type Array */ Panel.FOCUSABLE = [ "a", "button", "select", "textarea", "input", "iframe" ]; // Private CustomEvent listeners /* "beforeRender" event handler that creates an empty header for a Panel instance if its "draggable" configuration property is set to "true" and no header has been created. */ function createHeader(p_sType, p_aArgs) { if (!this.header && this.cfg.getProperty("draggable")) { this.setHeader(" "); } } /* "hide" event handler that sets a Panel instance's "width" configuration property back to its original value before "setWidthToOffsetWidth" was called. */ function restoreOriginalWidth(p_sType, p_aArgs, p_oObject) { var sOriginalWidth = p_oObject[0], sNewWidth = p_oObject[1], oConfig = this.cfg, sCurrentWidth = oConfig.getProperty("width"); if (sCurrentWidth == sNewWidth) { oConfig.setProperty("width", sOriginalWidth); } this.unsubscribe("hide", restoreOriginalWidth, p_oObject); } /* "beforeShow" event handler that sets a Panel instance's "width" configuration property to the value of its root HTML elements's offsetWidth */ function setWidthToOffsetWidth(p_sType, p_aArgs) { var oConfig, sOriginalWidth, sNewWidth; if (bIEQuirks) { oConfig = this.cfg; sOriginalWidth = oConfig.getProperty("width"); if (!sOriginalWidth || sOriginalWidth == "auto") { sNewWidth = (this.element.offsetWidth + "px"); oConfig.setProperty("width", sNewWidth); this.subscribe("hide", restoreOriginalWidth, [(sOriginalWidth || ""), sNewWidth]); } } } YAHOO.extend(Panel, Overlay, { /** * The Overlay initialization method, which is executed for Overlay and * all of its subclasses. This method is automatically called by the * constructor, and sets up all DOM references for pre-existing markup, * and creates required markup if it is not already present. * @method init * @param {String} el The element ID representing the Overlay <em>OR</em> * @param {HTMLElement} el The element representing the Overlay * @param {Object} userConfig The configuration object literal * containing the configuration that should be set for this Overlay. * See configuration documentation for more details. */ init: function (el, userConfig) { /* Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level */ Panel.superclass.init.call(this, el/*, userConfig*/); this.beforeInitEvent.fire(Panel); Dom.addClass(this.element, Panel.CSS_PANEL); this.buildWrapper(); if (userConfig) { this.cfg.applyConfig(userConfig, true); } this.subscribe("showMask", this._addFocusHandlers); this.subscribe("hideMask", this._removeFocusHandlers); this.subscribe("beforeRender", createHeader); this.subscribe("render", function() { this.setFirstLastFocusable(); this.subscribe("changeContent", this.setFirstLastFocusable); }); this.subscribe("show", this.focusFirst); this.initEvent.fire(Panel); }, /** * @method _onElementFocus * @private * * "focus" event handler for a focuable element. Used to automatically * blur the element when it receives focus to ensure that a Panel * instance's modality is not compromised. * * @param {Event} e The DOM event object */ _onElementFocus : function(e){ if(_currentModal === this) { var target = Event.getTarget(e), doc = document.documentElement, insideDoc = (target !== doc && target !== window); // mask and documentElement checks added for IE, which focuses on the mask when it's clicked on, and focuses on // the documentElement, when the document scrollbars are clicked on if (insideDoc && target !== this.element && target !== this.mask && !Dom.isAncestor(this.element, target)) { try { if (this.firstElement) { this.firstElement.focus(); } else { if (this._modalFocus) { this._modalFocus.focus(); } else { this.innerElement.focus(); } } } catch(err){ // Just in case we fail to focus try { if (insideDoc && target !== document.body) { target.blur(); } } catch(err2) { } } } } }, /** * @method _addFocusHandlers * @protected * * "showMask" event handler that adds a "focus" event handler to all * focusable elements in the document to enforce a Panel instance's * modality from being compromised. * * @param p_sType {String} Custom event type * @param p_aArgs {Array} Custom event arguments */ _addFocusHandlers: function(p_sType, p_aArgs) { if (!this.firstElement) { if (UA.webkit || UA.opera) { if (!this._modalFocus) { this._createHiddenFocusElement(); } } else { this.innerElement.tabIndex = 0; } } this.setTabLoop(this.firstElement, this.lastElement); Event.onFocus(document.documentElement, this._onElementFocus, this, true); _currentModal = this; }, /** * Creates a hidden focusable element, used to focus on, * to enforce modality for browsers in which focus cannot * be applied to the container box. * * @method _createHiddenFocusElement * @private */ _createHiddenFocusElement : function() { var e = document.createElement("button"); e.style.height = "1px"; e.style.width = "1px"; e.style.position = "absolute"; e.style.left = "-10000em"; e.style.opacity = 0; e.tabIndex = -1; this.innerElement.appendChild(e); this._modalFocus = e; }, /** * @method _removeFocusHandlers * @protected * * "hideMask" event handler that removes all "focus" event handlers added * by the "addFocusEventHandlers" method. * * @param p_sType {String} Event type * @param p_aArgs {Array} Event Arguments */ _removeFocusHandlers: function(p_sType, p_aArgs) { Event.removeFocusListener(document.documentElement, this._onElementFocus, this); if (_currentModal == this) { _currentModal = null; } }, /** * Sets focus to the first element in the Panel. * * @method focusFirst */ focusFirst: function (type, args, obj) { var el = this.firstElement; if (args && args[1]) { Event.stopEvent(args[1]); } if (el) { try { el.focus(); } catch(err) { // Ignore } } }, /** * Sets focus to the last element in the Panel. * * @method focusLast */ focusLast: function (type, args, obj) { var el = this.lastElement; if (args && args[1]) { Event.stopEvent(args[1]); } if (el) { try { el.focus(); } catch(err) { // Ignore } } }, /** * Sets up a tab, shift-tab loop between the first and last elements * provided. NOTE: Sets up the preventBackTab and preventTabOut KeyListener * instance properties, which are reset everytime this method is invoked. * * @method setTabLoop * @param {HTMLElement} firstElement * @param {HTMLElement} lastElement * */ setTabLoop : function(firstElement, lastElement) { var backTab = this.preventBackTab, tab = this.preventTabOut, showEvent = this.showEvent, hideEvent = this.hideEvent; if (backTab) { backTab.disable(); showEvent.unsubscribe(backTab.enable, backTab); hideEvent.unsubscribe(backTab.disable, backTab); backTab = this.preventBackTab = null; } if (tab) { tab.disable(); showEvent.unsubscribe(tab.enable, tab); hideEvent.unsubscribe(tab.disable,tab); tab = this.preventTabOut = null; } if (firstElement) { this.preventBackTab = new KeyListener(firstElement, {shift:true, keys:9}, {fn:this.focusLast, scope:this, correctScope:true} ); backTab = this.preventBackTab; showEvent.subscribe(backTab.enable, backTab, true); hideEvent.subscribe(backTab.disable,backTab, true); } if (lastElement) { this.preventTabOut = new KeyListener(lastElement, {shift:false, keys:9}, {fn:this.focusFirst, scope:this, correctScope:true} ); tab = this.preventTabOut; showEvent.subscribe(tab.enable, tab, true); hideEvent.subscribe(tab.disable,tab, true); } }, /** * Returns an array of the currently focusable items which reside within * Panel. The set of focusable elements the method looks for are defined * in the Panel.FOCUSABLE static property * * @method getFocusableElements * @param {HTMLElement} root element to start from. */ getFocusableElements : function(root) { root = root || this.innerElement; var focusable = {}; for (var i = 0; i < Panel.FOCUSABLE.length; i++) { focusable[Panel.FOCUSABLE[i]] = true; } function isFocusable(el) { if (el.focus && el.type !== "hidden" && !el.disabled && focusable[el.tagName.toLowerCase()]) { return true; } return false; } // Not looking by Tag, since we want elements in DOM order return Dom.getElementsBy(isFocusable, null, root); }, /** * Sets the firstElement and lastElement instance properties * to the first and last focusable elements in the Panel. * * @method setFirstLastFocusable */ setFirstLastFocusable : function() { this.firstElement = null; this.lastElement = null; var elements = this.getFocusableElements(); this.focusableElements = elements; if (elements.length > 0) { this.firstElement = elements[0]; this.lastElement = elements[elements.length - 1]; } if (this.cfg.getProperty("modal")) { this.setTabLoop(this.firstElement, this.lastElement); } }, /** * Initializes the custom events for Module which are fired * automatically at appropriate times by the Module class. */ initEvents: function () { Panel.superclass.initEvents.call(this); var SIGNATURE = CustomEvent.LIST; /** * CustomEvent fired after the modality mask is shown * @event showMaskEvent */ this.showMaskEvent = this.createEvent(EVENT_TYPES.SHOW_MASK); this.showMaskEvent.signature = SIGNATURE; /** * CustomEvent fired after the modality mask is hidden * @event hideMaskEvent */ this.hideMaskEvent = this.createEvent(EVENT_TYPES.HIDE_MASK); this.hideMaskEvent.signature = SIGNATURE; /** * CustomEvent when the Panel is dragged * @event dragEvent */ this.dragEvent = this.createEvent(EVENT_TYPES.DRAG); this.dragEvent.signature = SIGNATURE; }, /** * Initializes the class's configurable properties which can be changed * using the Panel's Config object (cfg). * @method initDefaultConfig */ initDefaultConfig: function () { Panel.superclass.initDefaultConfig.call(this); // Add panel config properties // /** * True if the Panel should display a "close" button * @config close * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.CLOSE.key, { handler: this.configClose, value: DEFAULT_CONFIG.CLOSE.value, validator: DEFAULT_CONFIG.CLOSE.validator, supercedes: DEFAULT_CONFIG.CLOSE.supercedes }); /** * Boolean specifying if the Panel should be draggable. The default * value is "true" if the Drag and Drop utility is included, * otherwise it is "false." <strong>PLEASE NOTE:</strong> There is a * known issue in IE 6 (Strict Mode and Quirks Mode) and IE 7 * (Quirks Mode) where Panels that either don't have a value set for * their "width" configuration property, or their "width" * configuration property is set to "auto" will only be draggable by * placing the mouse on the text of the Panel's header element. * To fix this bug, draggable Panels missing a value for their * "width" configuration property, or whose "width" configuration * property is set to "auto" will have it set to the value of * their root HTML element's offsetWidth before they are made * visible. The calculated width is then removed when the Panel is * hidden. <em>This fix is only applied to draggable Panels in IE 6 * (Strict Mode and Quirks Mode) and IE 7 (Quirks Mode)</em>. For * more information on this issue see: * SourceForge bugs #1726972 and #1589210. * @config draggable * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.DRAGGABLE.key, { handler: this.configDraggable, value: (Util.DD) ? true : false, validator: DEFAULT_CONFIG.DRAGGABLE.validator, supercedes: DEFAULT_CONFIG.DRAGGABLE.supercedes }); /** * Boolean specifying if the draggable Panel should be drag only, not interacting with drop * targets on the page. * <p> * When set to true, draggable Panels will not check to see if they are over drop targets, * or fire the DragDrop events required to support drop target interaction (onDragEnter, * onDragOver, onDragOut, onDragDrop etc.). * If the Panel is not designed to be dropped on any target elements on the page, then this * flag can be set to true to improve performance. * </p> * <p> * When set to false, all drop target related events will be fired. * </p> * <p> * The property is set to false by default to maintain backwards compatibility but should be * set to true if drop target interaction is not required for the Panel, to improve performance.</p> * * @config dragOnly * @type Boolean * @default false */ this.cfg.addProperty(DEFAULT_CONFIG.DRAG_ONLY.key, { value: DEFAULT_CONFIG.DRAG_ONLY.value, validator: DEFAULT_CONFIG.DRAG_ONLY.validator, supercedes: DEFAULT_CONFIG.DRAG_ONLY.supercedes }); /** * Sets the type of underlay to display for the Panel. Valid values * are "shadow," "matte," and "none". <strong>PLEASE NOTE:</strong> * The creation of the underlay element is deferred until the Panel * is initially made visible. For Gecko-based browsers on Mac * OS X the underlay elment is always created as it is used as a * shim to prevent Aqua scrollbars below a Panel instance from poking * through it (See SourceForge bug #836476). * @config underlay * @type String * @default shadow */ this.cfg.addProperty(DEFAULT_CONFIG.UNDERLAY.key, { handler: this.configUnderlay, value: DEFAULT_CONFIG.UNDERLAY.value, supercedes: DEFAULT_CONFIG.UNDERLAY.supercedes }); /** * True if the Panel should be displayed in a modal fashion, * automatically creating a transparent mask over the document that * will not be removed until the Panel is dismissed. * @config modal * @type Boolean * @default false */ this.cfg.addProperty(DEFAULT_CONFIG.MODAL.key, { handler: this.configModal, value: DEFAULT_CONFIG.MODAL.value, validator: DEFAULT_CONFIG.MODAL.validator, supercedes: DEFAULT_CONFIG.MODAL.supercedes }); /** * A KeyListener (or array of KeyListeners) that will be enabled * when the Panel is shown, and disabled when the Panel is hidden. * @config keylisteners * @type YAHOO.util.KeyListener[] * @default null */ this.cfg.addProperty(DEFAULT_CONFIG.KEY_LISTENERS.key, { handler: this.configKeyListeners, suppressEvent: DEFAULT_CONFIG.KEY_LISTENERS.suppressEvent, supercedes: DEFAULT_CONFIG.KEY_LISTENERS.supercedes }); /** * UI Strings used by the Panel * * @config strings * @type Object * @default An object literal with the properties shown below: * <dl> * <dt>close</dt><dd><em>String</em> : The string to use for the close icon. Defaults to "Close".</dd> * </dl> */ this.cfg.addProperty(DEFAULT_CONFIG.STRINGS.key, { value:DEFAULT_CONFIG.STRINGS.value, handler:this.configStrings, validator:DEFAULT_CONFIG.STRINGS.validator, supercedes:DEFAULT_CONFIG.STRINGS.supercedes }); }, // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // /** * The default event handler fired when the "close" property is changed. * The method controls the appending or hiding of the close icon at the * top right of the Panel. * @method configClose * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configClose: function (type, args, obj) { var val = args[0], oClose = this.close, strings = this.cfg.getProperty("strings"); if (val) { if (!oClose) { if (!m_oCloseIconTemplate) { m_oCloseIconTemplate = document.createElement("a"); m_oCloseIconTemplate.className = "container-close"; m_oCloseIconTemplate.href = "#"; } oClose = m_oCloseIconTemplate.cloneNode(true); this.innerElement.appendChild(oClose); oClose.innerHTML = (strings && strings.close) ? strings.close : " "; Event.on(oClose, "click", this._doClose, this, true); this.close = oClose; } else { oClose.style.display = "block"; } } else { if (oClose) { oClose.style.display = "none"; } } }, /** * Event handler for the close icon * * @method _doClose * @protected * * @param {DOMEvent} e */ _doClose : function (e) { Event.preventDefault(e); this.hide(); }, /** * The default event handler fired when the "draggable" property * is changed. * @method configDraggable * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configDraggable: function (type, args, obj) { var val = args[0]; if (val) { if (!Util.DD) { this.cfg.setProperty("draggable", false); return; } if (this.header) { Dom.setStyle(this.header, "cursor", "move"); this.registerDragDrop(); } this.subscribe("beforeShow", setWidthToOffsetWidth); } else { if (this.dd) { this.dd.unreg(); } if (this.header) { Dom.setStyle(this.header,"cursor","auto"); } this.unsubscribe("beforeShow", setWidthToOffsetWidth); } }, /** * The default event handler fired when the "underlay" property * is changed. * @method configUnderlay * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configUnderlay: function (type, args, obj) { var bMacGecko = (this.platform == "mac" && UA.gecko), sUnderlay = args[0].toLowerCase(), oUnderlay = this.underlay, oElement = this.element; function createUnderlay() { var bNew = false; if (!oUnderlay) { // create if not already in DOM if (!m_oUnderlayTemplate) { m_oUnderlayTemplate = document.createElement("div"); m_oUnderlayTemplate.className = "underlay"; } oUnderlay = m_oUnderlayTemplate.cloneNode(false); this.element.appendChild(oUnderlay); this.underlay = oUnderlay; if (bIEQuirks) { this.sizeUnderlay(); this.cfg.subscribeToConfigEvent("width", this.sizeUnderlay); this.cfg.subscribeToConfigEvent("height", this.sizeUnderlay); this.changeContentEvent.subscribe(this.sizeUnderlay); YAHOO.widget.Module.textResizeEvent.subscribe(this.sizeUnderlay, this, true); } if (UA.webkit && UA.webkit < 420) { this.changeContentEvent.subscribe(this.forceUnderlayRedraw); } bNew = true; } } function onBeforeShow() { var bNew = createUnderlay.call(this); if (!bNew && bIEQuirks) { this.sizeUnderlay(); } this._underlayDeferred = false; this.beforeShowEvent.unsubscribe(onBeforeShow); } function destroyUnderlay() { if (this._underlayDeferred) { this.beforeShowEvent.unsubscribe(onBeforeShow); this._underlayDeferred = false; } if (oUnderlay) { this.cfg.unsubscribeFromConfigEvent("width", this.sizeUnderlay); this.cfg.unsubscribeFromConfigEvent("height",this.sizeUnderlay); this.changeContentEvent.unsubscribe(this.sizeUnderlay); this.changeContentEvent.unsubscribe(this.forceUnderlayRedraw); YAHOO.widget.Module.textResizeEvent.unsubscribe(this.sizeUnderlay, this, true); this.element.removeChild(oUnderlay); this.underlay = null; } } switch (sUnderlay) { case "shadow": Dom.removeClass(oElement, "matte"); Dom.addClass(oElement, "shadow"); break; case "matte": if (!bMacGecko) { destroyUnderlay.call(this); } Dom.removeClass(oElement, "shadow"); Dom.addClass(oElement, "matte"); break; default: if (!bMacGecko) { destroyUnderlay.call(this); } Dom.removeClass(oElement, "shadow"); Dom.removeClass(oElement, "matte"); break; } if ((sUnderlay == "shadow") || (bMacGecko && !oUnderlay)) { if (this.cfg.getProperty("visible")) { var bNew = createUnderlay.call(this); if (!bNew && bIEQuirks) { this.sizeUnderlay(); } } else { if (!this._underlayDeferred) { this.beforeShowEvent.subscribe(onBeforeShow); this._underlayDeferred = true; } } } }, /** * The default event handler fired when the "modal" property is * changed. This handler subscribes or unsubscribes to the show and hide * events to handle the display or hide of the modality mask. * @method configModal * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configModal: function (type, args, obj) { var modal = args[0]; if (modal) { if (!this._hasModalityEventListeners) { this.subscribe("beforeShow", this.buildMask); this.subscribe("beforeShow", this.bringToTop); this.subscribe("beforeShow", this.showMask); this.subscribe("hide", this.hideMask); Overlay.windowResizeEvent.subscribe(this.sizeMask, this, true); this._hasModalityEventListeners = true; } } else { if (this._hasModalityEventListeners) { if (this.cfg.getProperty("visible")) { this.hideMask(); this.removeMask(); } this.unsubscribe("beforeShow", this.buildMask); this.unsubscribe("beforeShow", this.bringToTop); this.unsubscribe("beforeShow", this.showMask); this.unsubscribe("hide", this.hideMask); Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this); this._hasModalityEventListeners = false; } } }, /** * Removes the modality mask. * @method removeMask */ removeMask: function () { var oMask = this.mask, oParentNode; if (oMask) { /* Hide the mask before destroying it to ensure that DOM event handlers on focusable elements get removed. */ this.hideMask(); oParentNode = oMask.parentNode; if (oParentNode) { oParentNode.removeChild(oMask); } this.mask = null; } }, /** * The default event handler fired when the "keylisteners" property * is changed. * @method configKeyListeners * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configKeyListeners: function (type, args, obj) { var listeners = args[0], listener, nListeners, i; if (listeners) { if (listeners instanceof Array) { nListeners = listeners.length; for (i = 0; i < nListeners; i++) { listener = listeners[i]; if (!Config.alreadySubscribed(this.showEvent, listener.enable, listener)) { this.showEvent.subscribe(listener.enable, listener, true); } if (!Config.alreadySubscribed(this.hideEvent, listener.disable, listener)) { this.hideEvent.subscribe(listener.disable, listener, true); this.destroyEvent.subscribe(listener.disable, listener, true); } } } else { if (!Config.alreadySubscribed(this.showEvent, listeners.enable, listeners)) { this.showEvent.subscribe(listeners.enable, listeners, true); } if (!Config.alreadySubscribed(this.hideEvent, listeners.disable, listeners)) { this.hideEvent.subscribe(listeners.disable, listeners, true); this.destroyEvent.subscribe(listeners.disable, listeners, true); } } } }, /** * The default handler for the "strings" property * @method configStrings */ configStrings : function(type, args, obj) { var val = Lang.merge(DEFAULT_CONFIG.STRINGS.value, args[0]); this.cfg.setProperty(DEFAULT_CONFIG.STRINGS.key, val, true); }, /** * The default event handler fired when the "height" property is changed. * @method configHeight * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configHeight: function (type, args, obj) { var height = args[0], el = this.innerElement; Dom.setStyle(el, "height", height); this.cfg.refireEvent("iframe"); }, /** * The default custom event handler executed when the Panel's height is changed, * if the autofillheight property has been set. * * @method _autoFillOnHeightChange * @protected * @param {String} type The event type * @param {Array} args The array of arguments passed to event subscribers * @param {HTMLElement} el The header, body or footer element which is to be resized to fill * out the containers height */ _autoFillOnHeightChange : function(type, args, el) { Panel.superclass._autoFillOnHeightChange.apply(this, arguments); if (bIEQuirks) { var panel = this; setTimeout(function() { panel.sizeUnderlay(); },0); } }, /** * The default event handler fired when the "width" property is changed. * @method configWidth * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configWidth: function (type, args, obj) { var width = args[0], el = this.innerElement; Dom.setStyle(el, "width", width); this.cfg.refireEvent("iframe"); }, /** * The default event handler fired when the "zIndex" property is changed. * @method configzIndex * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configzIndex: function (type, args, obj) { Panel.superclass.configzIndex.call(this, type, args, obj); if (this.mask || this.cfg.getProperty("modal") === true) { var panelZ = Dom.getStyle(this.element, "zIndex"); if (!panelZ || isNaN(panelZ)) { panelZ = 0; } if (panelZ === 0) { // Recursive call to configzindex (which should be stopped // from going further because panelZ should no longer === 0) this.cfg.setProperty("zIndex", 1); } else { this.stackMask(); } } }, // END BUILT-IN PROPERTY EVENT HANDLERS // /** * Builds the wrapping container around the Panel that is used for * positioning the shadow and matte underlays. The container element is * assigned to a local instance variable called container, and the * element is reinserted inside of it. * @method buildWrapper */ buildWrapper: function () { var elementParent = this.element.parentNode, originalElement = this.element, wrapper = document.createElement("div"); wrapper.className = Panel.CSS_PANEL_CONTAINER; wrapper.id = originalElement.id + "_c"; if (elementParent) { elementParent.insertBefore(wrapper, originalElement); } wrapper.appendChild(originalElement); this.element = wrapper; this.innerElement = originalElement; Dom.setStyle(this.innerElement, "visibility", "inherit"); }, /** * Adjusts the size of the shadow based on the size of the element. * @method sizeUnderlay */ sizeUnderlay: function () { var oUnderlay = this.underlay, oElement; if (oUnderlay) { oElement = this.element; oUnderlay.style.width = oElement.offsetWidth + "px"; oUnderlay.style.height = oElement.offsetHeight + "px"; } }, /** * Registers the Panel's header for drag & drop capability. * @method registerDragDrop */ registerDragDrop: function () { var me = this; if (this.header) { if (!Util.DD) { return; } var bDragOnly = (this.cfg.getProperty("dragonly") === true); this.dd = new Util.DD(this.element.id, this.id, {dragOnly: bDragOnly}); if (!this.header.id) { this.header.id = this.id + "_h"; } this.dd.startDrag = function () { var offsetHeight, offsetWidth, viewPortWidth, viewPortHeight, scrollX, scrollY; if (YAHOO.env.ua.ie == 6) { Dom.addClass(me.element,"drag"); } if (me.cfg.getProperty("constraintoviewport")) { var nViewportOffset = Overlay.VIEWPORT_OFFSET; offsetHeight = me.element.offsetHeight; offsetWidth = me.element.offsetWidth; viewPortWidth = Dom.getViewportWidth(); viewPortHeight = Dom.getViewportHeight(); scrollX = Dom.getDocumentScrollLeft(); scrollY = Dom.getDocumentScrollTop(); if (offsetHeight + nViewportOffset < viewPortHeight) { this.minY = scrollY + nViewportOffset; this.maxY = scrollY + viewPortHeight - offsetHeight - nViewportOffset; } else { this.minY = scrollY + nViewportOffset; this.maxY = scrollY + nViewportOffset; } if (offsetWidth + nViewportOffset < viewPortWidth) { this.minX = scrollX + nViewportOffset; this.maxX = scrollX + viewPortWidth - offsetWidth - nViewportOffset; } else { this.minX = scrollX + nViewportOffset; this.maxX = scrollX + nViewportOffset; } this.constrainX = true; this.constrainY = true; } else { this.constrainX = false; this.constrainY = false; } me.dragEvent.fire("startDrag", arguments); }; this.dd.onDrag = function () { me.syncPosition(); me.cfg.refireEvent("iframe"); if (this.platform == "mac" && YAHOO.env.ua.gecko) { this.showMacGeckoScrollbars(); } me.dragEvent.fire("onDrag", arguments); }; this.dd.endDrag = function () { if (YAHOO.env.ua.ie == 6) { Dom.removeClass(me.element,"drag"); } me.dragEvent.fire("endDrag", arguments); me.moveEvent.fire(me.cfg.getProperty("xy")); }; this.dd.setHandleElId(this.header.id); this.dd.addInvalidHandleType("INPUT"); this.dd.addInvalidHandleType("SELECT"); this.dd.addInvalidHandleType("TEXTAREA"); } }, /** * Builds the mask that is laid over the document when the Panel is * configured to be modal. * @method buildMask */ buildMask: function () { var oMask = this.mask; if (!oMask) { if (!m_oMaskTemplate) { m_oMaskTemplate = document.createElement("div"); m_oMaskTemplate.className = "mask"; m_oMaskTemplate.innerHTML = " "; } oMask = m_oMaskTemplate.cloneNode(true); oMask.id = this.id + "_mask"; document.body.insertBefore(oMask, document.body.firstChild); this.mask = oMask; if (YAHOO.env.ua.gecko && this.platform == "mac") { Dom.addClass(this.mask, "block-scrollbars"); } // Stack mask based on the element zindex this.stackMask(); } }, /** * Hides the modality mask. * @method hideMask */ hideMask: function () { if (this.cfg.getProperty("modal") && this.mask) { this.mask.style.display = "none"; Dom.removeClass(document.body, "masked"); this.hideMaskEvent.fire(); } }, /** * Shows the modality mask. * @method showMask */ showMask: function () { if (this.cfg.getProperty("modal") && this.mask) { Dom.addClass(document.body, "masked"); this.sizeMask(); this.mask.style.display = "block"; this.showMaskEvent.fire(); } }, /** * Sets the size of the modality mask to cover the entire scrollable * area of the document * @method sizeMask */ sizeMask: function () { if (this.mask) { // Shrink mask first, so it doesn't affect the document size. var mask = this.mask, viewWidth = Dom.getViewportWidth(), viewHeight = Dom.getViewportHeight(); if (mask.offsetHeight > viewHeight) { mask.style.height = viewHeight + "px"; } if (mask.offsetWidth > viewWidth) { mask.style.width = viewWidth + "px"; } // Then size it to the document mask.style.height = Dom.getDocumentHeight() + "px"; mask.style.width = Dom.getDocumentWidth() + "px"; } }, /** * Sets the zindex of the mask, if it exists, based on the zindex of * the Panel element. The zindex of the mask is set to be one less * than the Panel element's zindex. * * <p>NOTE: This method will not bump up the zindex of the Panel * to ensure that the mask has a non-negative zindex. If you require the * mask zindex to be 0 or higher, the zindex of the Panel * should be set to a value higher than 0, before this method is called. * </p> * @method stackMask */ stackMask: function() { if (this.mask) { var panelZ = Dom.getStyle(this.element, "zIndex"); if (!YAHOO.lang.isUndefined(panelZ) && !isNaN(panelZ)) { Dom.setStyle(this.mask, "zIndex", panelZ - 1); } } }, /** * Renders the Panel by inserting the elements that are not already in * the main Panel into their correct places. Optionally appends the * Panel to the specified node prior to the render's execution. NOTE: * For Panels without existing markup, the appendToNode argument is * REQUIRED. If this argument is ommitted and the current element is * not present in the document, the function will return false, * indicating that the render was a failure. * @method render * @param {String} appendToNode The element id to which the Module * should be appended to prior to rendering <em>OR</em> * @param {HTMLElement} appendToNode The element to which the Module * should be appended to prior to rendering * @return {boolean} Success or failure of the render */ render: function (appendToNode) { return Panel.superclass.render.call(this, appendToNode, this.innerElement); }, /** * Removes the Panel element from the DOM and sets all child elements * to null. * @method destroy */ destroy: function () { Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this); this.removeMask(); if (this.close) { Event.purgeElement(this.close); } Panel.superclass.destroy.call(this); }, /** * Forces the underlay element to be repainted through the application/removal * of a yui-force-redraw class to the underlay element. * * @method forceUnderlayRedraw */ forceUnderlayRedraw : function () { var u = this.underlay; Dom.addClass(u, "yui-force-redraw"); setTimeout(function(){Dom.removeClass(u, "yui-force-redraw");}, 0); }, /** * Returns a String representation of the object. * @method toString * @return {String} The string representation of the Panel. */ toString: function () { return "Panel " + this.id; } }); }()); (function () { /** * <p> * Dialog is an implementation of Panel that can be used to submit form * data. * </p> * <p> * Built-in functionality for buttons with event handlers is included. * If the optional YUI Button dependancy is included on the page, the buttons * created will be instances of YAHOO.widget.Button, otherwise regular HTML buttons * will be created. * </p> * <p> * Forms can be processed in 3 ways -- via an asynchronous Connection utility call, * a simple form POST or GET, or manually. The YUI Connection utility should be * included if you're using the default "async" postmethod, but is not required if * you're using any of the other postmethod values. * </p> * @namespace YAHOO.widget * @class Dialog * @extends YAHOO.widget.Panel * @constructor * @param {String} el The element ID representing the Dialog <em>OR</em> * @param {HTMLElement} el The element representing the Dialog * @param {Object} userConfig The configuration object literal containing * the configuration that should be set for this Dialog. See configuration * documentation for more details. */ YAHOO.widget.Dialog = function (el, userConfig) { YAHOO.widget.Dialog.superclass.constructor.call(this, el, userConfig); }; var Event = YAHOO.util.Event, CustomEvent = YAHOO.util.CustomEvent, Dom = YAHOO.util.Dom, Dialog = YAHOO.widget.Dialog, Lang = YAHOO.lang, /** * Constant representing the name of the Dialog's events * @property EVENT_TYPES * @private * @final * @type Object */ EVENT_TYPES = { "BEFORE_SUBMIT": "beforeSubmit", "SUBMIT": "submit", "MANUAL_SUBMIT": "manualSubmit", "ASYNC_SUBMIT": "asyncSubmit", "FORM_SUBMIT": "formSubmit", "CANCEL": "cancel" }, /** * Constant representing the Dialog's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "POST_METHOD": { key: "postmethod", value: "async" }, "POST_DATA" : { key: "postdata", value: null }, "BUTTONS": { key: "buttons", value: "none", supercedes: ["visible"] }, "HIDEAFTERSUBMIT" : { key: "hideaftersubmit", value: true } }; /** * Constant representing the default CSS class used for a Dialog * @property YAHOO.widget.Dialog.CSS_DIALOG * @static * @final * @type String */ Dialog.CSS_DIALOG = "yui-dialog"; function removeButtonEventHandlers() { var aButtons = this._aButtons, nButtons, oButton, i; if (Lang.isArray(aButtons)) { nButtons = aButtons.length; if (nButtons > 0) { i = nButtons - 1; do { oButton = aButtons[i]; if (YAHOO.widget.Button && oButton instanceof YAHOO.widget.Button) { oButton.destroy(); } else if (oButton.tagName.toUpperCase() == "BUTTON") { Event.purgeElement(oButton); Event.purgeElement(oButton, false); } } while (i--); } } } YAHOO.extend(Dialog, YAHOO.widget.Panel, { /** * @property form * @description Object reference to the Dialog's * <code><form></code> element. * @default null * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ * level-one-html.html#ID-40002357">HTMLFormElement</a> */ form: null, /** * Initializes the class's configurable properties which can be changed * using the Dialog's Config object (cfg). * @method initDefaultConfig */ initDefaultConfig: function () { Dialog.superclass.initDefaultConfig.call(this); /** * The internally maintained callback object for use with the * Connection utility. The format of the callback object is * similar to Connection Manager's callback object and is * simply passed through to Connection Manager when the async * request is made. * @property callback * @type Object */ this.callback = { /** * The function to execute upon success of the * Connection submission (when the form does not * contain a file input element). * * @property callback.success * @type Function */ success: null, /** * The function to execute upon failure of the * Connection submission * @property callback.failure * @type Function */ failure: null, /** *<p> * The function to execute upon success of the * Connection submission, when the form contains * a file input element. * </p> * <p> * <em>NOTE:</em> Connection manager will not * invoke the success or failure handlers for the file * upload use case. This will be the only callback * handler invoked. * </p> * <p> * For more information, see the <a href="http://developer.yahoo.com/yui/connection/#file"> * Connection Manager documenation on file uploads</a>. * </p> * @property callback.upload * @type Function */ /** * The arbitraty argument or arguments to pass to the Connection * callback functions * @property callback.argument * @type Object */ argument: null }; // Add form dialog config properties // /** * The method to use for posting the Dialog's form. Possible values * are "async", "form", and "manual". * @config postmethod * @type String * @default async */ this.cfg.addProperty(DEFAULT_CONFIG.POST_METHOD.key, { handler: this.configPostMethod, value: DEFAULT_CONFIG.POST_METHOD.value, validator: function (val) { if (val != "form" && val != "async" && val != "none" && val != "manual") { return false; } else { return true; } } }); /** * Any additional post data which needs to be sent when using the * <a href="#config_postmethod">async</a> postmethod for dialog POST submissions. * The format for the post data string is defined by Connection Manager's * <a href="YAHOO.util.Connect.html#method_asyncRequest">asyncRequest</a> * method. * @config postdata * @type String * @default null */ this.cfg.addProperty(DEFAULT_CONFIG.POST_DATA.key, { value: DEFAULT_CONFIG.POST_DATA.value }); /** * This property is used to configure whether or not the * dialog should be automatically hidden after submit. * * @config hideaftersubmit * @type Boolean * @default true */ this.cfg.addProperty(DEFAULT_CONFIG.HIDEAFTERSUBMIT.key, { value: DEFAULT_CONFIG.HIDEAFTERSUBMIT.value }); /** * Array of object literals, each containing a set of properties * defining a button to be appended into the Dialog's footer. * * <p>Each button object in the buttons array can have three properties:</p> * <dl> * <dt>text:</dt> * <dd> * The text that will display on the face of the button. The text can * include HTML, as long as it is compliant with HTML Button specifications. * </dd> * <dt>handler:</dt> * <dd>Can be either: * <ol> * <li>A reference to a function that should fire when the * button is clicked. (In this case scope of this function is * always its Dialog instance.)</li> * * <li>An object literal representing the code to be * executed when the button is clicked. * * <p>Format:</p> * * <p> * <code>{ * <br> * <strong>fn:</strong> Function, // * The handler to call when the event fires. * <br> * <strong>obj:</strong> Object, // * An object to pass back to the handler. * <br> * <strong>scope:</strong> Object // * The object to use for the scope of the handler. * <br> * }</code> * </p> * </li> * </ol> * </dd> * <dt>isDefault:</dt> * <dd> * An optional boolean value that specifies that a button * should be highlighted and focused by default. * </dd> * </dl> * * <em>NOTE:</em>If the YUI Button Widget is included on the page, * the buttons created will be instances of YAHOO.widget.Button. * Otherwise, HTML Buttons (<code><BUTTON></code>) will be * created. * * @config buttons * @type {Array|String} * @default "none" */ this.cfg.addProperty(DEFAULT_CONFIG.BUTTONS.key, { handler: this.configButtons, value: DEFAULT_CONFIG.BUTTONS.value, supercedes : DEFAULT_CONFIG.BUTTONS.supercedes }); }, /** * Initializes the custom events for Dialog which are fired * automatically at appropriate times by the Dialog class. * @method initEvents */ initEvents: function () { Dialog.superclass.initEvents.call(this); var SIGNATURE = CustomEvent.LIST; /** * CustomEvent fired prior to submission * @event beforeSubmitEvent */ this.beforeSubmitEvent = this.createEvent(EVENT_TYPES.BEFORE_SUBMIT); this.beforeSubmitEvent.signature = SIGNATURE; /** * CustomEvent fired after submission * @event submitEvent */ this.submitEvent = this.createEvent(EVENT_TYPES.SUBMIT); this.submitEvent.signature = SIGNATURE; /** * CustomEvent fired for manual submission, before the generic submit event is fired * @event manualSubmitEvent */ this.manualSubmitEvent = this.createEvent(EVENT_TYPES.MANUAL_SUBMIT); this.manualSubmitEvent.signature = SIGNATURE; /** * CustomEvent fired after asynchronous submission, before the generic submit event is fired * * @event asyncSubmitEvent * @param {Object} conn The connection object, returned by YAHOO.util.Connect.asyncRequest */ this.asyncSubmitEvent = this.createEvent(EVENT_TYPES.ASYNC_SUBMIT); this.asyncSubmitEvent.signature = SIGNATURE; /** * CustomEvent fired after form-based submission, before the generic submit event is fired * @event formSubmitEvent */ this.formSubmitEvent = this.createEvent(EVENT_TYPES.FORM_SUBMIT); this.formSubmitEvent.signature = SIGNATURE; /** * CustomEvent fired after cancel * @event cancelEvent */ this.cancelEvent = this.createEvent(EVENT_TYPES.CANCEL); this.cancelEvent.signature = SIGNATURE; }, /** * The Dialog initialization method, which is executed for Dialog and * all of its subclasses. This method is automatically called by the * constructor, and sets up all DOM references for pre-existing markup, * and creates required markup if it is not already present. * * @method init * @param {String} el The element ID representing the Dialog <em>OR</em> * @param {HTMLElement} el The element representing the Dialog * @param {Object} userConfig The configuration object literal * containing the configuration that should be set for this Dialog. * See configuration documentation for more details. */ init: function (el, userConfig) { /* Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level */ Dialog.superclass.init.call(this, el/*, userConfig*/); this.beforeInitEvent.fire(Dialog); Dom.addClass(this.element, Dialog.CSS_DIALOG); this.cfg.setProperty("visible", false); if (userConfig) { this.cfg.applyConfig(userConfig, true); } this.showEvent.subscribe(this.focusFirst, this, true); this.beforeHideEvent.subscribe(this.blurButtons, this, true); this.subscribe("changeBody", this.registerForm); this.initEvent.fire(Dialog); }, /** * Submits the Dialog's form depending on the value of the * "postmethod" configuration property. <strong>Please note: * </strong> As of version 2.3 this method will automatically handle * asyncronous file uploads should the Dialog instance's form contain * <code><input type="file"></code> elements. If a Dialog * instance will be handling asyncronous file uploads, its * <code>callback</code> property will need to be setup with a * <code>upload</code> handler rather than the standard * <code>success</code> and, or <code>failure</code> handlers. For more * information, see the <a href="http://developer.yahoo.com/yui/ * connection/#file">Connection Manager documenation on file uploads</a>. * @method doSubmit */ doSubmit: function () { var Connect = YAHOO.util.Connect, oForm = this.form, bUseFileUpload = false, bUseSecureFileUpload = false, aElements, nElements, i, formAttrs; switch (this.cfg.getProperty("postmethod")) { case "async": aElements = oForm.elements; nElements = aElements.length; if (nElements > 0) { i = nElements - 1; do { if (aElements[i].type == "file") { bUseFileUpload = true; break; } } while(i--); } if (bUseFileUpload && YAHOO.env.ua.ie && this.isSecure) { bUseSecureFileUpload = true; } formAttrs = this._getFormAttributes(oForm); Connect.setForm(oForm, bUseFileUpload, bUseSecureFileUpload); var postData = this.cfg.getProperty("postdata"); var c = Connect.asyncRequest(formAttrs.method, formAttrs.action, this.callback, postData); this.asyncSubmitEvent.fire(c); break; case "form": oForm.submit(); this.formSubmitEvent.fire(); break; case "none": case "manual": this.manualSubmitEvent.fire(); break; } }, /** * Retrieves important attributes (currently method and action) from * the form element, accounting for any elements which may have the same name * as the attributes. Defaults to "POST" and "" for method and action respectively * if the attribute cannot be retrieved. * * @method _getFormAttributes * @protected * @param {HTMLFormElement} oForm The HTML Form element from which to retrieve the attributes * @return {Object} Object literal, with method and action String properties. */ _getFormAttributes : function(oForm){ var attrs = { method : null, action : null }; if (oForm) { if (oForm.getAttributeNode) { var action = oForm.getAttributeNode("action"); var method = oForm.getAttributeNode("method"); if (action) { attrs.action = action.value; } if (method) { attrs.method = method.value; } } else { attrs.action = oForm.getAttribute("action"); attrs.method = oForm.getAttribute("method"); } } attrs.method = (Lang.isString(attrs.method) ? attrs.method : "POST").toUpperCase(); attrs.action = Lang.isString(attrs.action) ? attrs.action : ""; return attrs; }, /** * Prepares the Dialog's internal FORM object, creating one if one is * not currently present. * @method registerForm */ registerForm: function() { var form = this.element.getElementsByTagName("form")[0]; if (this.form) { if (this.form == form && Dom.isAncestor(this.element, this.form)) { return; } else { Event.purgeElement(this.form); this.form = null; } } if (!form) { form = document.createElement("form"); form.name = "frm_" + this.id; this.body.appendChild(form); } if (form) { this.form = form; Event.on(form, "submit", this._submitHandler, this, true); } }, /** * Internal handler for the form submit event * * @method _submitHandler * @protected * @param {DOMEvent} e The DOM Event object */ _submitHandler : function(e) { Event.stopEvent(e); this.submit(); this.form.blur(); }, /** * Sets up a tab, shift-tab loop between the first and last elements * provided. NOTE: Sets up the preventBackTab and preventTabOut KeyListener * instance properties, which are reset everytime this method is invoked. * * @method setTabLoop * @param {HTMLElement} firstElement * @param {HTMLElement} lastElement * */ setTabLoop : function(firstElement, lastElement) { firstElement = firstElement || this.firstButton; lastElement = this.lastButton || lastElement; Dialog.superclass.setTabLoop.call(this, firstElement, lastElement); }, /** * Configures instance properties, pointing to the * first and last focusable elements in the Dialog's form. * * @method setFirstLastFocusable */ setFirstLastFocusable : function() { Dialog.superclass.setFirstLastFocusable.call(this); var i, l, el, elements = this.focusableElements; this.firstFormElement = null; this.lastFormElement = null; if (this.form && elements && elements.length > 0) { l = elements.length; for (i = 0; i < l; ++i) { el = elements[i]; if (this.form === el.form) { this.firstFormElement = el; break; } } for (i = l-1; i >= 0; --i) { el = elements[i]; if (this.form === el.form) { this.lastFormElement = el; break; } } } }, // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // /** * The default event handler fired when the "close" property is * changed. The method controls the appending or hiding of the close * icon at the top right of the Dialog. * @method configClose * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For * configuration handlers, args[0] will equal the newly applied value * for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configClose: function (type, args, obj) { Dialog.superclass.configClose.apply(this, arguments); }, /** * Event handler for the close icon * * @method _doClose * @protected * * @param {DOMEvent} e */ _doClose : function(e) { Event.preventDefault(e); this.cancel(); }, /** * The default event handler for the "buttons" configuration property * @method configButtons * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configButtons: function (type, args, obj) { var Button = YAHOO.widget.Button, aButtons = args[0], oInnerElement = this.innerElement, oButton, oButtonEl, oYUIButton, nButtons, oSpan, oFooter, i; removeButtonEventHandlers.call(this); this._aButtons = null; if (Lang.isArray(aButtons)) { oSpan = document.createElement("span"); oSpan.className = "button-group"; nButtons = aButtons.length; this._aButtons = []; this.defaultHtmlButton = null; for (i = 0; i < nButtons; i++) { oButton = aButtons[i]; if (Button) { oYUIButton = new Button({ label: oButton.text}); oYUIButton.appendTo(oSpan); oButtonEl = oYUIButton.get("element"); if (oButton.isDefault) { oYUIButton.addClass("default"); this.defaultHtmlButton = oButtonEl; } if (Lang.isFunction(oButton.handler)) { oYUIButton.set("onclick", { fn: oButton.handler, obj: this, scope: this }); } else if (Lang.isObject(oButton.handler) && Lang.isFunction(oButton.handler.fn)) { oYUIButton.set("onclick", { fn: oButton.handler.fn, obj: ((!Lang.isUndefined(oButton.handler.obj)) ? oButton.handler.obj : this), scope: (oButton.handler.scope || this) }); } this._aButtons[this._aButtons.length] = oYUIButton; } else { oButtonEl = document.createElement("button"); oButtonEl.setAttribute("type", "button"); if (oButton.isDefault) { oButtonEl.className = "default"; this.defaultHtmlButton = oButtonEl; } oButtonEl.innerHTML = oButton.text; if (Lang.isFunction(oButton.handler)) { Event.on(oButtonEl, "click", oButton.handler, this, true); } else if (Lang.isObject(oButton.handler) && Lang.isFunction(oButton.handler.fn)) { Event.on(oButtonEl, "click", oButton.handler.fn, ((!Lang.isUndefined(oButton.handler.obj)) ? oButton.handler.obj : this), (oButton.handler.scope || this)); } oSpan.appendChild(oButtonEl); this._aButtons[this._aButtons.length] = oButtonEl; } oButton.htmlButton = oButtonEl; if (i === 0) { this.firstButton = oButtonEl; } if (i == (nButtons - 1)) { this.lastButton = oButtonEl; } } this.setFooter(oSpan); oFooter = this.footer; if (Dom.inDocument(this.element) && !Dom.isAncestor(oInnerElement, oFooter)) { oInnerElement.appendChild(oFooter); } this.buttonSpan = oSpan; } else { // Do cleanup oSpan = this.buttonSpan; oFooter = this.footer; if (oSpan && oFooter) { oFooter.removeChild(oSpan); this.buttonSpan = null; this.firstButton = null; this.lastButton = null; this.defaultHtmlButton = null; } } this.changeContentEvent.fire(); }, /** * @method getButtons * @description Returns an array containing each of the Dialog's * buttons, by default an array of HTML <code><BUTTON></code> * elements. If the Dialog's buttons were created using the * YAHOO.widget.Button class (via the inclusion of the optional Button * dependancy on the page), an array of YAHOO.widget.Button instances * is returned. * @return {Array} */ getButtons: function () { return this._aButtons || null; }, /** * <p> * Sets focus to the first focusable element in the Dialog's form if found, * else, the default button if found, else the first button defined via the * "buttons" configuration property. * </p> * <p> * This method is invoked when the Dialog is made visible. * </p> * @method focusFirst */ focusFirst: function (type, args, obj) { var el = this.firstFormElement; if (args && args[1]) { Event.stopEvent(args[1]); } if (el) { try { el.focus(); } catch(oException) { // Ignore } } else { if (this.defaultHtmlButton) { this.focusDefaultButton(); } else { this.focusFirstButton(); } } }, /** * Sets focus to the last element in the Dialog's form or the last * button defined via the "buttons" configuration property. * @method focusLast */ focusLast: function (type, args, obj) { var aButtons = this.cfg.getProperty("buttons"), el = this.lastFormElement; if (args && args[1]) { Event.stopEvent(args[1]); } if (aButtons && Lang.isArray(aButtons)) { this.focusLastButton(); } else { if (el) { try { el.focus(); } catch(oException) { // Ignore } } } }, /** * Helper method to normalize button references. It either returns the * YUI Button instance for the given element if found, * or the passes back the HTMLElement reference if a corresponding YUI Button * reference is not found or YAHOO.widget.Button does not exist on the page. * * @method _getButton * @private * @param {HTMLElement} button * @return {YAHOO.widget.Button|HTMLElement} */ _getButton : function(button) { var Button = YAHOO.widget.Button; // If we have an HTML button and YUI Button is on the page, // get the YUI Button reference if available. if (Button && button && button.nodeName && button.id) { button = Button.getButton(button.id) || button; } return button; }, /** * Sets the focus to the button that is designated as the default via * the "buttons" configuration property. By default, this method is * called when the Dialog is made visible. * @method focusDefaultButton */ focusDefaultButton: function () { var button = this._getButton(this.defaultHtmlButton); if (button) { /* Place the call to the "focus" method inside a try/catch block to prevent IE from throwing JavaScript errors if the element is disabled or hidden. */ try { button.focus(); } catch(oException) { } } }, /** * Blurs all the buttons defined via the "buttons" * configuration property. * @method blurButtons */ blurButtons: function () { var aButtons = this.cfg.getProperty("buttons"), nButtons, oButton, oElement, i; if (aButtons && Lang.isArray(aButtons)) { nButtons = aButtons.length; if (nButtons > 0) { i = (nButtons - 1); do { oButton = aButtons[i]; if (oButton) { oElement = this._getButton(oButton.htmlButton); if (oElement) { /* Place the call to the "blur" method inside a try/catch block to prevent IE from throwing JavaScript errors if the element is disabled or hidden. */ try { oElement.blur(); } catch(oException) { // ignore } } } } while(i--); } } }, /** * Sets the focus to the first button created via the "buttons" * configuration property. * @method focusFirstButton */ focusFirstButton: function () { var aButtons = this.cfg.getProperty("buttons"), oButton, oElement; if (aButtons && Lang.isArray(aButtons)) { oButton = aButtons[0]; if (oButton) { oElement = this._getButton(oButton.htmlButton); if (oElement) { /* Place the call to the "focus" method inside a try/catch block to prevent IE from throwing JavaScript errors if the element is disabled or hidden. */ try { oElement.focus(); } catch(oException) { // ignore } } } } }, /** * Sets the focus to the last button created via the "buttons" * configuration property. * @method focusLastButton */ focusLastButton: function () { var aButtons = this.cfg.getProperty("buttons"), nButtons, oButton, oElement; if (aButtons && Lang.isArray(aButtons)) { nButtons = aButtons.length; if (nButtons > 0) { oButton = aButtons[(nButtons - 1)]; if (oButton) { oElement = this._getButton(oButton.htmlButton); if (oElement) { /* Place the call to the "focus" method inside a try/catch block to prevent IE from throwing JavaScript errors if the element is disabled or hidden. */ try { oElement.focus(); } catch(oException) { // Ignore } } } } } }, /** * The default event handler for the "postmethod" configuration property * @method configPostMethod * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For * configuration handlers, args[0] will equal the newly applied value * for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configPostMethod: function (type, args, obj) { this.registerForm(); }, // END BUILT-IN PROPERTY EVENT HANDLERS // /** * Built-in function hook for writing a validation function that will * be checked for a "true" value prior to a submit. This function, as * implemented by default, always returns true, so it should be * overridden if validation is necessary. * @method validate */ validate: function () { return true; }, /** * Executes a submit of the Dialog if validation * is successful. By default the Dialog is hidden * after submission, but you can set the "hideaftersubmit" * configuration property to false, to prevent the Dialog * from being hidden. * * @method submit */ submit: function () { if (this.validate()) { this.beforeSubmitEvent.fire(); this.doSubmit(); this.submitEvent.fire(); if (this.cfg.getProperty("hideaftersubmit")) { this.hide(); } return true; } else { return false; } }, /** * Executes the cancel of the Dialog followed by a hide. * @method cancel */ cancel: function () { this.cancelEvent.fire(); this.hide(); }, /** * Returns a JSON-compatible data structure representing the data * currently contained in the form. * @method getData * @return {Object} A JSON object reprsenting the data of the * current form. */ getData: function () { var oForm = this.form, aElements, nTotalElements, oData, sName, oElement, nElements, sType, sTagName, aOptions, nOptions, aValues, oOption, sValue, oRadio, oCheckbox, i, n; function isFormElement(p_oElement) { var sTag = p_oElement.tagName.toUpperCase(); return ((sTag == "INPUT" || sTag == "TEXTAREA" || sTag == "SELECT") && p_oElement.name == sName); } if (oForm) { aElements = oForm.elements; nTotalElements = aElements.length; oData = {}; for (i = 0; i < nTotalElements; i++) { sName = aElements[i].name; /* Using "Dom.getElementsBy" to safeguard user from JS errors that result from giving a form field (or set of fields) the same name as a native method of a form (like "submit") or a DOM collection (such as the "item" method). Originally tried accessing fields via the "namedItem" method of the "element" collection, but discovered that it won't return a collection of fields in Gecko. */ oElement = Dom.getElementsBy(isFormElement, "*", oForm); nElements = oElement.length; if (nElements > 0) { if (nElements == 1) { oElement = oElement[0]; sType = oElement.type; sTagName = oElement.tagName.toUpperCase(); switch (sTagName) { case "INPUT": if (sType == "checkbox") { oData[sName] = oElement.checked; } else if (sType != "radio") { oData[sName] = oElement.value; } break; case "TEXTAREA": oData[sName] = oElement.value; break; case "SELECT": aOptions = oElement.options; nOptions = aOptions.length; aValues = []; for (n = 0; n < nOptions; n++) { oOption = aOptions[n]; if (oOption.selected) { sValue = oOption.value; if (!sValue || sValue === "") { sValue = oOption.text; } aValues[aValues.length] = sValue; } } oData[sName] = aValues; break; } } else { sType = oElement[0].type; switch (sType) { case "radio": for (n = 0; n < nElements; n++) { oRadio = oElement[n]; if (oRadio.checked) { oData[sName] = oRadio.value; break; } } break; case "checkbox": aValues = []; for (n = 0; n < nElements; n++) { oCheckbox = oElement[n]; if (oCheckbox.checked) { aValues[aValues.length] = oCheckbox.value; } } oData[sName] = aValues; break; } } } } } return oData; }, /** * Removes the Panel element from the DOM and sets all child elements * to null. * @method destroy */ destroy: function () { removeButtonEventHandlers.call(this); this._aButtons = null; var aForms = this.element.getElementsByTagName("form"), oForm; if (aForms.length > 0) { oForm = aForms[0]; if (oForm) { Event.purgeElement(oForm); if (oForm.parentNode) { oForm.parentNode.removeChild(oForm); } this.form = null; } } Dialog.superclass.destroy.call(this); }, /** * Returns a string representation of the object. * @method toString * @return {String} The string representation of the Dialog */ toString: function () { return "Dialog " + this.id; } }); }()); (function () { /** * SimpleDialog is a simple implementation of Dialog that can be used to * submit a single value. Forms can be processed in 3 ways -- via an * asynchronous Connection utility call, a simple form POST or GET, * or manually. * @namespace YAHOO.widget * @class SimpleDialog * @extends YAHOO.widget.Dialog * @constructor * @param {String} el The element ID representing the SimpleDialog * <em>OR</em> * @param {HTMLElement} el The element representing the SimpleDialog * @param {Object} userConfig The configuration object literal containing * the configuration that should be set for this SimpleDialog. See * configuration documentation for more details. */ YAHOO.widget.SimpleDialog = function (el, userConfig) { YAHOO.widget.SimpleDialog.superclass.constructor.call(this, el, userConfig); }; var Dom = YAHOO.util.Dom, SimpleDialog = YAHOO.widget.SimpleDialog, /** * Constant representing the SimpleDialog's configuration properties * @property DEFAULT_CONFIG * @private * @final * @type Object */ DEFAULT_CONFIG = { "ICON": { key: "icon", value: "none", suppressEvent: true }, "TEXT": { key: "text", value: "", suppressEvent: true, supercedes: ["icon"] } }; /** * Constant for the standard network icon for a blocking action * @property YAHOO.widget.SimpleDialog.ICON_BLOCK * @static * @final * @type String */ SimpleDialog.ICON_BLOCK = "blckicon"; /** * Constant for the standard network icon for alarm * @property YAHOO.widget.SimpleDialog.ICON_ALARM * @static * @final * @type String */ SimpleDialog.ICON_ALARM = "alrticon"; /** * Constant for the standard network icon for help * @property YAHOO.widget.SimpleDialog.ICON_HELP * @static * @final * @type String */ SimpleDialog.ICON_HELP = "hlpicon"; /** * Constant for the standard network icon for info * @property YAHOO.widget.SimpleDialog.ICON_INFO * @static * @final * @type String */ SimpleDialog.ICON_INFO = "infoicon"; /** * Constant for the standard network icon for warn * @property YAHOO.widget.SimpleDialog.ICON_WARN * @static * @final * @type String */ SimpleDialog.ICON_WARN = "warnicon"; /** * Constant for the standard network icon for a tip * @property YAHOO.widget.SimpleDialog.ICON_TIP * @static * @final * @type String */ SimpleDialog.ICON_TIP = "tipicon"; /** * Constant representing the name of the CSS class applied to the element * created by the "icon" configuration property. * @property YAHOO.widget.SimpleDialog.ICON_CSS_CLASSNAME * @static * @final * @type String */ SimpleDialog.ICON_CSS_CLASSNAME = "yui-icon"; /** * Constant representing the default CSS class used for a SimpleDialog * @property YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG * @static * @final * @type String */ SimpleDialog.CSS_SIMPLEDIALOG = "yui-simple-dialog"; YAHOO.extend(SimpleDialog, YAHOO.widget.Dialog, { /** * Initializes the class's configurable properties which can be changed * using the SimpleDialog's Config object (cfg). * @method initDefaultConfig */ initDefaultConfig: function () { SimpleDialog.superclass.initDefaultConfig.call(this); // Add dialog config properties // /** * Sets the informational icon for the SimpleDialog * @config icon * @type String * @default "none" */ this.cfg.addProperty(DEFAULT_CONFIG.ICON.key, { handler: this.configIcon, value: DEFAULT_CONFIG.ICON.value, suppressEvent: DEFAULT_CONFIG.ICON.suppressEvent }); /** * Sets the text for the SimpleDialog * @config text * @type String * @default "" */ this.cfg.addProperty(DEFAULT_CONFIG.TEXT.key, { handler: this.configText, value: DEFAULT_CONFIG.TEXT.value, suppressEvent: DEFAULT_CONFIG.TEXT.suppressEvent, supercedes: DEFAULT_CONFIG.TEXT.supercedes }); }, /** * The SimpleDialog initialization method, which is executed for * SimpleDialog and all of its subclasses. This method is automatically * called by the constructor, and sets up all DOM references for * pre-existing markup, and creates required markup if it is not * already present. * @method init * @param {String} el The element ID representing the SimpleDialog * <em>OR</em> * @param {HTMLElement} el The element representing the SimpleDialog * @param {Object} userConfig The configuration object literal * containing the configuration that should be set for this * SimpleDialog. See configuration documentation for more details. */ init: function (el, userConfig) { /* Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level */ SimpleDialog.superclass.init.call(this, el/*, userConfig*/); this.beforeInitEvent.fire(SimpleDialog); Dom.addClass(this.element, SimpleDialog.CSS_SIMPLEDIALOG); this.cfg.queueProperty("postmethod", "manual"); if (userConfig) { this.cfg.applyConfig(userConfig, true); } this.beforeRenderEvent.subscribe(function () { if (! this.body) { this.setBody(""); } }, this, true); this.initEvent.fire(SimpleDialog); }, /** * Prepares the SimpleDialog's internal FORM object, creating one if one * is not currently present, and adding the value hidden field. * @method registerForm */ registerForm: function () { SimpleDialog.superclass.registerForm.call(this); this.form.innerHTML += "<input type=\"hidden\" name=\"" + this.id + "\" value=\"\"/>"; }, // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // /** * Fired when the "icon" property is set. * @method configIcon * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configIcon: function (type,args,obj) { var sIcon = args[0], oBody = this.body, sCSSClass = SimpleDialog.ICON_CSS_CLASSNAME, oIcon, oIconParent; if (sIcon && sIcon != "none") { oIcon = Dom.getElementsByClassName(sCSSClass, "*" , oBody); if (oIcon) { oIconParent = oIcon.parentNode; if (oIconParent) { oIconParent.removeChild(oIcon); oIcon = null; } } if (sIcon.indexOf(".") == -1) { oIcon = document.createElement("span"); oIcon.className = (sCSSClass + " " + sIcon); oIcon.innerHTML = " "; } else { oIcon = document.createElement("img"); oIcon.src = (this.imageRoot + sIcon); oIcon.className = sCSSClass; } if (oIcon) { oBody.insertBefore(oIcon, oBody.firstChild); } } }, /** * Fired when the "text" property is set. * @method configText * @param {String} type The CustomEvent type (usually the property name) * @param {Object[]} args The CustomEvent arguments. For configuration * handlers, args[0] will equal the newly applied value for the property. * @param {Object} obj The scope object. For configuration handlers, * this will usually equal the owner. */ configText: function (type,args,obj) { var text = args[0]; if (text) { this.setBody(text); this.cfg.refireEvent("icon"); } }, // END BUILT-IN PROPERTY EVENT HANDLERS // /** * Returns a string representation of the object. * @method toString * @return {String} The string representation of the SimpleDialog */ toString: function () { return "SimpleDialog " + this.id; } /** * <p> * Sets the SimpleDialog's body content to the HTML specified. * If no body is present, one will be automatically created. * An empty string can be passed to the method to clear the contents of the body. * </p> * <p><strong>NOTE:</strong> SimpleDialog provides the <a href="#config_text">text</a> * and <a href="#config_icon">icon</a> configuration properties to set the contents * of it's body element in accordance with the UI design for a SimpleDialog (an * icon and message text). Calling setBody on the SimpleDialog will not enforce this * UI design constraint and will replace the entire contents of the SimpleDialog body. * It should only be used if you wish the replace the default icon/text body structure * of a SimpleDialog with your own custom markup.</p> * * @method setBody * @param {String} bodyContent The HTML used to set the body. * As a convenience, non HTMLElement objects can also be passed into * the method, and will be treated as strings, with the body innerHTML * set to their default toString implementations. * <em>OR</em> * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only child of the body element. * <em>OR</em> * @param {DocumentFragment} bodyContent The document fragment * containing elements which are to be added to the body */ }); }()); (function () { /** * ContainerEffect encapsulates animation transitions that are executed when * an Overlay is shown or hidden. * @namespace YAHOO.widget * @class ContainerEffect * @constructor * @param {YAHOO.widget.Overlay} overlay The Overlay that the animation * should be associated with * @param {Object} attrIn The object literal representing the animation * arguments to be used for the animate-in transition. The arguments for * this literal are: attributes(object, see YAHOO.util.Anim for description), * duration(Number), and method(i.e. Easing.easeIn). * @param {Object} attrOut The object literal representing the animation * arguments to be used for the animate-out transition. The arguments for * this literal are: attributes(object, see YAHOO.util.Anim for description), * duration(Number), and method(i.e. Easing.easeIn). * @param {HTMLElement} targetElement Optional. The target element that * should be animated during the transition. Defaults to overlay.element. * @param {class} Optional. The animation class to instantiate. Defaults to * YAHOO.util.Anim. Other options include YAHOO.util.Motion. */ YAHOO.widget.ContainerEffect = function (overlay, attrIn, attrOut, targetElement, animClass) { if (!animClass) { animClass = YAHOO.util.Anim; } /** * The overlay to animate * @property overlay * @type YAHOO.widget.Overlay */ this.overlay = overlay; /** * The animation attributes to use when transitioning into view * @property attrIn * @type Object */ this.attrIn = attrIn; /** * The animation attributes to use when transitioning out of view * @property attrOut * @type Object */ this.attrOut = attrOut; /** * The target element to be animated * @property targetElement * @type HTMLElement */ this.targetElement = targetElement || overlay.element; /** * The animation class to use for animating the overlay * @property animClass * @type class */ this.animClass = animClass; }; var Dom = YAHOO.util.Dom, CustomEvent = YAHOO.util.CustomEvent, ContainerEffect = YAHOO.widget.ContainerEffect; /** * A pre-configured ContainerEffect instance that can be used for fading * an overlay in and out. * @method FADE * @static * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate * @param {Number} dur The duration of the animation * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object */ ContainerEffect.FADE = function (overlay, dur) { var Easing = YAHOO.util.Easing, fin = { attributes: {opacity:{from:0, to:1}}, duration: dur, method: Easing.easeIn }, fout = { attributes: {opacity:{to:0}}, duration: dur, method: Easing.easeOut }, fade = new ContainerEffect(overlay, fin, fout, overlay.element); fade.handleUnderlayStart = function() { var underlay = this.overlay.underlay; if (underlay && YAHOO.env.ua.ie) { var hasFilters = (underlay.filters && underlay.filters.length > 0); if(hasFilters) { Dom.addClass(overlay.element, "yui-effect-fade"); } } }; fade.handleUnderlayComplete = function() { var underlay = this.overlay.underlay; if (underlay && YAHOO.env.ua.ie) { Dom.removeClass(overlay.element, "yui-effect-fade"); } }; fade.handleStartAnimateIn = function (type, args, obj) { Dom.addClass(obj.overlay.element, "hide-select"); if (!obj.overlay.underlay) { obj.overlay.cfg.refireEvent("underlay"); } obj.handleUnderlayStart(); obj.overlay._setDomVisibility(true); Dom.setStyle(obj.overlay.element, "opacity", 0); }; fade.handleCompleteAnimateIn = function (type,args,obj) { Dom.removeClass(obj.overlay.element, "hide-select"); if (obj.overlay.element.style.filter) { obj.overlay.element.style.filter = null; } obj.handleUnderlayComplete(); obj.overlay.cfg.refireEvent("iframe"); obj.animateInCompleteEvent.fire(); }; fade.handleStartAnimateOut = function (type, args, obj) { Dom.addClass(obj.overlay.element, "hide-select"); obj.handleUnderlayStart(); }; fade.handleCompleteAnimateOut = function (type, args, obj) { Dom.removeClass(obj.overlay.element, "hide-select"); if (obj.overlay.element.style.filter) { obj.overlay.element.style.filter = null; } obj.overlay._setDomVisibility(false); Dom.setStyle(obj.overlay.element, "opacity", 1); obj.handleUnderlayComplete(); obj.overlay.cfg.refireEvent("iframe"); obj.animateOutCompleteEvent.fire(); }; fade.init(); return fade; }; /** * A pre-configured ContainerEffect instance that can be used for sliding an * overlay in and out. * @method SLIDE * @static * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate * @param {Number} dur The duration of the animation * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object */ ContainerEffect.SLIDE = function (overlay, dur) { var Easing = YAHOO.util.Easing, x = overlay.cfg.getProperty("x") || Dom.getX(overlay.element), y = overlay.cfg.getProperty("y") || Dom.getY(overlay.element), clientWidth = Dom.getClientWidth(), offsetWidth = overlay.element.offsetWidth, sin = { attributes: { points: { to: [x, y] } }, duration: dur, method: Easing.easeIn }, sout = { attributes: { points: { to: [(clientWidth + 25), y] } }, duration: dur, method: Easing.easeOut }, slide = new ContainerEffect(overlay, sin, sout, overlay.element, YAHOO.util.Motion); slide.handleStartAnimateIn = function (type,args,obj) { obj.overlay.element.style.left = ((-25) - offsetWidth) + "px"; obj.overlay.element.style.top = y + "px"; }; slide.handleTweenAnimateIn = function (type, args, obj) { var pos = Dom.getXY(obj.overlay.element), currentX = pos[0], currentY = pos[1]; if (Dom.getStyle(obj.overlay.element, "visibility") == "hidden" && currentX < x) { obj.overlay._setDomVisibility(true); } obj.overlay.cfg.setProperty("xy", [currentX, currentY], true); obj.overlay.cfg.refireEvent("iframe"); }; slide.handleCompleteAnimateIn = function (type, args, obj) { obj.overlay.cfg.setProperty("xy", [x, y], true); obj.startX = x; obj.startY = y; obj.overlay.cfg.refireEvent("iframe"); obj.animateInCompleteEvent.fire(); }; slide.handleStartAnimateOut = function (type, args, obj) { var vw = Dom.getViewportWidth(), pos = Dom.getXY(obj.overlay.element), yso = pos[1]; obj.animOut.attributes.points.to = [(vw + 25), yso]; }; slide.handleTweenAnimateOut = function (type, args, obj) { var pos = Dom.getXY(obj.overlay.element), xto = pos[0], yto = pos[1]; obj.overlay.cfg.setProperty("xy", [xto, yto], true); obj.overlay.cfg.refireEvent("iframe"); }; slide.handleCompleteAnimateOut = function (type, args, obj) { obj.overlay._setDomVisibility(false); obj.overlay.cfg.setProperty("xy", [x, y]); obj.animateOutCompleteEvent.fire(); }; slide.init(); return slide; }; ContainerEffect.prototype = { /** * Initializes the animation classes and events. * @method init */ init: function () { this.beforeAnimateInEvent = this.createEvent("beforeAnimateIn"); this.beforeAnimateInEvent.signature = CustomEvent.LIST; this.beforeAnimateOutEvent = this.createEvent("beforeAnimateOut"); this.beforeAnimateOutEvent.signature = CustomEvent.LIST; this.animateInCompleteEvent = this.createEvent("animateInComplete"); this.animateInCompleteEvent.signature = CustomEvent.LIST; this.animateOutCompleteEvent = this.createEvent("animateOutComplete"); this.animateOutCompleteEvent.signature = CustomEvent.LIST; this.animIn = new this.animClass(this.targetElement, this.attrIn.attributes, this.attrIn.duration, this.attrIn.method); this.animIn.onStart.subscribe(this.handleStartAnimateIn, this); this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this); this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn, this); this.animOut = new this.animClass(this.targetElement, this.attrOut.attributes, this.attrOut.duration, this.attrOut.method); this.animOut.onStart.subscribe(this.handleStartAnimateOut, this); this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this); this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, this); }, /** * Triggers the in-animation. * @method animateIn */ animateIn: function () { this.beforeAnimateInEvent.fire(); this.animIn.animate(); }, /** * Triggers the out-animation. * @method animateOut */ animateOut: function () { this.beforeAnimateOutEvent.fire(); this.animOut.animate(); }, /** * The default onStart handler for the in-animation. * @method handleStartAnimateIn * @param {String} type The CustomEvent type * @param {Object[]} args The CustomEvent arguments * @param {Object} obj The scope object */ handleStartAnimateIn: function (type, args, obj) { }, /** * The default onTween handler for the in-animation. * @method handleTweenAnimateIn * @param {String} type The CustomEvent type * @param {Object[]} args The CustomEvent arguments * @param {Object} obj The scope object */ handleTweenAnimateIn: function (type, args, obj) { }, /** * The default onComplete handler for the in-animation. * @method handleCompleteAnimateIn * @param {String} type The CustomEvent type * @param {Object[]} args The CustomEvent arguments * @param {Object} obj The scope object */ handleCompleteAnimateIn: function (type, args, obj) { }, /** * The default onStart handler for the out-animation. * @method handleStartAnimateOut * @param {String} type The CustomEvent type * @param {Object[]} args The CustomEvent arguments * @param {Object} obj The scope object */ handleStartAnimateOut: function (type, args, obj) { }, /** * The default onTween handler for the out-animation. * @method handleTweenAnimateOut * @param {String} type The CustomEvent type * @param {Object[]} args The CustomEvent arguments * @param {Object} obj The scope object */ handleTweenAnimateOut: function (type, args, obj) { }, /** * The default onComplete handler for the out-animation. * @method handleCompleteAnimateOut * @param {String} type The CustomEvent type * @param {Object[]} args The CustomEvent arguments * @param {Object} obj The scope object */ handleCompleteAnimateOut: function (type, args, obj) { }, /** * Returns a string representation of the object. * @method toString * @return {String} The string representation of the ContainerEffect */ toString: function () { var output = "ContainerEffect"; if (this.overlay) { output += " [" + this.overlay.toString() + "]"; } return output; } }; YAHOO.lang.augmentProto(ContainerEffect, YAHOO.util.EventProvider); })(); YAHOO.register("container", YAHOO.widget.Module, {version: "2.7.0", build: "1799"});