_FormWidgetMixin.js.uncompressed.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. define("dijit/form/_FormWidgetMixin", [
  2. "dojo/_base/array", // array.forEach
  3. "dojo/_base/declare", // declare
  4. "dojo/dom-attr", // domAttr.set
  5. "dojo/dom-style", // domStyle.get
  6. "dojo/_base/lang", // lang.hitch lang.isArray
  7. "dojo/mouse", // mouse.isLeft
  8. "dojo/sniff", // has("webkit")
  9. "dojo/window", // winUtils.scrollIntoView
  10. "../a11y" // a11y.hasDefaultTabStop
  11. ], function(array, declare, domAttr, domStyle, lang, mouse, has, winUtils, a11y){
  12. // module:
  13. // dijit/form/_FormWidgetMixin
  14. return declare("dijit.form._FormWidgetMixin", null, {
  15. // summary:
  16. // Mixin for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
  17. // which can be children of a `<form>` node or a `dijit/form/Form` widget.
  18. //
  19. // description:
  20. // Represents a single HTML element.
  21. // All these widgets should have these attributes just like native HTML input elements.
  22. // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
  23. //
  24. // They also share some common methods.
  25. // name: [const] String
  26. // Name used when submitting form; same as "name" attribute or plain HTML elements
  27. name: "",
  28. // alt: String
  29. // Corresponds to the native HTML `<input>` element's attribute.
  30. alt: "",
  31. // value: String
  32. // Corresponds to the native HTML `<input>` element's attribute.
  33. value: "",
  34. // type: [const] String
  35. // Corresponds to the native HTML `<input>` element's attribute.
  36. type: "text",
  37. // type: String
  38. // Apply aria-label in markup to the widget's focusNode
  39. "aria-label": "focusNode",
  40. // tabIndex: String
  41. // Order fields are traversed when user hits the tab key
  42. tabIndex: "0",
  43. _setTabIndexAttr: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
  44. // disabled: Boolean
  45. // Should this widget respond to user input?
  46. // In markup, this is specified as "disabled='disabled'", or just "disabled".
  47. disabled: false,
  48. // intermediateChanges: Boolean
  49. // Fires onChange for each value change or only on demand
  50. intermediateChanges: false,
  51. // scrollOnFocus: Boolean
  52. // On focus, should this widget scroll into view?
  53. scrollOnFocus: true,
  54. // Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
  55. // works with screen reader
  56. _setIdAttr: "focusNode",
  57. _setDisabledAttr: function(/*Boolean*/ value){
  58. this._set("disabled", value);
  59. domAttr.set(this.focusNode, 'disabled', value);
  60. if(this.valueNode){
  61. domAttr.set(this.valueNode, 'disabled', value);
  62. }
  63. this.focusNode.setAttribute("aria-disabled", value ? "true" : "false");
  64. if(value){
  65. // reset these, because after the domNode is disabled, we can no longer receive
  66. // mouse related events, see #4200
  67. this._set("hovering", false);
  68. this._set("active", false);
  69. // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
  70. var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex :
  71. ("_setTabIndexAttr" in this) ? this._setTabIndexAttr : "focusNode";
  72. array.forEach(lang.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
  73. var node = this[attachPointName];
  74. // complex code because tabIndex=-1 on a <div> doesn't work on FF
  75. if(has("webkit") || a11y.hasDefaultTabStop(node)){ // see #11064 about webkit bug
  76. node.setAttribute('tabIndex', "-1");
  77. }else{
  78. node.removeAttribute('tabIndex');
  79. }
  80. }, this);
  81. }else{
  82. if(this.tabIndex != ""){
  83. this.set('tabIndex', this.tabIndex);
  84. }
  85. }
  86. },
  87. _onFocus: function(/*String*/ by){
  88. // If user clicks on the widget, even if the mouse is released outside of it,
  89. // this widget's focusNode should get focus (to mimic native browser hehavior).
  90. // Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
  91. if(by == "mouse" && this.isFocusable()){
  92. // IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
  93. var focusConnector = this.connect(this.focusNode, "onfocus", function(){
  94. this.disconnect(mouseUpConnector);
  95. this.disconnect(focusConnector);
  96. });
  97. // Set a global event to handle mouseup, so it fires properly
  98. // even if the cursor leaves this.domNode before the mouse up event.
  99. var mouseUpConnector = this.connect(this.ownerDocumentBody, "onmouseup", function(){
  100. this.disconnect(mouseUpConnector);
  101. this.disconnect(focusConnector);
  102. // if here, then the mousedown did not focus the focusNode as the default action
  103. if(this.focused){
  104. this.focus();
  105. }
  106. });
  107. }
  108. if(this.scrollOnFocus){
  109. this.defer(function(){ winUtils.scrollIntoView(this.domNode); }); // without defer, the input caret position can change on mouse click
  110. }
  111. this.inherited(arguments);
  112. },
  113. isFocusable: function(){
  114. // summary:
  115. // Tells if this widget is focusable or not. Used internally by dijit.
  116. // tags:
  117. // protected
  118. return !this.disabled && this.focusNode && (domStyle.get(this.domNode, "display") != "none");
  119. },
  120. focus: function(){
  121. // summary:
  122. // Put focus on this widget
  123. if(!this.disabled && this.focusNode.focus){
  124. try{ this.focusNode.focus(); }catch(e){}/*squelch errors from hidden nodes*/
  125. }
  126. },
  127. compare: function(/*anything*/ val1, /*anything*/ val2){
  128. // summary:
  129. // Compare 2 values (as returned by get('value') for this widget).
  130. // tags:
  131. // protected
  132. if(typeof val1 == "number" && typeof val2 == "number"){
  133. return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
  134. }else if(val1 > val2){
  135. return 1;
  136. }else if(val1 < val2){
  137. return -1;
  138. }else{
  139. return 0;
  140. }
  141. },
  142. onChange: function(/*===== newValue =====*/){
  143. // summary:
  144. // Callback when this widget's value is changed.
  145. // tags:
  146. // callback
  147. },
  148. // _onChangeActive: [private] Boolean
  149. // Indicates that changes to the value should call onChange() callback.
  150. // This is false during widget initialization, to avoid calling onChange()
  151. // when the initial value is set.
  152. _onChangeActive: false,
  153. _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  154. // summary:
  155. // Called when the value of the widget is set. Calls onChange() if appropriate
  156. // newValue:
  157. // the new value
  158. // priorityChange:
  159. // For a slider, for example, dragging the slider is priorityChange==false,
  160. // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
  161. // onChange is only called form priorityChange=true events.
  162. // tags:
  163. // private
  164. if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
  165. // this block executes not for a change, but during initialization,
  166. // and is used to store away the original value (or for ToggleButton, the original checked state)
  167. this._resetValue = this._lastValueReported = newValue;
  168. }
  169. this._pendingOnChange = this._pendingOnChange
  170. || (typeof newValue != typeof this._lastValueReported)
  171. || (this.compare(newValue, this._lastValueReported) != 0);
  172. if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
  173. this._lastValueReported = newValue;
  174. this._pendingOnChange = false;
  175. if(this._onChangeActive){
  176. if(this._onChangeHandle){
  177. this._onChangeHandle.remove();
  178. }
  179. // defer allows hidden value processing to run and
  180. // also the onChange handler can safely adjust focus, etc
  181. this._onChangeHandle = this.defer(
  182. function(){
  183. this._onChangeHandle = null;
  184. this.onChange(newValue);
  185. }); // try to collapse multiple onChange's fired faster than can be processed
  186. }
  187. }
  188. },
  189. create: function(){
  190. // Overrides _Widget.create()
  191. this.inherited(arguments);
  192. this._onChangeActive = true;
  193. },
  194. destroy: function(){
  195. if(this._onChangeHandle){ // destroy called before last onChange has fired
  196. this._onChangeHandle.remove();
  197. this.onChange(this._lastValueReported);
  198. }
  199. this.inherited(arguments);
  200. }
  201. });
  202. });