dojo.provide("dojo.widget.Button");

dojo.require("dojo.lang.extras");
dojo.require("dojo.html.*");
dojo.require("dojo.html.selection");
dojo.require("dojo.widget.*");

/*
 * usage
 *	<button dojoType="button" onClick="...">Hello world</button>
 *
 *  var button1 = dojo.widget.createWidget("Button", {caption: "hello world", onClick: foo});
 *	document.body.appendChild(button1.domNode);
 */
dojo.widget.defineWidget(
	"dojo.widget.Button",
	dojo.widget.HtmlWidget,
	{
		// summary
		//	Basically the same thing as a normal HTML button, but with special styling.

		isContainer: true,

		// caption: String
		//	text to display in button
		caption: "",
		
		templatePath: dojo.uri.dojoUri("src/widget/templates/ButtonTemplate.html"),
		templateCssPath: dojo.uri.dojoUri("src/widget/templates/ButtonTemplate.css"),
		
		// inactiveImg: Url
		//	prefix of filename holding images (left, center, right) for button in normal state
		inactiveImg: "src/widget/templates/images/soriaButton-",
		
		// activeImg: Url
		//	prefix of filename holding images (left, center, right) for button when it's being hovered over
		activeImg: "src/widget/templates/images/soriaActive-",

		// pressedImg: Url
		//	prefix of filename holding images (left, center, right) for button between mouse-down and mouse-up
		pressedImg: "src/widget/templates/images/soriaPressed-",

		// disabledImg: Url
		//	prefix of filename holding images (left, center, right) for button when it's disabled (aka, grayed-out)
		disabledImg: "src/widget/templates/images/soriaDisabled-",
		
		// widget2height: Number
		//	shape of the button's end pieces;
		//	the height of the end pieces is a function of the button's height (which in turn is a function of the button's content),
		//	and then the width of the end pieces is relative to their height.
		width2height: 1.0/3.0,

		fillInTemplate: function(){
			if(this.caption){
				this.containerNode.appendChild(document.createTextNode(this.caption));
			}
			dojo.html.disableSelection(this.containerNode);
		},

		postCreate: function(){
			this._sizeMyself();
		},
	
		_sizeMyself: function(){
			// we cannot size correctly if any of our ancestors are hidden (display:none),
			// so temporarily attach to document.body
			if(this.domNode.parentNode){
				var placeHolder = document.createElement("span");
				dojo.html.insertBefore(placeHolder, this.domNode);
			}
			dojo.body().appendChild(this.domNode);
			
			this._sizeMyselfHelper();
			
			// Put this.domNode back where it was originally
			if(placeHolder){
				dojo.html.insertBefore(this.domNode, placeHolder);
				dojo.html.removeNode(placeHolder);
			}
		},

		_sizeMyselfHelper: function(){
			var mb = dojo.html.getMarginBox(this.containerNode);
			this.height = mb.height;
			this.containerWidth = mb.width;
			var endWidth= this.height * this.width2height;
	
			this.containerNode.style.left=endWidth+"px";
	
			this.leftImage.height = this.rightImage.height = this.centerImage.height = this.height;
			this.leftImage.width = this.rightImage.width = endWidth+1;
			this.centerImage.width = this.containerWidth;
			this.centerImage.style.left=endWidth+"px";
			this._setImage(this.disabled ? this.disabledImg : this.inactiveImg);

			if ( this.disabled ) {
				dojo.html.prependClass(this.domNode, "dojoButtonDisabled");
				this.domNode.removeAttribute("tabIndex");
				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", true);
			} else {
				dojo.html.removeClass(this.domNode, "dojoButtonDisabled");
				this.domNode.setAttribute("tabIndex", "0");
				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", false);
			}
				
			this.domNode.style.height=this.height + "px";
			this.domNode.style.width= (this.containerWidth+2*endWidth) + "px";
		},
	
		onMouseOver: function(/*Event*/ e){
			// summary: callback when user mouses-over the button
			if( this.disabled ){ return; }
			dojo.html.prependClass(this.buttonNode, "dojoButtonHover");
			this._setImage(this.activeImg);
		},
	
		onMouseDown: function(/*Event*/ e){
			// summary: callback when user starts to click the button
			if( this.disabled ){ return; }
			dojo.html.prependClass(this.buttonNode, "dojoButtonDepressed");
			dojo.html.removeClass(this.buttonNode, "dojoButtonHover");
			this._setImage(this.pressedImg);
		},

		onMouseUp: function(/*Event*/ e){
			// summary: callback when the user finishes clicking
			if( this.disabled ){ return; }
			dojo.html.prependClass(this.buttonNode, "dojoButtonHover");
			dojo.html.removeClass(this.buttonNode, "dojoButtonDepressed");
			this._setImage(this.activeImg);
		},
	
		onMouseOut: function(/*Event*/ e){
			// summary: callback when the user moves the mouse off the button
			if( this.disabled ){ return; }
			if( e.toElement && dojo.html.isDescendantOf(e.toElement, this.buttonNode) ){
				return; // Ignore IE mouseOut events that dont actually leave button - Prevents hover image flicker in IE
			}
			dojo.html.removeClass(this.buttonNode, "dojoButtonHover");
			dojo.html.removeClass(this.buttonNode, "dojoButtonDepressed");
			this._setImage(this.inactiveImg);
		},

		onKey: function(/*Event*/ e){
			// summary: callback when the user presses a key (on key-down)
			if (!e.key) { return; }
			var menu = dojo.widget.getWidgetById(this.menuId);
			if (e.key == e.KEY_ENTER || e.key == " "){
				this.onMouseDown(e);
				this.buttonClick(e);
				dojo.lang.setTimeout(this, "onMouseUp", 75, e);
				dojo.event.browser.stopEvent(e);
			}
			if(menu && menu.isShowingNow && e.key == e.KEY_DOWN_ARROW){
				// disconnect onBlur when focus moves into menu
				dojo.event.disconnect(this.domNode, "onblur", this, "onBlur");
				// allow event to propagate to menu
			}
		},

		onFocus: function(/*Event*/ e){
			// summary: callback on focus to the button
			var menu = dojo.widget.getWidgetById(this.menuId);
			if (menu){
				dojo.event.connectOnce(this.domNode, "onblur", this, "onBlur");
			}
		},

		onBlur: function(/*Event*/ e){
			// summary: callback when button loses focus
			var menu = dojo.widget.getWidgetById(this.menuId);
			if ( !menu ) { return; }
	
			if ( menu.close && menu.isShowingNow ){
				menu.close();
			}
		},

		buttonClick: function(/*Event*/ e){
			// summary: internal function for handling button clicks
			if(!this.disabled){ 
				// focus may fail when tabIndex is not supported on div's
				// by the browser, or when the node is disabled
				try { this.domNode.focus(); } catch(e2) {};
				this.onClick(e); 
			}
		},

		onClick: function(/*Event*/ e) {
			// summary: callback for when button is clicked; user can override this function
		},

		_setImage: function(/*String*/ prefix){
			this.leftImage.src=dojo.uri.dojoUri(prefix + "l.gif");
			this.centerImage.src=dojo.uri.dojoUri(prefix + "c.gif");
			this.rightImage.src=dojo.uri.dojoUri(prefix + "r.gif");
		},
		
		_toggleMenu: function(/*String*/ menuId){
			var menu = dojo.widget.getWidgetById(menuId); 
			if ( !menu ) { return; }
			if ( menu.open && !menu.isShowingNow) {
				var pos = dojo.html.getAbsolutePosition(this.domNode, false);
				menu.open(pos.x, pos.y+this.height, this);
			} else if ( menu.close && menu.isShowingNow ){
				menu.close();
			} else {
				menu.toggle();
			}
		},
		
		setCaption: function(/*String*/ content){
			// summary: reset the caption (text) of the button; takes an HTML string
			this.caption=content;
			this.containerNode.innerHTML=content;
			this._sizeMyself();
		},
		
		setDisabled: function(/*Boolean*/ disabled){
			// summary: set disabled state of button
			this.disabled=disabled;
			this._sizeMyself();
		}
	});

/*
 * usage
 *	<button dojoType="DropDownButton" menuId="mymenu">Hello world</button>
 *
 *  var button1 = dojo.widget.createWidget("DropDownButton", {caption: "hello world", menuId: foo});
 *	document.body.appendChild(button1.domNode);
 */
dojo.widget.defineWidget(
	"dojo.widget.DropDownButton",
	dojo.widget.Button,
	{
		// summary
		//		push the button and a menu shows up
		// menuId: String
		//	widget id of the menu that this button should activate
		menuId: "",

		// downArrow: Url
		//	path of arrow image to display to the right of the button text
		downArrow: "src/widget/templates/images/whiteDownArrow.gif",

		// disabledDownArray: Url
		//	path of arrow image to display to the right of the button text, when the button is disabled
		disabledDownArrow: "src/widget/templates/images/whiteDownArrow.gif",
	
		fillInTemplate: function(){
			dojo.widget.DropDownButton.superclass.fillInTemplate.apply(this, arguments);
	
			this.arrow = document.createElement("img");
			dojo.html.setClass(this.arrow, "downArrow");

			dojo.widget.wai.setAttr(this.domNode, "waiState", "haspopup", this.menuId);
		},

		_sizeMyselfHelper: function(){
			// draw the arrow (todo: why is the arror in containerNode rather than outside it?)
			this.arrow.src = dojo.uri.dojoUri(this.disabled ? this.disabledDownArrow : this.downArrow);
			this.containerNode.appendChild(this.arrow);

			dojo.widget.DropDownButton.superclass._sizeMyselfHelper.call(this);
		},

		onClick: function(/*Event*/ e){
			// summary: callback when button is clicked; user shouldn't override this function or else the menu won't toggle
			this._toggleMenu(this.menuId);
		}
	});

/*
 * usage
 *	<button dojoType="ComboButton" onClick="..." menuId="mymenu">Hello world</button>
 *
 *  var button1 = dojo.widget.createWidget("DropDownButton", {caption: "hello world", onClick: foo, menuId: "myMenu"});
 *	document.body.appendChild(button1.domNode);
 */
dojo.widget.defineWidget(
	"dojo.widget.ComboButton",
	dojo.widget.Button,
	{
		// summary
		//		left side is normal button, right side displays menu
		// menuId: String
		//	widget id of the menu that this button should activate
		menuId: "",
	
		templatePath: dojo.uri.dojoUri("src/widget/templates/ComboButtonTemplate.html"),
	
		// splitWidth: Integer
		//	# of pixels between left & right part of button
		splitWidth: 2,
		
		// arrowWidth: Integer
		//	width of segment holding down arrow
		arrowWidth: 5,
	
		_sizeMyselfHelper: function(/*Event*/ e){
			var mb = dojo.html.getMarginBox(this.containerNode);
			this.height = mb.height;
			this.containerWidth = mb.width;

			var endWidth= this.height/3;

			if(this.disabled){
				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", true);
				this.domNode.removeAttribute("tabIndex");
			}
			else {
				dojo.widget.wai.setAttr(this.domNode, "waiState", "disabled", false);
				this.domNode.setAttribute("tabIndex", "0");
			}
	
			// left part
			this.leftImage.height = this.rightImage.height = this.centerImage.height = 
				this.arrowBackgroundImage.height = this.height;
			this.leftImage.width = endWidth+1;
			this.centerImage.width = this.containerWidth;
			this.buttonNode.style.height = this.height + "px";
			this.buttonNode.style.width = endWidth + this.containerWidth + "px";
			this._setImage(this.disabled ? this.disabledImg : this.inactiveImg);

			// right part
			this.arrowBackgroundImage.width=this.arrowWidth;
			this.rightImage.width = endWidth+1;
			this.rightPart.style.height = this.height + "px";
			this.rightPart.style.width = this.arrowWidth + endWidth + "px";
			this._setImageR(this.disabled ? this.disabledImg : this.inactiveImg);
	
			// outer container
			this.domNode.style.height=this.height + "px";
			var totalWidth = this.containerWidth+this.splitWidth+this.arrowWidth+2*endWidth;
			this.domNode.style.width= totalWidth + "px";
		},
	
		_setImage: function(prefix){
			this.leftImage.src=dojo.uri.dojoUri(prefix + "l.gif");
			this.centerImage.src=dojo.uri.dojoUri(prefix + "c.gif");
		},
	
		/*** functions on right part of button ***/
		rightOver: function(/*Event*/ e){
			// summary:
			//	callback when mouse-over right part of button;
			//	onMouseOver() is the callback for the left side of the button.
			if( this.disabled ){ return; }
			dojo.html.prependClass(this.rightPart, "dojoButtonHover");
			this._setImageR(this.activeImg);
		},
	
		rightDown: function(/*Event*/ e){
			// summary:
			//	callback when mouse-down right part of button;
			//	onMouseDown() is the callback for the left side of the button.
			if( this.disabled ){ return; }
			dojo.html.prependClass(this.rightPart, "dojoButtonDepressed");
			dojo.html.removeClass(this.rightPart, "dojoButtonHover");
			this._setImageR(this.pressedImg);
		},

		rightUp: function(/*Event*/ e){
			// summary:
			//	callback when mouse-up right part of button;
			//	onMouseUp() is the callback for the left side of the button.
			if( this.disabled ){ return; }
			dojo.html.prependClass(this.rightPart, "dojoButtonHover");
			dojo.html.removeClass(this.rightPart, "dojoButtonDepressed");
			this._setImageR(this.activeImg);
		},
	
		rightOut: function(/*Event*/ e){
			// summary:
			//	callback when moving the mouse off of the right part of button;
			//	onMouseOut() is the callback for the left side of the button.
			if( this.disabled ){ return; }
			dojo.html.removeClass(this.rightPart, "dojoButtonHover");
			dojo.html.removeClass(this.rightPart, "dojoButtonDepressed");
			this._setImageR(this.inactiveImg);
		},

		rightClick: function(/*Event*/ e){
			// summary:
			//	callback when clicking the right part of button;
			//	onClick() is the callback for the left side of the button.
			if( this.disabled ){ return; }
			// focus may fail when tabIndex is not supported on div's
			// by the browser, or when the node is disabled
			try { this.domNode.focus(); } catch(e2) {};
			this._toggleMenu(this.menuId);
		},
	
		_setImageR: function(prefix){
			this.arrowBackgroundImage.src=dojo.uri.dojoUri(prefix + "c.gif");
			this.rightImage.src=dojo.uri.dojoUri(prefix + "r.gif");
		},

		/*** keyboard functions ***/
		
		onKey: function(/*Event*/ e){
			if (!e.key) { return; }
			var menu = dojo.widget.getWidgetById(this.menuId);
			if(e.key== e.KEY_ENTER || e.key == " "){
				this.onMouseDown(e);
				this.buttonClick(e);
				dojo.lang.setTimeout(this, "onMouseUp", 75, e);
				dojo.event.browser.stopEvent(e);
			} else if (e.key == e.KEY_DOWN_ARROW && e.altKey){
				this.rightDown(e);
				this.rightClick(e);
				dojo.lang.setTimeout(this, "rightUp", 75, e);
				dojo.event.browser.stopEvent(e);
			} else if(menu && menu.isShowingNow && e.key == e.KEY_DOWN_ARROW){
				// disconnect onBlur when focus moves into menu
				dojo.event.disconnect(this.domNode, "onblur", this, "onBlur");
				// allow event to propagate to menu
			}
		}
	});
