// Javascript Document

function dmenu(){		
	
	/* Menu Methods */
	/* ------------------------------------------------------------------ */
	
	//Show Menu
	this.show = function(_this, content, e, tg){
		e = (!e) ? window.event : e ;
		var target = (e.target) ? e.target : e.srcElement ;
		tg = (tg) ? tg : target ; //IE doesn't support srcElem for key events so provide an override path for fetching the e.target
		var pos = _this.get_abs_pos(tg);
		var x = pos['x'] + "px";
		var y = ( pos['y'] + tg.offsetHeight ) + "px";
		_this.current_elem = tg;
		if(document.getElementById("dm")){
			var dm = document.getElementById("dm");
			dm.style.display = "block";
			dm.style.left = x;
			dm.style.top = y;			
			content = (typeof content == 'string') ? document.createTextNode(content) : content ;
			content = content.cloneNode(true);
			_this.clear_child_nodes(dm);
			dm.appendChild(content);
		}else{
			var dm = document.createElement("div");
			dm.id = "dm";
			dm.style.display = "block";
			dm.style.position = "absolute";
			dm.style.left = x;
			dm.style.top = y;
			content = (typeof content == 'string') ? document.createTextNode(content) : content ;
			content = content.cloneNode(true);
			dm.appendChild(content);
			_this.c_activate = _this.curry( _this.activate, _this)
			_this.add_e_handler(dm, "mouseover", _this.c_activate);
			document.body.appendChild(dm);
		}
	}
	
	//Activate Menu
	this.activate = function(_this){
		var dm = document.getElementById("dm");
		_this.active = true;
		_this.c_hide = _this.curry( _this.hide, _this);
		_this.c_deactivate = _this.curry( _this.deactivate, _this);
		// Prevent Double Ups
		_this.remove_e_handler(dm, "mouseout", _this.c_deactivate);
		_this.remove_e_handler(dm, "mouseout", _this.c_hide);
		// Attach Callbacks
		_this.add_e_handler(dm, "mouseout", _this.c_deactivate);
		_this.add_e_handler(dm, "mouseout", _this.c_hide);
	}

	//Deactivate Menu
	this.deactivate = function(_this){
		_this.active = false;
	}
	
	//Hide Menu
	this.hide = function(_this,e){
		var dm = document.getElementById("dm");
		//Find what the mouse come from + has moved to
		if (!e) var e = window.event;
		//if the event is a mouseover then do some checks
		if(e.type=="mouseout" || e.type=="onmouseout"){
			var tg = (window.event) ? e.srcElement : e.target;
			var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
			//Kill function if not triggered by dm or the object that triggered the menu
			if(tg!=dm && tg!=_this.safe_hiding_elem) return ;
			//Move up the dom until we hit the element that has mouseout attached
			while(reltg != tg && reltg.nodeName != 'BODY' && reltg.nodeName != 'HTML'){
				reltg = reltg.parentNode;
			}
			//if we hit the mouseout attached elem -> reltg is inside tg -> kill func
			if(reltg==tg && tg!=_this.safe_hiding_elem) return;
		}
		//Delay hiding
		_this.c_h = _this.curry(_this.h,_this,tg);
		var t = setTimeout(_this.c_h,300);
	}

	//Hide Wrapper - Provides a nested mouseover check override 
	this.hide_wrap = function(_this,e){
		//Find what triggered the event
		if (!e) var e = window.event;
		var tg = (window.event) ? e.srcElement : e.target;
		_this.safe_hiding_elem = tg;
		_this.hide(_this,e);
	}

	//Real Hide Function
	this.h = function(_this,tg,hide_or){
		// Process Over Ride
		if( typeof hide_or == 'undefined' || !hide_or || hide_or==null ){
			var just_close_it	= false;
		}else if(typeof tg == 'undefined' || !tg || tg==null){
			var just_close_it	= true;
		}else{
			var just_close_it	= false;
		}
		// The actual function
		var dm = document.getElementById("dm");
		var close_menu = (tg==_this.current_elem || tg==dm) ? true : false ;
		if( (!_this.active && close_menu) || just_close_it ){ //Check if the mouse is on an active menu
			//Hide
			dm.style.display = "none";
			//Deactivate Menu
			_this.remove_e_handler(dm, "mouseout", _this.c_hide);
			_this.active = false;
		}
	}

	/* Supporting Methods */
	/* ------------------------------------------------------------------ */

	this.get_abs_pos = function(elem){
		var x = 0;
		var y = 0;
		for (var offMark = elem; offMark; offMark = offMark.offsetParent) {
			x += offMark.offsetLeft;
		}
		for (var offMark = elem; offMark; offMark = offMark.offsetParent) {
			y += offMark.offsetTop;
		}
		return { "x":x, "y":y };
	}

	this.clear_child_nodes = function(element){
		if ( element.hasChildNodes() ){
			while ( element.childNodes.length >= 1 ){
				element.removeChild( element.firstChild );
			} 
		}
	}
		
	this.add_e_handler = function(obj, e, func){
		//test if func exists - prevents problems in IE
		if(typeof func != "undefined"){		
			if(obj.attachEvent){
				return obj.attachEvent('on' + e, func);
			}else if(obj.addEventListener){
				return obj.addEventListener(e, func, false);
			}else{
				obj['on' + e] = func;
			}
		}
	}
	
	this.remove_e_handler = function(obj, e, func){
		//test if func exists - prevents problems in IE		
		if(typeof func != "undefined"){
			if (obj.detachEvent){
				return obj.detachEvent('on' + e, func);
			}else if(obj.removeEventListener){
				return obj.removeEventListener(e, func, false);
			}else{
				obj['on' + e] = null;
			}
		}
	}

	this.curry = function(method){
		var curried = [];
		for (var i = 1; i < arguments.length; i++) {
			curried.push(arguments[i]);
		}
		return function() {
			var args = [];
			for (var i = 0; i < curried.length; i++) {
				args.push(curried[i]);
			}
			for (var i = 0; i < arguments.length; i++) {
				args.push(arguments[i]);
			}
			return method.apply(null, args);
		}
	}	
	
	this.dumpObj = function(obj, name, indent, depth){

		depth = (depth) ? depth : 10 ;
		name = (name) ? name : "unknown" ;
		indent = (indent) ? indent : " " ;
 
		var MAX_DUMP_DEPTH = 10;
		if(depth > MAX_DUMP_DEPTH){
			return indent + name + ": <Maximum Depth Reached>\n";
		}
		if (typeof obj == "object"){
			var child = null;
			var output = indent + name;
			var total = 0;
			if(obj instanceof Array){
				total = obj.length;
				output += " (Array)\n";
			}else{
				for(var item in obj){
				   total++;
				}
				output += " (Object)\n";
			}
			output += indent + "Total item: " + total + "\n";
			indent += "\t";
			if(obj instanceof Array){
				for(var i = 0; i < obj.length; i++){
				   child = obj[i];
				   output += dumpObj(child, i, indent, depth + 1);
				}
			}else{
				for(var item in obj){
				   try{
					   child = obj[item];
				   }catch(e){
					   child = "<Unable to Evaluate>";
				   }
				   if(typeof child == "object"){
					   output += this.dumpObj(child, item, indent, depth + 1);
				   }else{
					   output += indent + item + ": " + child + "\n";
				   }
				}
			}
			return output;
		}else{
			return obj + " is not an object.";
		}
	}

	//Generate functions for manual use i.e. onmouseover="m.show_menu('Test Content',event);" onmouseout="m.hide_menu(event);"
	this.show_menu	= this.curry(this.show,this);
	this.hide_menu	= this.curry(this.hide_wrap,this);
	this.close_or	= this.curry(this.h,this,'',true);

}
