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

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

/**
 * Handles vertical drop-down menus.
 *
 * @namespace ZC.Core.Block
 * @class Menu_Menu
 * @extends ZC.Core.Block
 */
var oBlock = ZC.Core.Block.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()
{
	oBlock.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 = ZC.Core.Block.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()
{
	oBlock.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);
		elPanel = elPanelBody.parentNode;
	}
	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);
		}

		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: function() { U.Alert(_GT('The server was unable to retreive the data, please try again.')); },
				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,
				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)
{
	oBlock.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 (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 = ZC.Core.Block.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 = [];
	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);
	}

	sCookieID = this.GetAttribDefault('StoreSelectedTab');
	if (sCookieID)
	{
		Cookie = YAHOO.util.Cookie;
		iStoredIndex = Cookie.getSub('TabViewSelectedTab', sCookieID, Number);
		if (iStoredIndex)
		{
			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()
{
	oBlock.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);
}

/**
 * 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
 */
var oBlock = ZC.Core.Block.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;
}

})();

