/* Copyright (c) 2007, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.3.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); } if (!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 = {}, prop, property; for (prop in this.config) { property = this.config[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, oValue, 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) { 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; 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 OR * @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, 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 } }; /** * 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;"; /** * 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"); function createModuleTemplate() { if (!m_oModuleTemplate) { m_oModuleTemplate = document.createElement("div"); m_oModuleTemplate.innerHTML = ("
" + ""); 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 }); /** * Object or array of objects representing the ContainerEffect * classes that are active for animating the container. * @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 }); }, /** * 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. * @method init * @param {String} el The element ID representing the Module OR * @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, i, 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.element = el; if (el.id) { this.id = el.id; } child = this.element.firstChild; if (child) { do { switch (child.className) { case Module.CSS_HEADER: this.header = child; break; case Module.CSS_BODY: this.body = child; break; case Module.CSS_FOOTER: this.footer = child; break; } } 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); }, /** * Initialized an empty IFRAME that is placed out of the visible area * that can be used to detect text resize. * @method initResizeMonitor */ initResizeMonitor: function () { var oDoc, oIFrame, sHTML; function fireTextResize() { Module.textResizeEvent.fire(); } if (!YAHOO.env.ua.opera) { oIFrame = Dom.get("_yuiResizeMonitor"); if (!oIFrame) { oIFrame = document.createElement("iframe"); if (this.isSecure && Module.RESIZE_MONITOR_SECURE_URL && YAHOO.env.ua.ie) { oIFrame.src = Module.RESIZE_MONITOR_SECURE_URL; } /* Need to set "src" attribute of the iframe to prevent the browser from reporting duplicate cookies. (See SourceForge bug #1721755) */ if (YAHOO.env.ua.gecko) { sHTML = "" + ""; oIFrame.src = "data:text/html;charset=utf-8," + encodeURIComponent(sHTML); } oIFrame.id = "_yuiResizeMonitor"; /* 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"; document.body.appendChild(oIFrame); oIFrame.style.width = "10em"; oIFrame.style.height = "10em"; oIFrame.style.top = (-1 * oIFrame.offsetHeight) + "px"; oIFrame.style.left = (-1 * oIFrame.offsetWidth) + "px"; oIFrame.style.borderWidth = "0"; oIFrame.style.visibility = "visible"; if (YAHOO.env.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 (!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; } } }, /** * 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 nLeft = -1 * this.resizeMonitor.offsetWidth, nTop = -1 * this.resizeMonitor.offsetHeight; this.resizeMonitor.style.top = nTop + "px"; this.resizeMonitor.style.left = nLeft + "px"; }, /** * Sets the Module's header content to the HTML specified, or appends * the passed element to the header. If no header is present, one will * be automatically created. * @method setHeader * @param {String} headerContent The HTML used to set the header * OR * @param {HTMLElement} headerContent The HTMLElement to append to * the header */ setHeader: function (headerContent) { var oHeader = this.header || (this.header = createHeader()); if (typeof headerContent == "string") { oHeader.innerHTML = headerContent; } else { oHeader.innerHTML = ""; oHeader.appendChild(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} element The element to append 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, or appends the * passed element to the body. If no body is present, one will be * automatically created. * @method setBody * @param {String} bodyContent The HTML used to set the body OR * @param {HTMLElement} bodyContent The HTMLElement to append to the body */ setBody: function (bodyContent) { var oBody = this.body || (this.body = createBody()); if (typeof bodyContent == "string") { oBody.innerHTML = bodyContent; } else { oBody.innerHTML = ""; oBody.appendChild(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} element The element to append 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. * @method setFooter * @param {String} footerContent The HTML used to set the footer * OR * @param {HTMLElement} footerContent The HTMLElement to append to * the footer */ setFooter: function (footerContent) { var oFooter = this.footer || (this.footer = createFooter()); if (typeof footerContent == "string") { oFooter.innerHTML = footerContent; } else { oFooter.innerHTML = ""; oFooter.appendChild(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} element The element to append 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. * NOTE: 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. * @method render * @param {String} appendToNode The element id to which the Module * should be appended to prior to rendering OR * @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(element) { if (typeof element == "string") { element = document.getElementById(element); } if (element) { element.appendChild(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) { // Insert before first child if exists moduleElement.insertBefore(this.header, firstChild); } else { // Append to empty body because there are no children 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 */ // Insert before footer if exists in DOM if (this.footer && Dom.isAncestor( this.moduleElement, this.footer)) { moduleElement.insertBefore(this.body, this.footer); } else { // Append to element because there is no footer 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, e; 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(); for (e in this) { if (e instanceof CustomEvent) { e.unsubscribeAll(); } } }, /** * 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; } }, /** * 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 Module * @param {String} el The element ID representing the Overlay OR * @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, Overlay = YAHOO.widget.Overlay, 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, validator: Lang.isBoolean, supercedes: ["iframe", "visible"] }, "WIDTH": { key: "width", suppressEvent: true, supercedes: ["context", "fixedcenter", "iframe"] }, "HEIGHT": { key: "height", suppressEvent: true, supercedes: ["context", "fixedcenter", "iframe"] }, "ZINDEX": { key: "zindex", value: null }, "CONSTRAIN_TO_VIEWPORT": { key: "constraintoviewport", value: false, validator: Lang.isBoolean, supercedes: ["iframe", "x", "y", "xy"] }, "IFRAME": { key: "iframe", value: (YAHOO.env.ua.ie == 6 ? true : false), validator: Lang.isBoolean, supercedes: ["zindex"] } }; /** * 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. * @property YAHOO.widget.Overlay.IFRAME_SRC * @default 3 * @static * @final * @type Number */ Overlay.IFRAME_OFFSET = 3; /** * 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"; /** * 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) { if (YAHOO.env.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 (YAHOO.env.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; } YAHOO.extend(Overlay, Module, { /** * 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 OR * @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" && YAHOO.env.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); // Add overlay config properties // /** * The absolute x-coordinate position of the Overlay * @config x * @type Number * @default null */ this.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 */ this.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 */ this.cfg.addProperty(DEFAULT_CONFIG.XY.key, { handler: this.configXY, suppressEvent: DEFAULT_CONFIG.XY.suppressEvent, supercedes: DEFAULT_CONFIG.XY.supercedes }); /** * The array of context arguments for context-sensitive positioning. * The format is: [id or element, element corner, context corner]. * For example, setting this property to ["img1", "tl", "bl"] would * align the Overlay's top left corner to the context element's * bottom left corner. * @config context * @type Array * @default null */ this.cfg.addProperty(DEFAULT_CONFIG.CONTEXT.key, { handler: this.configContext, suppressEvent: DEFAULT_CONFIG.CONTEXT.suppressEvent, supercedes: DEFAULT_CONFIG.CONTEXT.supercedes }); /** * True if the Overlay should be anchored to the center of * the viewport. * @config fixedcenter * @type Boolean * @default false */ this.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 */ this.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 */ this.cfg.addProperty(DEFAULT_CONFIG.HEIGHT.key, { handler: this.configHeight, suppressEvent: DEFAULT_CONFIG.HEIGHT.suppressEvent, supercedes: DEFAULT_CONFIG.HEIGHT.supercedes }); /** * CSS z-index of the Overlay. * @config zIndex * @type Number * @default null */ this.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 */ this.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