 /**
  *
  * @package
  * This is a framework for managing buttons in a DHTML application.
  * <p>In addition to managing the static creation and tracking of the buttons,
  * it handles the following list of events:
  * 
  * Button event methods supported (Sets the CSS class name for the different events)
  * <ul>
  *		<li>mouseover</li>
  *		<li>mouseout</li>
  *		<li>mousedown</li>
  *		<li>mouseup</li>
  *		<li>mouseckick</li>
  *		<li>keypress</li>
  *	</ul>
  */


function defined(object) 
{
	var type = typeof(object);
	return !(type == "unknown") && !(type=="undefined");	
}

// Status bar message
function setMsg(msg)
{
	window.status = (msg || '');
	return true;
}

/////////// BUTTON /////////////
/**
 * Button class.
 * Builds a button instance using the supplied parameters.
 *
 * The Button class uses the following conventions:
 * A default className of '<b>button</b>' is used.
 * Any css class name can be used as the base class name using the className paramerter.
 * The base class name is decorated with the following:
 * <table>
 *  <tr><th>Event</th><th>Decoration</th><th>CSS Classname</th></tr>
 *  <tr><td>Mouse Over</td><td>Over</td><td>.buttonOver</td></tr>
 *  <tr><td>Mouse Down</td><td>Down</td><td>.buttonDown</td></tr>
 *  <tr><td>Show</td><td>Show</td><td>.buttonShow</td></tr>
 *  <tr><td>Hide</td><td>Hide</td><td>.buttonHide</td></tr>
 *  <tr><td>Disabled</td><td>Disable</td><td>.buttonDisabled</td></tr>
 * </table>
 * <br>If you use a class name of '<b>menuButton</b>' then the table becomes
 * <table>
 *  <tr><th>Event</th><th>Decoration</th><th>CSS Classname</th></tr>
 *  <tr><td>Mouse Over</td><td>Over</td><td>.menuButtonOver</td></tr>
 *  <tr><td>Mouse Down</td><td>Down</td><td>.menuButtonDown</td></tr>
 *  <tr><td>Show</td><td>Show</td><td>.menuButtonShow</td></tr>
 *  <tr><td>Hide</td><td>Hide</td><td>.menuButtonHide</td></tr>
 *  <tr><td>Disabled</td><td>Disable</td><td>.menuButtonDisabled</td></tr>
 * </table>
 * <br>
 * So to change the look of the button you only need to provide a CSS style sheet
 * and handle the button events as described above.<br>
 * This allows you to provide custom button looks in the same application simply
 * by providing different CSS styles and distinguished by the unique class prefix name.
 * <p>
 * @param text The button text to display.
 * @param image The url to an image to display with the button.
 * @param action The user defined function to associate with this button. 
 * @param className A CSS class to apply to the button. Overrides the default button CSS.
 * @param title The tooltip text to apply to this button.
 * @param enabled The default state of the button. Can be enabled, disabled, hidden
 *	<ul>
 * 		<li>Button.ENABLED = 1</li>
 * 		<li>Button.DISABLED = 2</li>
 * 		<li>Button.HIDDEN = 3</li>
 * </ul>
 *
 * @param name The user supplied ID name for this button. This maps the button
 *	to the name <code>Button[name] = this;</code>, so that the user can retrieve the
 *	button reference by name.
 * @param imgwidth Sets the width of the image. Use this to scale an image to fit nicely
 *	in the button area.
 * @param imgheight Sets the height of the image. Use this to scale an image to fit nicely
 *	in the button area.
 */
function Button(text, image, action, className, title, enabled, name, imgwidth, imgheight) 
{
    /** Internal ID of the button. */
    this.id = Button.nextId++;
    
    /** The text of the button. */
	this.text = text;
	
	/** The associated image of the button. */
	this.image = image;
	
	/** The JavaScript function to invoke. */
	this.action = action;

	if ( !className || className == "") 
	{
		className = "button";
	}
	
	/** The CSS classname to apply to to the button.
	 * Default is 'button'.
	 */
	this.className = className;
	
	/** The tooltip text. */
	this.title = title;


	// make sure that toString writes this item
	this.changed = true;
	this.html = "";

	/** Flag indicating whether the button is enabled. */
	this.enabled = Button.ENABLED;
	
	// check for creating disabled
	if ( defined(enabled) ) 
	{
		this.enabled = enabled;
	}
	
	/** User defined name. Not interpreted by the Button code. */
	this.name = "";
	this.setName(name);
//	if( defined(name) )
//	{
//		this.name = name;
//		
//		// user supplied name is mapped to this instance.
//		// This allows the user to retrieve teh button by name for
//		// manipulation.
//		Button[name] = this;
//	}
	
	/** User defined width for image. */
	this.imgwidth = imgwidth;
	
	/** User defined height for image. */
	this.imgheight = imgheight;
	
	// update the array
	Button.buttons[this.id] = this;	
}

//
// Button static properties
Button.nextId = 0;
Button.buttons = new Array();

Button.ENABLED = 1;
Button.DISABLED = 2;
Button.HIDDEN = 3;

function Button.stateString(state)
{
	var out = "";
	
	switch(state)
	{
		case Button.ENABLED:
			out = "enabled";
			break;
		case Button.DISABLED:
			out = "disabled";
			break;
		case Button.HIDDEN:
			out = "hidden";
			break;
		default:
			out = "unknown";
			break;
	}
	
	return out;
}

//
// Button methods

/**
 * Sets the button named.
 * The button name is used to retrieve a reference
 * to the button at runtime by the user code.
 * This value is not interpreted by the button
 * management code itself.
 *
 * @param name Unique string name for this button.
 */
Button.prototype.setName = function(name)
{
	if( defined(name) )
	{
		this.name = name;
		
		// user supplied name is mapped to this instance.
		// This allows the user to retrieve teh button by name for
		// manipulation.
		Button[name] = this;
	}
}

// Set the image. This does not dynamically update images. When toString is 
// called the new image will be used.
// TBD SKL: Add support for dynamically changing button icons.
Button.prototype.setImage = function(image) 
{
	if ( this.image != image ) 
	{
		this.image = image;
		this.changed = true;
	}
}

/**
 * Set the text value for the button.
 * This allows dynamic updating of the button display.
 *
 * @param text The new button text.
 */
Button.prototype.setText = function(text) 
{
	this.text = text;
	this.changed = true;
	this.update();
}

/** 
 * Set the focus to the button.
 */
Button.prototype.focus = function() 
{
	document.all['button_' + this.id].focus();
}

// Set the button to disabled.
Button.prototype.disable = function() 
{
	//if ( this.enabled ) 
	switch(this.enabled)
	{
		case Button.ENABLED:
		case Button.HIDDEN:
			this.enabled = false;
			this.changed = true;
			var div = document.all['button_' + this.id];

			// check that the div is written
			if ( defined(div) ) 
			{
				div.className = this.className + 'Disabled';
			}
			this.enabled = Button.DISABLED;
			break;
		defaut:
			break;
	}
}

// Set the button to enabled.
Button.prototype.enable = function() 
{
	//if ( !this.enabled ) 
	switch(this.enabled)
	{
		case Button.DISABLED:
		case Button.HIDDEN:
			this.enabled = true;
			this.changed = true;
			var div = document.all['button_' + this.id];
			// check that the div is written
			if ( defined(div) ) 
			{
				div.className = this.className;
			}
			this.enabled = Button.ENABLED;
			break;
		default:
			break;
	}
}

//
/**
 * Button event method
 * Sets the CSS class name for the different mouse events
 * <ul>
 *		<li>mouseover</li>
 *		<li>mouseout</li>
 *		<li>mousedown</li>
 *		<li>mouseup</li>
 *		<li>mouseckick</li>
 *		<li>keypress</li>
 *	</ul>
 */
Button.prototype.over = function() 
{
	switch(this.enabled)
	{
		case Button.ENABLED:	
			var div = document.all['button_' + this.id];
			div.className = this.className + 'Over';
			break;
		default:
			break;
	}

	event.cancelBubble = true;
	return false;	
}

/**
 * Handles the button out event.
 */
Button.prototype.out = function() 
{
	switch(this.enabled)
	{
		case Button.ENABLED:	
			var div = document.all['button_' + this.id];
			div.className = this.className;
			break;
		default:
			break;
	}
	event.cancelBubble = true;
	return false;
}

/**
 * Handles the button down event.
 */
Button.prototype.down = function() 
{
	switch(this.enabled)
	{
		case Button.ENABLED:	
			var div = document.all['button_' + this.id];
			div.className = this.className + 'Down';
			break;
		default:
			break;
	}
	event.cancelBubble = true;
	return false;
}

/**
 * Handles the button up event.
 */
Button.prototype.up = function() 
{
	switch(this.enabled)
	{
		case Button.ENABLED:	
			var div = document.all['button_' + this.id];
			div.className = this.className + 'Over';
			break;
		default:
			break;
	}
	event.cancelBubble = true;
	return false;
}

/**
 * Hides the button.
 */
Button.prototype.hide = function() 
{
	var div = document.all['button_' + this.id];
	div.className = this.className + 'Hide';
	this.enabled = Button.HIDDEN;
	
	if( event != null )
	{
		event.cancelBubble = true;
	}

	return false;
}


/**
 * Shows the button in the specified state.
 * 
 * @param flag true to enable, false to disable. Default is true;
 */
Button.prototype.show = function(flag) 
{
	var div = document.all['button_' + this.id];
	if( flag == null )
	{	
		flag = true;
	}
	
	if( flag )
	{
		this.enable();
	}
	else
	{
		this.disable();
	}
	
	if( event != null )
	{
		event.cancelBubble = true;
//		this.enabled = Button.DISABLED;		
	}
	
	return false;
}



/**
 * Invokes the registered JavaScript method.
 * Button must be enabled.
 * Any return value from the invoked method is returned
 * uninterpreted by the Button.
 *
 */
Button.prototype.click = function() 
{
	var retVal = true;

	switch(this.enabled)
	{
		case Button.ENABLED:	
			var div = document.all['button_' + this.id];
			if ( this.action ) 
			{
				retVal = eval(this.action);
			}
			else 
			{
				setMsg('Not Implemented');
			}
			break;
		default:
			break;
	}
	event.cancelBubble = true;
	return retVal;
}
 
/**
 * Handles the keypress event for the return key..
 * Button must be enabled.
 */
Button.prototype.keypress = function() 
{
	if( (this.enabled == Button.ENABLED) && event.keyCode == 13 ) 
	{
		this.click();
	}
}

/**
 * Updates the Button outer HTML.
 */
Button.prototype.update = function() 
{
	var div = document.all['button_' + this.id];
	div.outerHTML = this.toString();
}

// Get the html for the button
Button.prototype.toString = function() 
{
	if ( this.changed ) 
	{
		var imageHTML = '';
		if ( this.image ) 
		{
			var dimensionOverride = false;
			var dimensionString = "";
			if( this.imgwidth )
			{
				dimensionOverride = true;
				dimensionString = "width='" + this.imgwidth + "' ";				
			}
			if( this.imgheight )
			{
				dimensionOverride = true;
				dimensionString = "height='" + this.imgheight + "' ";				
			}
	
			imageHTML = 
			'<img src="' + this.image + '" align="absmiddle "' + dimensionString + '> ';
		} 
		else 
		{
			imageHTML = 
			'<img src="images/spacer.gif" width="5" height="15" align="absmiddle"> ';
		}

		var textHTML = '';
		if ( this.text ) 
		{
			textHTML = 
			this.text + '<img src="images/spacer.gif" width="8" align="absmiddle"> ';
		}
		else 
		{
			textHTML = '';
		}
		var buttonId = 'button_' + this.id;
		var className = this.className; 

		switch(this.enabled)
		{
			case Button.ENABLED:
				className = this.className;
				break;
			case Button.DISABLED:
				className = this.className + 'Disabled';
				break;
			case Button.HIDDEN:
				className = this.className + 'Hide';
				break;
		}
		
		this.html = 
			'<div id="' + buttonId 
			+ '" unselectable="on" atomicselection="true" nowrap class="' + className + '"' 
			+ " menuid=" + this.menuId
			+ ' name="' + this.name + '" ' 
			+	(this.title?'title="' + this.title + '"':'') + 
				'onmouseOver="return Button.buttons[' + this.id + '].over();"' + 
				'onmouseDown="return Button.buttons[' + this.id + '].down();"' +
				'onmouseUp="return Button.buttons[' + this.id + '].up();"' +
				'onmouseOut="return Button.buttons[' + this.id + '].out(this);"' +
				'onkeypress="return Button.buttons[' + this.id + '].keypress();"' +
				'onclick="return Button.buttons[' + this.id + '].click();"' +
				'onselectstart="event.cancelBubble=true;return false;"' + 
				'tabIndex="0">' +
				imageHTML +
				'<span>' + textHTML + '</span>' +
			'</div>';
		// don't recreate the button
		this.changed = false;
	}

	return this.html;
}

// Write a new button.
function makeButton(text, image, action, className, title, enabled, name, width, height) 
{
	document.write(new Button(text, image, action, className, title, enabled, name, width, height));
}

function makeButtonSpace(width) 
{
	document.write("<div class='inline'><img src='images/spacer.gif' width=' " + width + "' height='5'></div>");
}


/**
 * Implements a dropdown menu.
 * The MenuButton is buddied with a {@link menu.DynPopupMenu} object.
 * The HTML element naming conventions built into the MenuButton rendering
 * allow for the proper association and interaction between the MenuButton and
 * its menu body.
 *
 * @param text The text of the MenuButton.
 * @param image An optional image to display with the Menu.
 * @param className A CSS override for menu display. Use this to customize the look
 *	of the MenuButton.
 * @param menubody A DynPopupMenu instance that is the body of the menu.
 */
function MenuButton(text, image, className, menubody)
{
	// call base class constructor
	this.base(text, image, "return false;", className, "",Button.ENABLED);
		
	/** Associated DynPopupMenu body id. */
	this.menuId = null; 
	this.menubody = null;
	this.menu = null;//menu;
	
	
	this._init();
}

//
// Menu button - its a button that drops a menu instead of having an action.
//
MenuButton.prototype = new Button();
MenuButton.prototype.base = Button;
MenuButton.prototype.base_over = Button.prototype.over;
MenuButton.prototype.base_out = Button.prototype.out;
MenuButton.prototype.base_down = Button.prototype.down 
MenuButton.prototype.base_up = Button.prototype.up;
MenuButton.prototype.base_click = Button.prototype.click;
MenuButton.prototype.base_toString = Button.prototype.toString;

/** Internal initialization method. */
MenuButton.prototype._init = function()
{
	this.menubody = new DynPopupMenu(null,this.className);
	this.menubody.clearItems();
	this.menubody.hasMenuButton = true;
	
	this.menuId = this.menubody.id;
	
	var menubuttonName = "menuButtonName" + this.id;
	this.setName(menubuttonName);
	
	var menu = Menu.menus[this.menuId];
	this.menu = menu;
}

/**
 * Returns the HTML representation of the Menu.
 */
MenuButton.prototype.toString = function()
{
	var html = "";

	// build the button first.
	var base = this.base_toString();
	
	// now build the menu body.
	var body = this.menubody.toString();
	
	html += base + body;

	return html;	
}

/** 
 * Add a new Item to the menu. 
 *
 * @param text The text of the menu item.
 * @param action The JavaScript action to invoke when clicked.
 */
MenuButton.prototype.addItem = function(text,action)
{
	// pass onto the DynPopupMenu object. */
	this.menubody.addItem(text,action);
}

/** Enables a menu item specified by its index. */
MenuButton.prototype.enableItem = function(index)
{
	if( this.menu )
	{
		this.menu.enableItem(index);
	}
}

/** Enables a menu item specified by its index. */
MenuButton.prototype.disableItem = function(index)
{
	if( this.menu )
	{
		this.menu.disableItem(index);
	}
}

/**
 * Writes the MenuButton HTML to the document.
 */
MenuButton.prototype.write = function()
{
	document.write(this.toString());	
}

MenuButton.prototype.over = function() 
{
	var div = document.all['button_' + this.id];
	var menuPos = lowerLeft(div);
	++menuPos.clientY;
	Menu.setHotspot(menuPos); 
	Menu.activate(this.menuId);

	return this.base_over();
}

MenuButton.menu_closeCallback = function(button) 
{
	// the "this" attribute refers to the menu
	var div = document.all['button_' + button.id];
	div.className = button.className;
}

// Mouse left button, don't do anything
MenuButton.prototype.out = function(menu)
{
	var layer = document.all["menuLayer" + menu.menuid];
	menu.className = "menuButton";
	if( event.clientX < menu.offsetLeft + 2 ||
		event.clientX > menu.offsetLeft + menu.offsetWidth - 2 ||
		event.clientY < menu.offsetTop +2)
	{
		layer.style.visibility = "hidden";
	}
}

/** Eats the button down event. */
MenuButton.prototype.down = function() 
{
	event.cancelBubble = true;
	return false;
}

/** Eats the button up event. */
MenuButton.prototype.up = function() 
{
	event.cancelBubble = true;
	return false;
}

/**
 * Actives the menu body.
 */
MenuButton.prototype.click = function() 
{
	var div = document.all['button_' + this.id];
	var menuPos = lowerLeft(div);
	++menuPos.clientY;
	Menu.setHotspot(menuPos); 
	Menu.activate(this.menuId);

	event.cancelBubble = true;
	return false;
}

// Get the lowerLeft corner of an object
function lowerLeft(obj) 
{
	var pos = absolutePos(obj);
	pos.clientY += obj.offsetHeight;
	return pos;
}

function lowerRight(obj) 
{
	var pos = absolutePos(obj);
	pos.clientY += obj.offsetHeight;
	pos.clientX += obj.offsetWidth;
	return pos;
}

function upperRight(obj) 
{
	var pos = absolutePos(obj);
	pos.clientX += obj.offsetWidth;
	return pos;
}

// Get the upper right corner of an object
function absolutePos(obj) 
{
	var x = 0;
	var y = 0;
	
	while ( obj ) 
	{
		x += obj.offsetLeft;
		y += obj.offsetTop;

		obj = obj.offsetParent;
	}
	return {"clientX":x,"clientY":y};
}


//TRACE("Button Library Loaded");
