- Author:
- David Nickerson <nickerso@users.sourceforge.net>
- Date:
- 2009-07-16 02:00:03+12:00
- Desc:
- the starting point for the HH tutorial example
- Permanent Source URI:
- https://models.fieldml.org/workspace/a1/rawfile/f6a8f90307388eb4b040ee3566b84d88b59247f7/dojo-presentation/js/dojo/dojox/form/_HasDropDown.js
dojo.provide("dojox.form._HasDropDown");
dojo.require("dijit._Widget");
dojo.declare("dojox.form._HasDropDown",
null,
{
// summary:
// Mixin for widgets that need drop down ability.
//
// dropDownNode: DomNode
// responds to click events. "dropDownNode" can be set via
// a dojoAttachPoint assignment. If missing, then either focusNode
// or domNode (if focusNode is also missing) will be used.
dropDownNode: null,
// popupStateNode: DomNode
// the node to set the popupActive class on. If missing, then
// focusNode or dropDownNode (if focusNode is missing) will be used.
popupStateNode: null,
// aroundNode: DomNode
// the node to display the popup around. If missing, then
// domNode will be used.
aroundNode: null,
// dropDown: Widget
// the widget to display in the popup. This widget *must* be
// defined before the startup function is called.
dropDown: null,
// autoWidth: 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,
// _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,
_onMenuMouseup: function(/*Event*/ e){
// summary: called with the mouseup event if the mouseup occurred
// over the menu. You can try and use this event in
// order to automatically execute your dropdown (as
// if it were clicked). You mightwant to close your menu
// as a part of this function.
},
_onDropDownMouse: function(/*Event*/ e){
// summary: callback when the user mouse clicks on menu popup node
//
// We handle mouse events using onmousedown in order to allow for
// selecting via a drag. So, our click is already handled, unless
// we are executed via keypress - in which case, this._seenKeydown
// will be set to true.
if(e.type == "click" && !this._seenKeydown){ return; }
this._seenKeydown = false;
// If we are a mouse event, set up the mouseup handler
if(e.type == "mousedown"){
this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseup");
}
if(this.disabled || this.readOnly){ return; }
if(this._stopClickEvents){
dojo.stopEvent(e);
}
this.toggleDropDown();
// If we are a click, then we'll pretend we did a mouse up
if(e.type == "click" || e.type == "keypress"){
this._onDropDownMouseup();
}
},
_onDropDownMouseup: function(/*Event?*/ e){
// summary: callback when the user lifts their mouse - if we are
// over the menu, we execute it, otherwise, we focus our
// dropDown node. If the event is missing, then we are not
// a mouseup event.
if(e && this._docHandler){
this.disconnect(this._docHandler);
}
var dropDown = this.dropDown, overMenu = false;
if(e && this._opened){
// Find out if our target is somewhere in our dropdown widget
var t = e.target;
while(t && !overMenu){
if(dojo.hasClass(t, "dijitPopup")){
overMenu = true;
}else{
t = t.parentNode;
}
}
if(overMenu){
this._onMenuMouseup(e);
return;
}
}
if(this._opened && dropDown.focus){
// delay so that we don't steal our own focus.
window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
}else{
dijit.focus(this.popupStateNode);
}
},
_setupDropdown: function(){
// summary:
// set up nodes and connect our mouse and keypress events
this.dropDownNode = this.dropDownNode || this.focusNode || this.domNode;
this.popupStateNode = this.popupStateNode || this.focusNode || this.dropDownNode;
this.aroundNode = this.aroundNode || this.domNode;
this.connect(this.dropDownNode, "onmousedown", "_onDropDownMouse");
this.connect(this.dropDownNode, "onclick", "_onDropDownMouse");
this.connect(this.dropDownNode, "onkeydown", "_onDropDownKeydown");
this.connect(this.dropDownNode, "onblur", "_onDropDownBlur");
this.connect(this.dropDownNode, "onkeypress", "_onKey");
// If we have a _setStateClass function (which happens when
// we are a form widget), then we need to connect our open/close
// functions to it
if(this._setStateClass){
this.connect(this, "openDropDown", "_setStateClass");
this.connect(this, "closeDropDown", "_setStateClass");
}
},
postCreate: function(){
this._setupDropdown();
this.inherited("postCreate", arguments);
},
startup: function(){
dijit.popup.prepare(this.dropDown.domNode);
this.inherited("startup", arguments);
},
destroyDescendants: function(){
if(this.dropDown){
this.dropDown.destroyRecursive();
delete this.dropDown;
}
this.inherited("destroyDescendants", arguments);
},
_onDropDownKeydown: function(/*Event*/ e){
this._seenKeydown = true;
},
_onKeyPress: function(/*Event*/ e){
if(this._opened && e.charOrCode == dojo.keys.ESCAPE && !e.shiftKey && !e.ctrlKey && !e.altKey){
this.toggleDropDown();
dojo.stopEvent(e);
return;
}
this.inherited(arguments);
},
_onDropDownBlur: function(/*Event*/ e){
this._seenKeydown = false;
},
_onKey: function(/*Event*/ e){
// summary: callback when the user presses a key on menu popup node
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();
return;
}
if(e.keyCode == dojo.keys.DOWN_ARROW){
this._onDropDownMouse(e);
}
},
_onBlur: function(){
// summary: called magically when focus has shifted away from this widget and it's dropdown
this.closeDropDown();
// don't focus on button. the user has explicitly focused on something else.
this.inherited("_onBlur", arguments);
},
isLoaded: function(){
// summary: returns whether or not the dropdown is loaded. This can
// be overridden in order to force a call to loadDropDown().
return true;
},
loadDropDown: function(/* Function */ loadCallback){
// summary: loads the data for the dropdown, and at some point, calls
// the given callback
loadCallback();
},
toggleDropDown: function(){
// summary: toggle the drop-down widget; if it is up, close it, if not, open it
if(this.disabled || this.readOnly){ return; }
this.focus();
var dropDown = this.dropDown;
if(!dropDown){ 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 - it returns the
// return value of dijit.popup.open
var dropDown = this.dropDown;
var oldWidth=dropDown.domNode.style.width;
var self = this;
var retVal = dijit.popup.open({
parent: this,
popup: dropDown,
around: this.aroundNode,
orient:
// TODO: add user-defined positioning option, like in Tooltip.js
this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
onExecute: function(){
self.closeDropDown(true);
},
onCancel: function(){
self.closeDropDown(true);
},
onClose: function(){
dropDown.domNode.style.width = oldWidth;
self.popupStateNode.removeAttribute("popupActive");
self._opened = false;
self.state = "";
}
});
if(this.autoWidth && this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
var adjustNode = null;
if(!this.isLeftToRight()){
adjustNode = dropDown.domNode.parentNode;
var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
}
// make menu at least as wide as the node
if(dropDown.resize){
dropDown.resize({w: this.domNode.offsetWidth});
}else{
dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
}
if(adjustNode){
adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
}
}
this.popupStateNode.setAttribute("popupActive", "true");
this._opened=true;
this.state="Opened";
if(dropDown.focus){
dropDown.focus();
}
// 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
if(this._opened){
dijit.popup.close(this.dropDown);
if(focus){ this.focus(); }
this._opened = false;
this.state = "";
}
}
}
);