/*************************************************\
  Generalized DHTML menu engine v1.4
  
  REQUIRES PROTOTYPE AND GSSI PROTOTYPE EXTENSION
\*************************************************/

var DMenu = Class.create();

Object.extend( DMenu, {
	// Static methods/properties

	_list: $H({}),
	
	_timeouts: {},

	_menupositions: $H({
		left: { halign:'before', valign:'top' },
		right: { halign:'after', valign:'top' },
		top: { halign:'left', valign:'above' },
		bottom: { halign:'left', valign:'below' }
	}),

	_clearresettimer: function( menuname ) {
		var timeout = DMenu._timeouts[ '*' + menuname ];
		
		if ( timeout ) {
			clearTimeout(timeout);
		};
		
		DMenu._timeouts[ '*' + menuname ] = null;
	},

	_setresettimer: function( menuname ) {
		DMenu._timeouts[ '*' + menuname ] = setTimeout( function(){ DMenu._list[menuname].reset(); } , DMenu._list[menuname].disappeardelay );
	},

	_hidemenublockdirect: function( blockid ) {
		var block = $(blockid);
		block.style.visibility="hidden";
		$(blockid+'_iframe').style.display = "none";
	},

	showMenuBlock: function ( blockid, menuname, callingelement, issubmenu ) {
		DMenu._clearresettimer( menuname );
		var block = $(blockid);
		callingelement = $(callingelement);

		var menu = DMenu._list[ menuname ];
		var position = null;
		
		if (issubmenu) {
			position = menu.subposition;
		} else {
			position = menu.mainposition;
		}
		
		block.moveToElement( callingelement, position );
		Position.clone( block, $(block.id + '_iframe'), { setLeft:true, setTop:true, setWidth:true, setHeight:true } );
		
		DMenu._showWithAncestors( block.id, menuname );
		
	},
	
	_showWithAncestors: function ( blockid, menuname ) {
		// Show exactly this block and its ancestors.
		var menu = DMenu._list[ menuname ];
		var keepids = {};

		var ancestorids = blockid.split( '-' );
		var treeid = ancestorids[0];
		for( var i=1; i<ancestorids.length; i++ ) {
			treeid = treeid + '-' + ancestorids[i];
			keepids[treeid] = treeid;
		}		

		menu.submenuids.each( function( pair ) {
			var blockid = pair.value;
			if ( keepids[blockid] ) {
				$(blockid).style.visibility="visible";
				$(blockid + '_iframe').style.display="block";
			} else {		
				$(blockid).style.visibility="hidden";
				$(blockid + '_iframe').style.display="none";
			}
		});
		
	},

	hideMenu: function ( menuname ) {
		DMenu._clearresettimer( menuname );
		DMenu._setresettimer( menuname );
	},

	keepMenu: function ( menuname ) {
		DMenu._clearresettimer( menuname );
	},

	addMenu: function( name, menu ) {
		DMenu._list[name] = menu;
	}
});

DMenu.prototype = {
	// Instance methods/properties

	submenuids: $H({}),
	
	initialize: function( menuname, menufileURL, optionshash ) {
		optionshash = $H( { mainposition:'bottom', subposition:'right', disappeardelay:100, appeardelay:0 } ).merge( optionshash );
		menufileURL = menufileURL.toString();
		menuname = menuname.toString();
		
		this.menudata = null;
		this.menuhtml = null;
		this.disappeardelay = optionshash.disappeardelay;
		this.appeardelay = optionshash.appeardelay;
		this.mainposition = DMenu._menupositions[optionshash.mainposition] || DMenu._menupositions['bottom'];
		this.subposition = DMenu._menupositions[optionshash.subposition] || DMenu._menupositions['right'];
		this.showtimer = null;
		
		var newmenu = this;
		
		var ajax = new Ajax.Request(menufileURL, {
			method: 'get',
			asynchronous:false,
			onSuccess: function(transport) {
		        if(! newmenu.menuhtml) {	
				    newmenu._buildmenu( menuname, transport.responseText );
				};
			},
			onFailure: function(transport) {
				alert("Could not load the menu - the response was " + transport.status + " " + transport.statusText );
			}
		});	
	},

	
	_buildmenu: function( menuname, menucontent ) {
		
	    try {
		    eval( "this.menudata = " + menucontent);
		} catch (e) {
		    alert( "Could not load menu: " + e.message );
		}
		this.menuname = menuname;
		this.menuhtml = null;
		this.menuhtml = this._constructmenuhtml( this.menudata, this.menuname, this.menuname, "__top" );
		
		DMenu.addMenu( menuname, this );

		Event.observeDOMReady( function() { DMenu._list[menuname]._insert() } );		
	},

	_constructmenuhtml: function( menudata, localmenuname, mainmenuname ) {
	    if (this.menuhtml) { return this.menuhtml; };
		var val;
		var submenus = {};
		var result = "<iframe style='position:absolute;display:none;visibility:visible;z-index:4900;border:none;padding:0;margin:0;' id='dmenu_" + localmenuname + "_iframe'></iframe><ul class='dmenu' style='position:absolute;display:block;visibility:hidden;z-index:5000;' id='dmenu_" + localmenuname + "' onMouseOver='DMenu.keepMenu(\"" + mainmenuname + "\");' onMouseMove='DMenu.keepMenu(\"" + mainmenuname + "\");' onMouseOut='DMenu.hideMenu(\"" + mainmenuname + "\");'>\n";
		var subcount = 0;
		
		// Make sure blank menus are hidden entirely - no orphaned borders, etc.
		if ( Object.keys(menudata).length < 1 ) {
			return "<iframe style='position:absolute;display:none;visibility:visible;z-index:4900;border:none;padding:0;margin:0;' id='dmenu_" + localmenuname + "_iframe'></iframe><ul class='dmenu' style='position:absolute;display:none;visibility:hidden;z-index:5000;' id='dmenu_" + localmenuname + "' onMouseOver='DMenu.keepMenu(\"" + mainmenuname + "\");' onMouseMove='DMenu.keepMenu(\"" + mainmenuname + "\");' onMouseOut='DMenu.hideMenu(\"" + mainmenuname + "\");'>\n</ul>\n";
		}
		
		// Build the menu contents recursively.
		for( var prop in menudata ) {
			val = menudata[prop];
			if (val instanceof String ) {
				result += "<li onMouseOver='DMenu.keepMenu(\"" + mainmenuname + "\");'><a href='" + val + "' onMouseOver='DMenu._showWithAncestors(\"dmenu_" + localmenuname + "\", \"" + mainmenuname + "\") '>" + prop + "</a></li>\n";			
			} else if (val instanceof Object ) {
			    subcount++;
			    result += "<li class='submenu' onMouseOver='DMenu.keepMenu(\"" + mainmenuname + "\");'><a href='javascript:void(0);' onMouseOver='DMenu.showMenuBlock(\"dmenu_" + localmenuname + "-" + subcount + "\", \"" + mainmenuname + "\", this, true)'>" + prop.toString().escapeHTML() + "</a></li>\n";
			    submenus[ localmenuname + "-" + subcount ] = val;
			    this.submenuids[ '*' + prop ] = "dmenu_" + localmenuname + "-" + subcount; // The '*' salt ensures no collision with Hash functions.
			} else {
				result += "<li onMouseOver='DMenu.keepMenu(\"" + mainmenuname + "\");'><a href='" + val.toString() + "' onMouseOver='DMenu._showWithAncestors(\"dmenu_" + localmenuname + "\", \"" + mainmenuname + "\") '>" + prop + "</a></li>\n";			
			}
		}
		result += "</ul>\n";

		for( var subname in submenus ) {
			result += this._constructmenuhtml( submenus[subname], subname, mainmenuname );
		}

		return result;
	},

	showMenu: function( submenuname, callingelement ) {
	    if (this.showtimer) {
	        clearTimeout(this.showtimer);
	    }
	    var target = this;
		this.showtimer = setTimeout(function(){ target._executeShowMenu( submenuname, callingelement); } , this.appeardelay );
    },
    
	_executeShowMenu: function( submenuname, callingelement ) {
	    this.showtimer = null;
		if ( this.submenuids['*' + submenuname] ) {
			DMenu.showMenuBlock( this.submenuids['*' + submenuname], this.menuname, callingelement, false );
		}
	},

	hideMenu: function( ) {
	    if (this.showtimer) {
	        clearTimeout(this.showtimer);
	        this.showtimer = null;
	    }
		DMenu._setresettimer( this.menuname );
	},
	
	reset: function () {
		this.submenuids.each( function( pair ) {
			var blockid = pair.value;
			DMenu._hidemenublockdirect( blockid );
		});
	},
	
    _insert: function () {
	    if (! this.menuhtml) {
			this.menuhtml = this._constructmenuhtml( this.menudata, this.menuname, this.menuname, "__top" );
		}
    	new Insertion.Top( document.body, this.menuhtml );
    }
    
};	

// Compatibility
DMenu.prototype.showmenu = DMenu.prototype.showMenu;
DMenu.prototype.hidemenu = DMenu.prototype.hideMenu;