// updates:
// 01.06.07 ebb, Erstellung
// 17.10.07 ebb, Ajax Provider
// 21.10.07 ebb, AutoWidth implementiert
// 02.03.08 ebb, INode-Struktur als JSON String
//=================================================================================
//------- Ajax Menu Version 2.0 (INode als key/value Paar mit childs)
//=================================================================================

//------- globals

var version = "2.0";
var xmlhttp = null;
var selected_index = 0;

/*
 * 	HTML/Ajax Handler		INode
 * ============================================
 * 	resid					INode.ResID
 * 	message					INode.Message
 * 	actionid				INode.ActionID
 * 	bitmap1					INode.BitmapID
 * 	bitmap2					INode.BitmapID2
 * 	tooltip					INode.Tooltip
 * 	accelerator				INode.getAccelerator
 * 	params					---
 */

//=================================================================================
//------- bind
//=================================================================================

Function.prototype.bind = function(obj) {
	
	var method = this,
	temp = function() {
		return method.apply(obj, arguments);
	};

	return temp;
}
 
//=================================================================================
//------- framework.ui.MainMenu AJAX implementation
//=================================================================================

var Menu = {
	// init http request
	init: function() {
		selected_index = 0;
		if(window.XMLHttpRequest)
			xmlhttp = new XMLHttpRequest();
		else if(window.ActiveXObject)
			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		else
			alert("Your browser does not support XMLHTTP.");
	},
	// async load menu data
	load: function(url, sindex) {
		selected_index = sindex;
		var panel = document.getElementById('menubar');
		panel.innerHTML = "loading menu data...";
		xmlhttp.onreadystatechange = this.data_available;
		xmlhttp.open("GET", url, true);
		xmlhttp.send(null);
	},
	// request callback
	data_available: function () {

		if(xmlhttp.readyState != 4)
			return;

		var panel = document.getElementById('menubar');

		if (xmlhttp.status != 200) {
			panel.innerHTML = "Fehler beim Laden des Menüs...";
			return;
		}

		//------- generate MenuBar
		
		panel.innerHTML = "";
		new MainMenu(xmlhttp.responseText, -1, 302, 20, selected_index);
		
	}
	
};

//=================================================================================
//------- MainMenu (MenuBar)
//
//	node_data	:	JSON INode-Array from Ajax call
//	x			:	MenuBar horizontal position, -1 == center
//	y			:	MenuBar vertical position (absolute)
//	height		:	Menu-Item's height
//=================================================================================

function MainMenu(node_data, x, y, height, selected_index) {

	//------- generate INode Object Array from JSON String

	this.nodes = eval(node_data);		// array of top-level nodes
	
	//------- properties
	
	this.root = this;					// access root by childs :-(
	this.menu_id = "mi";				// menu_item.menu_id prefix
	this.menus = [];					// child items collection
	this.current_item = null;			// current selected item or null
	var level = 0;

	//=============================================================================
	//------- hide unrelated items
	//=============================================================================
	
	this.rcollapse = function(mitem) {
		
		//------- collapse all root child menus when mouse is off menu
		
		if(this.current_item == null) {
			// mouse is not over any item
			for(var i = 0; i < this.menus.length; i++) {
				this.menus[i].rcollapse(0);
			}
			return;
		} 
		
		//------- get menu_prefix ('mi_x') of current item to detect level 0 parent
		
		var prefix = this.current_item.menu_id.substring(0, 4);
		
		//------- collapse all non related level 0 items
		
		for(var i = 0; i < this.menus.length; i++) {
			var menu = this.menus[i];
			if(menu.menu_id != prefix)
				menu.rcollapse(0);
		}
		
		//------- hide item's siblings' childs
		
		var clevel = this.current_item.level;
		var item_index = this.current_item.index;
		
		for(var i = 0; i < this.current_item.parent.menus.length; i++) {
			if(i != item_index)
				this.current_item.parent.menus[i].rcollapse(clevel);
		}
		
	};
	
	//=============================================================================
	//------- calculate item's minimal required width
	//=============================================================================
	
	this.getMinWidth = function(caption, has_bitmap) {
		// text-width
		var width = caption.length * 7;
		// add offset
		width += 7;
		// add bitmap size
		if(has_bitmap)
			width += 20;
			
		return width;
	}
	
	//=============================================================================
	//------- calculate menubar width
	//=============================================================================
	
	var menu_width = 0;

	for(index = 0; index < this.nodes.length; index++) {
		// get top level node
		var node = this.nodes[index];
		// get node properties
		var item_caption = node.message;
		var has_bitmap = node.bitmap1.length > 0;
		// calculate items's width
		var width = this.getMinWidth(item_caption, has_bitmap);
		// calculate next items's position
		menu_width += width + 1;
	}

	//=============================================================================
	//------- calculate absolute horizontal Menu Position (center)
	//=============================================================================
	
	if(x < 0) {
		
		//------- center menubar
		
		var window_width = 0;
			
		if(window.innerWidth)
			window_width = window.innerWidth;
		else
			 window_width = document.body.offsetWidth;
	
		var x = (window_width - menu_width) / 2;
		
		if(x < 0)
			x = 0;
			
	}
	
	//=============================================================================
	//------- create level 0 items
	//=============================================================================

	for(index = 0; index < this.nodes.length; index++) {
		// get child's INode
		var node = this.nodes[index];
		// calculate items's width
		var item_caption = node.message;
		var has_bitmap = node.bitmap1.length > 0;
		// calculate items's width
		var width = this.getMinWidth(item_caption, has_bitmap);
		// mark selected level 0 item
		var is_selected = (index == selected_index);
		// create child menu item
		var child = new menu_item(this, node, level, index, x, y, width, height, is_selected);
		// store item in collection
		this.menus[index] = child;
		// calculate next items's position
		x += width + 1;
	}
	
	//=============================================================================
	//------- make root level visible
	//=============================================================================
	
	for(var i = 0; i < this.menus.length; i++)
		this.menus[i].setVisible(true);
	
}

//=================================================================================
//------- class menu item
//=================================================================================

function menu_item(parent, node, level, index, x, y, width, height, is_selected) {

	//------- store parameters
	
	this.parent  = parent;				// parent item
	this.node = node;					// INode data
	this.level = level;					// menu level
	this.index = index;					// 0-based item index
	this.root = parent.root;			// root = menu_bar
	this.is_visible = false;			// level 0 is made visible after creation
	this.menus = [];					// childs collection
	var target = "";					// optional href target
	
	//------- debouncing and show/hide delay
	
	this.expd_delay = 200;				// 200ms auto-expand-delay
	this.hide_delay = 200;				// 200ms auto-hide-delay

	this.showtimer = null;				// expand/debouncing timer
	this.hidetimer = null;				// collapse/debouncing timer

	//------- create unique menu id
	
	this.menu_id = this.parent.menu_id + '_' + this.index;
	
	//------- generate item's unique dom id's for outer href and inner div
	
	var inner_id = 'mi_' + this.menu_id + '_i';
	var outer_id = 'mi_' + this.menu_id + '_o';

	//=============================================================================
	//------- generate outer a element
	//=============================================================================

	this.outer_element = document.createElement('a');
	this.outer_element.id = outer_id;
	this.outer_element.className = "menu_outer_mouse_out" + this.level;
	
	//------- selected level 0 style
	
	if(this.level == 0 && is_selected)
		this.outer_element.className = "menu_outer_mouse_out" + this.level + "_selected";
	
	//------- href
	
	if(this.node.actionid != null && this.node.actionid.length > 0 && this.node.actionid != "null")
		this.outer_element.href = this.node.actionid;
	else
		this.outer_element.href = "#";

	this.outer_element.style.position = "absolute";
	this.outer_element.style.top = y + "px";
	this.outer_element.style.left = x + "px";
	this.outer_element.style.width = width + "px";
	this.outer_element.style.height = height + "px";
	this.outer_element.style.visibility = "hidden";
	this.outer_element.style.zIndex = this.level + 30;
	
	if(target.length > 0)
		this.outer_element.target = target;

	//=============================================================================
	//------- onclick event
	//=============================================================================
	
	this.onclick = function() {
		// don't go anywhere if item has no link defined
		return Boolean(this.node.actionid.length > 0);
	}
	
	// bind handler to this
	this.outer_element.onclick = this.onclick.bind(this);

	//=============================================================================
	//------- onmouseover
	//=============================================================================
	
	this.onmouseover = function() {
		
		// cancel both timers
		clearTimeout(this.hidetimer);
		this.hidetimer = null;
		clearTimeout(this.showtimer);
		this.showtimer = null;

		// register
		this.root.current_item = this;

		// apply rollover
		this.outer_element.className = "menu_outer_mouse_over" + this.level;
		this.inner_element.className = 'menu_inner';
		
		// (re)start expand-timer
		this.showtimer = setTimeout(this.expand.bind(this), this.expd_delay);
	};
	
	this.outer_element.onmouseover = this.onmouseover.bind(this);

	//=============================================================================
	//------- onmouseout
	//=============================================================================
	
	this.onmouseout = function() {
		
		// cancel both timers
		clearTimeout(this.hidetimer);
		this.hidetimer = null;
		clearTimeout(this.showtimer);
		this.showtimer = null;

		// dererister
		this.root.current_item = null;

		// apply rollout
		this.outer_element.className = "menu_outer_mouse_out" + this.level;
		
		//------- selected level 0 style
	
		if(this.level == 0 && is_selected)
			this.outer_element.className = "menu_outer_mouse_out" + this.level + "_selected";

		this.inner_element.className = 'menu_inner';
		
		// start collapse timer
		this.hidetimer = setTimeout(this.collapse.bind(this), this.hide_delay);
	};

	this.outer_element.onmouseout = this.onmouseout.bind(this);

	//=============================================================================
	//------- onmousedown
	//=============================================================================
	
	this.onmousedown = function() {
		// apply mouse down style
		this.outer_element.className = "menu_outer_mouse_out" + this.level;
		this.inner_element.className = 'menu_inner';
		// expand immediately
		this.expand();
	};

	this.outer_element.onmousedown = this.onmousedown.bind(this);

	//=============================================================================
	//------- expand child items
	//=============================================================================
	
	this.expand = function() {
		// mark timer as inactive
		this.showtimer = null;
		// show childs
		for(var i = 0; i < this.menus.length; i++)
			this.menus[i].setVisible(true);
	};
	
	//=============================================================================
	//------- hide child items (mouse-out hide timer)
	//=============================================================================
	
	this.collapse = function() {
		// mark timer as inactive
		this.hidetimer = null;
		// collapse siblings and childs by root
		this.root.rcollapse(this);
	};
	
	//=============================================================================
	//------- hide child items above level recursively
	//=============================================================================
	
	this.rcollapse = function(level) {
		// collapse childs
		for(var i = 0; i < this.menus.length; i++) {
			this.menus[i].rcollapse(level);
		}
		// collapse this
		if(this.level > level)
			this.setVisible(false);
	};
	
	//=============================================================================
	//------- set item visible/invisible
	//=============================================================================

	this.setVisible = function(state) {
		// xor states and toggle if required
		if(state) {
			// show item
			if(!this.is_visible) {
				this.outer_element.style.visibility = 'visible';
				this.is_visible = true;
			}
			
		} else {
			// hide item
			if(this.is_visible) {
				this.outer_element.style.visibility = 'hidden';
				this.is_visible = false;
			}
		}
		
	};

	//=============================================================================
	//------- generate inner div element
	//=============================================================================

	this.inner_element = document.createElement('div');
	this.inner_element.id = inner_id;
	this.inner_element.className = "menu_inner";
	
	//------- create img and append to inner element
	
	var spacer = "";
	
	if(this.node.bitmap1.length > 0)
	{
		var image = document.createElement('img');
		image.style.width = "16px";
		image.style.height = "16px";
		image.style.border = "none";
		image.src = this.node.bitmap1;
		this.inner_element.appendChild(image);
		spacer = " "
	}
	
	//------- create Text and append to inner element
	
	var textnode = document.createTextNode(spacer + this.node.message);
	this.inner_element.appendChild(textnode);
	
	//------- append inner to outer element
	
	this.outer_element.appendChild(this.inner_element);
	
	//------- append item to menubar
	
	var menuebar = document.getElementById('menubar');
	menuebar.appendChild(this.outer_element);

	//------- create "popup more" img
/*	
	if(this.level > 0)
	{
		var moreimg = document.createElement('img');

		moreimg.style.width = "7px";
		moreimg.style.height = "7px";
		moreimg.style.border = "none";
		moreimg.style.marginLeft = "100%";
		moreimg.src = "images/menu.more.gif";
		
		this.inner_element.appendChild(moreimg);
	}
*/	

	//=============================================================================
	//------- calculate common childs width
	//=============================================================================

	var child_width = 0;

	for(var i = 0; i < this.node.childs.length; i++) {
		
		// get child node
		var child_node = this.node.childs[i];
		// get child's caption
		var child_caption = child_node.message;
		// bitmap?
		var has_bitmap = child_node.bitmap1.length > 0;
		// get child's width
		var cw = this.root.getMinWidth(child_caption, has_bitmap) + 10;
		// store maximum
		if(cw > child_width)
			child_width = cw;
			
	}
	
	//------- level 0 child's width not below parent's width

	if(this.level == 0 && child_width < width) {
		child_width = width;
	}

	//=============================================================================
	//------- create childs
	//=============================================================================
	
	var child_level = this.level + 1;
	var child_x = x;
	var child_y = y;
	
	if(child_level == 1) {
		// level 1 items move vertically
		child_y = y + height + 1;
	} else {
		// level 2..level n
		child_x = x + width + 1;
	}
	
	for(var child_index = 0; child_index < this.node.childs.length; child_index++) {
		// get child node
		var child_node = this.node.childs[child_index];
		// create child item
		var child = new menu_item(this, child_node, child_level, child_index, child_x, child_y, child_width, height);
		// store child
		this.menus[child_index] = child;
		// calculate next item's position
		child_y += height + 1;
	}
		
}

