Select.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit.form.Select"] = true;
  8. dojo.provide("dijit.form.Select");
  9. dojo.require("dijit.form._FormSelectWidget");
  10. dojo.require("dijit._HasDropDown");
  11. dojo.require("dijit.Menu");
  12. dojo.require("dijit.Tooltip");
  13. dojo.requireLocalization("dijit.form", "validate", null, "ROOT,ar,az,bg,ca,cs,da,de,el,es,fi,fr,he,hr,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
  14. dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
  15. // summary:
  16. // An internally-used menu for dropdown that allows us a vertical scrollbar
  17. buildRendering: function(){
  18. // summary:
  19. // Stub in our own changes, so that our domNode is not a table
  20. // otherwise, we won't respond correctly to heights/overflows
  21. this.inherited(arguments);
  22. var o = (this.menuTableNode = this.domNode);
  23. var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
  24. if(o.parentNode){
  25. o.parentNode.replaceChild(n, o);
  26. }
  27. dojo.removeClass(o, "dijitMenuTable");
  28. n.className = o.className + " dijitSelectMenu";
  29. o.className = "dijitReset dijitMenuTable";
  30. dijit.setWaiRole(o,"listbox");
  31. dijit.setWaiRole(n,"presentation");
  32. n.appendChild(o);
  33. },
  34. postCreate: function(){
  35. // summary:
  36. // stop mousemove from selecting text on IE to be consistent with other browsers
  37. this.inherited(arguments);
  38. this.connect(this.domNode, "onmousemove", dojo.stopEvent);
  39. },
  40. resize: function(/*Object*/ mb){
  41. // summary:
  42. // Overridden so that we are able to handle resizing our
  43. // internal widget. Note that this is not a "full" resize
  44. // implementation - it only works correctly if you pass it a
  45. // marginBox.
  46. //
  47. // mb: Object
  48. // The margin box to set this dropdown to.
  49. if(mb){
  50. dojo.marginBox(this.domNode, mb);
  51. if("w" in mb){
  52. // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
  53. // 100% is safer than a pixel value because there may be a scroll bar with
  54. // browser/OS specific width.
  55. this.menuTableNode.style.width = "100%";
  56. }
  57. }
  58. }
  59. });
  60. dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
  61. // summary:
  62. // This is a "styleable" select box - it is basically a DropDownButton which
  63. // can take a <select> as its input.
  64. baseClass: "dijitSelect",
  65. templateString: dojo.cache("dijit.form", "templates/Select.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdojoAttachPoint=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" dojoAttachPoint=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} dojoAttachPoint=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"),
  66. // attributeMap: Object
  67. // Add in our style to be applied to the focus node
  68. attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
  69. // required: Boolean
  70. // Can be true or false, default is false.
  71. required: false,
  72. // state: String
  73. // Shows current state (ie, validation result) of input (Normal, Warning, or Error)
  74. state: "",
  75. // message: String
  76. // Currently displayed error/prompt message
  77. message: "",
  78. // tooltipPosition: String[]
  79. // See description of dijit.Tooltip.defaultPosition for details on this parameter.
  80. tooltipPosition: [],
  81. // emptyLabel: string
  82. // What to display in an "empty" dropdown
  83. emptyLabel: "&nbsp;",
  84. // _isLoaded: Boolean
  85. // Whether or not we have been loaded
  86. _isLoaded: false,
  87. // _childrenLoaded: Boolean
  88. // Whether or not our children have been loaded
  89. _childrenLoaded: false,
  90. _fillContent: function(){
  91. // summary:
  92. // Set the value to be the first, or the selected index
  93. this.inherited(arguments);
  94. // set value from selected option
  95. if(this.options.length && !this.value && this.srcNodeRef){
  96. var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
  97. this.value = this.options[si >= 0 ? si : 0].value;
  98. }
  99. // Create the dropDown widget
  100. this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
  101. dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
  102. },
  103. _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
  104. // summary:
  105. // For the given option, return the menu item that should be
  106. // used to display it. This can be overridden as needed
  107. if(!option.value && !option.label){
  108. // We are a separator (no label set for it)
  109. return new dijit.MenuSeparator();
  110. }else{
  111. // Just a regular menu option
  112. var click = dojo.hitch(this, "_setValueAttr", option);
  113. var item = new dijit.MenuItem({
  114. option: option,
  115. label: option.label || this.emptyLabel,
  116. onClick: click,
  117. disabled: option.disabled || false
  118. });
  119. dijit.setWaiRole(item.focusNode, "listitem");
  120. return item;
  121. }
  122. },
  123. _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
  124. // summary:
  125. // For the given option, add an option to our dropdown.
  126. // If the option doesn't have a value, then a separator is added
  127. // in that place.
  128. if(this.dropDown){
  129. this.dropDown.addChild(this._getMenuItemForOption(option));
  130. }
  131. },
  132. _getChildren: function(){
  133. if(!this.dropDown){
  134. return [];
  135. }
  136. return this.dropDown.getChildren();
  137. },
  138. _loadChildren: function(/*Boolean*/ loadMenuItems){
  139. // summary:
  140. // Resets the menu and the length attribute of the button - and
  141. // ensures that the label is appropriately set.
  142. // loadMenuItems: Boolean
  143. // actually loads the child menu items - we only do this when we are
  144. // populating for showing the dropdown.
  145. if(loadMenuItems === true){
  146. // this.inherited destroys this.dropDown's child widgets (MenuItems).
  147. // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
  148. // issues later in _setSelected). (see #10296)
  149. if(this.dropDown){
  150. delete this.dropDown.focusedChild;
  151. }
  152. if(this.options.length){
  153. this.inherited(arguments);
  154. }else{
  155. // Drop down menu is blank but add one blank entry just so something appears on the screen
  156. // to let users know that they are no choices (mimicing native select behavior)
  157. dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
  158. var item = new dijit.MenuItem({label: "&nbsp;"});
  159. this.dropDown.addChild(item);
  160. }
  161. }else{
  162. this._updateSelection();
  163. }
  164. this._isLoaded = false;
  165. this._childrenLoaded = true;
  166. if(!this._loadingStore){
  167. // Don't call this if we are loading - since we will handle it later
  168. this._setValueAttr(this.value);
  169. }
  170. },
  171. _setValueAttr: function(value){
  172. this.inherited(arguments);
  173. dojo.attr(this.valueNode, "value", this.get("value"));
  174. },
  175. _setDisplay: function(/*String*/ newDisplay){
  176. // summary:
  177. // sets the display for the given value (or values)
  178. var lbl = newDisplay || this.emptyLabel;
  179. this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
  180. dijit.setWaiState(this.focusNode, "valuetext", lbl);
  181. },
  182. validate: function(/*Boolean*/ isFocused){
  183. // summary:
  184. // Called by oninit, onblur, and onkeypress.
  185. // description:
  186. // Show missing or invalid messages if appropriate, and highlight textbox field.
  187. // Used when a select is initially set to no value and the user is required to
  188. // set the value.
  189. var isValid = this.isValid(isFocused);
  190. this._set("state", isValid ? "" : "Error");
  191. dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
  192. var message = isValid ? "" : this._missingMsg;
  193. if(this.message !== message){
  194. this._set("message", message);
  195. dijit.hideTooltip(this.domNode);
  196. if(message){
  197. dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
  198. }
  199. }
  200. return isValid;
  201. },
  202. isValid: function(/*Boolean*/ isFocused){
  203. // summary:
  204. // Whether or not this is a valid value. The only way a Select
  205. // can be invalid is when it's required but nothing is selected.
  206. return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
  207. },
  208. reset: function(){
  209. // summary:
  210. // Overridden so that the state will be cleared.
  211. this.inherited(arguments);
  212. dijit.hideTooltip(this.domNode);
  213. this._set("state", "");
  214. this._set("message", "")
  215. },
  216. postMixInProperties: function(){
  217. // summary:
  218. // set the missing message
  219. this.inherited(arguments);
  220. this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
  221. this.lang).missingMessage;
  222. },
  223. postCreate: function(){
  224. // summary:
  225. // stop mousemove from selecting text on IE to be consistent with other browsers
  226. this.inherited(arguments);
  227. this.connect(this.domNode, "onmousemove", dojo.stopEvent);
  228. },
  229. _setStyleAttr: function(/*String||Object*/ value){
  230. this.inherited(arguments);
  231. dojo.toggleClass(this.domNode, this.baseClass + "FixedWidth", !!this.tableNode.style.width);
  232. },
  233. isLoaded: function(){
  234. return this._isLoaded;
  235. },
  236. loadDropDown: function(/*Function*/ loadCallback){
  237. // summary:
  238. // populates the menu
  239. this._loadChildren(true);
  240. this._isLoaded = true;
  241. loadCallback();
  242. },
  243. closeDropDown: function(){
  244. // overriding _HasDropDown.closeDropDown()
  245. this.inherited(arguments);
  246. if(this.dropDown && this.dropDown.menuTableNode){
  247. // Erase possible width: 100% setting from _SelectMenu.resize().
  248. // Leaving it would interfere with the next openDropDown() call, which
  249. // queries the natural size of the drop down.
  250. this.dropDown.menuTableNode.style.width = "";
  251. }
  252. },
  253. uninitialize: function(preserveDom){
  254. if(this.dropDown && !this.dropDown._destroyed){
  255. this.dropDown.destroyRecursive(preserveDom);
  256. delete this.dropDown;
  257. }
  258. this.inherited(arguments);
  259. }
  260. });
  261. }