_FormWidget.js 9.6 KB

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