editor.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /**
  2. * DevExtreme (ui/editor/editor.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var _string = require("../../core/utils/string");
  11. var $ = require("../../core/renderer");
  12. var dataUtils = require("../../core/element_data");
  13. var Callbacks = require("../../core/utils/callbacks");
  14. var commonUtils = require("../../core/utils/common");
  15. var windowUtils = require("../../core/utils/window");
  16. var Guid = require("../../core/guid");
  17. var getDefaultAlignment = require("../../core/utils/position").getDefaultAlignment;
  18. var extend = require("../../core/utils/extend").extend;
  19. var Widget = require("../widget/ui.widget");
  20. var ValidationMixin = require("../validation/validation_mixin");
  21. var Overlay = require("../overlay");
  22. var EventsEngine = require("../../events/core/events_engine");
  23. var eventUtils = require("../../events/utils");
  24. var READONLY_STATE_CLASS = "dx-state-readonly";
  25. var INVALID_CLASS = "dx-invalid";
  26. var INVALID_MESSAGE = "dx-invalid-message";
  27. var INVALID_MESSAGE_CONTENT = "dx-invalid-message-content";
  28. var INVALID_MESSAGE_AUTO = "dx-invalid-message-auto";
  29. var INVALID_MESSAGE_ALWAYS = "dx-invalid-message-always";
  30. var VALIDATION_TARGET = "dx-validation-target";
  31. var VALIDATION_MESSAGE_MIN_WIDTH = 100;
  32. var READONLY_NAMESPACE = "editorReadOnly";
  33. var Editor = Widget.inherit({
  34. ctor: function() {
  35. this.showValidationMessageTimeout = null;
  36. this.validationRequest = Callbacks();
  37. this.callBase.apply(this, arguments);
  38. var $element = this.$element();
  39. if ($element) {
  40. dataUtils.data($element[0], VALIDATION_TARGET, this)
  41. }
  42. },
  43. _init: function() {
  44. this.callBase();
  45. this._initInnerOptionCache("validationTooltipOptions")
  46. },
  47. _getDefaultOptions: function() {
  48. return extend(this.callBase(), {
  49. value: null,
  50. name: "",
  51. onValueChanged: null,
  52. readOnly: false,
  53. isValid: true,
  54. validationError: null,
  55. validationMessageMode: "auto",
  56. validationBoundary: void 0,
  57. validationMessageOffset: {
  58. h: 0,
  59. v: 0
  60. },
  61. validationTooltipOptions: {}
  62. })
  63. },
  64. _attachKeyboardEvents: function() {
  65. if (this.option("readOnly")) {
  66. return
  67. }
  68. this.callBase();
  69. if (this._keyboardProcessor) {
  70. this._attachChildKeyboardEvents()
  71. }
  72. },
  73. _attachChildKeyboardEvents: commonUtils.noop,
  74. _setOptionsByReference: function() {
  75. this.callBase();
  76. extend(this._optionsByReference, {
  77. validationError: true
  78. })
  79. },
  80. _createValueChangeAction: function() {
  81. this._valueChangeAction = this._createActionByOption("onValueChanged", {
  82. excludeValidators: ["disabled", "readOnly"]
  83. })
  84. },
  85. _suppressValueChangeAction: function() {
  86. this._valueChangeActionSuppressed = true
  87. },
  88. _resumeValueChangeAction: function() {
  89. this._valueChangeActionSuppressed = false
  90. },
  91. _initMarkup: function() {
  92. this._toggleReadOnlyState();
  93. this._setSubmitElementName(this.option("name"));
  94. this.callBase();
  95. this._renderValidationState()
  96. },
  97. _raiseValueChangeAction: function(value, previousValue) {
  98. if (!this._valueChangeAction) {
  99. this._createValueChangeAction()
  100. }
  101. this._valueChangeAction(this._valueChangeArgs(value, previousValue))
  102. },
  103. _valueChangeArgs: function(value, previousValue) {
  104. return {
  105. value: value,
  106. previousValue: previousValue,
  107. event: this._valueChangeEventInstance
  108. }
  109. },
  110. _saveValueChangeEvent: function(e) {
  111. this._valueChangeEventInstance = e
  112. },
  113. _focusInHandler: function(e) {
  114. var _this = this;
  115. var isValidationMessageShownOnFocus = "auto" === this.option("validationMessageMode");
  116. if (this._canValueBeChangedByClick() && isValidationMessageShownOnFocus) {
  117. this._$validationMessage && this._$validationMessage.removeClass(INVALID_MESSAGE_AUTO);
  118. clearTimeout(this.showValidationMessageTimeout);
  119. this.showValidationMessageTimeout = setTimeout(function() {
  120. return _this._$validationMessage && _this._$validationMessage.addClass(INVALID_MESSAGE_AUTO)
  121. }, 150)
  122. }
  123. return this.callBase(e)
  124. },
  125. _canValueBeChangedByClick: function() {
  126. return false
  127. },
  128. _renderValidationState: function() {
  129. var isValid = this.option("isValid");
  130. var validationError = this.option("validationError");
  131. var validationMessageMode = this.option("validationMessageMode");
  132. var $element = this.$element();
  133. $element.toggleClass(INVALID_CLASS, !isValid);
  134. this.setAria("invalid", !isValid || void 0);
  135. if (!windowUtils.hasWindow()) {
  136. return
  137. }
  138. if (this._$validationMessage) {
  139. this._$validationMessage.remove();
  140. this.setAria("describedby", null);
  141. this._$validationMessage = null
  142. }
  143. if (!isValid && validationError && validationError.message) {
  144. this._$validationMessage = $("<div>").addClass(INVALID_MESSAGE).html((0, _string.encodeHtml)(validationError.message)).appendTo($element);
  145. var validationTarget = this._getValidationMessageTarget();
  146. this._validationMessage = this._createComponent(this._$validationMessage, Overlay, extend({
  147. integrationOptions: {},
  148. templatesRenderAsynchronously: false,
  149. target: validationTarget,
  150. shading: false,
  151. width: "auto",
  152. height: "auto",
  153. container: $element,
  154. position: this._getValidationMessagePosition("below"),
  155. closeOnOutsideClick: false,
  156. closeOnTargetScroll: false,
  157. animation: null,
  158. visible: true,
  159. propagateOutsideClick: true,
  160. _checkParentVisibility: false
  161. }, this._getInnerOptionsCache("validationTooltipOptions")));
  162. this._$validationMessage.toggleClass(INVALID_MESSAGE_AUTO, "auto" === validationMessageMode).toggleClass(INVALID_MESSAGE_ALWAYS, "always" === validationMessageMode);
  163. var messageId = "dx-" + new Guid;
  164. this._validationMessage.$content().addClass(INVALID_MESSAGE_CONTENT).attr("id", messageId);
  165. this.setAria("describedby", messageId);
  166. this._setValidationMessageMaxWidth();
  167. this._bindInnerWidgetOptions(this._validationMessage, "validationTooltipOptions")
  168. }
  169. },
  170. _setValidationMessageMaxWidth: function() {
  171. if (!this._validationMessage) {
  172. return
  173. }
  174. if (0 === this._getValidationMessageTarget().outerWidth()) {
  175. this._validationMessage.option("maxWidth", "100%");
  176. return
  177. }
  178. var validationMessageMaxWidth = Math.max(VALIDATION_MESSAGE_MIN_WIDTH, this._getValidationMessageTarget().outerWidth());
  179. this._validationMessage.option("maxWidth", validationMessageMaxWidth)
  180. },
  181. _getValidationMessageTarget: function() {
  182. return this.$element()
  183. },
  184. _getValidationMessagePosition: function(positionRequest) {
  185. var rtlEnabled = this.option("rtlEnabled");
  186. var messagePositionSide = getDefaultAlignment(rtlEnabled);
  187. var messageOriginalOffset = this.option("validationMessageOffset");
  188. var messageOffset = {
  189. h: messageOriginalOffset.h,
  190. v: messageOriginalOffset.v
  191. };
  192. var verticalPositions = "below" === positionRequest ? [" top", " bottom"] : [" bottom", " top"];
  193. if (rtlEnabled) {
  194. messageOffset.h = -messageOffset.h
  195. }
  196. if ("below" !== positionRequest) {
  197. messageOffset.v = -messageOffset.v
  198. }
  199. return {
  200. offset: messageOffset,
  201. boundary: this.option("validationBoundary"),
  202. my: messagePositionSide + verticalPositions[0],
  203. at: messagePositionSide + verticalPositions[1],
  204. collision: "none flip"
  205. }
  206. },
  207. _toggleReadOnlyState: function() {
  208. var readOnly = this.option("readOnly");
  209. this._toggleBackspaceHandler(readOnly);
  210. this.$element().toggleClass(READONLY_STATE_CLASS, !!readOnly);
  211. this.setAria("readonly", readOnly || void 0)
  212. },
  213. _toggleBackspaceHandler: function(isReadOnly) {
  214. var $eventTarget = this._keyboardEventBindingTarget();
  215. var eventName = eventUtils.addNamespace("keydown", READONLY_NAMESPACE);
  216. EventsEngine.off($eventTarget, eventName);
  217. if (isReadOnly) {
  218. EventsEngine.on($eventTarget, eventName, function(e) {
  219. if ("backspace" === eventUtils.normalizeKeyName(e)) {
  220. e.preventDefault()
  221. }
  222. })
  223. }
  224. },
  225. _dispose: function() {
  226. var element = this.$element()[0];
  227. dataUtils.data(element, VALIDATION_TARGET, null);
  228. clearTimeout(this.showValidationMessageTimeout);
  229. this.callBase()
  230. },
  231. _setSubmitElementName: function(name) {
  232. var $submitElement = this._getSubmitElement();
  233. if (!$submitElement) {
  234. return
  235. }
  236. if (name.length > 0) {
  237. $submitElement.attr("name", name)
  238. } else {
  239. $submitElement.removeAttr("name")
  240. }
  241. },
  242. _getSubmitElement: function() {
  243. return null
  244. },
  245. _optionChanged: function(args) {
  246. switch (args.name) {
  247. case "onValueChanged":
  248. this._createValueChangeAction();
  249. break;
  250. case "isValid":
  251. case "validationError":
  252. case "validationBoundary":
  253. case "validationMessageMode":
  254. this._renderValidationState();
  255. break;
  256. case "validationTooltipOptions":
  257. this._innerOptionChanged(this._validationMessage, args);
  258. break;
  259. case "readOnly":
  260. this._toggleReadOnlyState();
  261. this._refreshFocusState();
  262. break;
  263. case "value":
  264. if (args.value != args.previousValue) {
  265. this.validationRequest.fire({
  266. value: args.value,
  267. editor: this
  268. })
  269. }
  270. if (!this._valueChangeActionSuppressed) {
  271. this._raiseValueChangeAction(args.value, args.previousValue);
  272. this._saveValueChangeEvent(void 0)
  273. }
  274. break;
  275. case "width":
  276. this.callBase(args);
  277. this._setValidationMessageMaxWidth();
  278. break;
  279. case "name":
  280. this._setSubmitElementName(args.value);
  281. break;
  282. default:
  283. this.callBase(args)
  284. }
  285. },
  286. reset: function() {
  287. var defaultOptions = this._getDefaultOptions();
  288. this.option("value", defaultOptions.value)
  289. }
  290. }).include(ValidationMixin);
  291. module.exports = Editor;