TextBox.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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["dojox.mobile.app.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.mobile.app.TextBox"] = true;
  8. dojo.provide("dojox.mobile.app.TextBox");
  9. dojo.experimental("dojox.mobile.app.TextBox");
  10. dojo.require("dojox.mobile.app._Widget");
  11. dojo.require("dojox.mobile.app._FormWidget");
  12. dojo.declare(
  13. "dojox.mobile.app.TextBox",
  14. dojox.mobile.app._FormValueWidget, {
  15. // summary:
  16. // A base class for textbox form inputs
  17. // trim: Boolean
  18. // Removes leading and trailing whitespace if true. Default is false.
  19. trim: false,
  20. // uppercase: Boolean
  21. // Converts all characters to uppercase if true. Default is false.
  22. uppercase: false,
  23. // lowercase: Boolean
  24. // Converts all characters to lowercase if true. Default is false.
  25. lowercase: false,
  26. // propercase: Boolean
  27. // Converts the first character of each word to uppercase if true.
  28. propercase: false,
  29. // maxLength: String
  30. // HTML INPUT tag maxLength declaration.
  31. maxLength: "",
  32. // selectOnClick: [const] Boolean
  33. // If true, all text will be selected when focused with mouse
  34. selectOnClick: false,
  35. // placeHolder: String
  36. // Defines a hint to help users fill out the input field (as defined in HTML 5).
  37. // This should only contain plain text (no html markup).
  38. placeHolder: "",
  39. baseClass: "mblTextBox",
  40. attributeMap: dojo.delegate(dojox.mobile.app._FormValueWidget.prototype.attributeMap, {
  41. maxLength: "focusNode"
  42. }),
  43. buildRendering: function(){
  44. var node = this.srcNodeRef;
  45. // If an input is used as the source node, wrap it in a div
  46. if(!node || node.tagName != "INPUT"){
  47. node = dojo.create("input", {});
  48. }
  49. dojo.attr(node, {
  50. type: "text",
  51. value: dojo.attr(node, "value") || "",
  52. placeholder: this.placeHolder || null
  53. });
  54. this.domNode = this.textbox = this.focusNode = node;
  55. },
  56. _setPlaceHolderAttr: function(v){
  57. this.placeHolder = v;
  58. if(this.textbox){
  59. dojo.attr(this.textbox, "placeholder", v);
  60. }
  61. },
  62. _getValueAttr: function(){
  63. // summary:
  64. // Hook so attr('value') works as we like.
  65. // description:
  66. // For `dijit.form.TextBox` this basically returns the value of the <input>.
  67. //
  68. // For `dijit.form.MappedTextBox` subclasses, which have both
  69. // a "displayed value" and a separate "submit value",
  70. // This treats the "displayed value" as the master value, computing the
  71. // submit value from it via this.parse().
  72. return this.parse(this.get('displayedValue'), this.constraints);
  73. },
  74. _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  75. // summary:
  76. // Hook so attr('value', ...) works.
  77. //
  78. // description:
  79. // Sets the value of the widget to "value" which can be of
  80. // any type as determined by the widget.
  81. //
  82. // value:
  83. // The visual element value is also set to a corresponding,
  84. // but not necessarily the same, value.
  85. //
  86. // formattedValue:
  87. // If specified, used to set the visual element value,
  88. // otherwise a computed visual value is used.
  89. //
  90. // priorityChange:
  91. // If true, an onChange event is fired immediately instead of
  92. // waiting for the next blur event.
  93. var filteredValue;
  94. if(value !== undefined){
  95. // TODO: this is calling filter() on both the display value and the actual value.
  96. // I added a comment to the filter() definition about this, but it should be changed.
  97. filteredValue = this.filter(value);
  98. if(typeof formattedValue != "string"){
  99. if(filteredValue !== null
  100. && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
  101. formattedValue = this.filter(this.format(filteredValue, this.constraints));
  102. }else{ formattedValue = ''; }
  103. }
  104. }
  105. if(formattedValue != null && formattedValue != undefined
  106. && ((typeof formattedValue) != "number" || !isNaN(formattedValue))
  107. && this.textbox.value != formattedValue){
  108. this.textbox.value = formattedValue;
  109. }
  110. this.inherited(arguments, [filteredValue, priorityChange]);
  111. },
  112. // displayedValue: String
  113. // For subclasses like ComboBox where the displayed value
  114. // (ex: Kentucky) and the serialized value (ex: KY) are different,
  115. // this represents the displayed value.
  116. //
  117. // Setting 'displayedValue' through attr('displayedValue', ...)
  118. // updates 'value', and vice-versa. Otherwise 'value' is updated
  119. // from 'displayedValue' periodically, like onBlur etc.
  120. //
  121. // TODO: move declaration to MappedTextBox?
  122. // Problem is that ComboBox references displayedValue,
  123. // for benefit of FilteringSelect.
  124. displayedValue: "",
  125. _getDisplayedValueAttr: function(){
  126. // summary:
  127. // Hook so attr('displayedValue') works.
  128. // description:
  129. // Returns the displayed value (what the user sees on the screen),
  130. // after filtering (ie, trimming spaces etc.).
  131. //
  132. // For some subclasses of TextBox (like ComboBox), the displayed value
  133. // is different from the serialized value that's actually
  134. // sent to the server (see dijit.form.ValidationTextBox.serialize)
  135. return this.filter(this.textbox.value);
  136. },
  137. _setDisplayedValueAttr: function(/*String*/value){
  138. // summary:
  139. // Hook so attr('displayedValue', ...) works.
  140. // description:
  141. // Sets the value of the visual element to the string "value".
  142. // The widget value is also set to a corresponding,
  143. // but not necessarily the same, value.
  144. if(value === null || value === undefined){ value = '' }
  145. else if(typeof value != "string"){ value = String(value) }
  146. this.textbox.value = value;
  147. this._setValueAttr(this.get('value'), undefined, value);
  148. },
  149. format: function(/* String */ value, /* Object */ constraints){
  150. // summary:
  151. // Replacable function to convert a value to a properly formatted string.
  152. // tags:
  153. // protected extension
  154. return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
  155. },
  156. parse: function(/* String */ value, /* Object */ constraints){
  157. // summary:
  158. // Replacable function to convert a formatted string to a value
  159. // tags:
  160. // protected extension
  161. return value; // String
  162. },
  163. _refreshState: function(){
  164. // summary:
  165. // After the user types some characters, etc., this method is
  166. // called to check the field for validity etc. The base method
  167. // in `dijit.form.TextBox` does nothing, but subclasses override.
  168. // tags:
  169. // protected
  170. },
  171. _onInput: function(e){
  172. if(e && e.type && /key/i.test(e.type) && e.keyCode){
  173. switch(e.keyCode){
  174. case dojo.keys.SHIFT:
  175. case dojo.keys.ALT:
  176. case dojo.keys.CTRL:
  177. case dojo.keys.TAB:
  178. return;
  179. }
  180. }
  181. if(this.intermediateChanges){
  182. var _this = this;
  183. // the setTimeout allows the key to post to the widget input box
  184. setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
  185. }
  186. this._refreshState();
  187. },
  188. postCreate: function(){
  189. // setting the value here is needed since value="" in the template causes "undefined"
  190. // and setting in the DOM (instead of the JS object) helps with form reset actions
  191. this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values shuld be the same
  192. this.inherited(arguments);
  193. if(dojo.isMoz || dojo.isOpera){
  194. this.connect(this.textbox, "oninput", this._onInput);
  195. }else{
  196. this.connect(this.textbox, "onkeydown", this._onInput);
  197. this.connect(this.textbox, "onkeyup", this._onInput);
  198. this.connect(this.textbox, "onpaste", this._onInput);
  199. this.connect(this.textbox, "oncut", this._onInput);
  200. }
  201. },
  202. _blankValue: '', // if the textbox is blank, what value should be reported
  203. filter: function(val){
  204. // summary:
  205. // Auto-corrections (such as trimming) that are applied to textbox
  206. // value on blur or form submit.
  207. // description:
  208. // For MappedTextBox subclasses, this is called twice
  209. // - once with the display value
  210. // - once the value as set/returned by attr('value', ...)
  211. // and attr('value'), ex: a Number for NumberTextBox.
  212. //
  213. // In the latter case it does corrections like converting null to NaN. In
  214. // the former case the NumberTextBox.filter() method calls this.inherited()
  215. // to execute standard trimming code in TextBox.filter().
  216. //
  217. // TODO: break this into two methods in 2.0
  218. //
  219. // tags:
  220. // protected extension
  221. if(val === null){ return this._blankValue; }
  222. if(typeof val != "string"){ return val; }
  223. if(this.trim){
  224. val = dojo.trim(val);
  225. }
  226. if(this.uppercase){
  227. val = val.toUpperCase();
  228. }
  229. if(this.lowercase){
  230. val = val.toLowerCase();
  231. }
  232. if(this.propercase){
  233. val = val.replace(/[^\s]+/g, function(word){
  234. return word.substring(0,1).toUpperCase() + word.substring(1);
  235. });
  236. }
  237. return val;
  238. },
  239. _setBlurValue: function(){
  240. this._setValueAttr(this.get('value'), true);
  241. },
  242. _onBlur: function(e){
  243. if(this.disabled){ return; }
  244. this._setBlurValue();
  245. this.inherited(arguments);
  246. if(this._selectOnClickHandle){
  247. this.disconnect(this._selectOnClickHandle);
  248. }
  249. if(this.selectOnClick && dojo.isMoz){
  250. this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
  251. }
  252. },
  253. _onFocus: function(/*String*/ by){
  254. if(this.disabled || this.readOnly){ return; }
  255. // Select all text on focus via click if nothing already selected.
  256. // Since mouse-up will clear the selection need to defer selection until after mouse-up.
  257. // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
  258. if(this.selectOnClick && by == "mouse"){
  259. this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
  260. // Only select all text on first click; otherwise users would have no way to clear
  261. // the selection.
  262. this.disconnect(this._selectOnClickHandle);
  263. // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
  264. // and if not, then select all the text
  265. var textIsNotSelected;
  266. textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
  267. if(textIsNotSelected){
  268. this.selectInputText(this.textbox);
  269. }
  270. });
  271. }
  272. this._refreshState();
  273. this.inherited(arguments);
  274. },
  275. reset: function(){
  276. // Overrides dijit._FormWidget.reset().
  277. // Additionally resets the displayed textbox value to ''
  278. this.textbox.value = '';
  279. this.inherited(arguments);
  280. }
  281. }
  282. );
  283. }