dijit/_HasDropDown.js

  • Provides:

    • dijit._HasDropDown
  • dijit._HasDropDown

    • type
      Function
    • summary
      Mixin for widgets that need drop down ability.
  • dijit._HasDropDown._buttonNode

    • tags: protected
    • type
      DomNode
    • summary
      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.
  • dijit._HasDropDown._arrowWrapperNode

    • tags: protected
    • type
      DomNode
    • summary
      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.
  • dijit._HasDropDown._popupStateNode

    • tags: protected
    • type
      DomNode
    • summary
      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.
  • dijit._HasDropDown._aroundNode

    • tags: protected
    • type
      DomNode
    • summary
      The node to display the popup around.
      Can be set via a dojoAttachPoint assignment.
      If missing, then domNode will be used.
  • dijit._HasDropDown.dropDown

    • tags: protected
    • type
      Widget
    • summary
      The widget to display as a popup.  This widget *must* be
      defined before the startup function is called.
  • dijit._HasDropDown.autoWidth

    • tags: protected
    • type
      Boolean
    • summary
      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
  • dijit._HasDropDown.forceWidth

    • tags: protected
    • type
      Boolean
    • summary
      Set to true to make the drop down exactly as wide as this
      widget.  Overrides autoWidth.
  • dijit._HasDropDown.maxHeight

    • tags: protected
    • type
      Integer
    • summary
      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
  • dijit._HasDropDown.dropDownPosition

    • tags: const
    • type
      String[
    • summary
      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.
  • dijit._HasDropDown._stopClickEvents

    • type
      Boolean
    • summary
      When set to false, the click events will not be stopped, in
      case you want to use them in your subwidget
  • dijit._HasDropDown._onDropDownMouseDown

    • type
      Function
    • parameters:
      • e: (typeof Event)
    • source: [view]
         if(this.disabled || this.readOnly){ return; }


         dojo.stopEvent(e);


         this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");


         this.toggleDropDown();
    • summary
      Callback when the user mousedown's on the arrow icon
  • dijit._HasDropDown._onDropDownMouseUp

    • type
      Function
    • parameters:
      • e: (typeof Event)
    • source: [view]
         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);
         }
    • 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 &lt;select&gt; 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.
  • dijit._HasDropDown._onDropDownClick

    • type
      Function
    • parameters:
      • e: (typeof Event)
    • source: [view]
      define("dijit/_HasDropDown", ["dojo", "dijit", "dijit/_Widget"], function(dojo, dijit) {


      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 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 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);
    • summary
  • dijit._HasDropDown.isLoaded

    • type
      Function
    • source: [view]
         return true;
    • summary
      Returns whether or not the dropdown is loaded.  This can
      be overridden in order to force a call to loadDropDown().
    • tags:
  • dijit._HasDropDown.loadDropDown

    • type
      Function
    • parameters:
      • loadCallback: (typeof Function)
    • source: [view]
         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:
  • dijit._HasDropDown.toggleDropDown

    • type
      Function
    • source: [view]
         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();
         }
    • 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:
  • dijit._HasDropDown.openDropDown

    • type
      Function
    • source: [view]
         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;
    • 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).
    • return_summary
      return value of dijit.popup.open()
    • tags:
  • dijit._HasDropDown.closeDropDown

    • type
      Function
    • parameters:
      • focus: (typeof Boolean)
        If true, refocuses the button widget
    • source: [view]
         if(this._opened){
          if(focus){ this.focus(); }
          dijit.popup.close(this.dropDown);
          this._opened = false;
         }
    • summary
      Closes the drop down on this widget
    • tags:
  • dijit._HasDropDown._docHandler

    • summary
  • dijit._HasDropDown._toggleOnKeyUp

    • summary
  • dijit._HasDropDown._preparedNode

    • summary
  • dijit._HasDropDown._explicitDDWidth

    • summary
  • dijit._HasDropDown._explicitDDHeight

    • summary
  • dijit._HasDropDown._opened

    • summary
  • dijit

    • type
      Object
    • summary