DropdownButton_wev8.js 5.8 KB
/*
	Copyright (c) 2004-2005, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

/* TODO:
 * - make the dropdown "smart" so it can't get cutoff on bottom of page, sides of page, etc.
 */

dojo.provide("dojo.widget.html.DropdownButton");

dojo.require("dojo.event.*");
dojo.require("dojo.widget.*");
dojo.require("dojo.widget.HtmlWidget");
dojo.require("dojo.uri.Uri");
dojo.require("dojo.dom");
dojo.require("dojo.style");
dojo.require("dojo.html");

dojo.widget.html.DropdownButton = function() {
	// mix in the button properties
	dojo.widget.DropdownButton.call(this);
	dojo.widget.HtmlWidget.call(this);
}

dojo.inherits(dojo.widget.html.DropdownButton, dojo.widget.HtmlWidget);

dojo.lang.extend(dojo.widget.html.DropdownButton, {
	
	// In IE, event handlers on objects inside buttons don't work correctly, so
	// we just set onClick on the button itself.
	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlDropDownButtonTemplate.html"),
	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate_wev8.css"),

	// attach points
	button: null,
	table: null,
	labelCell: null,
	borderCell: null,
	arrowCell: null,
	arrow: null,

	fillInTemplate: function(args, frag) {
		// input data (containing the anchor for the button itself, plus the
		// thing to display when you push the down arrow)
		var input = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];

		// Recursively expand widgets inside of the <dojo:dropdownButton>
		var parser = new dojo.xml.Parse();
		var frag = parser.parseElement(input, null, true);
		var ary = dojo.widget.getParser().createComponents(frag);

		this.a = dojo.dom.getFirstChildElement(input);	// the button contents
		this.menu = dojo.dom.getNextSiblingElement(this.a);	// the menu under the button
		
		this.disabled = dojo.html.hasClass(this.a, "disabled");
		if( this.disabled ) {
			dojo.html.addClass(this.button, "dojoDisabled");
			this.domNode.setAttribute("disabled", "true");
		}

		dojo.html.disableSelection(this.a);
		this.a.style["text-decoration"]="none";
		this.labelCell.appendChild(this.a);

		this.arrow.src =
			dojo.uri.dojoUri("src/widget/templates/images/dropdownButtonsArrow" +
			(this.disabled ? "-disabled" : "") + "_wev8.gif");

		// Attach menu to body so that it appears above other buttons
		this.menu.style.position="absolute";
		this.menu.style.display="none";
		this.menu.style["z-index"] = 99;
		dojo.html.body().appendChild(this.menu);
	},

	postCreate: function() {
		if ( dojo.render.html.ie ) {
			// Compensate for IE's weird padding of button content, which seems to be relative
			// to the length of the content
			var contentWidth = dojo.style.getOuterWidth(this.table);
			this.labelCell.style["left"] = "-" + (contentWidth / 10) + "px";
			this.arrowCell.style["left"] = (contentWidth / 10) + "px";
		}

		// Make menu at least as wide as the button
		var buttonWidth = dojo.style.getOuterWidth(this.button);
		var menuWidth = dojo.style.getOuterWidth(this.menu);
		if ( buttonWidth > menuWidth ) {
			dojo.style.setOuterWidth(this.menu, buttonWidth);
		}
	},

	// If someone clicks anywhere else on the screen (including another menu),
	// then close this menu.
	onCanvasMouseDown: function(e) {
		if( !dojo.dom.isDescendantOf(e.target, this.button) &&
			!dojo.dom.isDescendantOf(e.target, this.menu) ) {
			this.hideMenu();
		}
	},

	eventWasOverArrow: function(e) {
		// want to use dojo.html.overElement() but also need to detect clicks
		// on the area between the arrow and the edge of the button
		var eventX = e.clientX;
		var borderX = dojo.style.totalOffsetLeft(this.borderCell);
		return (eventX > borderX );
	},

	onMouseOver: function(e) {
		dojo.html.addClass(this.button, "dojoButtonHover");
		dojo.html.removeClass(this.button, "dojoButtonNoHover");
	},
	
	onMouseOut: function(e) {
		dojo.html.removeClass(this.button, "dojoButtonHover");
		dojo.html.addClass(this.button, "dojoButtonNoHover");
	},

	onClick: function(e) {
		if ( this.eventWasOverArrow(e) ) {
			this._onClickArrow();
		} else {
			this._onClickButton();
		}
	},

	// Action when the user presses the button
	_onClickButton: function(e) {
		if ( this.a ) {
			if ( this.a.click ) {
				this.a.click();
			} else if ( this.a.href ) {
				location.href = this.a.href;
			}
		}
	},

	// Action when user presses the arrow
	_onClickArrow: function() {
		if ( this.menu.style.display == "none" ) {
			this.showMenu();
		} else {
			this.hideMenu();
		}
	},
	
	showMenu: function() {
		if ( this.disabled )
			return;

		// Position it accordingly, relative to screen root (since
		// it's attached to document.body)
		this.menu.style.left = dojo.style.totalOffsetLeft(this.button) + "px";
		this.menu.style.top = dojo.style.totalOffsetTop(this.button) + dojo.style.getOuterHeight(this.button) + "px";

		// Display the menu; do this funky code below to stop the menu from extending
		// all the way to the right edge of the screen.
		// TODO: retest simple display="" to confirm that it doesn't work.
		try {
			this.menu.style.display="table";	// mozilla
		} catch(e) {
			this.menu.style.display="block";	// IE
		}

		// If someone clicks somewhere else on the screen then close the menu
		dojo.event.connect(document.documentElement, "onmousedown", this, "onCanvasMouseDown");
		
		// When someone clicks the menu, after the menu handles the event,
		// close the menu (be careful not to close the menu too early or else
		// the menu will never receive the event.)
		dojo.event.connect(this.menu, "onclick", this, "hideMenu");
	},

	hideMenu: function() {
		this.menu.style.display = "none";
		dojo.event.disconnect(document.documentElement, "onmousedown", this, "onCanvasMouseDown");
		dojo.event.disconnect(this.menu, "onclick", this, "hideMenu");
	}
});