/*
Copyright (c) 2009, DiotekYui! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.DiotekYui.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 DiotekYui.util
	* @class Config
	* @constructor
	* @param {Object} owner The owner Object to which this Config Object belongs
	*/
	DiotekYui.util.Config = function (owner) {

		if (owner) {
			this.init(owner);
		}


	};


	var Lang = DiotekYui.lang,
		CustomEvent = DiotekYui.util.CustomEvent,
		Config = DiotekYui.util.Config;


	/**
	 * Constant representing the CustomEvent type for the config changed event.
	 * @property DiotekYui.util.Config.CONFIG_CHANGED_EVENT
	 * @private
	 * @static
	 * @final
	 */
	Config.CONFIG_CHANGED_EVENT = "configChanged";
	
	/**
	 * Constant representing the boolean type string
	 * @property DiotekYui.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 DiotekYui.util.Config.alreadySubscribed
	* @static
	* @param {DiotekYui.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;

	};

	DiotekYui.lang.augmentProto(Config, DiotekYui.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 DiotekYui, 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 DiotekYui.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.
	*/
	DiotekYui.widget.Module = function (el, userConfig) {
		if (el) {
			this.init(el, userConfig);
		} else {
		}
	};

	var Dom = DiotekYui.util.Dom,
		Config = DiotekYui.util.Config,
		Event = DiotekYui.util.Event,
		CustomEvent = DiotekYui.util.CustomEvent,
		Module = DiotekYui.widget.Module,
		UA = DiotekYui.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: DiotekYui.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 DiotekYui.widget.Module.IMG_ROOT
	* @static
	* @final
	* @type String
	*/
	Module.IMG_ROOT = null;
	
	/**
	* Constant representing the prefix path to use for securely served images
	* @property DiotekYui.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 DiotekYui.widget.Module.CSS_MODULE
	* @static
	* @final
	* @type String
	*/
	Module.CSS_MODULE = "yui-module";
	
	/**
	* Constant representing the module header
	* @property DiotekYui.widget.Module.CSS_HEADER
	* @static
	* @final
	* @type String
	*/
	Module.CSS_HEADER = "hd";

	/**
	* Constant representing the module body
	* @property DiotekYui.widget.Module.CSS_BODY
	* @static
	* @final
	* @type String
	*/
	Module.CSS_BODY = "bd";
	
	/**
	* Constant representing the module footer
	* @property DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.widget.Module.forceDocumentRedraw
	 * @static
	 */
	Module.forceDocumentRedraw = function() {
		var docEl = document.documentElement;
		if (docEl) {
			docEl.className += " ";
			docEl.className = DiotekYui.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 DiotekYui.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 DiotekYui.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.",
								 "DiotekYui.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;
		}
	};

	DiotekYui.lang.augmentProto(Module, DiotekYui.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 DiotekYui.widget
	* @class Overlay
	* @extends DiotekYui.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
	*/
	DiotekYui.widget.Overlay = function (el, userConfig) {
		DiotekYui.widget.Overlay.superclass.constructor.call(this, el, userConfig);
	};

	var Lang = DiotekYui.lang,
		CustomEvent = DiotekYui.util.CustomEvent,
		Module = DiotekYui.widget.Module,
		Event = DiotekYui.util.Event,
		Dom = DiotekYui.util.Dom,
		Config = DiotekYui.util.Config,
		UA = DiotekYui.env.ua,
		Overlay = DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.widget.Overlay.BOTTOM_RIGHT
	* @static
	* @final
	* @type String
	*/
	Overlay.BOTTOM_RIGHT = "br";

	/**
	* Constant representing the default CSS class used for an Overlay
	* @property DiotekYui.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 DiotekYui.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 DiotekYui.widget.Overlay.windowScrollEvent
	*/
	Overlay.windowScrollEvent = new CustomEvent("windowScroll");

	/**
	* A singleton CustomEvent used for reacting to the DOM event for
	* window resize
	* @event DiotekYui.widget.Overlay.windowResizeEvent
	*/
	Overlay.windowResizeEvent = new CustomEvent("windowResize");

	/**
	* The DOM event handler used to fire the CustomEvent for window scroll
	* @method DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.widget.Overlay._TRIGGER_MAP
	 * @type Object
	 * @static
	 * @private
	 */
	Overlay._TRIGGER_MAP = {
		"windowScroll" : Overlay.windowScrollEvent,
		"windowResize" : Overlay.windowResizeEvent,
		"textResize"   : Module.textResizeEvent
	};

	DiotekYui.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 = DiotekYui.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 &#60;String|HTMLElement&#62;</dt>
			* <dd>A reference to the context element to which the overlay should be aligned (or it's id).</dd>
			* <dt>overlayCorner &#60;String&#62;</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 &#60;String&#62;</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) &#60;Array[String|CustomEvent]&#62;</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 (!DiotekYui.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 (DiotekYui.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();
				x = (viewPortWidth / 2) - (elementWidth / 2) - 40;
			} 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 
		* DiotekYui.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 = DiotekYui.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 DiotekYui.widget
	* @namespace DiotekYui.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
	*/
	DiotekYui.widget.OverlayManager = function (userConfig) {
		this.init(userConfig);
	};

	var Overlay = DiotekYui.widget.Overlay,
		Event = DiotekYui.util.Event,
		Dom = DiotekYui.util.Dom,
		Config = DiotekYui.util.Config,
		CustomEvent = DiotekYui.util.CustomEvent,
		OverlayManager = DiotekYui.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 DiotekYui.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 DiotekYui.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 DiotekYui.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 {DiotekYui.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 DiotekYui.widget
	* @class Tooltip
	* @extends DiotekYui.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.
	*/
	DiotekYui.widget.Tooltip = function (el, userConfig) {
		DiotekYui.widget.Tooltip.superclass.constructor.call(this, el, userConfig);
	};

	var Lang = DiotekYui.lang,
		Event = DiotekYui.util.Event,
		CustomEvent = DiotekYui.util.CustomEvent,
		Dom = DiotekYui.util.Dom,
		Tooltip = DiotekYui.widget.Tooltip,
		UA = DiotekYui.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 DiotekYui.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);
	}

	DiotekYui.extend(Tooltip, DiotekYui.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 === "" || DiotekYui.lang.isUndefined(txt) || DiotekYui.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 DiotekYui.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 = DiotekYui.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 DiotekYui.widget
	* @class Panel
	* @extends DiotekYui.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.
	*/
	DiotekYui.widget.Panel = function (el, userConfig) {
		DiotekYui.widget.Panel.superclass.constructor.call(this, el, userConfig);
	};

	var _currentModal = null;

	var Lang = DiotekYui.lang,
		Util = DiotekYui.util,
		Dom = Util.Dom,
		Event = Util.Event,
		CustomEvent = Util.CustomEvent,
		KeyListener = DiotekYui.util.KeyListener,
		Config = Util.Config,
		Overlay = DiotekYui.widget.Overlay,
		Panel = DiotekYui.widget.Panel,
		UA = DiotekYui.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: "<img src='/images/common/blank.gif' width='25' height='25'>"
				}
			}
		};

	/**
	* Constant representing the default CSS class used for a Panel
	* @property DiotekYui.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 DiotekYui.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 DiotekYui.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("&#160;");
		}
	}

	/* 
		"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]);
			
			}
		}
	}

	DiotekYui.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 DiotekYui.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 : "&#160;";

					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);
						DiotekYui.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);
					DiotekYui.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 (DiotekYui.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" && DiotekYui.env.ua.gecko) {
						this.showMacGeckoScrollbars();
					}

					me.dragEvent.fire("onDrag", arguments);
				};

				this.dd.endDrag = function () {

					if (DiotekYui.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 = "&#160;";
				}
				oMask = m_oMaskTemplate.cloneNode(true);
				oMask.id = this.id + "_mask";

				document.body.insertBefore(oMask, document.body.firstChild);

				this.mask = oMask;

				if (DiotekYui.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 (!DiotekYui.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 DiotekYui.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 DiotekYui.widget
	* @class Dialog
	* @extends DiotekYui.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.
	*/
	DiotekYui.widget.Dialog = function (el, userConfig) {
		DiotekYui.widget.Dialog.superclass.constructor.call(this, el, userConfig);
	};

	var Event = DiotekYui.util.Event,
		CustomEvent = DiotekYui.util.CustomEvent,
		Dom = DiotekYui.util.Dom,
		Dialog = DiotekYui.widget.Dialog,
		Lang = DiotekYui.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 DiotekYui.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 (DiotekYui.widget.Button && oButton instanceof DiotekYui.widget.Button) {
						oButton.destroy();
					}
					else if (oButton.tagName.toUpperCase() == "BUTTON") {
						Event.purgeElement(oButton);
						Event.purgeElement(oButton, false);
					}
				}
				while (i--);
			}
		}
	}

	DiotekYui.extend(Dialog, DiotekYui.widget.Panel, { 

		/**
		* @property form
		* @description Object reference to the Dialog's 
		* <code>&#60;form&#62;</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.DiotekYui.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="DiotekYui.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, &#47;&#47;
			*       The handler to call when  the event fires.
			*       <br>
			*       <strong>obj:</strong> Object, &#47;&#47; 
			*       An  object to pass back to the handler.
			*       <br>
			*       <strong>scope:</strong> Object &#47;&#47; 
			*       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 DiotekYui.widget.Button. 
			* Otherwise, HTML Buttons (<code>&#60;BUTTON&#62;</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 DiotekYui.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>&#60;input type="file"&#62;</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.DiotekYui.com/yui/
		* connection/#file">Connection Manager documenation on file uploads</a>.
		* @method doSubmit
		*/
		doSubmit: function () {

			var Connect = DiotekYui.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 && DiotekYui.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 = DiotekYui.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>&#60;BUTTON&#62;</code> 
		* elements.  If the Dialog's buttons were created using the 
		* DiotekYui.widget.Button class (via the inclusion of the optional Button 
		* dependancy on the page), an array of DiotekYui.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 DiotekYui.widget.Button does not exist on the page.
		 *
		 * @method _getButton
		 * @private
		 * @param {HTMLElement} button
		 * @return {DiotekYui.widget.Button|HTMLElement}
		 */
		_getButton : function(button) {
			var Button = DiotekYui.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 DiotekYui.widget
	* @class SimpleDialog
	* @extends DiotekYui.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.
	*/
	DiotekYui.widget.SimpleDialog = function (el, userConfig) {
	
		DiotekYui.widget.SimpleDialog.superclass.constructor.call(this, 
			el, userConfig);
	
	};

	var Dom = DiotekYui.util.Dom,
		SimpleDialog = DiotekYui.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 DiotekYui.widget.SimpleDialog.ICON_BLOCK
	* @static
	* @final
	* @type String
	*/
	SimpleDialog.ICON_BLOCK = "blckicon";
	
	/**
	* Constant for the standard network icon for alarm
	* @property DiotekYui.widget.SimpleDialog.ICON_ALARM
	* @static
	* @final
	* @type String
	*/
	SimpleDialog.ICON_ALARM = "alrticon";
	
	/**
	* Constant for the standard network icon for help
	* @property DiotekYui.widget.SimpleDialog.ICON_HELP
	* @static
	* @final
	* @type String
	*/
	SimpleDialog.ICON_HELP  = "hlpicon";
	
	/**
	* Constant for the standard network icon for info
	* @property DiotekYui.widget.SimpleDialog.ICON_INFO
	* @static
	* @final
	* @type String
	*/
	SimpleDialog.ICON_INFO  = "infoicon";
	
	/**
	* Constant for the standard network icon for warn
	* @property DiotekYui.widget.SimpleDialog.ICON_WARN
	* @static
	* @final
	* @type String
	*/
	SimpleDialog.ICON_WARN  = "warnicon";
	
	/**
	* Constant for the standard network icon for a tip
	* @property DiotekYui.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 DiotekYui.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 DiotekYui.widget.SimpleDialog.CSS_SIMPLEDIALOG
	* @static
	* @final
	* @type String
	*/
	SimpleDialog.CSS_SIMPLEDIALOG = "yui-simple-dialog";

	
	DiotekYui.extend(SimpleDialog, DiotekYui.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 = "&#160;";

				} 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 DiotekYui.widget
	* @class ContainerEffect
	* @constructor
	* @param {DiotekYui.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 DiotekYui.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 DiotekYui.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 
	* DiotekYui.util.Anim. Other options include DiotekYui.util.Motion.
	*/
	DiotekYui.widget.ContainerEffect = function (overlay, attrIn, attrOut, targetElement, animClass) {

		if (!animClass) {
			animClass = DiotekYui.util.Anim;
		}

		/**
		* The overlay to animate
		* @property overlay
		* @type DiotekYui.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 = DiotekYui.util.Dom,
		CustomEvent = DiotekYui.util.CustomEvent,
		ContainerEffect = DiotekYui.widget.ContainerEffect;


	/**
	* A pre-configured ContainerEffect instance that can be used for fading 
	* an overlay in and out.
	* @method FADE
	* @static
	* @param {DiotekYui.widget.Overlay} overlay The Overlay object to animate
	* @param {Number} dur The duration of the animation
	* @return {DiotekYui.widget.ContainerEffect} The configured ContainerEffect object
	*/
	ContainerEffect.FADE = function (overlay, dur) {

		var Easing = DiotekYui.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 && DiotekYui.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 && DiotekYui.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 {DiotekYui.widget.Overlay} overlay The Overlay object to animate
	* @param {Number} dur The duration of the animation
	* @return {DiotekYui.widget.ContainerEffect} The configured ContainerEffect object
	*/
	ContainerEffect.SLIDE = function (overlay, dur) {
		var Easing = DiotekYui.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, DiotekYui.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;
		}
	};

	DiotekYui.lang.augmentProto(ContainerEffect, DiotekYui.util.EventProvider);

})();

DiotekYui.register("container", DiotekYui.widget.Module, {version: "2.7.0", build: "1799"});
