ComboBox.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. define("dojox/mobile/ComboBox", [
  2. "dojo/_base/kernel",
  3. "dojo/_base/declare",
  4. "dojo/_base/lang",
  5. "dojo/_base/window",
  6. "dojo/dom-geometry",
  7. "dojo/dom-style",
  8. "dojo/window",
  9. "dijit/form/_AutoCompleterMixin",
  10. "dijit/popup",
  11. "./_ComboBoxMenu",
  12. "./TextBox",
  13. "./sniff"
  14. ], function(kernel, declare, lang, win, domGeometry, domStyle, windowUtils, AutoCompleterMixin, popup, ComboBoxMenu, TextBox, has){
  15. kernel.experimental("dojox.mobile.ComboBox"); // should be using a more native search-type UI
  16. /*=====
  17. TextBox = dojox.mobile.TextBox;
  18. AutoCompleterMixin = dijit.form._AutoCompleterMixin;
  19. =====*/
  20. return declare("dojox.mobile.ComboBox", [TextBox, AutoCompleterMixin], {
  21. // summary:
  22. // A non-templated auto-completing text box widget
  23. //
  24. // dropDownClass: [protected extension] String
  25. // Name of the dropdown widget class used to select a date/time.
  26. // Subclasses should specify this.
  27. dropDownClass: "dojox.mobile._ComboBoxMenu",
  28. // initially disable selection since iphone displays selection handles that makes it hard to pick from the list
  29. selectOnClick: false,
  30. autoComplete: false,
  31. // dropDown: [protected] Widget
  32. // The widget to display as a popup. This widget *must* be
  33. // defined before the startup function is called.
  34. dropDown: null,
  35. // maxHeight: [protected] Integer
  36. // The max height for our dropdown.
  37. // Any dropdown taller than this will have scrollbars.
  38. // Set to -1 to limit height to available space in viewport
  39. maxHeight: -1,
  40. // dropDownPosition: [const] String[]
  41. // This variable controls the position of the drop down.
  42. // It's an array of strings with the following values:
  43. //
  44. // * before: places drop down to the left of the target node/widget, or to the right in
  45. // the case of RTL scripts like Hebrew and Arabic
  46. // * after: places drop down to the right of the target node/widget, or to the left in
  47. // the case of RTL scripts like Hebrew and Arabic
  48. // * above: drop down goes above target node
  49. // * below: drop down goes below target node
  50. //
  51. // The list is positions is tried, in order, until a position is found where the drop down fits
  52. // within the viewport.
  53. //
  54. dropDownPosition: ["below","above"],
  55. _throttleOpenClose: function(){
  56. // prevent open/close in rapid succession
  57. if(this._throttleHandler){
  58. clearTimeout(this._throttleHandler);
  59. }
  60. this._throttleHandler = setTimeout(lang.hitch(this, function(){ this._throttleHandler = null; }), 500);
  61. },
  62. _onFocus: function(){
  63. this.inherited(arguments);
  64. if(!this._opened && !this._throttleHandler){
  65. this._startSearchAll(); // show dropdown if user is selecting Next/Previous from virtual keyboard
  66. }
  67. },
  68. onInput: function(e){
  69. this._onKey(e);
  70. this.inherited(arguments);
  71. },
  72. _setListAttr: function(v){
  73. this._set('list', v); // needed for Firefox 4+ to prevent HTML5 mode
  74. },
  75. closeDropDown: function(){
  76. // summary:
  77. // Closes the drop down on this widget
  78. // tags:
  79. // protected
  80. this._throttleOpenClose();
  81. if(this.startHandler){
  82. this.disconnect(this.startHandler);
  83. this.startHandler = null;
  84. if(this.moveHandler){ this.disconnect(this.moveHandler); }
  85. if(this.endHandler){ this.disconnect(this.endHandler); }
  86. }
  87. this.inherited(arguments);
  88. popup.close(this.dropDown);
  89. this._opened = false;
  90. },
  91. openDropDown: function(){
  92. // summary:
  93. // Opens the dropdown for this widget. To be called only when this.dropDown
  94. // has been created and is ready to display (ie, it's data is loaded).
  95. // returns:
  96. // return value of popup.open()
  97. // tags:
  98. // protected
  99. var wasClosed = !this._opened;
  100. var dropDown = this.dropDown,
  101. ddNode = dropDown.domNode,
  102. aroundNode = this.domNode,
  103. self = this;
  104. // TODO: isn't maxHeight dependent on the return value from popup.open(),
  105. // ie, dependent on how much space is available (BK)
  106. if(!this._preparedNode){
  107. this._preparedNode = true;
  108. // Check if we have explicitly set width and height on the dropdown widget dom node
  109. if(ddNode.style.width){
  110. this._explicitDDWidth = true;
  111. }
  112. if(ddNode.style.height){
  113. this._explicitDDHeight = true;
  114. }
  115. }
  116. // Code for resizing dropdown (height limitation, or increasing width to match my width)
  117. var myStyle = {
  118. display: "",
  119. overflow: "hidden",
  120. visibility: "hidden"
  121. };
  122. if(!this._explicitDDWidth){
  123. myStyle.width = "";
  124. }
  125. if(!this._explicitDDHeight){
  126. myStyle.height = "";
  127. }
  128. domStyle.set(ddNode, myStyle);
  129. // Figure out maximum height allowed (if there is a height restriction)
  130. var maxHeight = this.maxHeight;
  131. if(maxHeight == -1){
  132. // limit height to space available in viewport either above or below my domNode
  133. // (whichever side has more room)
  134. var viewport = windowUtils.getBox(),
  135. position = domGeometry.position(aroundNode, false);
  136. maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
  137. }
  138. // Attach dropDown to DOM and make make visibility:hidden rather than display:none
  139. // so we call startup() and also get the size
  140. popup.moveOffScreen(dropDown);
  141. if(dropDown.startup && !dropDown._started){
  142. dropDown.startup(); // this has to be done after being added to the DOM
  143. }
  144. // Get size of drop down, and determine if vertical scroll bar needed
  145. var mb = domGeometry.position(this.dropDown.containerNode, false);
  146. var overHeight = (maxHeight && mb.h > maxHeight);
  147. if(overHeight){
  148. mb.h = maxHeight;
  149. }
  150. // Adjust dropdown width to match or be larger than my width
  151. mb.w = Math.max(mb.w, aroundNode.offsetWidth);
  152. domGeometry.setMarginBox(ddNode, mb);
  153. var retVal = popup.open({
  154. parent: this,
  155. popup: dropDown,
  156. around: aroundNode,
  157. orient: this.dropDownPosition,
  158. onExecute: function(){
  159. self.closeDropDown();
  160. },
  161. onCancel: function(){
  162. self.closeDropDown();
  163. },
  164. onClose: function(){
  165. self._opened = false;
  166. }
  167. });
  168. this._opened=true;
  169. if(wasClosed){
  170. if(retVal.aroundCorner.charAt(0) == 'B'){ // is popup below?
  171. this.domNode.scrollIntoView(true); // scroll to top
  172. }
  173. this.startHandler = this.connect(win.doc.documentElement, has("touch") ? "ontouchstart" : "onmousedown",
  174. lang.hitch(this, function(){
  175. var isMove = false;
  176. this.moveHandler = this.connect(win.doc.documentElement, has("touch") ? "ontouchmove" : "onmousemove", function(){ isMove = true; });
  177. this.endHandler = this.connect(win.doc.documentElement, has("touch") ? "ontouchend" : "onmouseup", function(){ if(!isMove){ this.closeDropDown(); } });
  178. })
  179. );
  180. }
  181. return retVal;
  182. },
  183. postCreate: function(){
  184. this.inherited(arguments);
  185. this.connect(this.domNode, "onclick", "_onClick");
  186. },
  187. _onClick: function(/*Event*/ e){
  188. // throttle clicks to prevent double click from doing double actions
  189. if(!this._throttleHandler){
  190. if(this.opened){
  191. this.closeDropDown();
  192. }else{
  193. this._startSearchAll();
  194. }
  195. }
  196. }
  197. });
  198. });