summaryrefslogtreecommitdiff
path: root/lib/dijit/_HasDropDown.js
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2011-11-08 20:40:44 +0400
committerAndrew Dolgov <[email protected]>2011-11-08 20:40:44 +0400
commit81bea17aefb26859f825b9293c7c99192874806e (patch)
treefb244408ca271affa2899adb634788802c9a89d8 /lib/dijit/_HasDropDown.js
parent870a70e109ac9e80a88047044530de53d0404ec7 (diff)
upgrade Dojo to 1.6.1
Diffstat (limited to 'lib/dijit/_HasDropDown.js')
-rw-r--r--lib/dijit/_HasDropDown.js633
1 files changed, 434 insertions, 199 deletions
diff --git a/lib/dijit/_HasDropDown.js b/lib/dijit/_HasDropDown.js
index 6606acb00..e1e9c1ca2 100644
--- a/lib/dijit/_HasDropDown.js
+++ b/lib/dijit/_HasDropDown.js
@@ -1,208 +1,443 @@
/*
- Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
+ Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
-if(!dojo._hasResource["dijit._HasDropDown"]){
-dojo._hasResource["dijit._HasDropDown"]=true;
+if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit._HasDropDown"] = true;
dojo.provide("dijit._HasDropDown");
-dojo.require("dijit._base.place");
dojo.require("dijit._Widget");
-dojo.declare("dijit._HasDropDown",null,{_buttonNode:null,_arrowWrapperNode:null,_popupStateNode:null,_aroundNode:null,dropDown:null,autoWidth:true,forceWidth:false,maxHeight:0,dropDownPosition:["below","above"],_stopClickEvents:true,_onDropDownMouseDown:function(e){
-if(this.disabled||this.readOnly){
-return;
-}
-this._docHandler=this.connect(dojo.doc,"onmouseup","_onDropDownMouseUp");
-this.toggleDropDown();
-},_onDropDownMouseUp:function(e){
-if(e&&this._docHandler){
-this.disconnect(this._docHandler);
-}
-var _1=this.dropDown,_2=false;
-if(e&&this._opened){
-var c=dojo.position(this._buttonNode,true);
-if(!(e.pageX>=c.x&&e.pageX<=c.x+c.w)||!(e.pageY>=c.y&&e.pageY<=c.y+c.h)){
-var t=e.target;
-while(t&&!_2){
-if(dojo.hasClass(t,"dijitPopup")){
-_2=true;
-}else{
-t=t.parentNode;
-}
-}
-if(_2){
-t=e.target;
-if(_1.onItemClick){
-var _3;
-while(t&&!(_3=dijit.byNode(t))){
-t=t.parentNode;
-}
-if(_3&&_3.onClick&&_3.getParent){
-_3.getParent().onItemClick(_3,e);
-}
-}
-return;
-}
-}
-}
-if(this._opened&&_1.focus){
-window.setTimeout(dojo.hitch(_1,"focus"),1);
-}
-},_onDropDownClick:function(e){
-if(this._stopClickEvents){
-dojo.stopEvent(e);
-}
-},_setupDropdown:function(){
-this._buttonNode=this._buttonNode||this.focusNode||this.domNode;
-this._popupStateNode=this._popupStateNode||this.focusNode||this._buttonNode;
-this._aroundNode=this._aroundNode||this.domNode;
-this.connect(this._buttonNode,"onmousedown","_onDropDownMouseDown");
-this.connect(this._buttonNode,"onclick","_onDropDownClick");
-this.connect(this._buttonNode,"onkeydown","_onDropDownKeydown");
-this.connect(this._buttonNode,"onkeyup","_onKey");
-if(this._setStateClass){
-this.connect(this,"openDropDown","_setStateClass");
-this.connect(this,"closeDropDown","_setStateClass");
-}
-var _4={"after":this.isLeftToRight()?"Right":"Left","before":this.isLeftToRight()?"Left":"Right","above":"Up","below":"Down","left":"Left","right":"Right"}[this.dropDownPosition[0]]||this.dropDownPosition[0]||"Down";
-dojo.addClass(this._arrowWrapperNode||this._buttonNode,"dijit"+_4+"ArrowButton");
-},postCreate:function(){
-this._setupDropdown();
-this.inherited(arguments);
-},destroyDescendants:function(){
-if(this.dropDown){
-if(!this.dropDown._destroyed){
-this.dropDown.destroyRecursive();
-}
-delete this.dropDown;
-}
-this.inherited(arguments);
-},_onDropDownKeydown:function(e){
-if(e.keyCode==dojo.keys.DOWN_ARROW||e.keyCode==dojo.keys.ENTER||e.keyCode==dojo.keys.SPACE){
-e.preventDefault();
-}
-},_onKey:function(e){
-if(this.disabled||this.readOnly){
-return;
-}
-var d=this.dropDown;
-if(d&&this._opened&&d.handleKey){
-if(d.handleKey(e)===false){
-return;
-}
-}
-if(d&&this._opened&&e.keyCode==dojo.keys.ESCAPE){
-this.toggleDropDown();
-}else{
-if(d&&!this._opened&&(e.keyCode==dojo.keys.DOWN_ARROW||e.keyCode==dojo.keys.ENTER||e.keyCode==dojo.keys.SPACE)){
-this.toggleDropDown();
-if(d.focus){
-setTimeout(dojo.hitch(d,"focus"),1);
-}
-}
-}
-},_onBlur:function(){
-this.closeDropDown();
-this.inherited(arguments);
-},isLoaded:function(){
-return true;
-},loadDropDown:function(_5){
-_5();
-},toggleDropDown:function(){
-if(this.disabled||this.readOnly){
-return;
-}
-this.focus();
-var _6=this.dropDown;
-if(!_6){
-return;
-}
-if(!this._opened){
-if(!this.isLoaded()){
-this.loadDropDown(dojo.hitch(this,"openDropDown"));
-return;
-}else{
-this.openDropDown();
-}
-}else{
-this.closeDropDown();
-}
-},openDropDown:function(){
-var _7=this.dropDown;
-var _8=_7.domNode;
-var _9=this;
-if(!this._preparedNode){
-dijit.popup.moveOffScreen(_8);
-this._preparedNode=true;
-if(_8.style.width){
-this._explicitDDWidth=true;
-}
-if(_8.style.height){
-this._explicitDDHeight=true;
-}
-}
-if(this.maxHeight||this.forceWidth||this.autoWidth){
-var _a={display:"",visibility:"hidden"};
-if(!this._explicitDDWidth){
-_a.width="";
-}
-if(!this._explicitDDHeight){
-_a.height="";
-}
-dojo.style(_8,_a);
-var mb=dojo.marginBox(_8);
-var _b=(this.maxHeight&&mb.h>this.maxHeight);
-dojo.style(_8,{overflowX:"hidden",overflowY:_b?"auto":"hidden"});
-if(_b){
-mb.h=this.maxHeight;
-if("w" in mb){
-mb.w+=16;
-}
-}else{
-delete mb.h;
-}
-delete mb.t;
-delete mb.l;
-if(this.forceWidth){
-mb.w=this.domNode.offsetWidth;
-}else{
-if(this.autoWidth){
-mb.w=Math.max(mb.w,this.domNode.offsetWidth);
-}else{
-delete mb.w;
-}
-}
-if(dojo.isFunction(_7.resize)){
-_7.resize(mb);
-}else{
-dojo.marginBox(_8,mb);
-}
-}
-var _c=dijit.popup.open({parent:this,popup:_7,around:this._aroundNode,orient:dijit.getPopupAroundAlignment((this.dropDownPosition&&this.dropDownPosition.length)?this.dropDownPosition:["below"],this.isLeftToRight()),onExecute:function(){
-_9.closeDropDown(true);
-},onCancel:function(){
-_9.closeDropDown(true);
-},onClose:function(){
-dojo.attr(_9._popupStateNode,"popupActive",false);
-dojo.removeClass(_9._popupStateNode,"dijitHasDropDownOpen");
-_9._opened=false;
-_9.state="";
-}});
-dojo.attr(this._popupStateNode,"popupActive","true");
-dojo.addClass(_9._popupStateNode,"dijitHasDropDownOpen");
-this._opened=true;
-this.state="Opened";
-return _c;
-},closeDropDown:function(_d){
-if(this._opened){
-if(_d){
-this.focus();
-}
-dijit.popup.close(this.dropDown);
-this._opened=false;
-this.state="";
-}
-}});
+
+
+dojo.declare("dijit._HasDropDown",
+ null,
+ {
+ // summary:
+ // Mixin for widgets that need drop down ability.
+
+ // _buttonNode: [protected] DomNode
+ // The button/icon/node to click to display the drop down.
+ // Can be set via a dojoAttachPoint assignment.
+ // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
+ _buttonNode: null,
+
+ // _arrowWrapperNode: [protected] DomNode
+ // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
+ // on where the drop down is set to be positioned.
+ // Can be set via a dojoAttachPoint assignment.
+ // If missing, then _buttonNode will be used.
+ _arrowWrapperNode: null,
+
+ // _popupStateNode: [protected] DomNode
+ // The node to set the popupActive class on.
+ // Can be set via a dojoAttachPoint assignment.
+ // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
+ _popupStateNode: null,
+
+ // _aroundNode: [protected] DomNode
+ // The node to display the popup around.
+ // Can be set via a dojoAttachPoint assignment.
+ // If missing, then domNode will be used.
+ _aroundNode: null,
+
+ // dropDown: [protected] Widget
+ // The widget to display as a popup. This widget *must* be
+ // defined before the startup function is called.
+ dropDown: null,
+
+ // autoWidth: [protected] Boolean
+ // Set to true to make the drop down at least as wide as this
+ // widget. Set to false if the drop down should just be its
+ // default width
+ autoWidth: true,
+
+ // forceWidth: [protected] Boolean
+ // Set to true to make the drop down exactly as wide as this
+ // widget. Overrides autoWidth.
+ forceWidth: false,
+
+ // maxHeight: [protected] Integer
+ // The max height for our dropdown.
+ // Any dropdown taller than this will have scrollbars.
+ // Set to 0 for no max height, or -1 to limit height to available space in viewport
+ maxHeight: 0,
+
+ // dropDownPosition: [const] String[]
+ // This variable controls the position of the drop down.
+ // It's an array of strings with the following values:
+ //
+ // * before: places drop down to the left of the target node/widget, or to the right in
+ // the case of RTL scripts like Hebrew and Arabic
+ // * after: places drop down to the right of the target node/widget, or to the left in
+ // the case of RTL scripts like Hebrew and Arabic
+ // * above: drop down goes above target node
+ // * below: drop down goes below target node
+ //
+ // The list is positions is tried, in order, until a position is found where the drop down fits
+ // within the viewport.
+ //
+ dropDownPosition: ["below","above"],
+
+ // _stopClickEvents: Boolean
+ // When set to false, the click events will not be stopped, in
+ // case you want to use them in your subwidget
+ _stopClickEvents: true,
+
+ _onDropDownMouseDown: function(/*Event*/ e){
+ // summary:
+ // Callback when the user mousedown's on the arrow icon
+
+ if(this.disabled || this.readOnly){ return; }
+
+ dojo.stopEvent(e);
+
+ this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
+
+ this.toggleDropDown();
+ },
+
+ _onDropDownMouseUp: function(/*Event?*/ e){
+ // summary:
+ // Callback when the user lifts their mouse after mouse down on the arrow icon.
+ // If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
+ // dropDown node. If the event is missing, then we are not
+ // a mouseup event.
+ //
+ // This is useful for the common mouse movement pattern
+ // with native browser <select> nodes:
+ // 1. mouse down on the select node (probably on the arrow)
+ // 2. move mouse to a menu item while holding down the mouse button
+ // 3. mouse up. this selects the menu item as though the user had clicked it.
+ if(e && this._docHandler){
+ this.disconnect(this._docHandler);
+ }
+ var dropDown = this.dropDown, overMenu = false;
+
+ if(e && this._opened){
+ // This code deals with the corner-case when the drop down covers the original widget,
+ // because it's so large. In that case mouse-up shouldn't select a value from the menu.
+ // Find out if our target is somewhere in our dropdown widget,
+ // but not over our _buttonNode (the clickable node)
+ var c = dojo.position(this._buttonNode, true);
+ if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
+ !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
+ var t = e.target;
+ while(t && !overMenu){
+ if(dojo.hasClass(t, "dijitPopup")){
+ overMenu = true;
+ }else{
+ t = t.parentNode;
+ }
+ }
+ if(overMenu){
+ t = e.target;
+ if(dropDown.onItemClick){
+ var menuItem;
+ while(t && !(menuItem = dijit.byNode(t))){
+ t = t.parentNode;
+ }
+ if(menuItem && menuItem.onClick && menuItem.getParent){
+ menuItem.getParent().onItemClick(menuItem, e);
+ }
+ }
+ return;
+ }
+ }
+ }
+ if(this._opened && dropDown.focus && dropDown.autoFocus !== false){
+ // Focus the dropdown widget - do it on a delay so that we
+ // don't steal our own focus.
+ window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
+ }
+ },
+
+ _onDropDownClick: function(/*Event*/ e){
+ // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
+ if(this._stopClickEvents){
+ dojo.stopEvent(e);
+ }
+ },
+
+ buildRendering: function(){
+ this.inherited(arguments);
+
+ this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
+ this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
+
+ // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
+ // based on where drop down will normally appear
+ var defaultPos = {
+ "after" : this.isLeftToRight() ? "Right" : "Left",
+ "before" : this.isLeftToRight() ? "Left" : "Right",
+ "above" : "Up",
+ "below" : "Down",
+ "left" : "Left",
+ "right" : "Right"
+ }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
+ dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
+ },
+
+ postCreate: function(){
+ // summary:
+ // set up nodes and connect our mouse and keypress events
+
+ this.inherited(arguments);
+
+ this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
+ this.connect(this._buttonNode, "onclick", "_onDropDownClick");
+ this.connect(this.focusNode, "onkeypress", "_onKey");
+ this.connect(this.focusNode, "onkeyup", "_onKeyUp");
+ },
+
+ destroy: function(){
+ if(this.dropDown){
+ // Destroy the drop down, unless it's already been destroyed. This can happen because
+ // the drop down is a direct child of <body> even though it's logically my child.
+ if(!this.dropDown._destroyed){
+ this.dropDown.destroyRecursive();
+ }
+ delete this.dropDown;
+ }
+ this.inherited(arguments);
+ },
+
+ _onKey: function(/*Event*/ e){
+ // summary:
+ // Callback when the user presses a key while focused on the button node
+
+ if(this.disabled || this.readOnly){ return; }
+
+ var d = this.dropDown, target = e.target;
+ if(d && this._opened && d.handleKey){
+ if(d.handleKey(e) === false){
+ /* false return code means that the drop down handled the key */
+ dojo.stopEvent(e);
+ return;
+ }
+ }
+ if(d && this._opened && e.charOrCode == dojo.keys.ESCAPE){
+ this.closeDropDown();
+ dojo.stopEvent(e);
+ }else if(!this._opened &&
+ (e.charOrCode == dojo.keys.DOWN_ARROW ||
+ ( (e.charOrCode == dojo.keys.ENTER || e.charOrCode == " ") &&
+ //ignore enter and space if the event is for a text input
+ ((target.tagName || "").toLowerCase() !== 'input' ||
+ (target.type && target.type.toLowerCase() !== 'text'))))){
+ // Toggle the drop down, but wait until keyup so that the drop down doesn't
+ // get a stray keyup event, or in the case of key-repeat (because user held
+ // down key for too long), stray keydown events
+ this._toggleOnKeyUp = true;
+ dojo.stopEvent(e);
+ }
+ },
+
+ _onKeyUp: function(){
+ if(this._toggleOnKeyUp){
+ delete this._toggleOnKeyUp;
+ this.toggleDropDown();
+ var d = this.dropDown; // drop down may not exist until toggleDropDown() call
+ if(d && d.focus){
+ setTimeout(dojo.hitch(d, "focus"), 1);
+ }
+ }
+ },
+
+ _onBlur: function(){
+ // summary:
+ // Called magically when focus has shifted away from this widget and it's dropdown
+
+ // Don't focus on button if the user has explicitly focused on something else (happens
+ // when user clicks another control causing the current popup to close)..
+ // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
+ // it when you display:none a node with focus.
+ var focusMe = dijit._curFocus && this.dropDown && dojo.isDescendant(dijit._curFocus, this.dropDown.domNode);
+
+ this.closeDropDown(focusMe);
+
+ this.inherited(arguments);
+ },
+
+ isLoaded: function(){
+ // summary:
+ // Returns whether or not the dropdown is loaded. This can
+ // be overridden in order to force a call to loadDropDown().
+ // tags:
+ // protected
+
+ return true;
+ },
+
+ loadDropDown: function(/* Function */ loadCallback){
+ // summary:
+ // Loads the data for the dropdown, and at some point, calls
+ // the given callback. This is basically a callback when the
+ // user presses the down arrow button to open the drop down.
+ // tags:
+ // protected
+
+ loadCallback();
+ },
+
+ toggleDropDown: function(){
+ // summary:
+ // Callback when the user presses the down arrow button or presses
+ // the down arrow key to open/close the drop down.
+ // Toggle the drop-down widget; if it is up, close it, if not, open it
+ // tags:
+ // protected
+
+ if(this.disabled || this.readOnly){ return; }
+ if(!this._opened){
+ // If we aren't loaded, load it first so there isn't a flicker
+ if(!this.isLoaded()){
+ this.loadDropDown(dojo.hitch(this, "openDropDown"));
+ return;
+ }else{
+ this.openDropDown();
+ }
+ }else{
+ this.closeDropDown();
+ }
+ },
+
+ openDropDown: function(){
+ // summary:
+ // Opens the dropdown for this widget. To be called only when this.dropDown
+ // has been created and is ready to display (ie, it's data is loaded).
+ // returns:
+ // return value of dijit.popup.open()
+ // tags:
+ // protected
+
+ var dropDown = this.dropDown,
+ ddNode = dropDown.domNode,
+ aroundNode = this._aroundNode || this.domNode,
+ self = this;
+
+ // Prepare our popup's height and honor maxHeight if it exists.
+
+ // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
+ // ie, dependent on how much space is available (BK)
+
+ if(!this._preparedNode){
+ this._preparedNode = true;
+ // Check if we have explicitly set width and height on the dropdown widget dom node
+ if(ddNode.style.width){
+ this._explicitDDWidth = true;
+ }
+ if(ddNode.style.height){
+ this._explicitDDHeight = true;
+ }
+ }
+
+ // Code for resizing dropdown (height limitation, or increasing width to match my width)
+ if(this.maxHeight || this.forceWidth || this.autoWidth){
+ var myStyle = {
+ display: "",
+ visibility: "hidden"
+ };
+ if(!this._explicitDDWidth){
+ myStyle.width = "";
+ }
+ if(!this._explicitDDHeight){
+ myStyle.height = "";
+ }
+ dojo.style(ddNode, myStyle);
+
+ // Figure out maximum height allowed (if there is a height restriction)
+ var maxHeight = this.maxHeight;
+ if(maxHeight == -1){
+ // limit height to space available in viewport either above or below my domNode
+ // (whichever side has more room)
+ var viewport = dojo.window.getBox(),
+ position = dojo.position(aroundNode, false);
+ maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
+ }
+
+ // Attach dropDown to DOM and make make visibility:hidden rather than display:none
+ // so we call startup() and also get the size
+ if(dropDown.startup && !dropDown._started){
+ dropDown.startup();
+ }
+
+ dijit.popup.moveOffScreen(dropDown);
+ // Get size of drop down, and determine if vertical scroll bar needed
+ var mb = dojo._getMarginSize(ddNode);
+ var overHeight = (maxHeight && mb.h > maxHeight);
+ dojo.style(ddNode, {
+ overflowX: "hidden",
+ overflowY: overHeight ? "auto" : "hidden"
+ });
+ if(overHeight){
+ mb.h = maxHeight;
+ if("w" in mb){
+ mb.w += 16; // room for vertical scrollbar
+ }
+ }else{
+ delete mb.h;
+ }
+
+ // Adjust dropdown width to match or be larger than my width
+ if(this.forceWidth){
+ mb.w = aroundNode.offsetWidth;
+ }else if(this.autoWidth){
+ mb.w = Math.max(mb.w, aroundNode.offsetWidth);
+ }else{
+ delete mb.w;
+ }
+
+ // And finally, resize the dropdown to calculated height and width
+ if(dojo.isFunction(dropDown.resize)){
+ dropDown.resize(mb);
+ }else{
+ dojo.marginBox(ddNode, mb);
+ }
+ }
+
+ var retVal = dijit.popup.open({
+ parent: this,
+ popup: dropDown,
+ around: aroundNode,
+ orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
+ onExecute: function(){
+ self.closeDropDown(true);
+ },
+ onCancel: function(){
+ self.closeDropDown(true);
+ },
+ onClose: function(){
+ dojo.attr(self._popupStateNode, "popupActive", false);
+ dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
+ self._opened = false;
+ }
+ });
+ dojo.attr(this._popupStateNode, "popupActive", "true");
+ dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
+ this._opened=true;
+
+ // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
+ return retVal;
+ },
+
+ closeDropDown: function(/*Boolean*/ focus){
+ // summary:
+ // Closes the drop down on this widget
+ // focus:
+ // If true, refocuses the button widget
+ // tags:
+ // protected
+
+ if(this._opened){
+ if(focus){ this.focus(); }
+ dijit.popup.close(this.dropDown);
+ this._opened = false;
+ }
+ }
+
+ }
+);
+
}