--- /dev/null
+/*
+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"});