Project: Dojo Toolkit
Code Location: http://svn.dojotoolkit.org/src/dijit/trunk//dijit/trunk
Browse
/
Download File
TitlePane.js
define([
	"dojo/_base/array", // array.forEach
	"dojo/_base/declare", // declare
	"dojo/dom", // dom.setSelectable
	"dojo/dom-attr", // domAttr.set or get domAttr.remove
	"dojo/dom-class", // domClass.replace
	"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.getMarginBox
	"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
	"dojo/has",
	"dojo/_base/kernel", // kernel.deprecated
	"dojo/keys", // keys.DOWN_ARROW keys.ENTER
	"./_CssStateMixin",
	"./_TemplatedMixin",
	"./layout/ContentPane",
	"dojo/text!./templates/TitlePane.html",
	"./_base/manager",    // defaultDuration
	"./a11yclick"	// template uses ondijitclick
], function(array, declare, dom, domAttr, domClass, domGeometry, fxUtils, has, kernel, keys,
			_CssStateMixin, _TemplatedMixin, ContentPane, template, manager){

	// module:
	//		dijit/TitlePane

	var TitlePane = declare("dijit.TitlePane", [ContentPane, _TemplatedMixin, _CssStateMixin], {
		// summary:
		//		A pane with a title on top, that can be expanded or collapsed.
		//
		// description:
		//		An accessible container with a title Heading, and a content
		//		section that slides open and closed. TitlePane is an extension to
		//		`dijit/layout/ContentPane`, providing all the useful content-control aspects from it.
		//
		// example:
		//	|	// load a TitlePane from remote file:
		//	|	var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
		//	|	foo.startup();
		//
		// example:
		//	|	<!-- markup href example: -->
		//	|	<div data-dojo-type="dijit/TitlePane" data-dojo-props="href: 'foobar.html', title: 'Title'"></div>
		//
		// example:
		//	|	<!-- markup with inline data -->
		//	|	<div data-dojo-type="dijit/TitlePane" title="Title">
		//	|		<p>I am content</p>
		//	|	</div>

		// title: String
		//		Title of the pane
		title: "",
		_setTitleAttr: { node: "titleNode", type: "innerHTML" }, // override default where title becomes a hover tooltip

		// open: Boolean
		//		Whether pane is opened or closed.
		open: true,

		// toggleable: Boolean
		//		Whether pane can be opened or closed by clicking the title bar.
		toggleable: true,

		// tabIndex: String
		//		Tabindex setting for the title (so users can tab to the title then
		//		use space/enter to open/close the title pane)
		tabIndex: "0",

		// duration: Integer
		//		Time in milliseconds to fade in/fade out
		duration: manager.defaultDuration,

		// baseClass: [protected] String
		//		The root className to be placed on this widget's domNode.
		baseClass: "dijitTitlePane",

		templateString: template,

		// doLayout: [protected] Boolean
		//		Don't change this parameter from the default value.
		//		This ContentPane parameter doesn't make sense for TitlePane, since TitlePane
		//		is never a child of a layout container, nor should TitlePane try to control
		//		the size of an inner widget.
		doLayout: false,

		// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
		_setTooltipAttr: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't

		buildRendering: function(){
			this.inherited(arguments);
			dom.setSelectable(this.titleNode, false);
		},

		postCreate: function(){
			this.inherited(arguments);

			// Hover and focus effect on title bar, except for non-toggleable TitlePanes
			// This should really be controlled from _setToggleableAttr() but _CssStateMixin
			// doesn't provide a way to disconnect a previous _trackMouseState() call
			if(this.toggleable){
				this._trackMouseState(this.titleBarNode, this.baseClass + "Title");
			}

			// setup open/close animations
			var hideNode = this.hideNode, wipeNode = this.wipeNode;
			this._wipeIn = fxUtils.wipeIn({
				node: wipeNode,
				duration: this.duration,
				beforeBegin: function(){
					hideNode.style.display = "";
				}
			});
			this._wipeOut = fxUtils.wipeOut({
				node: wipeNode,
				duration: this.duration,
				onEnd: function(){
					hideNode.style.display = "none";
				}
			});
		},

		_setOpenAttr: function(/*Boolean*/ open, /*Boolean*/ animate){
			// summary:
			//		Hook to make set("open", boolean) control the open/closed state of the pane.
			// open: Boolean
			//		True if you want to open the pane, false if you want to close it.

			array.forEach([this._wipeIn, this._wipeOut], function(animation){
				if(animation && animation.status() == "playing"){
					animation.stop();
				}
			});

			if(animate){
				var anim = this[open ? "_wipeIn" : "_wipeOut"];
				anim.play();
			}else{
				this.hideNode.style.display = this.wipeNode.style.display = open ? "" : "none";
			}

			// load content (if this is the first time we are opening the TitlePane
			// and content is specified as an href, or href was set when hidden)
			if(this._started){
				if(open){
					this._onShow();
				}else{
					this.onHide();
				}
			}

			this.containerNode.setAttribute("aria-hidden", open ? "false" : "true");
			this.focusNode.setAttribute("aria-pressed", open ? "true" : "false");

			this._set("open", open);

			this._setCss();
		},

		_setToggleableAttr: function(/*Boolean*/ canToggle){
			// summary:
			//		Hook to make set("toggleable", boolean) work.
			// canToggle: Boolean
			//		True to allow user to open/close pane by clicking title bar.

			this.focusNode.setAttribute("role", canToggle ? "button" : "heading");
			if(canToggle){
				this.focusNode.setAttribute("aria-controls", this.id + "_pane");
				this.focusNode.setAttribute("tabIndex", this.tabIndex);
				this.focusNode.setAttribute("aria-pressed", this.open);
			}else{
				domAttr.remove(this.focusNode, "aria-controls");
				domAttr.remove(this.focusNode, "tabIndex");
				domAttr.remove(this.focusNode, "aria-pressed");
			}

			this._set("toggleable", canToggle);

			this._setCss();
		},

		_setContentAttr: function(/*String|DomNode|Nodelist*/ content){
			// summary:
			//		Hook to make set("content", ...) work.
			//		Typically called when an href is loaded.  Our job is to make the animation smooth.

			if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
				// we are currently *closing* the pane (or the pane is closed), so just let that continue
				this.inherited(arguments);
			}else{
				if(this._wipeIn && this._wipeIn.status() == "playing"){
					this._wipeIn.stop();
				}

				// freeze container at current height so that adding new content doesn't make it jump
				domGeometry.setMarginBox(this.wipeNode, { h: domGeometry.getMarginBox(this.wipeNode).h });

				// add the new content (erasing the old content, if any)
				this.inherited(arguments);

				// call _wipeIn.play() to animate from current height to new height
				if(this._wipeIn){
					this._wipeIn.play();
				}else{
					this.hideNode.style.display = "";
				}
			}
		},

		toggle: function(){
			// summary:
			//		Switches between opened and closed state
			// tags:
			//		private

			this._setOpenAttr(!this.open, true);
		},

		_setCss: function(){
			// summary:
			//		Set the open/close css state for the TitlePane
			// tags:
			//		private

			var node = this.titleBarNode || this.focusNode;
			var oldCls = this._titleBarClass;
			this._titleBarClass = this.baseClass + "Title" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
			domClass.replace(node, this._titleBarClass, oldCls || "");

			// Back compat, remove for 2.0
			domClass.replace(node, this._titleBarClass.replace("TitlePaneTitle", ""), (oldCls || "").replace("TitlePaneTitle", ""));

			this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
		},

		_onTitleKey: function(/*Event*/ e){
			// summary:
			//		Handler for when user hits a key
			// tags:
			//		private

			if(e.keyCode == keys.DOWN_ARROW && this.open){
				this.containerNode.focus();
				e.preventDefault();
			}
		},

		_onTitleClick: function(){
			// summary:
			//		Handler when user clicks the title bar
			// tags:
			//		private
			if(this.toggleable){
				this.toggle();
			}
		},

		setTitle: function(/*String*/ title){
			// summary:
			//		Deprecated.  Use set('title', ...) instead.
			// tags:
			//		deprecated
			kernel.deprecated("dijit.TitlePane.setTitle() is deprecated.  Use set('title', ...) instead.", "", "2.0");
			this.set("title", title);
		}
	});

	if(has("dojo-bidi")){
		TitlePane.extend({
			_setTitleAttr: function(/*String*/ title){
				// Override default where title becomes a hover tooltip
				this._set("title", title);
				this.titleNode.innerHTML = title;
				this.applyTextDir(this.titleNode);
			},

			_setTooltipAttr: function(/*String*/ tooltip){
				this._set("tooltip", tooltip);
				if(this.textDir){
					tooltip = this.enforceTextDirWithUcc(null, tooltip);
				}
				domAttr.set(this.focusNode, "title", tooltip);			// focusNode spans the entire width, titleNode doesn't
			},

			_setTextDirAttr: function(textDir){
				if(this._created && this.textDir != textDir){
					this._set("textDir", textDir);
					this.set("title", this.title);
					this.set("tooltip", this.tooltip);
				}
			}
		});
	}

	return TitlePane;
});