/*	 -------------------------------------------------------------------------
	FILE:		Overlay.js
	PURPOSE:	Provides a popup iframe for rendering a server page

	Copyright 2007,2008,2009 (c) FlowTech Solutions, Inc.	(www.FlowTech-Solutions.com)

	Permission is hereby granted, free of charge, to any person obtaining
	a copy of this software and associated documentation files (the
	"Software"), to deal in the Software without restriction, including
	without limitation the rights to use, copy, modify, merge, publish,
	distribute, sublicense, and/or sell copies of the Software, and to
	permit persons to whom the Software is furnished to do so, subject to
	the following conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
	LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
	OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
	WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


	PUBLIC						DESCRIPTION
	---------------			------------------------------------------------
	initialize
	pop
	destroy
	size
	position
	getContainerID
	isAChildOverlay				boolean to indicate if this instance is a child overlay
	getOverlayProperties		retrieves the iframeContainer, chromeContainer, and Options of the child overlay

------------------------------------------------------------------------- */

var Overlay = function()
{
	// Singleton class for a given window (DOM)
	if (window.Overlay) return window.Overlay;


	/* ------------------------------ P U B L I C ------------------------------ */
	var _public =
	{

		//	Identifies key information about it's own instance, and parent
		// Registers the elements that subscribe to overlay events (data)
		// Executed when the DOM is fully constructed
		initialize: function()
		{/*POPULATE THE FOLLOWING DATA MEMBERS
					myName:				// overlay name (originated from myOptions.name)
					myWindow:			// current window
					myDocument:		// document (iframe document or window document)
					myIframe:			// iframe element containing this instance of overlay (actual iframe element)
					myIframe_id:		// ID of iframe element containing this instance of overlay
					myIframeDOM:		// iframe document
					myParentWindow:		// parent window
					myParentDocument:	// parent document
					myParentOverlay:	 null,			// parent Overlay class/object
					myOptions:			null,			// object of properties used for creating this instance of overlay
					myChromeContainer:	null,	// chrome container containing this instance of overlay
			*/

			_private.myWindow = window.self;
			_private.myDocument = $(window.self.document);
			_private.myIframe = $(_private.myWindow.frameElement);
			if (_private.myIframe)
			{	// Existence of the frameElement element tells us that we have an iframe
				_private.myIframe_id = _private.myIframe.id;
				_private.myIframeDOM = _private.myIframe.contentWindow || _private.myIframe.contentDocument;
				_private.myIframeDOM = (_private.myIframeDOM.document) ? _private.myIframeDOM.document : _private.myIframeDOM;
			}
			_private.myParentWindow = (window.self.parent && window.self!=window.self.parent.window)? window.self.parent.window : null;
			_private.myParentDocument = (_private.myParentWindow)? _private.myParentWindow.document : null;
			_private.myParentOverlay = (_private.myParentWindow) ? _private.myParentWindow.Overlay : null;
			if (_private.myIframe && _private.myParentOverlay)
			{	// Now we need to use a small trick and ask our parent Overlay object for a copy of our configuration properties
				_private.myName = _private.myIframe.name.substr(0,_private.myIframe.name.lastIndexOf('_'));
				_private.myChromeContainer = $(_private.myParentDocument.getElementById(_private.myName + "_chrome"));
				var myOverlayProperties = _private.myParentOverlay.getOverlayProperties(_private.myName);
				_private.myOptions = myOverlayProperties.myOptions;
			}
			_private.zIndex = _private.getMaxZIndex(); // this should be done again before displaying a child overlay

			// Load the default style
			setTimeout(function(){_private.loadStyleFile('script/css/Overlay.css');},0);

			// Establish a listener for Overlay triggered events  (elements with event attribute)
			var elements = $$('*[event]');
			elements.each(function(currentElement,index){
				if ( currentElement.tagName == 'a' )
					currentElement.href = 'javascript:_private.handleEvent(event)';
				else
					currentElement.observe('click',_private.handleOverlayEvent);
				});

			// Fire a custom event to my parent to indicate my page is loaded
			if ( _private.myParentOverlay )
			{
				_private.myParentOverlay.childDOMLoaded(_private.myName, _private.myChromeContainer,
					_private.myIframe, _private.myIframeDOM, new Object(_private.myOptions));
			}
		},


		// POP an OVERLAY  (overlayname is a key element and thus required)
		pop: function(overlayname, options)
		{
			if ( !overlayname  ||  overlayname=='' ) return(false);

			// If for some reason the overlay already exists... destroy it
			_private.destroy(overlayname, _public.getOverlayProperties(overlayname));

			// Capture the user options for creation of the overlay
			var configurationOptions = Object.extend(
				{
				className:		"overlay",		// default window style/skin	 (overlay=>modal overlay, help=>modeless overlay)
				top:					"center",		// absolute position of the top edge (px units assumed)
				left:					"center",		// absolute position of the left edge (px units assumed)
				width:				450,				// default window width (px units assumed)
				height:				300,				// default window height (px units assumed)
				resizable:			false,			// allows user to resize the window (uses chrome support)
				draggable:		false,			// enable titlebar window dragging (uses chrome support)
				modal:				true,				// makes window modal or modeless
				scrollable:			"auto",			// yes or no or auto  (not supported in earlier version of IE or Opera)
				url:					null,				// url to populate the overlay from
				html:				null,				// HTML to populate into the overlay (done before url)
				title:					"",					// Accessability attribute for the iframe
				childContentLabel: "PAGECONTAINER",		// ID of containing element of the child DOM contents (used for resizing after DOM loaded)
				busy:				{message:"Retrieving information, please wait ...", delayFog:0, delaySpinner:200}, returnFieldFocus : null // form field to receive focus when the overlay is closed
				}, options||{});
			configurationOptions.name = overlayname;

			_private.pop(configurationOptions);
			return(true);
		},


		// Resizes the containing iframe of the current instance if no name is provided or child overlay if provided
		size: function(name,width,height)
		{
			var overlayProperties = _public.getOverlayProperties(name);
			var iframeContainer = overlayProperties.myIframe;

			// Capture the new width and/or height properties
			var configurationOptions = overlayProperties.myOptions;
			configurationOptions.width = (width && width!='') ? width : configurationOptions.width;
			configurationOptions.height = (height && height!='') ? height : configurationOptionss.height;

			// Establish Size of the overlay (via iframe)
			var iframeSize = _private.getSize(iframeContainer,configurationOptions);
			iframeContainer.setStyle({width:iframeSize.width, height:iframeSize.height});
		},


		// Reposition the containing containing chrome of the current instance if no name is provided or child overlay if provided
		position: function(name,left,top)
		{
			var overlayProperties = _public.getOverlayProperties(name);
			var chromeContainer = overlayProperties.myChromeContainer;

			// Capture the new width and/or height properties
			configurationOptions = new Object(overlayProperties.myOptions);
			configurationOptions.left = (left && left!='') ? left : configurationOption.left;
			configurationOptions.top = (top && top!='') ? top : configurationOption.top;

			// Establish Position of the overlay (via chromeContainer)
			var chromePosition = _private.position(chromeContainer,configurationOptions);
			chromeContainer.setStyle({left:chromePosition.left, top:chromePosition.top});
		},


		// Returns the iFrameID of the current containing iFrame assuming we are a child overlay
		getContainerId: function()
		{
			return( _private.myIframe_id );
		},

		// Return true/false if child overlay exist
		haveChildOverlays: function()
		{
				return( _private.myChildOverlays.length );
		},

		// Determines if the iFrameID is a child overlay
		isAChildOverlay: function(name)
		{
			name = (!name || name=='') ? ((_private.myOptions && _private.myOptions.name) ? _private.myOptions.name : null) : name;
			if (!name) return(false);

			if ( name == _private.myOptions.name ) return( _private.myParentOverlay );
			return( !!_public.getOverlayProperties(name) );
		},


		// Retrieves the properties of a child overlay (MY private variables)
		//	 Returns it's own properties if no name is provided
		getOverlayProperties: function(name)
		{
			name = (!name || name=='') ? ((_private.myOptions && _private.myOptions.name) ? _private.myOptions.name : null) : name;
			if (!name) return(false);
			return( _private.getOverlayProperties(name) );
		},


		//
		setTitle: function(name,title)
		{
			var properties = _public.getOverlayProperties(name);
			if (properties && properties.myParentDocument)
			{
				var elm = properties.myParentDocument.getElementById(properties.myName+'_title');
				if (elm)	elm.innerHTML = title;
			}
		},


		// Child DOM has been loaded. Re-size and re-position the child overlay window
		childDOMLoaded: function(name, chromeContainer, iframeContainer, IframeDOM, configurationOptions)
		{
			// Need to re-establish the iframe DOM element (the temporary please wait content gets replaced by actual page)
			var	overlayProperties = _public.getOverlayProperties(name)
					childContentLabel = overlayProperties.myOptions.childContentLabel;
			overlayProperties.myIframeDOM = IframeDOM;

			// Establish Size of the overlay (via iframe)
			var pageContainer = IframeDOM.getElementById(childContentLabel) || IframeDOM.body;
			var ifrmsize = _private.getPageSize(pageContainer,configurationOptions);
			$(iframeContainer).setStyle({width:ifrmsize.pageWidth+'px', height:ifrmsize.pageHeight+'px'});

			// Establish Position of the overlay (via chromeContainer)
			var chromePosition = _private.position(chromeContainer,configurationOptions);
			$(chromeContainer).setStyle({left:chromePosition.left, top:chromePosition.top});

			var clonedOverlayProperties = new Object(overlayProperties);
			//$(document).fire("Overlay:loaded", {name:name, overlayProperties:clonedOverlayProperties});
			setTimeout( function(){
				$(document).fire("Overlay:loaded", {name:name, overlayProperties:clonedOverlayProperties});
				}, 50);
		},


		// Destroys the specified overlay and related UI elements
		// Id no ID is specified  it destroys the CURRENT overlay (the overlay this codebase is currently resident in) and related UI elements
		destroy: function (name)
		{
			name = (!name || name=='') ? ((_private.myOptions && _private.myOptions.name) ? _private.myOptions.name : null) : name;
			if (!name) return(false);

			if ( name == _private.myName )
			{	// Self destruction, must have parent perform destruction on our behalf, cant actually destroy my own instance
				setTimeout(function(){_private.myParentOverlay.destroy(name);},0);
				return(true);
			}
			else
			{	// Check children to determine which to destroy
				for (var index=0, len=_private.myChildOverlays.length; index<len; index++)
				{
					if (name == _private.myChildOverlays[index].myName)
					{	// Destroy the child overlay
						var childProperties = _private.myChildOverlays[index];
						_private.destroy(name,childProperties);
						_private.myChildOverlays.slice(index,1);
						return(true);
					}
				}
				return(false);
			}
		},


		internalField : function(fieldName)
		{
			return (_private[fieldName]);
		},


		dump: function(obj, maxLevels)
		{
			_private.dumpObject(obj, maxLevels);
		}
	};


	/* ------------------------------ P R I V A T E ---------------------------- */
	var _private =
	{	// the MY variables are relative to the instance of THIS Overlay object (single instance in the current DOM)
		myName:						null,			// name of the current instantiated overlay
		myWindow:					null,			// current window
		myDocument:				null,			// document (iframe document or window document)
		myIframe:					null,			// iframe element containing this instance of overlay (actual iframe element)
		myIframe_id:				null,			// ID of iframe element containing this instance of overlay
		myIframeDOM:				null,			// iframe document
		myParentWindow:			null,			// parent window
		myParentDocument:		null,			// parent document
		myParentOverlay:			null,			// parent Overlay class/object
		myOptions:					null,			// object of properties used for creating this instance of overlay
		myChromeContainer:	null,			// chrome container containing this instance of overlay
		myChildOverlays:			[],				// array of child Overlays (iframe elements) created via the Pop()

		_ELEMENT_NODE :		1,				// node type is element
		_topOverlay :				null,			// points to parent's Overlay if it exists
		_subscriptions :				[],				// array indexed by [eventName][sourceFieldName] = elements IDs subscribing to the event
		_events :						[],				// array indexed by ['event_source_element'] contains eventNames
		_dragMode :					false,		// drag mode indicator
		_sizeMode :					false,		// size mode indicator  (true-actively resizing, false not)


		// Pops an iframe (and supporting chrome) relative to the current DOM/Window for rendering a web page
		//		Should only be called by a parent window to create a child overlay, assumes initialize() has been executed al
		//		1st the chrome container and chrome are constructed (round corner border and drag handles) --> options.name+"_chrome"
		//		2nd creates an iframe and configures it's properties (e.g. scrolling) - actual container of the content page --> options.name+"_overlay"
		//		3rd Pushes iframe into chrome container
		//		4th Display Busy message
		//		5th Registers the newly created Chrome, iframe, and options used to build it
		//		6th Establish behaviors Resizing, Dragging,
		pop : function(configurationOptions)
		{
			// Patch for IE bleed through bug
			_private.hideDropDowns();

			try
			{
				var	overlayProperties = {	/* Overlay Properties of the NEW overlay being created */
							myName:configurationOptions.name,
							myWindow:null,
							myDocument:null,
							myIframe:null,
							myIframe_id:null,
							myIframeDOM:null,
							myParentWindow:window.self,
							myParentDocument:$(window.self.document),
							myParentOverlay:$(window.self.document).Overlay,
							myOptions:configurationOptions,
							myChromeContainer:null,
							myChildOverlays:[] },
						current_zindex = _private.getMaxZIndex(),		// determine max index of current window (DOM)
						pageSize = _private.getPageSize(); // determine page dimensions of current window

				// Load the Overlay style sheet into the parent window space for renderinf the overlay chrome correctly.
				setTimeout(function(){_private.loadStyleFile('script/css/'+configurationOptions.className+'.css');},0);

				// Create the overall container for the overlay (this element is used for positioning the overlay within the parent (current) window
				var chromeContainer = $(document.createElement("div"));
				chromeContainer = Object.extend( chromeContainer,
					{id:configurationOptions.name+"_chrome", name:configurationOptions.name+"_chrome", className:configurationOptions.className});
				chromeContainer.setStyle({zIndex:current_zindex+19, backgroundColor:'transparent', border:"0px none transparent"});
				_private.myDocument.body.appendChild(chromeContainer);
				overlayProperties.myChromeContainer = chromeContainer;

				// Draggable handle area
				var titleHTML =
					'<tr><td class="' + configurationOptions.className + '_w"></td>' +
						'<td>'+
							'<table class="table_window" style="width:100%;height:22px;background-color:#D9D9D9;"><tr>' +
								'<td id="' + configurationOptions.name + '_title" class="' + configurationOptions.name + '_drag ' + configurationOptions.name + '_draggable ' + configurationOptions.className + '_title">'+configurationOptions.title+'</td>'+
								'<td class="' + configurationOptions.className + '_title" align="top" style="width:13px;"><img id="'+configurationOptions.name+'_Close" align="top" src="script/css/' + configurationOptions.className + '/close.gif" width="13" height="13" title="Close" border="0"/></td>'+
							'</tr></table>'+
						'</td>'+
						'<td class="' + configurationOptions.className + '_e"></td></tr>';

				// Add the chrome to the container
				var chrome =
					'<table class="table_window"><tbody>' +
					'<tr><td class="' + configurationOptions.className + '_nw ' + configurationOptions.name + '_draggable"></td>' +
						'<td class="' + configurationOptions.className + '_n ' + configurationOptions.name + '_draggable"></td>' +
						'<td class="' + configurationOptions.className + '_ne"></td></tr>' +
					titleHTML +
					'<tr><td class="' + configurationOptions.className + '_w"></td>' +
						'<td id="' + configurationOptions.className + '_container">' +
						'<div id="' + configurationOptions.name + '_border" class="' + configurationOptions.className + '_border" ><!-- iframe goes here --></div>' +
						'</td><td id="' + configurationOptions.name + '_e" class="' + configurationOptions.className + '_e"></td></tr>'  +
					'<tr><td class="' + configurationOptions.className + '_sw"></td>'+
						'<td id="' + configurationOptions.name + '_s" class="' + configurationOptions.className + '_s"></td>'+
						'<td id="' + configurationOptions.name + '_se" class="' + configurationOptions.className + '_se"></td></tr>' +
					'</tbody></table>';
				chromeContainer = $(configurationOptions.name + "_chrome");
				chromeContainer.innerHTML = chrome;

				// Create the iframe container for the actual content (also the element that gets re-sized based on rendered content
				var iframeContainer = $(document.createElement("iframe"));
				iframeContainer = Object.extend( iframeContainer,
					{id:configurationOptions.name+"_overlay", name:configurationOptions.name+"_overlay",
					className:configurationOptions.className+'_overlay', title:configurationOptions.title,
					marginWidth:0, marginHeight:0, frameBorder:"0px none transparent"});
				iframeContainer.setStyle({zIndex:current_zindex+20, marginWidth:0, marginHeight:0, width:configurationOptions.width, height:configurationOptions.height});

				// Push the iframe into the Chrome container
				var div = _private.myDocument.getElementById(configurationOptions.name + '_border');
				div.appendChild(iframeContainer);
				overlayProperties.myIframe = iframeContainer;
				overlayProperties.myIframe_id = iframeContainer.id;
				overlayProperties.myIframeDOM = iframeContainer.contentWindow || iframeContainer.contentDocument;
				overlayProperties.myIframeDOM = (overlayProperties.myIframeDOM.document) ? overlayProperties.myIframeDOM.document : overlayProperties.myIframeDOM;

				// BUG FIX for Internet Explorer
				if(self.frames[configurationOptions.name+"_overlay"].name != configurationOptions.name+"_overlay")
					{self.frames[configurationOptions.name+"_overlay"].name = configurationOptions.name+"_overlay";}

				// Establish scrolling for the iframe container
				switch (configurationOptions.scrollable.toLowerCase())
				{
					case 'true':
					case 'yes':
						iframeContainer.scrolling = 'yes';
						break;
					case 'vertical':
						iframeContainer.className += ' '+configurationOptions.className+'_VerticalScrolling';
						break;
					case 'horizontal':
						iframeContainer.className += ' '+configurationOptions.className+'_HorizontalScrolling';
						break;
					case 'auto':
						//iframeContainer.className += ' '+configurationOptions.className+'_AutoScrolling';
						iframeContainer.scrolling = configurationOptions.scrollable;
						break;
					case 'both':
						iframeContainer.className += ' '+configurationOptions.className+'_BothScrolling';
						break;
					case 'no':
					case 'none':
						iframeContainer.className += ' '+configurationOptions.className+'_NoScrolling';
						iframeContainer.scrolling = 'no';
						break;
				}

				// Register the new overlay  (retain the chromeContainer, iframeContainer and the options used to create it)
				_private.myChildOverlays[_private.myChildOverlays.length] = overlayProperties;

				iframeContainer.setStyle({width:'450px', height:'300px'});
				chromeContainer.setStyle({left:'200px', top:'100px'});

				// Load the contents of the iframe
				if ((configurationOptions.url && configurationOptions.url!='') || (configurationOptions.html && configurationOptions.html!=''))
				{
					var iframeContainerDOM = iframeContainer.contentWindow || iframeContainer.contentDocument;
					iframeContainerDOM = iframeContainerDOM.document ? iframeContainerDOM.document : iframeContainerDOM;

					iframeContainerDOM.open("text/html", "replace");

					var busyInitiation = '';
					if (( typeof(Busy)!='undefined' ) && configurationOptions.busy.message)
					{	// Put the Busy message up while the actual content is being loaded
						configurationOptions.busy.message = configurationOptions.busy.message ? configurationOptions.busy.message : 'Retrieving information ...';
						configurationOptions.busy.delayFog = configurationOptions.busy.delayFog ? configurationOptions.busy.delayFog : 0;
						configurationOptions.busy.delaySpinner = configurationOptions.busy.delaySpinner ? configurationOptions.busy.delaySpinner : 200;
						busyInitiation =	"Busy.showBusyTimer('"+configurationOptions.busy.message+"',"+configurationOptions.busy.delayFog+","+configurationOptions.busy.delaySpinner+");";
					}

					if (configurationOptions.url && configurationOptions.url!='')
					{
						var cntnt = "<!DOCTYPE>"+
											"<html>"+
											"<head>" +
												"<script type='text/javascript' src='scriptcompressed/prototype-1.6.1.0.js'></script>" +
												"<script type='text/javascript' src='scriptcompressed/Common.js'></script>" +
												"<script type='text/javascript' src='scriptcompressed/Busy.js'></script>" +
												"<script type='text/javascript'>" +
													"function pageInit()" +
													"{" +
															busyInitiation +
															"setTimeout(function(){window.location.href='"+configurationOptions.url+"';},50);"+
													"}" +
													"Event.observe(document, 'dom:loaded', pageInit);"+
												"</script>" +
											"</head>" +
											"<body >&nbsp;</body>" +
											"</html>";
						iframeContainerDOM.write( cntnt );
					}
					else
						iframeContainerDOM.write(configurationOptions.html);
					iframeContainerDOM.close();
				}

				/*  WHEN THE CHILD DOM IS LOADED, THEN RESIZE IF AUTO SPECIFIED, AND POSITION IF CENTER
				// Establish size of the content container (iframe)
				var iframeSize = _private.size(iframeContainer);
				iframeContainer.setStyle({width:iframeSize.width, height:iframeSize.height});

				// Establish Position of the overlay (via chromeContainer)
				var chromePosition = _private.position(chromeContainer);
				chromeContainer.setStyle({left:chromePosition.left, top:chromePosition.top});
				*/

				// Listen for the Close button
				elm = $(configurationOptions.name+'_Close');
				if (elm)
				{
					elm.optionsName = configurationOptions.name;
					Event.observe( elm, 'click', function(evnt){var name=configurationOptions.name; _public.destroy(name)});
				}

				// Enable the draggable feature by adding listeners to the draggable areas
				if ( configurationOptions.draggable )
				{
					var elm, dragElms = $(configurationOptions.name + "_chrome").getElementsByClassName(configurationOptions.name + '_draggable');
					for (var i=0; i<dragElms.length; i++)
							{
							elm = dragElms[i];
							elm.overlayProperties = overlayProperties;
							Event.observe(elm, 'mousedown', _private.dragOBJ);
							};
				}

				if ( configurationOptions.resizable )
				{// Make the Bottom, Right, and Bottom Right borders user handles for resizing the overlay

					// Change the cursor for the right chrome border to let the user know they can change width
					var elm =$(configurationOptions.name + '_e');
					elm.addClassName(configurationOptions.className + "_e_sizer");
					elm.overlayProperties = overlayProperties;
					Event.observe( elm, 'mousedown', _private.resizeOBJ );

					// Change the cursor for the bottom chrome border to let the user know they can change height
					elm = $(configurationOptions.name + '_s');
					elm.addClassName(configurationOptions.className + "_s_sizer");
					elm.overlayProperties = overlayProperties;
					Event.observe( elm, 'mousedown', _private.resizeOBJ );

					// Change the cursor for the bottom right chrome border to let the user know they can change size
					elm = $(configurationOptions.name + '_se');
					elm.addClassName(configurationOptions.className + "_sizer");
					elm.overlayProperties = overlayProperties;
					Event.observe( elm, 'mousedown', _private.resizeOBJ );
				}

				// If the overlay is modal, catch all/ignore all overlay background  mouse and keyboard events
				if ( configurationOptions.modal )
				{	// Create div element that encompasses the whole page (DOM relative)
					var underlay = $(document.createElement("div"));
					underlay = Object.extend( underlay, {id:configurationOptions.name+"_underlay", className:configurationOptions.className+'_underlay'});
					underlay.setStyle({display:'inline', position:'absolute', top:'0px', left:'0px', height:pageSize.pageHeight + 'px', width:'100%', zIndex:current_zindex+18});

					_private.myDocument.body.insertBefore(underlay, _private.myDocument.body.firstChild);
					var undrly = $(configurationOptions.name + "_underlay");
					chromeContainer.underlay = undrly;
					Event.observe( undrly, 'click', _private.emptyFunction );
				}
			}
			catch (err)
			{
				_private.logError( err);
			}
		},


		// Determines the position of the Chrome container given the configuration options requested
		position: function(chromeContainer,configurationOptions)
		{
			var offsets = chromeContainer.positionedOffset();
			var top     = offsets[1];
			var left    = offsets[0];
			var chrmeSize = chromeContainer.getDimensions();
			var pageSize = _private.getPageSize();
			var pageCenter  = {y:parseInt(pageSize.pageHeight/2.0), x:parseInt(pageSize.pageWidth/2.0)};
			var windowCenter  = {y:parseInt(pageSize.windowHeight/2.0), x:parseInt(pageSize.windowWidth/2.0)};
			switch (configurationOptions.left)
			{
				case 'left':
					left = 0;
					break;
				case 'right':
					left = parseInt(Math.abs(pageSize.windowWidth - chrmeSize.width)) ;
					break;
				case 'center':
					left = parseInt(Math.abs(windowCenter.x - chrmeSize.width / 2.0)) ;
					break;
				case '':
					break;
				default:
					left = Math.floor(parseFloat(configurationOptions.left)) ;
					break;
			}
			switch (configurationOptions.top)
			{
				case 'top':
					top = 0;
					break;
				case 'bottom':
					top = parseInt(Math.abs(pageSize.windowHeight - chrmeSize.height));
					break;
				case 'center':
					top = parseInt(Math.abs(windowCenter.y - chrmeSize.height / 2.0)) ;
					break;
				case '':
					break;
				default:
					top = Math.floor(parseFloat(configurationOptions.top));
					break;
			}

			//var top = pageSize.scrollTop + (pageSize.windowHeight < chrmeSize.height) ? 1 : top;
			//var left = pageSize.scrollLeft + (pageSize.windowWidth < chrmeSize.width) ? 1 : left;
			return( {left:left+'px', top:top+'px'} );
		},


		// Determines the width and height of the iframe container given the requested configuration options
		getSize: function(iframeContainer,configurationOptions)
		{
			var	width = (configurationOptions.width == 'auto') ? iframeContainer.clientWidth : Math.ceil(parseFloat(configurationOptions.width)),
					height = (configurationOptions.height == 'auto') ? iframeContainer.clientHeight : Math.ceil(parseFloat(configurationOptions.width));
			return( {width:width+'px', height:height+'px'} );
		},


		// Destroys the overlay and related UI elements
		destroy: function (name,overlayProperties)
		{
			if (!name || name=='' || !overlayProperties) return;
			$(document).fire("Overlay:destroy", name);

			// Restore the dropdowns
			_private.unhideDropDowns();

			// Retrieve property info of the overlay to be destroyed
			var	configurationOptions = overlayProperties.myOptions,
					chromeContainer = overlayProperties.myChromeContainer,
					iframeContainer = overlayProperties.myIframeContainer,
					myParentDocument = overlayProperties.myParentDocument;

			// If draggable, remove listeners
			if ( chromeContainer && configurationOptions.draggable )
			{
				var dragElms = $$('.'+configurationOptions.name + '_draggable');
				dragElms.invoke('stopObserving', 'mousedown');	//, _private.dragOBJ );
				/*dragElms.each(function(elm,index) {Event.stopObserving( elm, 'mousedown', _private.dragOBJ );});*/
			}

			// If resizable, remove listeners
			if ( chromeContainer && configurationOptions.resizable )
			{
				var sizeElms = $$('.'+configurationOptions.className + "_e_sizer");
				sizeElms.invoke('stopObserving', 'mousedown');	//, _private.resizeOBJ );
				sizeElms = $$('.'+configurationOptions.className + "_s_sizer");
				sizeElms.invoke('stopObserving', 'mousedown');	//, _private.resizeOBJ );
				sizeElms = $$('.'+configurationOptions.className + "_sizer");
				sizeElms.invoke('stopObserving', 'mousedown');	//, _private.resizeOBJ );
			}

			// Remove the underlay div element (modal)
			if ( chromeContainer.underlay )
			{
				Event.stopObserving(chromeContainer.underlay, 'click', _private.emptyFunction );
				(chromeContainer.underlay.parentNode).removeChild(chromeContainer.underlay);
			}

			// If form field to receive focus was provided set focus otherwise 1st field in the form
			if (configurationOptions.returnFieldFocus && $(configurationOptions.returnFieldFocus))
			{	$(configurationOptions.returnFieldFocus).focus(); }
			//else
			//{	_private.setFocusToFirstFormField();  }

			// Dispose of the overlay reference and configuration parameters associated with it
			for (var index=0, len=_private.myChildOverlays.length; index<len; index++)
				{
					if (name == _private.myChildOverlays[index].myOptions.name)	// TODO: _private.myChildOverlays[index].myName
					{	// Destroy the child properties entry in the myChildOverlays array
					_private.myChildOverlays.splice(index,1);
					break;
					}
				}

			// Let me parent window know this overlay is destroyed
			if (myParentDocument)
				setTimeout( function(){$(myParentDocument).fire("Overlay:destroyed", {name:name});}, 50);

			// Remove the chromeContainer, iframe, and all content elements
			if ( chromeContainer )
			{
				(chromeContainer.parentNode).removeChild(chromeContainer);
				chromeContainer = null;
			}
		},


		// Retrieves the maximum ZIndex value across the whole page
		getMaxZIndex: function()
		{
			var allElems = window.top.document.getElementsByTagName ? window.top.document.getElementsByTagName("*") : window.top.document.all;
			var maxZIndex = 0;
			for(var i=0; i<allElems.length; i++)
			{
				var zIndex = allElems[i].zIndex ? allElems[i].zIndex : allElems[i].style.zIndex;
				maxZIndex = (parseInt(zIndex) > maxZIndex) ? parseInt(zIndex) : maxZIndex;
			}
			return maxZIndex;
		},


		// Determine the page dimensions
		getPageSize: function(parent)
		{
			parent = $(parent || document.body);
			var windowWidth, windowHeight, pageHeight, pageWidth;
			if ( parent  != document.body )
			{
				windowWidth = parent.getWidth();
				windowHeight = parent.getHeight();
				pageWidth = parent.scrollWidth;
				pageHeight = parent.scrollHeight;
			}
			else
			{
				var xScroll, yScroll;
				if (window.innerHeight && window.scrollMaxY)
				{
				xScroll = document.body.scrollWidth;
				yScroll = window.innerHeight + window.scrollMaxY;
				}
				else if (document.body.scrollHeight > document.body.offsetHeight)
				{ // all but Explorer Mac
					xScroll = document.body.scrollWidth;
					yScroll = document.body.scrollHeight;
				}
				else
				{ // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
					xScroll = document.body.offsetWidth;
					yScroll = document.body.offsetHeight;
				}

				if (self.innerHeight)
				{  // all except Explorer
					windowWidth = self.innerWidth;
					windowHeight = self.innerHeight;
				}
				else if (document.documentElement && document.documentElement.clientHeight)
				{ // Explorer 6 Strict Mode
					windowWidth = document.documentElement.clientWidth;
					windowHeight = document.documentElement.clientHeight;
				}
				else if (document.body)
				{ // other Explorers
					windowWidth = document.body.clientWidth;
					windowHeight = document.body.clientHeight;
				}

				// for small pages with total height less then height of the viewport
				pageHeight = ( yScroll < windowHeight ) ? windowHeight : yScroll;

				// for small pages with total width less then width of the viewport
				pageWidth = (xScroll < windowWidth) ? windowWidth : xScroll;
			}

			var scrOfX = 0, scrOfY = 0;
			if (typeof( window.pageYOffset ) == 'number')
			{
				//Netscape compliant
				scrOfY = window.pageYOffset;
				scrOfX = window.pageXOffset;
			}
			else if (document.body && (document.body.scrollLeft || document.body.scrollTop))
			{
				//DOM compliant
				scrOfY = document.body.scrollTop;
				scrOfX = document.body.scrollLeft;
			}
			else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop))
			{
				//IE6 standards compliant mode
				scrOfY = document.documentElement.scrollTop;
				scrOfX = document.documentElement.scrollLeft;
			}
			return( {pageWidth: pageWidth ,pageHeight: pageHeight , windowWidth: windowWidth, windowHeight: windowHeight, scrollLeft: scrOfX, scrollTop: scrOfY} );
		},

		// Simple empty listener, stops the event from bubbling
		emptyFunction : function(evt)
		{
			var	elm = evt.element(),
					args = $A(arguments);
			evt.stop();	// stop the event from bubbling
			return(false);
		},


		// Initiates the drag function of the overlay via mousedown event
		dragOBJ : function (evnt)
		{// Context is the calling page's DOM
			if (evnt) Event.stop(evnt);

			// the element the user used to initiate the dragging should have the following property: myChromeContainer
			var	dragElement = evnt.element();

			// Dont bother trying to perform draggin without the availability of the overlay properties
			if (!dragElement.overlayProperties) return;

			// Extract a few key pieces of info from the overlayProperties object
			var	doc = _private.myDocument,
					optionsName =  dragElement.overlayProperties.myName,
					chromeContainer = dragElement.overlayProperties.myChromeContainer,	// (thats what we actually position)
					iframeDOM = dragElement.overlayProperties.myIframeDOM;		// used for capturing mouse movements within the iframe

			// We are now in drag mode, disconnect drag initiating event listening
			var dragElms = $$('.'+optionsName + '_draggable');
			dragElms.invoke('stopObserving', 'mousedown', _private.dragOBJ );
			var dragMode = true;
			function drag(evnt)
			{
				if( dragMode )
				{
					var pos = {x:evnt.screenX, y:evnt.screenY};
					//dragElement.innerHTML = "(" + pos.x + ", " + pos.y + ")"
					var dX = pos.x - eX;
					var dY = pos.y - eY;
					chromeContainer.style.left = (oX + dX) + 'px';
					chromeContainer.style.top = (oY + dY) + 'px';
				}
			}
			function stopdrag()
			{
				dragMode = false;
				// Stop listening for mouse events
				Event.stopObserving( doc.body, 'mousemove', drag );
				Event.stopObserving( doc.body, 'mouseup', stopdrag );
				Event.stopObserving( iframeDOM.body, 'mousemove', drag );
				Event.stopObserving( iframeDOM.body, 'mouseup', stopdrag );
				// Re-establishing listening that initiates the drag operation
				dragElms.invoke('observe', 'mousedown', _private.dragOBJ );
			}

			var oX=parseInt(chromeContainer.style.left);
			var oY=parseInt(chromeContainer.style.top);
			var eX=evnt.screenX;
			var eY=evnt.screenY;
			// Capture mouse movements in parent(current) DOM space
			Event.observe( doc.body, 'mousemove', drag );
			Event.observe( doc.body, 'mouseup', stopdrag );
			// Overlay child DOM - capture mouse movement
			Event.observe( iframeDOM.body, 'mousemove', drag );
			Event.observe( iframeDOM.body, 'mouseup', stopdrag );
		},


		// Initiates the resize function of the overlay
		resizeOBJ : function (evnt)
		{// Context is the calling page's DOM because the initial resizing event is in the chrome
			if (evnt) Event.stop(evnt);

			// the element the user used to initiate the resizing should have the following property: myChromeContainer
			// Dont bother trying to perform resizing without the availability of the overlay properties
			var	resizeElement = evnt.element();
			if (!resizeElement || !resizeElement.overlayProperties) return;

			// Extract a few key pieces of info from the overlayProperties object
			var	doc = _private.myDocument,
					optionsClassName = resizeElement.overlayProperties.myName,
					iframeContainer = resizeElement.overlayProperties.myIframe,		// (this is the element we actually resize)
					iframeDOM = resizeElement.overlayProperties.myIframeDOM,		// used for capturing mouse movements within the iframe
					pageSize = _private.getPageSize(),												// calling windows page size (current context)
					direction = (evnt.target.className.indexOf("_e_sizer") >= 0) ? 'H' :(evnt.target.className.indexOf("_s_sizer") >= 0) ? 'V' : 'B';

			// We are now in sizing mode disconnect initiating event listening
			var sizeElms = $$('.'+optionsClassName + '_sizer', '.'+optionsClassName + '_e_sizer', '.'+optionsClassName + '_s_sizer');
			sizeElms.invoke('stopObserving', 'mousedown', _private.resizeOBJ );
			var sizeMode = true;

			function sizing(evnt)
			{
				if( sizeMode )
				{
					var pos = {x:evnt.screenX, y:evnt.screenY};
					var dX = pos.x - eX;
					var dY = pos.y - eY;
					dX = (direction != 'V') ? dX : 0;
					dY = (direction != 'H') ? dY : 0;
					var tX = (oX + dX);
					var tY = (oY + dY);
					if (tX < 0) tX = Math.abs(dX) / 3.0;
					if (tY < 0) tY = Math.abs(dY) / 3.0;
					iframeContainer.setStyle({width: tX+'px', height:tY+'px'});
					//resizeElement.innerHTML = "(" + pos.x + ", " + pos.y + ")"
				}
			}
			function stopsizing()
			{
				sizeMode = false;
				Event.stopObserving( doc.body, 'mousemove', sizing );
				Event.stopObserving( doc.body, 'mouseup', stopsizing );
				Event.stopObserving( iframeDOM.body, 'mousemove', sizing );
				Event.stopObserving( iframeDOM.body, 'mouseup', stopsizing );
				// Re-establishing listening that initiates the drag operation
				sizeElms.invoke('observe', 'mousedown', _private.resizeOBJ );
			}

			var oX=parseInt(iframeContainer.clientWidth);
			var oY=parseInt(iframeContainer.clientHeight);
			var eX=evnt.screenX;
			var eY=evnt.screenY;
			// Capture mouse movements in parent(current) DOM space
			Event.observe( doc.body, 'mousemove', sizing );
			Event.observe( doc.body, 'mouseup', stopsizing );
			// Overlay child DOM - capture mouse movement
			Event.observe( iframeDOM.body, 'mousemove', sizing );
			Event.observe( iframeDOM.body, 'mouseup', stopsizing );
		},


		// Hides a list of SELECT elements (use for IE browser only)
		hideDropDowns: function()
		{
			if (!Prototype.Browser.IE) return;
			var	elements = document.getElementsByTagName("select"),
					element, description, dLength;
			for (e = elements.length-1; e>=0; e--)
			{
				element = elements[e];
				if (element.style.display != 'none')
				{
					description = (element.selectedIndex >= 0) ? element.options[element.selectedIndex].text : '';
					dLength = Math.ceil(1.1*description.length);
					dLength = (dLength <= 0) ? 1: dLength;
					var newInputelement = document.createElement('input');
					newInputelement = Object.extend(newInputelement,
						{id:"x_"+element.id, readOnly:true, className:'ReadOnly', type:'text', value:description, size:dLength});
					element.parentNode.insertBefore(newInputelement,element.nextSibling);
					newInputelement.style.width = parseInt(element.style.width)+'px';
					newInputelement.style.height = parseInt(element.style.height)+'px';
					element.style.display = 'none';
				}
			}
		},

		// Unhides all the elements that were hidden  (use for IE browser only)
		unhideDropDowns: function()
		{
			if (!Prototype.Browser.IE) return;
			var	elements = document.getElementsByTagName("select"),
					element, tmpElement, e;
			for (e = elements.length-1; e>=0; e--)
			{
				element = elements[e];
				tmpElement = $("x_"+element.id);
				if (tmpElement) element.parentNode.removeChild(tmpElement);
				element.style.display = '';
			}
		},


		//	Enable the inclusion of dependent library components
		loadStyleFile: function(StyleSheetName)
		{
			var headID = document.getElementsByTagName("head")[0];
			var cssNode = document.createElement('link');
			cssNode.type = 'text/css';
			cssNode.rel = 'stylesheet';
			cssNode.href = StyleSheetName;
			cssNode.media = 'screen';
			headID.appendChild(cssNode);
		},


		// Handles events (to be progated to the parent window) generated by the current window/overlay
		handleOverlayEvent : function( evnt )
		{
			try
			{
				// Determine the source element of the event
				SourceElement = null;
				var SourceElement = evnt.currentTarget ? evnt.currentTarget : this;
				if( SourceElement && (SourceElement.nodeType == 3 || SourceElement.nodeType == 4 ) )
				{// Note that Safari and Konqueror sometimes make the target point to the textNode inside the element. This is a patch for that
					SourceElement = SourceElement.parentNode;
				}
				Event.stop(evnt);		// Stop the event from bubbling to it's parent element

				// Process each event specified in the event attribute
				var	eventAttribute = SourceElement.readAttribute('event'),
						eventPairs = eventAttribute.replace(/\s+$/,'').split(' ');
				for (var p=0; p<eventPairs.length; p++)
				{	// each event entry is of the form:		eventName:elementscope
					var	pieces = eventPairs[p].split(':'),
							eventName = pieces[0].replace(/^\s+/,'').replace(/\s+$/,''),
							elementScope= pieces[1].replace(/^\s+/,'').replace(/\s+$/,'');

					if (eventName.toLowerCase() == 'overlay')
					{
						switch (elementScope.toLowerCase() )
						{
						case 'close':
							// Stop listening for the original event triggering this
							Event.stopObserving( SourceElement, 'click', _private.handleOverlayEvent );
							_public.destroy();
							break;
						case 'center':
							_public.position(null,'center','center');
							break;
						}
					}
					else
					{	// Assemble the Event object to publish to the parent document
						var DataObj = {eventName:eventName, elementScope:elementScope, eventElementID:SourceElement.id};
						DataObj.Data = {};

						// Assemble the Data object from the elements that are publishing within the scope element
						var elementsToPublish = $(elementScope).getElementsBySelector('*[publish*="'+eventName+':"]');
						for (var e=0; e<elementsToPublish.length; e++)
						{
							var element = elementsToPublish[e];
							var eventAttribute = element.readAttribute('publish');
							var publishPairs = eventAttribute.replace(/\s+$/,'').split(' ');
							for (var p=0; p<publishPairs.length; p++)
							{
								var pieces = publishPairs[p].split(':');
								var publishEventName = pieces[0].replace(/^\s+/,'').replace(/\s+$/,'');
								if (eventName != publishEventName) continue;
								var publishFieldName = pieces[1].replace(/^\s+/,'').replace(/\s+$/,'');

								var fieldvalue = (typeof(element.getValue)=='function') ? element.getValue() : element.innerHTML;

								DataObj.Data[publishFieldName] = fieldvalue;
							}
						}
					}

					// Publish the event by calling the parent Overlay to publish the event
					if (_private.myParentOverlay)
					{
						$(_private.myParentDocument.body).fire("Overlay:"+eventName, DataObj);
					}
				}
			}
			catch (err)
			{
				_private.logError( err );
			}
		},


		// Retrieves the properties of a child overlay ( the MY private variables)
		getOverlayProperties: function(name)
		{
			if (!name || name=='')  return(null);

			if ( _private.myOptions && name == _private.myOptions.name )
				return({	/* Overlay Properties */
							myName:_private.myName,
							myWindow:_private.myWindow,
							myDocument:_private.myDocument,
							myIframe:_private.myIframe,
							myIframe_id:_private.myIframe_id,
							myIframeDOM:_private.myIframeDOM,
							myParentWindow:_private.myParentWindow,
							myParentDocument:_private.myParentDocument,
							myParentOverlay:_private.myParentOverlay,
							myOptions:_private.myOptions,
							myChromeContainer:_private.myChromeContainer,
							myChildOverlays:_private.myChildOverlays
							});

			// Check the immediate children for a recognizable name
			for (var index=0, len=_private.myChildOverlays.length; index<len; index++)
			{
				if (name == _private.myChildOverlays[index].myOptions.name)
					return(_private.myChildOverlays[index]);
			}

			// Not found as an immediate child, recurse to check it's children overlays for the name
			/*
			for (var index=0, len=_private.myChildOverlays.length; index<len; index++)
			{
				var childProperties = _private.myChildOverlays[index];
				var overlayProperties = childProperties.myWindow.Overlay.getOverlayProperties(name);
				if (overlayProperties) return(overlayProperties);
			}
			*/
			return(null);
		},




		// DEBUG && UTILITY ROUTINES
		encodeToHtml: function (strng)
		{
			if ( !strng || strng=='' ) return strng;
			var encodedHtml = strng.toString();
			return encodedHtml.split("\x3C").join("&lt;").split("\x26").join("&amp;").split("\x3E").join("&gt;").split("\x22").join("&quot;");
		},
		logError: function(err)
		{
			err.browser = navigator.userAgent;
			_private.dumpObject( err );
			return err;
		},
		inspect: function(obj, maxLevels, level)
		{
			var str = '', type, msg;

			// Start Input Validations
			// Don't touch, we start iterating at level zero
			if(level == null)  level = 0;

			// At least you want to show the first level
			if(maxLevels == null) maxLevels = 1;
			if(maxLevels < 1) return '<font color="red">Error: Levels number must be > 0</font>';

			// We start with a non null object
			if(obj == null) return '<font color="red">Error: Object <b>NULL</b></font>';
			// End Input Validations

			// Each Iteration must be indented
			str += '<ul>';

			// Start iterations for all objects in obj
			for(property in obj)
			{
			  try
			  {
				// Show "property" and "type property"
				type =  typeof(obj[property]);
				switch (type)
				{
				case "function":
					str += '<li>(' + type + ') ' + property + '()</li>';
					break;
				case "string":
					str += '<li>(' + type + ') ' + property + ( (obj[property]==null)?(': <b>null</b>'):(': <b>'+decodeURIComponent(obj[property]))) + '</b></li>';
					break;
				case "boolean":
					str += '<li>(' + type + ') ' + property + ( (obj[property]==null)?(': <b>null</b>'):(': <b>'+obj[property] ? 'true':'false')) + '</b></li>';
					break;
				case "number":
					str += '<li>(' + type + ') ' + property + ( (obj[property]==null)?(': <b>null</b>'):(': <b>'+obj[property])) + '</b></li>';
					break;
				case "object":
					// We keep iterating if this property is an Object, non null
					// and we are inside the required number of levels
					str += '<li>(' + type + ') ' + property + '</li>';
					if( (obj[property] != null) && (level+1 < maxLevels) )
						str += _private.inspect(obj[property], maxLevels, level+1);
					break;
				}
			  }
			  catch(err)
			  {
				// Is there some properties in obj we can't access? Print it red.
				if(typeof(err) == 'string') msg = err;
				else if(err.message)        msg = err.message;
				else if(err.description)    msg = err.description;
				else								 msg = 'Unknown';

				str += '<li><font color="red">(Error) ' + property + ': ' + msg +'</font></li>';
			  }
			}

			  // Close indent
			  str += '</ul>';
			return str;
		},
		dumpObject : function(obj,maxlevels)
		{
			var str = _private.inspect(obj,maxlevels);
			var win=window.open('about:blank','_blank','top=100,left=50,width=550,height=500,directories=no,location=no,scrollbars=yes,resizable=yes');
			win.document.write( str );
		}
	};

	return _public;

}();


/// When the DOM is constructed, make sure the formMgr initialization is executed.
Event.observe(document, "dom:loaded", Overlay.initialize);



