/*
 * hyCMS
 * Copyright(C)2008 by Friedrich Gräter
 * Published under the terms of the Lesser GNU General Public License v2
 *
 * WidgetKit backend: Container Widgets
 *
 * Please Note: This module has to be clean from all dependencies to predicateJS!
 *
 *				Also this module requires "widget-kit.css" to be
 *				embedded to the parent site for styling the widgets.
 *
 */

/*
 * wk_sizeToNumber(size)
 *
 * Converts a string of the form "XXXXpx" (whereas X are digits) to
 * a number.

 */
function wk_sizeToNumber(sz)
{
	if (sz == null) return 0; 
	
	return Number(sz.match(/[0-9]*/)[0]) 
};


/*
 * wk_createView(id, height, width[, relatedDocument])
 *
 * Creates an empty view with the given size.
 *
 * This view has the following methods:
 *
 *	resize(height, width)			Changes the dimension of the view
 *
 * I triggers the following events:
 *
 *	resized							The view was resized
 *
 */
function wk_createView(id, height, width, relatedDocument)
{
	if (relatedDocument == null)
		relatedDocument = document;

	var view = relatedDocument.createElement("div");
	
	view.wk_type = "view";
		
	view.id = id;
	view.className = "wk_view";
	
	// Events
	wk_declareEvents(view, "resized");
		
	// Methods
	view.resize = function(height, width) 
	{
		view.style.height = height+"px";
		view.style.width = width+"px";
		
		wk_triggerEvent(view, "resized");
	}

	// Setup
	view.resize(height, width);

	return view;
}

/*
 * wk_createHoverView(id, height, width[, relatedDocument])
 *
 * Creates an empty view with the given size, that will get
 * full opacity, when hovered.
 *
 */
function wk_createHoverView(id, height, width, relatedDocument)
{
	var view = wk_createView(id, height, width, relatedDocument);
	
	view.wk_type = "hoverView";
	view.className = "wk_hoverView";
	
	return view;
}

/*
 * wk_createOrientedBox(id, direction, fixedDimension, spacing, [relatedDocument])
 *
 * Creates a layout view. The layout positions its childs in the
 * given "direction" ('horizontal', 'vertical'). It has a fixed dimension
 * (width for horizontal and height for vertical). The size of the fixed
 * dimension is stored in "fixedDimension". If "fixedDimension" is -1,
 * the size of the biggest child will be used. The parameter "spacing" denotes
 * how much space should be left between two children.
 *
 * This view has the following methods:
 *
 *	add(widget, position, dimConstraint)		Adds a new child widget. If dimConstraint==-1, the width/height 
 *												(vertical/horizontal) of the child will be automatically updated, 
 *												if it provides a resized event. Otherwise it is fixed to this value.
 *												Position defines, where to fill the element in (-1 == end).
 *
 *	remove(view)								Removes a child widget
 *
 *	getChildCount()								Returns the count of entries in the box
 *
 *	updateSize()								Recalculates the size of the box
 *
 *	getChildNodes()								Returns a list of all child nodes
 *
 * It sends the following events:
 *
 *	resized								The hbox has been resized
 *
 */
function wk_createOrientedBox(id, direction, fixedDimension, spacing, relatedDocument)
{
	var fixedDimName = direction == "horizontal" ? "width" : "height";
	var growingDimName  = direction == "horizontal" ? "height" : "width";
	var fixedMarginName = direction == "horizontal" ? "left" : "top";
	var growingMarginName = direction == "vertical" ? "top" : "left";

	var fixedDimOffsetName = direction == "horizontal" ? "offsetWidth" : "offsetHeight";
	var growingDimOffsetName  = direction == "horizontal" ? "offsetHeight" : "offsetWidth";
	var childStyle = direction == "horizontal" ? "block" : "inline-block";

	if (relatedDocument == null)
		relatedDocument = document;

	var view = relatedDocument.createElement("div");

	view.id = id;
	view.wk_type = "orientedBox";
	view.className = "wk_orientedBox_"+direction;		
	view.childCount = 0;
	
	// Private method
	function __getMargins(widget, directionSpec)
	{
		if ((relatedDocument.defaultView != null) && (relatedDocument.defaultView.getComputedStyle != null))
		{
			var selectLower = directionSpec == "left" ? "margin-left" : "margin-top";
			var selectUpper = directionSpec == "left" ? "margin-right" : "margin-bottom";
			var computedStyle = relatedDocument.defaultView.getComputedStyle(widget, null);

			var lower = computedStyle.getPropertyValue(selectLower);
			var upper = computedStyle.getPropertyValue(selectUpper);

			if (widget.style.getPropertyValue(selectLower) == "auto")
				lower = "0px";
			if (widget.style.getPropertyValue(selectUpper) == "auto")
				upper = "0px";
				
			return wk_sizeToNumber(lower) + wk_sizeToNumber(upper);
		}
		
		return 0;		
	}
		
	// Methods
	view.add = function(widget, position, dimConstraint)
	{
		var child = relatedDocument.createElement("span");
		child.className = "wk_orientedBoxChild_"+direction;
		child.style.display = childStyle;
		child.style.position = "relative";
		child.dimConstraint = dimConstraint;
		
		view.childCount ++;

		child.appendChild(widget);

		if ((position < 0) || (position > view.childNodes.length))
			view.appendChild(child);
		else
			view.insertBefore(child, view.childNodes[position]);
			
		// Connect to child resized event
		if ((widget.events != null) && (widget.events.resized != null))
			widget.events.resized.push( function() { view.updateSize(); } );
			
		// Calculate new size
		view.updateSize();	
	}


	view.remove = function(widget)
	{
		if (    (widget.parentNode != null) 
			 && (widget.parentNode.parentNode != null)
			 && (widget.parentNode.parentNode == view)
		   )
			view.removeChild(widget.parentNode);

		view.updateSize();
	}

	view.updateSize = function()
	{
		var fixedMax = fixedDimension;
		var growSum = 0;

		// Calculate dimensions
		for (var idx = 0; idx < view.childNodes.length; idx ++) {
			var child = view.childNodes[idx];
			var widget = child.firstChild;

			var fixedMargin = __getMargins(widget, fixedMarginName);
			var growingMargin = __getMargins(widget, growingMarginName);
							
			if (fixedDimension == -1) {
				var wSize;
			
				if (wk_sizeToNumber(widget.style[fixedDimName]) == 0)
					wSize = widget[fixedDimOffsetName];
				else
					wSize = wk_sizeToNumber(widget.style[fixedDimName]);
				
				fixedMax = Math.max(fixedMax, wSize) + fixedMargin;
			}

			if (child.dimConstraint == -1) {
				var wSize = wk_sizeToNumber(widget.style[growingDimName]);
			
				if (wSize == 0)
					wSize = widget[growingDimOffsetName];

				growSum += wSize + growingMargin;
				widget.__orientBoxRealGrowSize = wSize  + growingMargin;
			}
			else
				growSum += child.dimConstraint;


		}
		
		// Apply spacing
		growSum += spacing * (view.childNodes.length - 1);
	
		// Setup container dimensions
		view.style[fixedDimName] = fixedMax + "px";
		view.style[growingDimName] = growSum + "px";

		// Setup child dimensions
		for (var idx = 0; idx < view.childNodes.length; idx ++) {
			var child = view.childNodes[idx];
			var widget = child.firstChild;	
			var do_spacing = (idx == view.childNodes.length - 1) ? 0 : spacing;

			// Setup fixed side
			child.style[fixedDimName] = fixedMax + "px";	
			
			if (fixedDimension == -1) {
				//widget.style[fixedDimName] = fixedMax + "px";
			}
			 else {
			 	// Constraint widget to maximum
			 	if (wk_sizeToNumber(widget.style[fixedDimName]) > fixedMax) {
			 		widget.style[fixedDimName] = fixedMax + "px";
			 	}
			}
			
			// Setup growing side
			if (child.dimConstraint != -1) {
			 	widget.style[growingDimName] = child.dimConstraint + "px";
			 	widget.__orientBoxRealGrowSize = child.dimConstraint;
			}
			
			child.style[growingDimName] = (widget.__orientBoxRealGrowSize + do_spacing) + "px";			

			// Notify child about layout change
			wk_triggerEvent(widget, "layout");		
		}
		
		// Notify change
		wk_triggerEvent(view, "resized");
	}
	
	view.getChildCount = function()
	{
		return view.childNodes.length;
	}
	
	view.getChildNodes = function()
	{
		var nodes = [];
		
		for (var idx = 0; idx < view.childNodes.length; idx ++) {		
			nodes.push(view.childNodes[idx].firstChild);
		}
	}
	
	// Events
	wk_declareEvents(view, "resized");
		
	return view;
}


