(function() {
var L = YAHOO.lang, U = ZC.Util, Dom = YAHOO.util.Dom, Evt = YAHOO.util.Event, YUIPECONTENT = 'yui-pe-content', HIDE = 'hide', ZCBlock = ZC.Core.Block;

/**
 * Core CMS components
 * @module Core
 */

/**
 * Handles vertical drop-down menus.
 *
 * @namespace ZC.Core.Block
 * @class Menu_Menu
 * @extends ZC.Core.Block
 */
var oBlock = ZCBlock.Create('Menu_Menu');
oBlock.prototype.CustomSetupEnd = function()
{
	var elMenu, aMenuLinks, oMenu;
   
	elMenu = this.GetElement();
	if (!elMenu)
		return false;

	Dom.removeClass(elMenu, YUIPECONTENT);

	oMenu = new YAHOO.widget.MenuBar(elMenu, { zindex: 5 });
	oMenu.render(); 
	oMenu.show(); 

	U.ForEach(oMenu.getSubmenus(), function(oSubmenu) { oSubmenu.cfg.setProperty('zindex', 5); });

	this.oYUIMenuBar = oMenu;

	aMenuLinks = Dom.getElementsByClassName('menulink', 'a');
	if (aMenuLinks.length)
	{
		Evt.on(aMenuLinks, 'click', function(oEvent)
		{
			var elTarget = Evt.getTarget(oEvent),
				aSubmenus = oMenu.getSubmenus(),
				sGoToMenuID = elTarget.href.replace(/.*#/, ''), 
				i, iMax;

			for (i = 0, iMax = aSubmenus.length; i < iMax; i++)
			{
				if (aSubmenus[i].id == sGoToMenuID)
				{
					aSubmenus[i].show();
					aSubmenus[i].parent.cfg.setProperty('selected', true);
					aSubmenus[i].focus();
					Evt.stopEvent(oEvent);
					return;
				}
			}
		}, this, true);
	}

	return true;
}

oBlock.prototype.Destruct = function()
{
	ZCBlock.Menu_Menu.superclass.Destruct.apply(this, arguments);

	if (this.oYUIMenuBar)
	{
		this.oYUIMenuBar.body.innerHTML = '';
		this.oYUIMenuBar = null;
	}
}

oBlock.prototype._IDPrefix = 'menu-';

/**
 * @namespace ZC.Core.Block
 * @class Layout_Accordion
 * @extends ZC.Core.Block
 */
oBlock = ZCBlock.Create('Accordion', 'Core', undefined, 'Layout');
oBlock.prototype.CustomSetupEnd = function()
{
	var elAccordion = this.GetElement();

	if (!elAccordion)
		return false;

	Dom.removeClass(elAccordion, YUIPECONTENT);
	this.oYUIAccordionView = new YAHOO.widget.AccordionView(elAccordion, {
		animationSpeed: this.GetAttribDefault('AnimSpeed', 0.5),
		collapsible: this.GetAttribDefault('Collapsible', true),
		expandable: this.GetAttribDefault('Multiple', false),
		width: this.GetAttribDefault('Width', '100%')
	});

	this._aAccordionBlocks = this.GetAttribDefault('AccordionBlocks');
	if (this._aAccordionBlocks)
	{
		this.oYUIAccordionView.on('panelOpen', function(oEvent) {
			var iPanelID = oEvent.index;

			// This will return false if a request has been made to load
			// content, which stops the panel from opening. The success handler
			// for the content request will open the panel once the content has
			// arrived.
			return this.LoadPanel(iPanelID, false, oEvent.panel);
		}, this, true);


		// Check for panels with StartOpen and trigger them to load.
		// Done in AfterManagerInit, as the accordion uses ManagerInit to call "setInPage"
		ZC.JSManager.GetEvent('AfterManagerInit').subscribe(function()
		{
			U.ForEach(this._aAccordionBlocks, function(aBlockInfo, iPanelID)
			{
				var elPanelBody;

				if (aBlockInfo.StartOpen)
				{
					this.LoadPanel(iPanelID, false);
				}
			}, this);
		}, this, true);
	}

	return true;
}

oBlock.prototype.Destruct = function()
{
	ZC.Core.Layout.Accordion.superclass.Destruct.apply(this, arguments);

	if (this.oYUIAccordionView)
	{
		this.oYUIAccordionView.destruct();
		this.oYUIAccordionView = null;
	}
}

oBlock.prototype._IDPrefix = 'accordion-';

/**
 * Loads the panel specifed by iPanelID into elPanelBody.
 * @param {Number} iPanelID the id of the panel to load (0 = first panel, etc)
 * @param {Boolean} bReload if true, then the block will be reloaded, otherwise it will ignore panels that are already loaded
 * @param {HTMLElement} elPanel the panel element (optional, will be looked up otherwise)
 */
oBlock.prototype.LoadPanel = function(iPanelID, bReload, elPanel)
{
	var elPanelBody, fnPanelLoaded, sLoadingClass = 'zc-accordion-loading',
		oLoadingIndicator = {
			Show: function() { Dom.addClass(elPanel, sLoadingClass); },
			Hide: function() { Dom.removeClass(elPanel, sLoadingClass); }
		};

	if (L.isUndefined(elPanel))
	{
		elPanelBody = Dom.get('accordion-' + this.UniqueID() + '--' + iPanelID);
		if (elPanelBody)
		{
			elPanel = elPanelBody.parentNode;
		}
		else
		{
			// panel may not be present
			return false;
		}
	}
	else
	{
		elPanelBody = elPanel.lastChild;
	}

	if (elPanelBody && !L.isUndefined(this._aAccordionBlocks[iPanelID]) && (bReload || !this._aAccordionBlocks[iPanelID].Loaded))
	{
		fnPanelLoaded = function()
		{
			this._aAccordionBlocks[iPanelID].Loaded = true;
			this._aAccordionBlocks[iPanelID].ForceReload = false;
			if (this.oYUIAccordionView)
				this.oYUIAccordionView.openPanel(iPanelID);
		}
		fnFailure = function() 
		{
			U.Alert(U.GetText('The server was unable to retreive the data, please try again.')); 
		}

		if (this._aAccordionBlocks[iPanelID].ContentURL)
		{
			var oCallback = {
			customevents: { onStart: oLoadingIndicator.Show, onComplete: oLoadingIndicator.Hide },
				success: function(o) { elPanelBody.innerHTML = o.responseText; fnPanelLoaded.call(this); }, 
				failure: fnFailure,
				scope: this
			};
			YAHOO.util.Connect.asyncRequest('GET', this._aAccordionBlocks[iPanelID].ContentURL, oCallback, null);
		}
		else
		{
			var oGetPostVars = { panelid: iPanelID };

			if (bReload || this._aAccordionBlocks[iPanelID].ForceReload)
				oGetPostVars.reloadPanel = 1;

			this.AjaxRequest({ 
				Transaction: 'FetchBlockSection', 
				GetPostVars: oGetPostVars,
				OnSuccess: fnPanelLoaded,
				OnFailure: fnFailure,
				LoadingIndicator: oLoadingIndicator
			});
		}

		return false;
	}

	return true;
}

/**
 * Reloads panels in the accordion. If the panel is closed, then just flag it as needing a reload.
 * @param {Array} panel ids to reload, or unspecified to reload all
 */
oBlock.prototype.ReloadPanels = function(aPanels)
{
	if (L.isNumber(aPanels))
	{
		aPanels = [aPanels];
	}
	else if (!L.isArray(aPanels))
	{
		aPanels = U.Keys(this._aAccordionBlocks);
	}

	U.ForEach(aPanels, function(iPanelID) { 
		if (this.oYUIAccordionView.isPanelOpen(iPanelID))
		{
			this.LoadPanel(iPanelID, true); 
		}
		else
		{
			this._aAccordionBlocks[iPanelID].Loaded = false;
			this._aAccordionBlocks[iPanelID].ForceReload = true;
		}
	}, this);
}

/*
 * Adds the 'ReloadPanels' and 'SetInPage' action for AJAX responses.
 */
oBlock.prototype.ProcessAjaxResponse = function(oResponse, oCustom, oFinishEvent)
{
	ZC.Core.Layout.Accordion.superclass.ProcessAjaxResponse.apply(this, arguments);

	U.ForEach(oResponse, function(oData, sAction)
	{
		switch (sAction)
		{
			case 'ReloadPanels':
				this.ReloadPanels(oData);
				break;

			case 'SetInPage':
				this.SetInPage(oData);
				break;
		}	
	}, this);
}

/**
 * Used by the server-side code to inform us of panels that are in-page (if
 * deciding based on size, the server-side code doesn't know until after the
 * JSConfig has been output.
 * @param {Array} aInPage array of panel ids that are in-page
 * @private
 */
oBlock.prototype.SetInPage = function(aInPage)
{
	U.ForEach(aInPage, function (iPanelID) { 
		if (!L.isUndefined(this._aAccordionBlocks[iPanelID])) this._aAccordionBlocks[iPanelID].Loaded = true; 
	}, this);
}

/**
 * Called when the AccordionBlocks attrib is set. This will probably be during
 * a reload, so we want to make sure our header elements are updated.
 */
oBlock.prototype.AttribMethod_AccordionBlocks = function(aBlocks)
{
	// called from the constructor (Header is not in the config array on page load), or our accordion doesn't yet exist, do nothing.
	if (!this.oYUIAccordionView || aBlocks.length == 0 || L.isUndefined(aBlocks[0].Header) || this._bAttribProviderSetup)
		return;

	var i, iMax, oPanel;

	for (i = 0, iMax = aBlocks.length; i < iMax; ++i)
	{
		elPanel = this.oYUIAccordionView.getPanel(i);
		this.oYUIAccordionView.setPanelHeader(i, aBlocks[i].Header, aBlocks[i].HeaderClass);
	}
}


/**
 * @namespace ZC.Core.Block
 * @class TabView
 * @extends ZC.Core.Block
 */
oBlock = ZCBlock.Create('TabView');
oBlock.prototype.CustomSetupEnd = function()
{
	var elTabs, aTabs, aTabLinks, sOrientation = this.GetAttribDefault('Orientation', 'top'), bTopAndBottom = false,
		elBottomTabsDiv, elTabParent, elContentParent, fnSwitchTab, fnOnChangeTab, Cookie, sCookieID, iStoredIndex, 
		sChangeTabEvent, oChangeTabEvent, iTabIndex, iSelectedTabIndex;

	elTabs = this.GetElement();
	if (!elTabs)
	{
		YAHOO.log('unable to find element with ID ' + (this._IDPrefix + this.UniqueID()), 'debug', 'TabView');
		return false;
	}

	// special case: we add our own clone of the tabs at the bottom and handle clicks / class changes on them separate from the YUI tabview code
	// TODO: get similar support integrated into YUI so we don't have to do this.
	if (sOrientation == 'top+bottom')
	{
		bTopAndBottom = true;
		sOrientation = 'top';
	}

	this.oYUITabView = new YAHOO.widget.TabView(elTabs, { orientation: sOrientation });

	this.oTabs = {};
	this.aTabIndices = [];
	this.aTabLabelToIndex = [];
	iTabIndex = iSelectedTabIndex = 0;
	U.ForEach(this.GetAttribDefault('Tabs', []), function(oTabDef, sTabID) 
	{
		var oAjaxTabDef, oTab;

		if (oTabDef.Selected)
			iSelectedTabIndex = iTabIndex;

		if (oTabDef.AjaxURL)
		{
			var oAjaxTabDef = {
				label: oTabDef.Caption,
				dataSrc: oTabDef.AjaxURL,
				cacheData: (L.isUndefined(oTabDef.CacheData) ? true : oTabDef.CacheData),
				active: (iSelectedTabIndex == iTabIndex)
			};

			oTab = new YAHOO.widget.Tab(oAjaxTabDef);
			this.oYUITabView.addTab(oTab, iTabIndex);
		}
		else
		{
			oTab = this.oYUITabView.getTab(iTabIndex);
		}

		// create a mapping from tab id => YUI Tab object, and index => tab ID
		this.oTabs[sTabID] = oTab;
		this.aTabIndices[iTabIndex] = sTabID;

		iTabIndex++;
	}, this);

	if (bTopAndBottom)
	{
		elTabParent = this.oYUITabView.getElementsByClassName('yui-nav', 'ul' )[0];
		elContentParent = this.oYUITabView.getElementsByClassName('yui-content')[0];
		if (elTabParent && elContentParent)
		{
			// we can make use of the YUI tab-styles by creating a container div for the bottom tabs with the yui-navset-bottom class
			elBottomTabsDiv = document.createElement('div');
			elBottomTabsDiv.className = 'yui-navset-bottom';
			elBottomTabsDiv.appendChild(elTabParent.cloneNode(true));
			Dom.insertAfter(elBottomTabsDiv, elContentParent);

			fnSwitchTab = function(oEvent, iIndex)
			{
				Evt.stopEvent(oEvent);
				this.oYUITabView.set('activeIndex', iIndex);
			}
			U.ForEach(Dom.getChildren(elBottomTabsDiv.firstChild), function(elTab, iIndex)
			{
				var oTab, fnPrevAddClass, fnPrevRemoveClass;

				oTab = this.oYUITabView.getTab(iIndex);

				Evt.on(elTab, 'click', fnSwitchTab, iIndex, this);

				fnPrevAddClass = oTab.addClass;
				oTab.addClass = function (sClass)
				{
					Dom.addClass(elTab, sClass);
					fnPrevAddClass.apply(this, arguments);
				}
				fnPrevRemoveClass = oTab.removeClass;
				oTab.removeClass = function (sClass)
				{
					Dom.removeClass(elTab, sClass);
					fnPrevRemoveClass.apply(this, arguments);
				}
			}, this);
		}
	}

	aTabLinks = Dom.getElementsByClassName('tabviewlink', 'a');
	if (aTabLinks.length)
	{
		Evt.on(aTabLinks, 'click', function(oEvent)
		{
			var elTarget = Evt.getTarget(oEvent),
				sTabID = elTarget.href.replace(/.*#/, '');

			if (!L.isUndefined(this.oTabs[sTabID]))
			{
				this.oYUITabView.set('activeTab', this.oTabs[sTabID]);
				Evt.stopEvent(oEvent);
			}
		}, this, true);
	}

	/* Build an array of label to tab index for all tabs. This includes tabs that were added automatically as well as manually */
	U.ForEach(this.oYUITabView.get('tabs'), function (tab, index)
	{
		this.aTabLabelToIndex[tab._getLabel()] = index;
	}, this);


	sCookieID = this.GetAttribDefault('StoreSelectedTab');
	if (sCookieID)
	{
		Cookie = YAHOO.util.Cookie;
		iStoredIndex = Cookie.getSub('TabViewSelectedTab', sCookieID, Number);
		if (iStoredIndex && !this.GetAttribDefault('IgnoreStoredTab'))	// IgnoreStored tab is set when a tab is forced selected and should not be overridden by users prev (stored) selection
		{
			this.oYUITabView.set('activeIndex', iStoredIndex);
		}

		this.oYUITabView.on('activeIndexChange', function(oEvent)
		{
			var oExpiry = new Date();
			oExpiry.setUTCFullYear(oExpiry.getUTCFullYear() + 1);
			Cookie.setSub('TabViewSelectedTab', sCookieID, this.oYUITabView.get('activeIndex'), { expires: oExpiry });
		}, this, true);
	}

	sChangeTabEvent = this.GetAttribDefault('ChangeTabEvent');
	if (sChangeTabEvent)
	{
		oChangeTabEvent = ZC.JSManager.GetEvent(sChangeTabEvent);
	}
			
	fnOnChangeTab = function(iNewIndex, iOldIndex)
	{
		// any non-yui classes on the tabs, add a "classname-selected" version, to cope with the fact IE6 can't handle multi-class CSS selectors.
		U.ForEach(aTabs, function(oTab, iIndex) 
		{
			var aClasses = oTab.get('element').className.split(/\s/);
			U.ForEach(aClasses, function (sClass)
			{
				if (sClass == 'selected')
				{
					return;
				}
				if (sClass.substr(sClass.length - 9) == '-selected' && iIndex != iNewIndex)
				{
					oTab.removeClass(sClass);
				}
				else if (iIndex == iNewIndex)
				{
					oTab.addClass(sClass + '-selected');
				}
			});
		});
				
		if (oChangeTabEvent)
		{
			oChangeTabEvent.fire(this.aTabIndices[iNewIndex], this.aTabIndices[iOldIndex]);
		}
	}

	this.oYUITabView.on('activeIndexChange', function(oEvent) { fnOnChangeTab.call(this, oEvent.newValue, oEvent.prevValue); }, this, true);	
	ZC.JSManager.GetEvent('ManagerInit').subscribe(function() { fnOnChangeTab.call(this, this.oYUITabView.get('activeIndex')); }, this, true);

	return true;
}

oBlock.prototype.Destruct = function()
{
	ZCBlock.TabView.superclass.Destruct.apply(this, arguments);

	this.oYUITabView = null;
	this.oTabs = null;
}

oBlock.prototype._IDPrefix = 'tabview-';

/**
 * Enables or disables the given tab
 * @param {String} sTabID the id of the tab to enable/disable
 * @param {Boolean} bEnable if true (default), then enable, otherwise disable
 */
oBlock.prototype.EnableTab = function(sTabID, bEnable)
{
	if (L.isUndefined(bEnable))
		bEnable = true;

	if (!L.isUndefined(this.oTabs[sTabID]))
		this.oTabs[sTabID].set('disabled', !bEnable);
}

/**
 * Disables the given tab
 * @param {String} sTabID the id of the tab to disable
 */
oBlock.prototype.DisableTab = function(sTabID)
{
	this.EnableTab(sTabID, false);
}

/**
 * Adds a class to a tab element
 * @param {String} sTabID the id of the tab to add the class to
 * @param {String} sClass the class to add
 */
oBlock.prototype.AddTabClass = function(sTabID, sClass)
{
	if (!L.isUndefined(this.oTabs[sTabID]))
		this.oTabs[sTabID].addClass(sClass);
}

/**
 * Removes a class from a tab element
 * @param {String} sTabID the id of the tab to remove the class from
 * @param {String} sClass the class to remove
 */
oBlock.prototype.RemoveTabClass = function(sTabID, sClass)
{
	if (!L.isUndefined(this.oTabs[sTabID]))
		this.oTabs[sTabID].removeClass(sClass);
}



/**
 * Changes to a tab by index or name
 * @param {String/Number} mIndex either the index of the tab, or its name
 */
oBlock.prototype.ChangeTab = function(mIndex)
{
	if (L.isString(mIndex) && !mIndex.match(/^\d+$/))
	{
		mIndex = U.IndexOf(this.aTabIndices, mIndex);
	}

	this.oYUITabView.set('activeIndex', Number(mIndex));
}

/**
 * @namespace ZC.Core.Block
 * @extends ZC.Core.Block.TabView
 * @class Layout.Tabbed
 */
ZCBlock.Create('Tabbed', 'Core', oBlock, 'Layout');

/**
 * Sets up the sorter/pager links on TableDataAdmin blocks so that they use AJAX requests.
 * @namespace ZC.Core.Block
 * @extends ZC.Core.Block
 * @class Recordset_Load_Search
 */
oBlock = ZCBlock.Create('Recordset_Load_Search');
oBlock.prototype.CustomSetupEnd = function()
{
	var elListInner = Dom.get('list-inner-' + this.UniqueID()),
		sLoadingClass = 'dbad-list-loading',
		elSorterSelect = Dom.get('frmSorter.rstsortby'),
		fnClickHandler;

	if (!elListInner)
		return true;

	fnClickHandler = function(oEvent, elTarget, elListInner)
	{
		Evt.stopEvent(oEvent);
		this.AjaxRequest({ Transaction: 'SortOrPageChange', URL: elTarget.href });
	}

	Evt.delegate(elListInner, 'click', fnClickHandler, '.caption a, .pager a', this, true);

	if (elSorterSelect)
	{
		elSorterSelect.onchange = '';
		Evt.on(elSorterSelect, 'change', function() { 
			this.AjaxRequest({ Transaction: 'SortOrPageChange', GetPostVars: elSorterSelect.value });
		}, this, true);
	}

	return true;
}

/**
 * Displays a progressbar using the YUI ProgressBar widget.
 * @namespace ZC.Core.Block
 * @extends ZC.Core.Block
 * @class YUIProgressBar
 */
oBlock = ZCBlock.Create('YUIProgressBar');
oBlock.prototype.CustomSetupEnd = function()
{
	var elContainer = this.GetElement(), elPB, aConfig, oAnim;

	if (!elContainer)
		return false;

	Dom.removeClass(elContainer, YUIPECONTENT);

	aConfig = {
		anim: Boolean(this.GetAttribDefault('Animation')),
		direction: this.GetAttribDefault('Direction', 'ltr'),
		height: this.GetAttribDefault('Height', '20px'),
		width: this.GetAttribDefault('Width', '200px'),
		value: this.GetAttrib('Value'),
		minValue: this.GetAttribDefault('MinValue'),
		maxValue: this.GetAttribDefault('MaxValue')
	};
	this.oYUIProgressBar = new YAHOO.widget.ProgressBar(aConfig);
	this.oYUIProgressBar.render(elContainer, Dom.getFirstChild(elContainer));

	return true;
}

oBlock.prototype.AttribMethod_Animation = function(aAnimDef, aOldDef)
{
	this.oYUIProgressBar.set('anim', (aAnimDef !== false));

	if (L.isObject(aAnimDef))
	{
		var oAnim = this.oYUIProgressBar.get('anim');
		if (!L.isUndefined(aAnimDef.Duration))
		{
			oAnim.duration = Number(aAnimDef.Duration);
		}
		if (!L.isUndefined(aAnimDef.Easing) && !L.isUndefined(YAHOO.util.Easing[aAnimDef.Easing]))
		{
			oAnim.method = YAHOO.util.Easing[aAnimDef.Easing];
		}
	}
}

oBlock.prototype.AttribMethod_Value = function(Value, OldValue)
{
	if (Value < this.GetAttribDefault('MinValue', 0) || Value > this.GetAttribDefault('MaxValue', 100))
	{
		this.SetAttrib('Value', OldValue);
		return;
	}

	this.oYUIProgressBar.set('value', Value);
	this._UpdateMessage();
}

oBlock.prototype.AttribMethod_MinValue = function(Value)
{
	this.oYUIProgressBar.set('minValue', Value);
	this._UpdateMessage();
}

oBlock.prototype.AttribMethod_MaxValue = function(Value)
{
	this.oYUIProgressBar.set('maxValue', Value);
	this._UpdateMessage();
}

oBlock.prototype.AttribMethod_Width = function(Value)
{
	this.oYUIProgressBar.set('width', Value);
}

oBlock.prototype.AttribMethod_Message = 
oBlock.prototype._UpdateMessage = function()
{
	var sMessageTemplate = this.GetAttribDefault('Message'), aTags = {}, 
		iValue = this.GetAttribDefault('Value'),
		iMinValue = this.GetAttribDefault('MinValue', 0),
		iMaxValue = this.GetAttribDefault('MaxValue', 100);
	
	if (!sMessageTemplate)
		return;

	if (L.isUndefined(this.elMessage))
	{
		this.elMessage = Dom.getFirstChildBy(this.GetElement(), function(el) { return Dom.hasClass(el, 'yui-pb-message'); });

		if (!this.elMessage)
			this.elMessage = false;
	}

	if (this.elMessage === false)
		return;

	aTags = {
		Value: iValue,
		MinValue: iMinValue,
		MaxValue: iMaxValue,
		Remaining: iMaxValue - iValue,
		Percent: Math.round((iValue / (iMaxValue - iMinValue)) * 100)
	};

	this.elMessage.innerHTML = U.ReplaceTags(sMessageTemplate, aTags);
}

var oPopDownMessageBlocks = {};

oBlock = ZCBlock.Create('PopDownMessage');
oBlock.prototype.CustomSetupEnd = function()
{
	var elContainer = this.GetElement(), elPB, aConfig, oAnim;

	if (!elContainer)
		return false;

	oPopDownMessageBlocks[this.UniqueID()] = this;

	// move the message container to the top level of the document to prevent its ancestors from affecting the styling.
	document.body.insertBefore(elContainer, document.body.firstChild);
	Dom.removeClass(elContainer, YUIPECONTENT);
	Dom.addClass(elContainer, HIDE);
	this.CalculateMessageHeight();
	return true;
}

/**
 * Calculates the height of the message element. Should be called after any action which may have caused the height to change.
 */
oBlock.prototype.CalculateMessageHeight = function()
{
	var elContainer = this.GetElement(), bWasHidden = Dom.hasClass(elContainer, HIDE);

	if (bWasHidden)
		Dom.removeClass(elContainer, HIDE);
		
	this.iMessageHeight = Dom.getRegion(elContainer).height;

	if (bWasHidden)
		Dom.addClass(elContainer, HIDE);
	else
		this.UpdateMargin();

	return this.iMessageHeight;
}

oBlock.prototype.AttribMethod_Visible = function (bShow, bOldValue)
{
	var iScrollTop = 0, bIE6 = (YAHOO.env.ua.ie && YAHOO.env.ua.ie < 7),
		elContainer = this.GetElement(), oRegion, 
		fnResizeMessage, fnOnScroll, fnUpdateMargin;
	   
	fnResizeMessage	= function()
	{
		// Make sure the message fills the width of the browser
		var iStyleWidth = Number(Dom.getStyle(elContainer, 'width').replace(/px/, '')),
			bHidden = Dom.hasClass(elContainer, HIDE),
			oRegion;

		if (!L.isNumber(iStyleWidth))
		{
			// Set width to a fixed value, work out the difference between the style width and the actual element width (borders, margins, etc)
			Dom.setStyle(elContainer, 'width', '600px');
			iStyleWidth = 600;
		}

		if (bHidden)
		{
			Dom.setStyle(elContainer, 'top', '-20000px');
			Dom.removeClass(elContainer, HIDE);
		}
		oRegion = Dom.getRegion(elContainer);
		if (bHidden)
		{
			Dom.addClass(elContainer, HIDE);
		}

		Dom.setStyle(elContainer, 'width', (Dom.getViewportWidth() - (oRegion.width - iStyleWidth)) + 'px');
	}

	fnOnScroll = function()
	{
		var iScrollTop = U.GetPageScrollTop();
		Dom.setStyle(elContainer, 'top', iScrollTop + 'px');
	}

	if (this.bShowing == bShow || (!bShow && L.isUndefined(bOldValue)))		// old value is undefined on construction
		return;

	this.bShowing = bShow;

	if (bShow)
	{
		U.ForEach(oPopDownMessageBlocks, function(oBlock)
		{
			if (oBlock != this && oBlock.GetAttribDefault('Visible'))
				oBlock.SetAttrib('Visible', false);
		}, this);

		fnResizeMessage();
		Evt.on(window, 'resize', fnResizeMessage, this, true);
	}
	else
	{
		Evt.removeListener(window, 'resize', fnResizeMessage, this, true);
	}

	if (bIE6)
	{
		// IE6 doesn't support position: fixed
		Dom.setStyle(elContainer, 'position', 'absolute');
	}

	if (this.oMessageAnim)
	{
		this.oMessageAnim.stop();
		this.oMessageAnim = null;
	}

	if (bShow)
	{
		if (bIE6)
		{
			this.oMessageAnim = new YAHOO.util.Anim(elContainer, { opacity: { from: 0, to: 1} }, this.GetAttribDefault('AnimDuration', 0.5));

			this.oMessageAnim.onStart.subscribe(function() 
			{ 
				Dom.removeClass(elContainer, HIDE);
				oRegion = Dom.getRegion(elContainer);
				document.body.style.marginTop = oRegion.height + "px";
				fnOnScroll();
			});
			this.oMessageAnim.onComplete.subscribe(function() { Evt.on(window, 'scroll', fnOnScroll); });
		}
		else
		{
			this.oMessageAnim = new YAHOO.util.Anim(elContainer, { top: { from: (iScrollTop - this.iMessageHeight), to: iScrollTop }}, this.GetAttribDefault('AnimDuration', 0.5));
			this.oMessageAnim.onStart.subscribe(function() { Dom.removeClass(elContainer, HIDE); }, this, true);
		}
	}
	else
	{
		if (bIE6)
		{
			this.oMessageAnim = new YAHOO.util.Anim(elContainer, { opacity: { from: 1, to: 0 } }, this.GetAttribDefault('AnimDuration', 0.5));
			this.oMessageAnim.onStart.subscribe(function() { Evt.removeListener(window, 'scroll', fnOnScroll); });
			this.oMessageAnim.onComplete.subscribe(function() 
			{
				oRegion = Dom.getRegion(elContainer);
				Dom.addClass(elContainer, HIDE);
				document.body.style.marginTop = "0px";
				// IE has some bizarre rendering bugs which are fixed by forcing a repaint:
				document.body.style.display = 'none';
				document.body.style.display = '';
			});
		}
		else
		{
			this.oMessageAnim = new YAHOO.util.Anim(elContainer, { top: { from: iScrollTop, to: (iScrollTop - this.iMessageHeight) }}, this.GetAttribDefault('AnimDuration', 0.5));
			this.oMessageAnim.onComplete.subscribe(function() { Dom.addClass(elContainer, HIDE); });
		}
	}

	if (!bIE6)
	{
		this.oMessageAnim.onComplete.subscribe(function() { 
			L.later(0, this, this.UpdateMargin); 
		}, this, true);
	}
	this.oMessageAnim.animate();
}

// this needs to be co-ordinated between all pop-down blocks, so this timeout is used by all of them
var oUpdateMarginTimeout;

oBlock.prototype.UpdateMargin = function()
{
	var oRegion, iTopMargin, iOldTopMargin, iScrollTop, elContainer = this.GetElement();

	if (!this.GetAttrib('Visible'))
	{
		// check to see if any other messages are now visible, in which case we leave them to update the margin when they're finished animating.
		for (var sBlockName in oPopDownMessageBlocks)
		{
			if (L.hasOwnProperty(oPopDownMessageBlocks, sBlockName) && oPopDownMessageBlocks[sBlockName].GetAttrib('Visible'))
				return;
		}
	}

	oRegion = Dom.getRegion(elContainer);
	if (!oRegion)
	{
		if (Dom.hasClass(elContainer, HIDE))
		{
			Dom.removeClass(elContainer, HIDE);
			oRegion = Dom.getRegion(elContainer);
			Dom.addClass(elContainer, HIDE);
		}

		if (!oRegion)
			return;
	}

	iTopMargin = oRegion.height + Number(Dom.getStyle(elContainer, 'top').replace(/px$/,''));
	iOldTopMargin = Number(Dom.getStyle(document.body, 'margin-top').replace(/px$/,''));
	iScrollTop = U.GetPageScrollTop() + (iTopMargin - iOldTopMargin);

	if (iTopMargin >= 0)
		Dom.setStyle(document.body, 'margin-top', iTopMargin + 'px');

	U.SetPageScrollTop(Math.max(0, iScrollTop));

	if (YAHOO.env.ua.ie)
	{
		// IE has some bizarre rendering bugs which are fixed by forcing a repaint:
		document.body.style.display = 'none';
		document.body.style.display = '';
	}
}

oBlock.prototype.AttribMethod_ContainerClass = function (sNewClass, sOldClass)
{
	var elContainer = this.GetElement();

	if (sOldClass)
	{
		Dom.removeClass(elContainer, sOldClass);
	}

	Dom.addClass(elContainer, sNewClass);
	this.CalculateMessageHeight();
}

oBlock.prototype.AttribMethod_MessageBody = function(sMessage)
{
	var elContainer = this.GetElement();
	elContainer.innerHTML = sMessage;
	this.CalculateMessageHeight();
}

})();

