_FormWidget.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.mobile.app._FormWidget"] = true;
  8. dojo.provide("dojox.mobile.app._FormWidget");
  9. dojo.experimental("dojox.mobile.app._FormWidget");
  10. dojo.require("dojo.window");
  11. dojo.require("dijit._WidgetBase");
  12. dojo.declare("dojox.mobile.app._FormWidget", dijit._WidgetBase, {
  13. // summary:
  14. // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
  15. // which can be children of a <form> node or a `dojox.mobile.app.Form` widget.
  16. //
  17. // description:
  18. // Represents a single HTML element.
  19. // All these widgets should have these attributes just like native HTML input elements.
  20. // You can set them during widget construction or afterwards, via `dijit._WidgetBase.attr`.
  21. //
  22. // They also share some common methods.
  23. // name: String
  24. // Name used when submitting form; same as "name" attribute or plain HTML elements
  25. name: "",
  26. // alt: String
  27. // Corresponds to the native HTML <input> element's attribute.
  28. alt: "",
  29. // value: String
  30. // Corresponds to the native HTML <input> element's attribute.
  31. value: "",
  32. // type: String
  33. // Corresponds to the native HTML <input> element's attribute.
  34. type: "text",
  35. // disabled: Boolean
  36. // Should this widget respond to user input?
  37. // In markup, this is specified as "disabled='disabled'", or just "disabled".
  38. disabled: false,
  39. // intermediateChanges: Boolean
  40. // Fires onChange for each value change or only on demand
  41. intermediateChanges: false,
  42. // scrollOnFocus: Boolean
  43. // On focus, should this widget scroll into view?
  44. scrollOnFocus: false,
  45. // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
  46. attributeMap: dojo.delegate(dijit._WidgetBase.prototype.attributeMap, {
  47. value: "focusNode",
  48. id: "focusNode",
  49. alt: "focusNode",
  50. title: "focusNode"
  51. }),
  52. postMixInProperties: function(){
  53. // Setup name=foo string to be referenced from the template (but only if a name has been specified)
  54. // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
  55. // Regarding escaping, see heading "Attribute values" in
  56. // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
  57. this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
  58. this.inherited(arguments);
  59. },
  60. postCreate: function(){
  61. this.inherited(arguments);
  62. this.connect(this.domNode, "onmousedown", "_onMouseDown");
  63. },
  64. _setDisabledAttr: function(/*Boolean*/ value){
  65. this.disabled = value;
  66. dojo.attr(this.focusNode, 'disabled', value);
  67. if(this.valueNode){
  68. dojo.attr(this.valueNode, 'disabled', value);
  69. }
  70. },
  71. _onFocus: function(e){
  72. if(this.scrollOnFocus){
  73. dojo.window.scrollIntoView(this.domNode);
  74. }
  75. this.inherited(arguments);
  76. },
  77. isFocusable: function(){
  78. // summary:
  79. // Tells if this widget is focusable or not. Used internally by dijit.
  80. // tags:
  81. // protected
  82. return !this.disabled && !this.readOnly
  83. && this.focusNode && (dojo.style(this.domNode, "display") != "none");
  84. },
  85. focus: function(){
  86. // summary:
  87. // Put focus on this widget
  88. this.focusNode.focus();
  89. },
  90. compare: function(/*anything*/val1, /*anything*/val2){
  91. // summary:
  92. // Compare 2 values (as returned by attr('value') for this widget).
  93. // tags:
  94. // protected
  95. if(typeof val1 == "number" && typeof val2 == "number"){
  96. return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
  97. }else if(val1 > val2){
  98. return 1;
  99. }else if(val1 < val2){
  100. return -1;
  101. }else{
  102. return 0;
  103. }
  104. },
  105. onChange: function(newValue){
  106. // summary:
  107. // Callback when this widget's value is changed.
  108. // tags:
  109. // callback
  110. },
  111. // _onChangeActive: [private] Boolean
  112. // Indicates that changes to the value should call onChange() callback.
  113. // This is false during widget initialization, to avoid calling onChange()
  114. // when the initial value is set.
  115. _onChangeActive: false,
  116. _handleOnChange: function(/*anything*/ newValue, /* Boolean? */ priorityChange){
  117. // summary:
  118. // Called when the value of the widget is set. Calls onChange() if appropriate
  119. // newValue:
  120. // the new value
  121. // priorityChange:
  122. // For a slider, for example, dragging the slider is priorityChange==false,
  123. // but on mouse up, it's priorityChange==true. If intermediateChanges==true,
  124. // onChange is only called form priorityChange=true events.
  125. // tags:
  126. // private
  127. this._lastValue = newValue;
  128. if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
  129. // this block executes not for a change, but during initialization,
  130. // and is used to store away the original value (or for ToggleButton, the original checked state)
  131. this._resetValue = this._lastValueReported = newValue;
  132. }
  133. if((this.intermediateChanges || priorityChange || priorityChange === undefined) &&
  134. ((typeof newValue != typeof this._lastValueReported) ||
  135. this.compare(newValue, this._lastValueReported) != 0)){
  136. this._lastValueReported = newValue;
  137. if(this._onChangeActive){
  138. if(this._onChangeHandle){
  139. clearTimeout(this._onChangeHandle);
  140. }
  141. // setTimout allows hidden value processing to run and
  142. // also the onChange handler can safely adjust focus, etc
  143. this._onChangeHandle = setTimeout(dojo.hitch(this,
  144. function(){
  145. this._onChangeHandle = null;
  146. this.onChange(newValue);
  147. }), 0); // try to collapse multiple onChange's fired faster than can be processed
  148. }
  149. }
  150. },
  151. create: function(){
  152. // Overrides _Widget.create()
  153. this.inherited(arguments);
  154. this._onChangeActive = true;
  155. },
  156. destroy: function(){
  157. if(this._onChangeHandle){ // destroy called before last onChange has fired
  158. clearTimeout(this._onChangeHandle);
  159. this.onChange(this._lastValueReported);
  160. }
  161. this.inherited(arguments);
  162. },
  163. _onMouseDown: function(e){
  164. // If user clicks on the button, even if the mouse is released outside of it,
  165. // this button should get focus (to mimics native browser buttons).
  166. // This is also needed on chrome because otherwise buttons won't get focus at all,
  167. // which leads to bizarre focus restore on Dialog close etc.
  168. if(this.isFocusable()){
  169. // Set a global event to handle mouseup, so it fires properly
  170. // even if the cursor leaves this.domNode before the mouse up event.
  171. var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
  172. if(this.isFocusable()){
  173. this.focus();
  174. }
  175. this.disconnect(mouseUpConnector);
  176. });
  177. }
  178. },
  179. selectInputText: function(/*DomNode*/element, /*Number?*/ start, /*Number?*/ stop){
  180. // summary:
  181. // Select text in the input element argument, from start (default 0), to stop (default end).
  182. // TODO: use functions in _editor/selection.js?
  183. var _window = dojo.global;
  184. var _document = dojo.doc;
  185. element = dojo.byId(element);
  186. if(isNaN(start)){ start = 0; }
  187. if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
  188. dijit.focus(element);
  189. if(_window["getSelection"] && element.setSelectionRange){
  190. element.setSelectionRange(start, stop);
  191. }
  192. }
  193. });
  194. dojo.declare("dojox.mobile.app._FormValueWidget", dojox.mobile.app._FormWidget,
  195. {
  196. // summary:
  197. // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
  198. // description:
  199. // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
  200. // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
  201. // works as expected.
  202. // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
  203. // directly in the template as read by the parser in order to function. IE is known to specifically
  204. // require the 'name' attribute at element creation time. See #8484, #8660.
  205. // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
  206. // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
  207. // Seems like we really want value removed from attributeMap altogether
  208. // (although there's no easy way to do that now)
  209. // readOnly: Boolean
  210. // Should this widget respond to user input?
  211. // In markup, this is specified as "readOnly".
  212. // Similar to disabled except readOnly form values are submitted.
  213. readOnly: false,
  214. attributeMap: dojo.delegate(dojox.mobile.app._FormWidget.prototype.attributeMap, {
  215. value: "",
  216. readOnly: "focusNode"
  217. }),
  218. _setReadOnlyAttr: function(/*Boolean*/ value){
  219. this.readOnly = value;
  220. dojo.attr(this.focusNode, 'readOnly', value);
  221. },
  222. postCreate: function(){
  223. this.inherited(arguments);
  224. // Update our reset value if it hasn't yet been set (because this.set()
  225. // is only called when there *is* a value)
  226. if(this._resetValue === undefined){
  227. this._resetValue = this.value;
  228. }
  229. },
  230. _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
  231. // summary:
  232. // Hook so attr('value', value) works.
  233. // description:
  234. // Sets the value of the widget.
  235. // If the value has changed, then fire onChange event, unless priorityChange
  236. // is specified as null (or false?)
  237. this.value = newValue;
  238. this._handleOnChange(newValue, priorityChange);
  239. },
  240. _getValueAttr: function(){
  241. // summary:
  242. // Hook so attr('value') works.
  243. return this._lastValue;
  244. },
  245. undo: function(){
  246. // summary:
  247. // Restore the value to the last value passed to onChange
  248. this._setValueAttr(this._lastValueReported, false);
  249. },
  250. reset: function(){
  251. // summary:
  252. // Reset the widget's value to what it was at initialization time
  253. this._hasBeenBlurred = false;
  254. this._setValueAttr(this._resetValue, true);
  255. }
  256. });
  257. }