/// <reference path="OwcControl.js" />
/// <reference path="OwcUiElements.js" />
/**=========================================================================
 File    : OwcPanes.js
 Author  : Sergey Steinvil
 Created : 01-Aug-2006
 Summary : Olive pane controls
*=========================================================================*/
if (typeof(JScript) == "undefined")
	throw DHTML.newError(0, "JavaScript OOP API is not included.  Add JScriptCore.js to your file");

if (typeof(Olive) == "undefined" || !Olive.Controls)
	throw DHTML.newError(0, "Olive Web SDK core classes are not included.  Add OwcControl.js to your file");

// -------------------------- Constants -------------------------
Olive.Controls.controlTypeNames.PaneList = "pane-list";
Olive.Controls.controlTypeNames.Pane = "pane";
Olive.Controls.controlTypeNames.PaneSplitter = "pane-splitter";

Olive.Controls.attributes.PaneSize = Olive.Controls.xmlns + "pane-size";

/*=============================================================================
 * Interface : Olive.Controls.IPaneListItem
 * Summary   : This interface should be implemented by controls, whose size 
 *             will be managed by pane list control
=============================================================================*/
if (!Olive.Controls.IPaneListItem)
{
	Olive.Controls.IPaneListItem = new Interface("Olive.Controls.IPaneListItem");

	Olive.Controls.IPaneListItem.Implement = function OwcIPaneListItem_Implement(rObject, bDynamic)
	{
		if (!this.Register(rObject, bDynamic))
			return;	// Already implemented
		Olive.IUiElements.ImplementInterface(rObject, bDynamic);

		// Fields
		JScript.Type.DeclareField(rObject, "m_nPaneItemSize", -1);	// Pane item size (0.0-1.0)
		JScript.Type.DeclareField(rObject, "m_nPaneItemWidth", -1);	// Width of pane item in pixels as last assigned by parent
		JScript.Type.DeclareField(rObject, "m_nPaneItemHeight", -1);	// Height of pane item in pixels as last assigned by parent
		JScript.Type.DeclareField(rObject, "m_sPaneMaximizeToolTip", null);
		JScript.Type.DeclareField(rObject, "m_sPaneMinimizeToolTip", null);	
		JScript.Type.DeclareField(rObject, "m_isPaneBeforeSplitter", false); // flag for pane before splitter
		JScript.Type.DeclareField(rObject, "m_isPaneAfterSplitter", false); // flag for pane after splitter

		// Properties
		JScript.Type.DeclareField(rObject, "paneContainer", null);	// Reference to the parent pane list
		JScript.Type.DeclareField(rObject, "resizablePane", true);	// Pane is resizable

		// Methods
		JScript.Type.DeclareMethod(rObject, "isVisible", OwcIPaneListItem_isVisible);
		JScript.Type.DeclareMethod(rObject, "show", OwcIPaneListItem_show);
		JScript.Type.DeclareMethod(rObject, "hide", OwcIPaneListItem_hide);
		JScript.Type.DeclareMethod(rObject, "isMinimized", OwcIPaneListItem_isMinimized);
		JScript.Type.DeclareMethod(rObject, "minimize", OwcIPaneListItem_minimize);
		JScript.Type.DeclareMethod(rObject, "restore", OwcIPaneListItem_restore);
		JScript.Type.DeclareMethod(rObject, "toggle", OwcIPaneListItem_toggle);
		JScript.Type.DeclareMethod(rObject, "toggleVisible", OwcIPaneListItem_toggleVisible);
		JScript.Type.DeclareMethod(rObject, "isColumnItem", OwcIPaneListItem_isColumnItem);
		JScript.Type.DeclareMethod(rObject, "getPaneItemSizeFactor", OwcIPaneListItem_getPaneItemSizeFactor);

		// Overrides
		JScript.Type.OverrideMethod(rObject, "bindHtmlElement", OwcIPaneListItem_bindHtmlElement);
		JScript.Type.OverrideMethod(rObject, "parseControlHtmlAttr", OwcIPaneListItem_parseControlHtmlAttr);
	} // Olive.Controls.IPaneListItem.Implement()

	function OwcIPaneListItem_isVisible()
	{
		return this.isStateSet(Olive.IState.State.Visible, Olive.IState.State.VisibleMask);
	} // Olive.Controls.IPaneListItem.isVisible()

	function OwcIPaneListItem_show()
	{
		this.changeState(true, Olive.IState.State.Visible, Olive.IState.State.VisibleMask);			
		if (this.paneContainer)
			this.paneContainer.recalcLayout();
	} // Olive.Controls.IPaneListItem.show()

	function OwcIPaneListItem_hide()
	{
		this.changeState(false, Olive.IState.State.Visible, Olive.IState.State.VisibleMask);
		if (this.paneContainer)
		    this.paneContainer.recalcLayout();
	} // Olive.Controls.IPaneListItem.hide()

	function OwcIPaneListItem_toggleVisible()
	{
		this.toggleState(Olive.IState.State.VisibleMask);
		if (this.paneContainer)
			this.paneContainer.recalcLayout();
	} // Olive.Controls.IPaneListItem.toggleVisible()

	function OwcIPaneListItem_isMinimized()
	{
		return this.isStateSet(Olive.IState.State.Minimized);
	} // Olive.Controls.IPaneListItem.isMinimized()

	function OwcIPaneListItem_minimize()
	{
		this.changeState(true, Olive.IState.State.Minimized);
		if (this.paneContainer)
			this.paneContainer.recalcLayout();
	} // Olive.Controls.IPaneListItem.minimize()

	function OwcIPaneListItem_toggle()
	{
		this.toggleState(Olive.IState.State.Minimized);
		if (this.paneContainer)
			this.paneContainer.recalcLayout();
	} // Olive.Controls.IPaneListItem.toggle()
	
	function OwcIPaneListItem_restore()
	{
		this.changeState(false, Olive.IState.State.Minimized);		
		if (this.paneContainer)
			this.paneContainer.recalcLayout();
	} // Olive.Controls.IPaneListItem.restore()
	
	function OwcIPaneListItem_isColumnItem()
	{
		if (this.paneContainer)
			return this.paneContainer.isHorizontal();
		return false;
	} // Olive.Controls.IPaneListItem.isColumnItem()

	function OwcIPaneListItem_getPaneItemSizeFactor()
	{
		if (this.m_nPaneItemSize > 0)
			return this.m_nPaneItemSize;
		return -1;
	} // Olive.Controls.IPaneListItem.getPaneItemSizeFactor()

	function OwcIPaneListItem_bindHtmlElement(oHtmlElement)
	{
		this.paneContainer = Olive.Object.FindParentImplementing(Olive.Controls.IPaneList, this);

		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);
	} // Olive.Controls.IPaneListItem.bindHtmlElement()

	function OwcIPaneListItem_parseControlHtmlAttr()
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);

		// Read pane size
		var sPaneSize = DHTML.getAttr(this.HtmlElement, Olive.Controls.attributes.PaneSize);
		if (sPaneSize && sPaneSize.match(/\d+(\.\d+)?\%/gi))
		{
			sPaneSize = sPaneSize.replace(/\%/g, "");
			this.m_nPaneItemSize = parseFloat(sPaneSize) / 100.0;
		}
	} // Olive.Controls.IPaneListItem.parseControlHtmlAttr()
} // Define Olive.Controls.IPaneListItem interface

/*=============================================================================
 * Interface : Olive.Controls.IPaneList
 * Summary   : This interface should be implemented by controls that contain
 *             list (row or column) of adjascent panes and/or splitters
=============================================================================*/
if (!Olive.Controls.IPaneList)
{
	Olive.Controls.IPaneList = new Interface("Olive.Controls.IPaneList");

	// Custom HTML attributes
	Olive.Controls.IPaneList.attributes = 
	{
		Kind : Olive.Controls.prefix + ":" + "kind",
		FitParent : Olive.Controls.prefix + ":" + "fit-parent"
	}

	// Pane list kinds
	Olive.Controls.IPaneList.kind = 
	{
		Rows : "rows",
		Columns : "columns"
	}

	Olive.Controls.IPaneList.Implement = function OwcIPaneList_Implement(rObject, bDynamic)
	{
		if (!Olive.Controls.IPaneList.Register(rObject, bDynamic))
			return;	// Already implemented
			
		// Fields
		JScript.Type.DeclareField(rObject, "m_arrPaneItems", null);
		JScript.Type.DeclareField(rObject, "m_bFitParent", true);
		JScript.Type.DeclareField(rObject, "m_recalcLayoutTimeoutID", null);
		JScript.Type.DeclareField(rObject, "m_nBeforeSplitterPaneProportion", -1); // proportion of the pane width/height that is before splitter to the full width/height of the both panes participating in resizing with splitter		

		// Properties
		JScript.Type.DeclareField(rObject, "kind", Olive.Controls.IPaneList.kind.Rows);
		JScript.Type.DeclareMethod(rObject, "get_PaneItemsCount", OwcIPaneList_get_PaneItemsCount);
		JScript.Type.DeclareMethod(rObject, "get_PaneItem", OwcIPaneList_get_PaneItem);
		JScript.Type.DeclareMethod(rObject, "getSplitterPaneProportion", OwcIPaneList_getSplitterPaneProportion);
		JScript.Type.DeclareMethod(rObject, "setSplitterPaneProportion", OwcIPaneList_setSplitterPaneProportion);

		// Methods:
		JScript.Type.DeclareMethod(rObject, "isHorizontal", OwcIPaneList_isHorizontal);

		// Internal methods:
		JScript.Type.DeclareMethod(rObject, "recalcLayoutByTimeout", OwcIPaneList_recalcLayoutByTimeout);
		JScript.Type.DeclareMethod(rObject, "recalcLayout", OwcIPaneList_recalcLayout);
		JScript.Type.DeclareMethod(rObject, "resizeWithSplitter", OwcIPaneList_resizeWithSplitter);

		// Overrides
		JScript.Type.OverrideMethod(rObject, "constructControl", OwcIPaneList_constructControl);
		JScript.Type.OverrideMethod(rObject, "parseControlHtmlAttr", OwcIPaneList_parseControlHtmlAttr);
		JScript.Type.OverrideMethod(rObject, "initControl", OwcIPaneList_initControl);
		JScript.Type.OverrideMethod(rObject, "termControl", OwcIPaneList_termControl);
		
		// Helpers
		JScript.Type.DeclareMethod(rObject, "collectPaneItems", OwcIPaneList_collectPaneItems);
		JScript.Type.DeclareMethod(rObject, "filterPaneItems", OwcIPaneList_filterPaneItems);
		JScript.Type.DeclareMethod(rObject, "appendPaneItem", OwcIPaneList_appendPaneItem);
		JScript.Type.DeclareMethod(rObject, "_firePanesResized", OwcIPaneList_firePanesResized);
		
		// Events
		rObject.registerOwcEventsClass2("panesResized", "onPanesResized", true);
	} // Olive.Controls.IPaneList.Implement()

	function OwcIPaneList_get_PaneItemsCount()
	{
		if (!this.m_arrPaneItems)
			return 0;
		return this.m_arrPaneItems.length;
	}

	function OwcIPaneList_get_PaneItem(vPane)
	{
		if (!this.m_arrPaneItems)
			return 0;
		if (typeof(vPane) == "number")
			return this.m_arrPaneItems[vPane];
		var sPaneId = String(vPane);
		var iPaneItem, nPanesItemsCount = this.m_arrPaneItems.length;
		for (iPaneItem=0; iPaneItem < nPanesItemsCount; ++iPaneItem)
		{
			var oPaneItem = this.m_arrPaneItems[iPaneItem];
			if (oPaneItem.m_sId == sPaneId)
				return oPaneItem;
		}
		return null;
	}
	
	function OwcIPaneList_getSplitterPaneProportion()
	{
	    return this.m_nBeforeSplitterPaneProportion;
	}
	
	function OwcIPaneList_setSplitterPaneProportion(vBeforeSplitterPaneProportion)
	{
	    var nBeforeSplitterPaneProportion = typeof(vBeforeSplitterPaneProportion) == "number" ? vBeforeSplitterPaneProportion : parseFloat(vBeforeSplitterPaneProportion);
        if  (!isNaN(nBeforeSplitterPaneProportion) && (nBeforeSplitterPaneProportion > 0) && (nBeforeSplitterPaneProportion < 1))
        {
            if (this.m_nBeforeSplitterPaneProportion != nBeforeSplitterPaneProportion)
            {
                this.m_nBeforeSplitterPaneProportion = nBeforeSplitterPaneProportion;
                this.recalcLayout();
            }
        }
	}

	function OwcIPaneList_isHorizontal()
	{
		return (this.kind.toLowerCase() == Olive.Controls.IPaneList.kind.Columns);
	} // Olive.Controls.IPaneList.isHorizontal()

	function OwcIPaneList_constructControl(oWebPage, oHtmlElement, oControlParent)
	{
		// Auto-implement Olive.Controls.IPane interface if one of ancestors
		// implements Olive.Controls.IPaneList
		if (Olive.Controls.IPaneList.IsImplementedBy(oControlParent) ||
			Olive.Object.FindParentImplementing(Olive.Controls.IPaneList, oControlParent))
		{
			Olive.Controls.IPane.Implement(this, true);
		} // {if} One of ancestors implements Olive.Controls.IPaneList
		else
		{
			function onWindowResize(oEvent)
			{
			    var oPane = arguments.callee.oThis;
				oPane.recalcLayout();
                // Fix for bug 10084:
                // When resizing (reducing) window that contains panes, gaps between content and window border may occur.
                // This is caused when style on body is overflow auto and resizing of panes done with respect to root element.
                // When reducing window, browser see what content exceeds the client area and adds scrollbars.
                // The event onWindowResize raised, but at this time we don't see difference between clientWidth and scrollWidth. We don't have any indication in code about scroll bars.
                // But the dimensions browser gives as are with subtracted size of scrollbars, these dimensions are used to educe Panes so they will fit client area.
                // Browser feels that now content is less when client area and removes scrollbars. And we see the gaps which are former place of scrollbars.
                // To solve this problem we call the recalculate layout second time, after browser has finished its calculations and removed scrollbars. 
                // After second call Panes will perfectly fit client area of window. 
                // Note: unwanted effect still have place blinking of scrollbars and gap while resizing and resize calculations.
//20070711 bug 10084 is OK without timeout after fixes to autoSize() and css changes. Timeout causes short width for content in panelist with 1 pane, 1 header and 1 content on Safari
				//oPane.m_recalcLayoutTimeoutID = JScript_Timeout.setTimeout(oPane.recalcLayoutByTimeout, 100, oPane, null);
			}
			onWindowResize.oThis = this;
			window.onresize = onWindowResize;
		} // {else} Top-most pane items list
	
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);
	} // Olive.Controls.IPaneList.constructControl()

	function OwcIPaneList_parseControlHtmlAttr()
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);

		this.m_bFitParent = DHTML.getAttrBoolean(this.HtmlElement, Olive.Controls.IPaneList.attributes.FitParent, this.m_bFitParent);

		var sListKind = DHTML.getAttr(this.HtmlElement, Olive.Controls.IPaneList.attributes.Kind);
		if (sListKind)
			this.kind = sListKind;
	} // Olive.Controls.IPaneList.parseControlHtmlAttr()
	
	function OwcIPaneList_initControl()
	{
		// Compute layout
		this.collectPaneItems();

		// Apply default styles on this pane list if it is root
		if (!Olive.Controls.IPane.IsImplementedBy(this))
			DHTML.addCssClassToElem(this.HtmlElement, "PaneRoot");

		var bIsHorizontal = this.isHorizontal();
		var iPaneItem, nPanesItemsCount = this.m_arrPaneItems.length;
		var oPaneBeforeSplitter = null;
		var oPaneAfterSplitter = null;
		var bSplitterMatched = false;
		for (iPaneItem=0; iPaneItem < nPanesItemsCount; ++iPaneItem)
		{
			var oPaneItem = this.m_arrPaneItems[iPaneItem];
			// 1. Apply predefined CSS classes on pane list items
			oPaneItem.initStateFromHtml();
			if (bIsHorizontal)
				DHTML.addCssClassToElem(oPaneItem.HtmlElement, "PaneListColumn");
			else
				DHTML.addCssClassToElem(oPaneItem.HtmlElement, "PaneListRow");
		    
		    // 2. Find resizable panes before and after splitter
		    // Skip hidden pane items
			if (!oPaneItem.isVisible())
				continue;

			// Non-resizable or minimized pane items have fixed size
			if (!oPaneItem.resizablePane)
			{
				// Check whether we reached splitter item
				if (!bSplitterMatched && (oPaneItem.controlType == Olive.Controls.controlTypeNames.PaneSplitter))
					bSplitterMatched = true;
				continue;
			}

			if (bSplitterMatched)
			{
				oPaneAfterSplitter = oPaneItem;
			}
			else
			{
				oPaneBeforeSplitter = oPaneItem;
			}
		}
		
		if (bSplitterMatched)
		{
		    oPaneAfterSplitter.m_isPaneAfterSplitter = true;
		    oPaneBeforeSplitter.m_isPaneBeforeSplitter = true;
		}

		this.recalcLayout();

		// Initialize sub-controls
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);
	} // Olive.Controls.IPaneList.initControl()

    function OwcIPaneList_termControl()
    {
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);
		if(this.m_recalcLayoutTimeoutID)
			JScript_Timeout.clearTimeout(this.m_recalcLayoutTimeoutID);
    }// Olive.Controls.IPaneList.termControl()
    
    function OwcIPaneList_recalcLayoutByTimeout()
    {
        this.m_recalcLayoutTimeoutID = null;
        this.recalcLayout();        
    }// Olive.Controls.IPaneList.recalcLayoutByTimeout()
    
	function OwcIPaneList_recalcLayout(nClientWidth, nClientHeight)
	{
		if (!this.m_arrPaneItems)
			return;	// Not initialized yet
		var bIsHorizontal = this.isHorizontal();
		var arrResizableItems = new Array();
		var nDistributeSize = 0;

		// Compute avaiable viewport size
		if ((nClientWidth == undefined) || (nClientWidth < 0) ||
			(nClientHeight == undefined) || (nClientHeight < 0))
		{
			var oRootElem;
			if (!Olive.Controls.IPaneListItem.IsImplementedBy(this))
			{
				if (this.m_bFitParent)
					oRootElem = this.getOwnerPage().m_oRootElem;
				else if (this.HtmlElement.offsetParent)
					oRootElem = this.HtmlElement.offsetParent;
				else
					oRootElem = this.HtmlElement;

				if ((nClientWidth == undefined) || (nClientWidth < 0))
					nClientWidth = oRootElem.clientWidth;
				if ((nClientHeight == undefined) || (nClientHeight < 0))
					nClientHeight = oRootElem.clientHeight;

				if (nClientWidth < 10 || nClientHeight < 10)
					return;

				// Update size of the root pane
				DHTML.setElemWidth(this.HtmlElement, oRootElem.clientWidth);
				DHTML.setElemHeight(this.HtmlElement, oRootElem.clientHeight);
			}
			else
			{
				if ((nClientWidth == undefined) || (nClientWidth < 0))
					nClientWidth = this.m_nPaneItemWidth;
				if ((nClientHeight == undefined) || (nClientHeight < 0))
					nClientHeight = this.m_nPaneItemHeight;
			}
		}

		// Collect information from pane items
		nDistributeSize = (bIsHorizontal ? nClientWidth : nClientHeight);
		var iPaneItem, nPanesItemsCount = this.m_arrPaneItems.length;
		var bAllResizablePanesAreVisible = true;
		for (iPaneItem=0; iPaneItem < nPanesItemsCount; ++iPaneItem)
		{
			var oPaneItem = this.m_arrPaneItems[iPaneItem];

			// Skip hidden pane items
			if (!oPaneItem.isVisible())
			{
				if (oPaneItem.resizablePane)
				{
				    bAllResizablePanesAreVisible = false;
				}
				continue;
			}
			
			if (oPaneItem.isMinimized() && oPaneItem.resizablePane)
			{
			    bAllResizablePanesAreVisible = false;
			} 

			// Non-resizable or minimized pane items have fixed size
			if (!oPaneItem.resizablePane || oPaneItem.isMinimized())
			{
				if (bIsHorizontal)
				{
					if (oPaneItem.setPaneItemSize)
						oPaneItem.setPaneItemSize(-1, nClientHeight);
					nDistributeSize -= DHTML.getElemWidth(oPaneItem.HtmlElement);
				}
				else
				{
					if (oPaneItem.setPaneItemSize)
						oPaneItem.setPaneItemSize(nClientWidth, -1);
					if (oPaneItem.isMinimized())
					{
					    var oHeaderElem = oPaneItem.getUiElement(Olive.Controls.IPane.UiElement.Header);
		                if (oHeaderElem)
			                nDistributeSize -= DHTML.getElemHeight(oHeaderElem);
					}
					else
					    nDistributeSize -= DHTML.getElemHeight(oPaneItem.HtmlElement);
				}
			} // {if} Pane item is not resizable or minimized
			else
				arrResizableItems.push(oPaneItem);
		} // {for} Collect information from pane items

		Debug.trace("----- Resizing '" + this.m_sId + "' " + this.kind + " pane list -----");
		Debug.trace("Client size     : " + String(nClientWidth) + " x " + String(nClientHeight));
		Debug.trace("Distribute size : " + String(nDistributeSize) + " (fixed size = " + 
					String((bIsHorizontal ? nClientWidth : nClientHeight) - nDistributeSize) + ")");

		// Compute size for resizable panes
		nPanesItemsCount = arrResizableItems.length;
		if (nPanesItemsCount > 0)
		{
			var arrPaneSize = new Array(nPanesItemsCount);
			var nRemainedSize = nDistributeSize;
			var nEqualSizedPanesCount = 0;

			// Compute size for items with user-defined size factor
			for (iPaneItem=0; iPaneItem < nPanesItemsCount; ++iPaneItem)
			{
				var oPaneItem = arrResizableItems[iPaneItem];
				var nFactor = oPaneItem.getPaneItemSizeFactor();
				var nPaneSize = -1;
                
                if ((this.m_nBeforeSplitterPaneProportion > 0) && bAllResizablePanesAreVisible)
                {
                    // if the panes in the pane-list were resized with splitter and all of the panes are visible
                    // recalculate the sizes of the panes according to the proportion between pane sizes
                    var nCalculatedFactor = oPaneItem.m_isPaneBeforeSplitter ? this.m_nBeforeSplitterPaneProportion : (1 - this.m_nBeforeSplitterPaneProportion);
			        nPaneSize = Math.round(nDistributeSize * nCalculatedFactor);				         
			        nRemainedSize -= nPaneSize;
                }
                else if (nFactor > 0)
				{
					nPaneSize = Math.round(nDistributeSize * nFactor);					    
					nRemainedSize -= nPaneSize;					
				}
				else
					++nEqualSizedPanesCount;
				arrPaneSize[iPaneItem] = nPaneSize;
			} // {for} Compute size for items with user-defined size factor

			// Ditribute remained space equally between panes
			if ((nEqualSizedPanesCount > 0) && (nRemainedSize > 0))
			{
				var nEqualSizedPaneSize = Math.round(nRemainedSize / nEqualSizedPanesCount);
				var nFirstEqualSizedPaneSize = nEqualSizedPaneSize + (nRemainedSize % nEqualSizedPanesCount);
				var bFirstEqualSizedPane = true;
				
				for (iPaneItem=0; iPaneItem < nPanesItemsCount; ++iPaneItem)
				{
					if (arrPaneSize[iPaneItem] >= 0)
						continue;

					if (bFirstEqualSizedPane)
					{
						arrPaneSize[iPaneItem] = nFirstEqualSizedPaneSize;
						bFirstEqualSizedPane = false;
					}
					else
						arrPaneSize[iPaneItem] = nEqualSizedPaneSize;
				} // {for} Update equal-sized pane items
			} // {if} Ditribute remained space equally between panes

			// Resize pane items
			var nPaneItemSize = ((nPanesItemsCount == 0) ? 0 : (nDistributeSize / nPanesItemsCount));
			for (iPaneItem=0; iPaneItem < nPanesItemsCount; ++iPaneItem)
			{
				var oPaneItem = arrResizableItems[iPaneItem];
				var nItemWidth, nItemHeight;
				
				// Non-resizable or minimized pane items have fixed size
				if (bIsHorizontal)
				{
					nItemWidth = arrPaneSize[iPaneItem];
					nItemHeight = nClientHeight;
				}
				else
				{
					nItemWidth = nClientWidth;
					nItemHeight = arrPaneSize[iPaneItem];
				}

				Debug.trace("Item [" + oPaneItem.m_sId + "]: " + String(nItemWidth) + " x " + String(nItemHeight));
				oPaneItem.setPaneItemSize(nItemWidth, nItemHeight);
			} // {for} Update size of pane items
		} // {if} Compute size for resizable panes

        // Fire the panes resized event to enable the application to perform any custom action upon.
		this._firePanesResized(false);

		Debug.trace("----- Resized '" + this.m_sId + "' " + this.kind + " pane list -----\n");
	} // Olive.Controls.IPaneList.recalcLayout()

	function OwcIPaneList_resizeWithSplitter(oSplitter, nOffset)
	{
		if (!this.m_arrPaneItems)
			return false;	// Not initialized yet

		// Find resizable panes before and after splitter
		var oPaneBeforeSplitter = null;
		var oPaneAfterSplitter = null;
		
		var iPaneItem, nPanesItemsCount = this.m_arrPaneItems.length;
		for (iPaneItem = 0; iPaneItem < nPanesItemsCount; ++iPaneItem)
		{
			var oPaneItem = this.m_arrPaneItems[iPaneItem];
            if (oPaneItem.m_isPaneBeforeSplitter)
            {
               oPaneBeforeSplitter = oPaneItem; 
            }
            else if (oPaneItem.m_isPaneAfterSplitter)
            {
               oPaneAfterSplitter = oPaneItem; 
            }
		} // {for} Find resizable panes before and after splitter
		
		if (!oPaneBeforeSplitter || !oPaneAfterSplitter)
			return false;	// No resizable panes are found
		
		// Resize panes
		if (this.isHorizontal())
		{
			var nLeftWidth = oPaneBeforeSplitter.m_nPaneItemWidth;
			var nRightWidth = oPaneAfterSplitter.m_nPaneItemWidth;

			oPaneBeforeSplitter.setPaneItemSize(nLeftWidth + nOffset, -1);
			oPaneAfterSplitter.setPaneItemSize(nRightWidth - nOffset, -1);
			
			// calculate the proportion of the pane width that is before splitter to the full width of the both panes 
			this.m_nBeforeSplitterPaneProportion = Math.abs((nLeftWidth + nOffset)/(nLeftWidth + nRightWidth));
		}
		else
		{
			var nTopHeight = oPaneBeforeSplitter.m_nPaneItemHeight;
			var nBottomHeight = oPaneAfterSplitter.m_nPaneItemHeight;

			oPaneBeforeSplitter.setPaneItemSize(-1, nTopHeight + nOffset);
			oPaneAfterSplitter.setPaneItemSize(-1, nBottomHeight - nOffset);
			
			// calculate the proportion of the pane height that is before splitter to the full height of the both panes
			this.m_nBeforeSplitterPaneProportion = Math.abs((nTopHeight + nOffset)/(nTopHeight + nBottomHeight));
		}		
		
		// Fire the panes resized event to enable the application to perform any custom action upon.
		this._firePanesResized(true);
	} // Olive.Controls.IPaneList.resizeWithSplitter()

	function OwcIPaneList_collectPaneItems()
	{
		// Scan controls (recursively) and update data fields from their values
		var oScanner = new Olive.Controls.Scanner();
		oScanner.m_arrPaneItems = new Array();
		oScanner.setProcessControlCallback(this.appendPaneItem);
		oScanner.addFilter(this.filterPaneItems);
		oScanner.scan(this);
		
		this.m_arrPaneItems = oScanner.m_arrPaneItems;
	} // Olive.Controls.IPaneList.collectPaneItems()

	function OwcIPaneList_filterPaneItems(ctxScanner, oControl)
	{
		// Collect all controls implementing Olive.Controls.IPaneListItem
		// interface, but skip standalone panes.  The definition of standalone
		// pane is "pane that is not managed by container"
		if (Olive.Controls.IPaneListItem.IsImplementedBy(oControl) &&
			(!Olive.Controls.IPane.IsImplementedBy(oControl) || 
				!oControl.get_standalonePane()))
		{
			// Process this control, skip children
			return Olive.Controls.Scanner.FilterMode.Process;
		}

		// Skip this control, scan children
		return (Olive.Controls.Scanner.FilterMode.Reject + Olive.Controls.Scanner.FilterMode.StopFilter);
	} // Olive.Controls.IPaneList.filterPaneItems()

	function OwcIPaneList_appendPaneItem(ctxScanner, oControl)
	{
		Array_appendItem(ctxScanner.m_arrPaneItems, oControl);
	} // Olive.Controls.IPaneList.appendPaneItem()

	function OwcIPaneList_firePanesResized(bResizedWithSplitter)
	{
        var oEvent = this.createOwcEventObject("panesResized");
        oEvent.bResizedWithSplitter = bResizedWithSplitter;
        this.fireOwcEvent(oEvent);
        Object_Destroy(oEvent);
	 } // Olive.Controls.IPaneList.firePanesResized()
	 
} // Define Olive.Controls.IPaneList interface

/*=============================================================================
 Interface : Olive.Controls.IPaneSplitter
 Summary   : This interface should be implemented by pane splitter controls
=============================================================================*/
if (!Olive.Controls.IPaneSplitter)
{
	Olive.Controls.IPaneSplitter = new Interface("Olive.Controls.IPaneSplitter");

	Olive.Controls.IPaneSplitter.HDragger = new Olive.MoveDragger("dragLayer dragHorizontal");
	Olive.Controls.IPaneSplitter.VDragger = new Olive.MoveDragger("dragLayer dragVertical");

	Olive.Controls.IPaneSplitter.Implement = function OwcIPaneSplitter_Implement(rObject, bDynamic)
	{
		if (!this.Register(rObject, bDynamic))
			return;	// Already implemented
		Olive.Controls.IPaneListItem.ImplementInterface(rObject, bDynamic);

		// Fields
		JScript.Type.DeclareField(rObject, "resizablePane", false);	// Splitters are not resiable

		// Methods
		JScript.Type.DeclareMethod(rObject, "onDragStart", OwcIPaneSplitter_onDragStart);
		JScript.Type.DeclareMethod(rObject, "onDragMove", OwcIPaneSplitter_onDragMove);
		JScript.Type.DeclareMethod(rObject, "onDragEnd", OwcIPaneSplitter_onDragEnd);

		// Overrides
		JScript.Type.OverrideMethod(rObject, "bindHtmlElement", OwcIPaneSplitter_bindHtmlElement);
		JScript.Type.OverrideMethod(rObject, "initControl", OwcIPaneSplitter_initControl);
	} // Olive.Controls.IPaneSplitter.Implement()

	function OwcIPaneSplitter_onDragStart(oDragger)
	{
		// TODO: Compute possible drag range
		var xMin = -1, xMax = -1, yMin = -1, yMax = -1;

		var oRootElem = DHTML.getRootElement();
		if (this.isColumnItem())
		{
			xMin = DHTML.getElemClientLeft(oRootElem) + 50;
			xMax = Math.max(xMin, DHTML.getElemClientRight(oRootElem) - 50);
		}
		else
		{
			yMin = DHTML.getElemClientTop(oRootElem) + 50;
			yMax = Math.max(yMin, DHTML.getElemClientBottom(oRootElem) - 50);
		}
		Debug.trace(String(xMin) + " x " + String(xMax));
		
		// Create drag tracker element
		var oDragTrackerElem = this.HtmlElement.ownerDocument.createElement("div");
		if (this.isColumnItem())
			oDragTrackerElem.className = "dragTrackerVertical";
		else
			oDragTrackerElem.className = "dragTrackerHorizontal";

		oDragTrackerElem.style.left = String(DHTML.getElemClientLeft(this.HtmlElement)) + "px";
		oDragTrackerElem.style.top = String(DHTML.getElemClientTop(this.HtmlElement)) + "px";
		DHTML.setElemWidth(oDragTrackerElem, DHTML.getElemWidth(this.HtmlElement));
		DHTML.setElemHeight(oDragTrackerElem, DHTML.getElemHeight(this.HtmlElement));

		// Initialize tracker
		oDragger.setDragTracker(oDragTrackerElem, xMin, yMin, xMax, yMax);
	} // Olive.Controls.IPaneSplitter.onDragStart()
	
	function OwcIPaneSplitter_onDragMove(oDragger, pointMouse, oEvent)
	{
		// Debug.trace("move: (x=" + String(pointMouse.x) + ", y=" + String(pointMouse.y) + ")");
	} // Olive.Controls.IPaneSplitter.onDragMove()

	function OwcIPaneSplitter_onDragEnd(oDragger, pointMouse, oEvent, bCancel)
	{
		if (bCancel)
			return;

		var nOffset = 0;
		if (this.isColumnItem())
		{
			nOffset = pointMouse.x - oDragger.m_pointEnter.x;

			var sCssDirection = DHTML.getStyle(this.HtmlElement, "direction").toLowerCase();
			if (sCssDirection == "rtl")
				nOffset = -nOffset;
		}
		else
			nOffset = pointMouse.y - oDragger.m_pointEnter.y;
		this.paneContainer.resizeWithSplitter(this, nOffset);
	} // Olive.Controls.IPaneSplitter.onDragEnd()

	function OwcIPaneSplitter_bindHtmlElement(oHtmlElement)
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);
		
		// Define handlers for dragging events
		oHtmlElement.ondragstart = function test_ondragStart(oDragger)
		{
			OwcGetControlFromHtmlElem(this).onDragStart(oDragger);
		}

		oHtmlElement.ondragmove = function test_ondragMove(oDragger, pointMouse, oEvent)
		{
			OwcGetControlFromHtmlElem(this).onDragMove(oDragger, pointMouse, oEvent);
		}

		oHtmlElement.ondragend = function test_ondragEnd(oDragger, pointMouse, oEvent, bCancel)
		{
			OwcGetControlFromHtmlElem(this).onDragEnd(oDragger, pointMouse, oEvent, bCancel);
		}
	} // Olive.Controls.IPaneSplitter.bindHtmlElement()

	function OwcIPaneSplitter_initControl()
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);

		// Enable drag
		var oDragger = null;
		if (this.isColumnItem())
			oDragger = Olive.Controls.IPaneSplitter.HDragger;
		else
			oDragger = Olive.Controls.IPaneSplitter.VDragger;
		oDragger.enableDrag(this.HtmlElement);
	} // Olive.Controls.IPaneSplitter.initControl()
} // Define Olive.Controls.IPaneSplitter interface

/*=============================================================================
 Interface : Olive.Controls.IPane
 Summary   : Olive pane interface
=============================================================================*/
if (!Olive.Controls.IPane)
{
	Olive.Controls.IPane = new Interface("Olive.Controls.IPane");

	// Command names
	Olive.Controls.IPane.Command = 
	{
		togglePane : "toggle",
		minimizePane : "minimize",
		restorePane : "restore",
		showPane : "show",
		hidePane : "hide"
	}

	// UI element names
	Olive.Controls.IPane.UiElement = 
	{
		Header : "header",
		Content : "content",
		Footer : "footer",
		ToggleCommand : "toggle-command"
	}

	// CSS class names
	Olive.Controls.IPane.cssClass = 
	{
		PaneMinimizeCommand : "PaneMinimizeCommand",
		PaneMaximizeCommand : "PaneMaximizeCommand"
	}

	// Custom HTML attributes
	Olive.Controls.IPane.attributes = 
	{
		Resizable : Olive.Controls.xmlns + "resizable",
		PaneMaximizeToolTip : Olive.Controls.xmlns + "PaneMaximizeToolTip",
		PaneMinimizeToolTip : Olive.Controls.xmlns + "PaneMinimizeToolTip",
		Standalone : Olive.Controls.xmlns + "standalone"
	}

	Olive.Controls.IPane.Implement = function OwcIPane_Implement(rObject, bDynamic)
	{
		if (!Olive.Controls.IPane.Register(rObject, bDynamic))
			return;	// Already implemented
		Olive.Controls.IPaneListItem.ImplementInterface(rObject, bDynamic);

		// Fields
		JScript.Type.DeclareField(rObject, "m_bStandalone", false);

		// Methods:
		JScript.Type.DeclareMethod(rObject, "setPaneItemSize", OwcIPane_setPaneItemSize);
		JScript.Type.DeclareMethod(rObject, "getToolTip", OwcIPane_getToolTip);
		JScript.Type.DeclareMethod(rObject, "get_standalonePane", OwcIPane_get_standalonePane);
		JScript.Type.DeclareMethod(rObject, "set_standalonePane", OwcIPane_set_standalonePane);

		// Commands
		Olive.CmdTarget.RegisterCommand(rObject, this.Command.minimizePane, "minimize");
		Olive.CmdTarget.RegisterCommand(rObject, this.Command.restorePane, "restore");
		Olive.CmdTarget.RegisterCommand(rObject, this.Command.togglePane, "toggle");
		Olive.CmdTarget.RegisterCommand(rObject, this.Command.showPane, "show");
		Olive.CmdTarget.RegisterCommand(rObject, this.Command.hidePane, "hide");

		// Overrides
		JScript.Type.OverrideMethod(rObject, "parseControlHtmlAttr", OwcIPane_parseControlHtmlAttr);
		JScript.Type.OverrideMethod(rObject, "updateUiStateHtml", OwcIPane_updateUiStateHtml);
		JScript.Type.OverrideMethod(rObject, "updateUiState", OwcIPane_updateUiState);
		JScript.Type.OverrideMethod(rObject, "bindUiElement", OwcIPane_bindUiElement);
	} // Olive.Controls.IPane.Implement()

	function OwcIPane_setPaneItemSize(nWidth, nHeight)
	{
		var bPaneIsColumn = this.isColumnItem();

		if ((!bPaneIsColumn || this.resizablePane) && (nWidth >= 0))
			DHTML.setElemWidth(this.HtmlElement, nWidth);
		if ((bPaneIsColumn || this.resizablePane) && (nHeight >= 0))
			DHTML.setElemHeight(this.HtmlElement, nHeight);

		// Compute height of the pane's content (content height = total height - header height)
		var nContentHeight = nHeight;
		var nContentWidth = nWidth;

		var oHeaderElem = this.getUiElement(Olive.Controls.IPane.UiElement.Header);
		var oContentElem = this.getUiElement(Olive.Controls.IPane.UiElement.Content);
		var oFooterElem = this.getUiElement(Olive.Controls.IPane.UiElement.Footer);

		if (oContentElem && oContentElem.offsetParent && (oContentElem.offsetParent != this.HtmlElement))
		{
			if (nHeight > 0)
			{
				// DHTML_setElemHeight(oContentElem.offsetParent, nHeight);
				nContentHeight = Math.min(nContentHeight, oContentElem.offsetParent.clientHeight);
			}
			if (nWidth > 0)
			{
				DHTML.setElemWidth(oContentElem.offsetParent, nWidth);
				nContentWidth = Math.min(nContentWidth, oContentElem.offsetParent.clientWidth);
			}
		}

		if (oHeaderElem)
		{
			if (nContentWidth > 0)
				DHTML.setElemWidth(oHeaderElem, nContentWidth);
			if (nContentHeight > 0)
				nContentHeight -= oHeaderElem.offsetHeight;
		}
		if (oFooterElem)
		{
			if (nContentWidth > 0)
				DHTML.setElemWidth(oFooterElem, nContentWidth);
			if (nContentHeight > 0)
				nContentHeight -= oFooterElem.offsetHeight;
		}

		// Update height of the pane content
		if (oContentElem)
		{
			if (nContentHeight > 0)
				DHTML.setElemHeight(oContentElem, nContentHeight);
			if (nContentWidth > 0)
				DHTML.setElemWidth(oContentElem, nContentWidth);
		}

		if (nContentWidth > 0)
			this.m_nPaneItemWidth = nContentWidth;
		if (nContentHeight > 0)
			this.m_nPaneItemHeight = nContentHeight;

		if (Olive.Controls.IPaneList.IsImplementedBy(this))
			this.recalcLayout(nContentWidth, nContentHeight);
	} // Olive.Controls.IPane.setPaneItemSize()

	function OwcIPane_parseControlHtmlAttr()
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);

		this.resizablePane = DHTML.getAttrBoolean(this.HtmlElement,
													Olive.Controls.IPane.attributes.Resizable,
													this.resizablePane);
		this.m_bStandalone = DHTML.getAttrBoolean(this.HtmlElement,
													Olive.Controls.IPane.attributes.Standalone,
													this.m_bStandalone);
		this.m_sPaneMaximizeToolTip = DHTML.getAttr( this.HtmlElement,
													Olive.Controls.IPane.attributes.PaneMaximizeToolTip );
		if( !this.m_sPaneMaximizeToolTip ) {
			this.m_sPaneMaximizeToolTip = this.getResString( "PANE_MAXIMIZE_TOOL_TIP" );
		}
		this.m_sPaneMinimizeToolTip = DHTML.getAttr( this.HtmlElement,
													Olive.Controls.IPane.attributes.PaneMinimizeToolTip );
		if( !this.m_sPaneMinimizeToolTip ) {
			this.m_sPaneMinimizeToolTip = this.getResString( "PANE_MINIMIZE_TOOL_TIP" );
		}
	} // Olive.Controls.IPane.parseControlHtmlAttr()

	//get tool tip "maximize" / "minimize"
	function OwcIPane_getToolTip( bMinimized )
	{
		return bMinimized ? this.m_sPaneMaximizeToolTip : this.m_sPaneMinimizeToolTip;
	}

	function OwcIPane_get_standalonePane()
	{
		return this.m_bStandalone;
	}
	
	function OwcIPane_set_standalonePane(bStandalone)
	{
		this.m_bStandalone = bStandalone;
	}

	function OwcIPane_updateUiStateHtml(nNewState, nPrevState)
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);

		// When resizable pane is minimized, we need to remove the CSS width/height
		// property, which was set by pane container, otherwise recalculation
		// of layout will not work (container will respect the height of pane
		// before minimizing it)
		if (this.resizablePane)
		{
			var bMinimized = ((nNewState & Olive.IState.State.Minimized) == Olive.IState.State.Minimized);
			if (bMinimized)
			{
				if (this.isColumnItem())
					this.HtmlElement.style.width = "";
				else
					this.HtmlElement.style.height = "";
			}
		}
	}
	
	function OwcIPane_bindUiElement(oUiHtmlElem, sUiElemType)
	{
		if (this.BaseClassMethod)
			this.BaseClassMethod.apply(this, arguments);

		// When pane is initially minimized, it cannot hide content
		// HTML element during construction, since UI element is not 
		// parsed at that moment yet.  Here we fix thge situation:

	}

	function OwcIPane_updateUiState(nNewState, nPrevState)
	{
		// Update UI state of the whole pane
		this.updateUiStateHtml(nNewState, nPrevState);

		var bVisible = ((nNewState & Olive.IState.State.VisibleMask) == Olive.IState.State.Visible);
		if (!bVisible)
			return;

		//---------- Update sub-elements ----------
		var bMinimized = ((nNewState & Olive.IState.State.Minimized) == Olive.IState.State.Minimized);

		//---------- Show / Hide content ----------
		var oContentElem = this.getUiElement(Olive.Controls.IPane.UiElement.Content);
		if (oContentElem)
		{
			if (bMinimized)
				DHTML.addCssClassToElem(oContentElem, "Hidden");
			else
				DHTML.removeCssClassFromElem(oContentElem, "Hidden");
		} // Update sub-elements

		//---------- Update "minimize/restore" icon ----------
		var oToggleElem = this.getUiElement(Olive.Controls.IPane.UiElement.ToggleCommand);
		if (oToggleElem)
		{
			if (bMinimized)
			{
				DHTML.removeCssClassFromElem(oToggleElem, Olive.Controls.IPane.cssClass.PaneMinimizeCommand);
				DHTML.addCssClassToElem(oToggleElem, Olive.Controls.IPane.cssClass.PaneMaximizeCommand);
			}
			else
			{
				DHTML.removeCssClassFromElem(oToggleElem, Olive.Controls.IPane.cssClass.PaneMaximizeCommand);
				DHTML.addCssClassToElem(oToggleElem, Olive.Controls.IPane.cssClass.PaneMinimizeCommand);
			}
			var sToolTip = this.getToolTip( bMinimized );
			if( sToolTip )
			{
				setAttr( oToggleElem, "title", sToolTip );
			}
		} // Update sub-elements
	} // Olive.Controls.IPane.updateUiState()
} // Define Olive.Controls.IPane interface

/*=============================================================================
 Class   : Olive.Controls.Pane
 Summary : Olive pane control (analog of frame in HTML)
=============================================================================*/
if (!Olive.Controls.Pane)
{
	Olive.Controls.Pane = function OwcPane()
	{
		if (!DHTML.Layout.initialized)
			DHTML.Layout();
	} // Olive.Controls.Pane()

	JScript.Type.RegisterClass("Olive.Controls.Pane", Olive.Controls.Pane, Olive.Controls.Control, [Olive.Controls.IPane]);
	Olive.Controls.RegisterControlType(Olive.Controls.controlTypeNames.Pane, Olive.Controls.Pane);
} // Define Olive.Controls.Pane class

/*=============================================================================
 Class   : Olive.Controls.PaneSplitter
 Summary : Olive pane splitter control
=============================================================================*/
if (!Olive.Controls.PaneSplitter)
{
	Olive.Controls.PaneSplitter = function OwcPaneSplitter()
	{
		if (!DHTML.Layout.initialized)
			DHTML.Layout();
	} // Olive.Controls.PaneSplitter()

	JScript.Type.RegisterClass("Olive.Controls.PaneSplitter", Olive.Controls.PaneSplitter, Olive.Controls.Control, [Olive.Controls.IPaneSplitter]);
	Olive.Controls.RegisterControlType(Olive.Controls.controlTypeNames.PaneSplitter, Olive.Controls.PaneSplitter);
} // Define Olive.Controls.PaneSplitter class

/*=============================================================================
 Class   : Olive.Controls.PaneList
 Summary : Olive pane list control (analog of frameset in HTML)
=============================================================================*/
if (!Olive.Controls.PaneList)
{
	Olive.Controls.PaneList = function OwcPaneList()
	{
		if (!DHTML.Layout.initialized)
			DHTML.Layout();
	} // Olive.Controls.PaneList()

	JScript.Type.RegisterClass("Olive.Controls.PaneList", Olive.Controls.PaneList, Olive.Controls.Control, [Olive.Controls.IPaneList]);
	Olive.Controls.RegisterControlType(Olive.Controls.controlTypeNames.PaneList, Olive.Controls.PaneList);
} // Define Olive.Controls.PaneList class
