UNPKG

devextreme

Version:

JavaScript/TypeScript Component Suite for Responsive Web Development

598 lines (596 loc) • 21.8 kB
/** * DevExtreme (cjs/__internal/ui/text_box/text_editor.mask.js) * Version: 25.2.7 * Build date: Tue May 05 2026 * * Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine")); var _wheel = require("../../../common/core/events/core/wheel"); var _index = require("../../../common/core/events/utils/index"); var _message = _interopRequireDefault(require("../../../common/core/localization/message")); var _renderer = _interopRequireDefault(require("../../../core/renderer")); var _extend = require("../../../core/utils/extend"); var _string = require("../../../core/utils/string"); var _type = require("../../../core/utils/type"); var _m_selectors = require("../../core/utils/m_selectors"); var _m_text_editor = _interopRequireDefault(require("../../ui/text_box/m_text_editor.base")); var _text_editorMask = require("../../ui/text_box/text_editor.mask.rule"); var _text_editorMask2 = _interopRequireDefault(require("../../ui/text_box/text_editor.mask.strategy")); var _utils = _interopRequireDefault(require("../../ui/text_box/utils.caret")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } const EMPTY_CHAR = " "; const ESCAPED_CHAR = "\\"; const TEXTEDITOR_MASKED_CLASS = "dx-texteditor-masked"; const FORWARD_DIRECTION = "forward"; const BACKWARD_DIRECTION = "backward"; const DROP_EVENT_NAME = "drop"; const isNumericChar = char => /[0-9]/.test(char); const isLiteralChar = char => { const code = char.charCodeAt(0); return code > 64 && code < 91 || code > 96 && code < 123 || code > 127 }; const isSpaceChar = char => " " === char; const buildInMaskRules = { 0: /[0-9]/, 9: /[0-9\s]/, "#": /[-+0-9\s]/, L: char => isLiteralChar(char), l: char => isLiteralChar(char) || isSpaceChar(char), C: /\S/, c: /./, A: char => isLiteralChar(char) || isNumericChar(char), a: char => isLiteralChar(char) || isNumericChar(char) || isSpaceChar(char) }; class TextEditorMask extends _m_text_editor.default { _getDefaultOptions() { return Object.assign({}, super._getDefaultOptions(), { mask: "", maskChar: "_", maskRules: {}, maskInvalidMessage: _message.default.format("validation-mask"), useMaskedValue: false, showMaskMode: "always" }) } _supportedKeys() { const result = super._supportedKeys(); const keyHandlerMap = { del: this._maskStrategy.getHandler("del"), enter: this._changeHandler }; Object.entries(keyHandlerMap).forEach(_ref => { let [key, handler] = _ref; const parentHandler = result[key]; result[key] = e => { const { mask: mask } = this.option(); if (mask && handler) { handler.call(this, e) } null === parentHandler || void 0 === parentHandler || parentHandler(e) } }); return result } _getSubmitElement() { const { mask: mask } = this.option(); const submitElement = !mask ? super._getSubmitElement() : this._$hiddenElement; return submitElement } _init() { super._init(); this._initMaskStrategy() } _initMaskStrategy() { this._maskStrategy = new _text_editorMask2.default(this) } _initMarkup() { this._renderHiddenElement(); super._initMarkup() } _attachMouseWheelEventHandlers() { if (!this._hasMouseWheelHandler()) { return } const input = this._input(); const eventName = (0, _index.addNamespace)(_wheel.name, this.NAME); const mouseWheelAction = this._createAction(e => { const { event: event } = e; if ((0, _m_selectors.focused)(input) && !(0, _index.isCommandKeyPressed)(event)) { this._onMouseWheel(event); event.preventDefault(); event.stopPropagation() } }); _events_engine.default.off(input, eventName); _events_engine.default.on(input, eventName, e => { mouseWheelAction({ event: e }) }) } _hasMouseWheelHandler() { return false } _onMouseWheel(e) {} _useMaskBehavior() { const { mask: mask } = this.option(); return Boolean(mask) } _attachDropEventHandler() { const useMaskBehavior = this._useMaskBehavior(); if (!useMaskBehavior) { return } const eventName = (0, _index.addNamespace)("drop", this.NAME); const input = this._input(); _events_engine.default.off(input, eventName); _events_engine.default.on(input, eventName, e => { e.preventDefault() }) } _render() { this._attachMouseWheelEventHandlers(); this._renderMask(); super._render(); this._attachDropEventHandler() } _renderHiddenElement() { const { mask: mask } = this.option(); if (mask) { this._$hiddenElement = (0, _renderer.default)("<input>").attr("type", "hidden").appendTo(this._inputWrapper()) } } _removeHiddenElement() { var _this$_$hiddenElement; null === (_this$_$hiddenElement = this._$hiddenElement) || void 0 === _this$_$hiddenElement || _this$_$hiddenElement.remove() } _renderMask() { this.$element().removeClass("dx-texteditor-masked"); this._maskRulesChain = null; this._maskStrategy.detachEvents(); const { mask: mask } = this.option(); if (!mask) { return } this.$element().addClass("dx-texteditor-masked"); this._maskStrategy.attachEvents(); this._parseMask(); this._renderMaskedValue() } _changeHandler(e) { const $input = this._input(); const inputValue = $input.val(); if (inputValue === this._changedValue) { return } this._changedValue = inputValue; const changeEvent = (0, _index.createEvent)(e, { type: "change" }); _events_engine.default.trigger($input, changeEvent) } _parseMask() { const { maskRules: maskRules } = this.option(); this._maskRules = (0, _extend.extend)({}, buildInMaskRules, maskRules); this._maskRulesChain = this._parseMaskRule(0) } _parseMaskRule(index) { const { mask: mask } = this.option(); if (!(0, _type.isDefined)(mask) || index >= mask.length) { return new _text_editorMask.EmptyMaskRule({}) } const currentMaskChar = mask[index]; const isEscapedChar = "\\" === currentMaskChar; const result = isEscapedChar ? new _text_editorMask.StubMaskRule({ maskChar: mask[index + 1] }) : this._getMaskRule(currentMaskChar); const nextIndex = index + 1 + Number(isEscapedChar); const recursiveResult = this._parseMaskRule(nextIndex); result.next(recursiveResult); return result } _getMaskRule(pattern) { if (!this._maskRules) { return new _text_editorMask.StubMaskRule({ maskChar: pattern }) } const matchingEntry = Object.entries(this._maskRules).find(_ref2 => { let [rulePattern] = _ref2; return rulePattern === pattern }); if (matchingEntry) { const [, allowedChars] = matchingEntry; const ruleConfig = { pattern: pattern, allowedChars: allowedChars }; const { maskChar: maskChar } = this.option(); return new _text_editorMask.MaskRule((0, _extend.extend)({ maskChar: maskChar || " " }, ruleConfig)) } return new _text_editorMask.StubMaskRule({ maskChar: pattern }) } _renderMaskedValue() { if (!this._maskRulesChain) { return } const { value: optionValue } = this.option(); const value = optionValue || ""; this._maskRulesChain.clear(this._normalizeChainArguments()); const chainArgs = { length: null === value || void 0 === value ? void 0 : value.length }; const prop = this._isMaskedValueMode() ? "text" : "value"; chainArgs[prop] = value; this._handleChain(chainArgs); this._displayMask() } _replaceSelectedText(text, selection, char) { if (void 0 === char) { return text } const textBefore = text.slice(0, selection.start); const textAfter = text.slice(selection.end); const edited = `${textBefore}${char}${textAfter}`; return edited } _isMaskedValueMode() { const { useMaskedValue: useMaskedValue } = this.option(); return Boolean(useMaskedValue) } _displayMask(caret) { const currentCaret = caret ?? this._caret(); const finalCaret = { start: (null === currentCaret || void 0 === currentCaret ? void 0 : currentCaret.start) ?? 0, end: (null === currentCaret || void 0 === currentCaret ? void 0 : currentCaret.end) ?? 0 }; this._renderValue(); this._caret(finalCaret) } _isValueEmpty() { return (0, _string.isEmpty)(this._value) } _shouldShowMask() { const { showMaskMode: showMaskMode } = this.option(); if ("onFocus" === showMaskMode) { return (0, _m_selectors.focused)(this._input()) || !this._isValueEmpty() } return true } _showMaskPlaceholder() { if (this._shouldShowMask()) { var _this$_maskRulesChain; const text = null === (_this$_maskRulesChain = this._maskRulesChain) || void 0 === _this$_maskRulesChain ? void 0 : _this$_maskRulesChain.text(); this.option({ text: text }); const { showMaskMode: showMaskMode } = this.option(); if ("onFocus" === showMaskMode) { this._renderDisplayText(text) } } } _renderValue() { if (this._maskRulesChain) { this._showMaskPlaceholder(); if (this._$hiddenElement) { const value = this._maskRulesChain.value(); const submitElementValue = !(0, _string.isEmpty)(value) ? this._getPreparedValue() : ""; this._$hiddenElement.val(submitElementValue) } } return super._renderValue() } _getPreparedValue() { return this._convertToValue().replace(/\s+$/, "") } _valueChangeEventHandler() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key] } if (!this._maskRulesChain) { super._valueChangeEventHandler(...args); return } const [e] = args; this._saveValueChangeEvent(e); const preparedValue = this._getPreparedValue(); this.option({ value: preparedValue }) } _isControlKeyFired(e) { const normalizedKeyName = (0, _index.normalizeKeyName)(e); const isControlKey = (0, _type.isDefined)(normalizedKeyName) ? this._isControlKey(normalizedKeyName) : false; return isControlKey || (0, _index.isCommandKeyPressed)(e) } _handleChain(args) { var _this$_maskRulesChain2; const handledCount = (null === (_this$_maskRulesChain2 = this._maskRulesChain) || void 0 === _this$_maskRulesChain2 ? void 0 : _this$_maskRulesChain2.handle(this._normalizeChainArguments(args))) ?? 0; this._updateMaskInfo(); return handledCount } _normalizeChainArguments(args) { var _this$_maskRulesChain3; return Object.assign({}, args, { index: 0, fullText: null === (_this$_maskRulesChain3 = this._maskRulesChain) || void 0 === _this$_maskRulesChain3 ? void 0 : _this$_maskRulesChain3.text() }) } _convertToValue(text) { if (this._isMaskedValueMode()) { return this._replaceMaskCharWithEmpty(text || this._textValue || "") } return text || this._value || "" } _replaceMaskCharWithEmpty(text) { const { maskChar: maskChar } = this.option(); return text.replace(new RegExp(maskChar, "g"), " ") } _maskKeyHandler(e, keyHandler) { const { readOnly: readOnly } = this.option(); if (readOnly) { return } this.setForwardDirection(); e.preventDefault(); this._handleSelection(); const previousText = this._input().val(); const raiseInputEvent = () => { if (previousText !== this._input().val()) { _events_engine.default.trigger(this._input(), "input") } }; const handled = keyHandler(); if (handled) { handled.then(raiseInputEvent) } else { var _this$_maskRulesChain4; this.setForwardDirection(); this._adjustCaret(); this._displayMask(); null === (_this$_maskRulesChain4 = this._maskRulesChain) || void 0 === _this$_maskRulesChain4 || _this$_maskRulesChain4.reset(); raiseInputEvent() } } _handleKey(key, direction) { this._direction(direction || "forward"); this._adjustCaret(key); this._handleKeyChain(key); this._moveCaret() } _handleSelection() { if (!this._hasSelection()) { return } const caret = this._caret(); const caretStart = (null === caret || void 0 === caret ? void 0 : caret.start) ?? 0; const caretEnd = (null === caret || void 0 === caret ? void 0 : caret.end) ?? 0; const emptyChars = new Array(caretEnd - caretStart + 1).join(" "); this._handleKeyChain(emptyChars) } _handleKeyChain(chars) { const caret = this._caret(); const caretStart = (null === caret || void 0 === caret ? void 0 : caret.start) ?? 0; const caretEnd = (null === caret || void 0 === caret ? void 0 : caret.end) ?? 0; const start = this.isForwardDirection() ? caretStart : caretStart - 1; const end = this.isForwardDirection() ? caretEnd : caretEnd - 1; const length = start === end ? 1 : end - start; this._handleChain({ text: chars, start: start, length: length }) } _tryMoveCaretBackward() { var _this$_caret, _this$_caret2; this.setBackwardDirection(); const currentCaret = null === (_this$_caret = this._caret()) || void 0 === _this$_caret ? void 0 : _this$_caret.start; this._adjustCaret(); return !currentCaret || currentCaret !== (null === (_this$_caret2 = this._caret()) || void 0 === _this$_caret2 ? void 0 : _this$_caret2.start) } _adjustCaret(char) { var _this$_caret3, _this$_maskRulesChain5; const caretStart = (null === (_this$_caret3 = this._caret()) || void 0 === _this$_caret3 ? void 0 : _this$_caret3.start) ?? 0; const isForwardDirection = this.isForwardDirection(); const caret = null === (_this$_maskRulesChain5 = this._maskRulesChain) || void 0 === _this$_maskRulesChain5 ? void 0 : _this$_maskRulesChain5.adjustedCaret(caretStart, isForwardDirection, char ?? ""); this._caret({ start: caret, end: caret }) } _moveCaret() { var _this$_caret4, _this$_maskRulesChain6; const currentCaret = (null === (_this$_caret4 = this._caret()) || void 0 === _this$_caret4 ? void 0 : _this$_caret4.start) ?? 0; const maskRuleIndex = currentCaret + (this.isForwardDirection() ? 0 : -1); const caret = null !== (_this$_maskRulesChain6 = this._maskRulesChain) && void 0 !== _this$_maskRulesChain6 && _this$_maskRulesChain6.isAccepted(maskRuleIndex) ? currentCaret + (this.isForwardDirection() ? 1 : -1) : currentCaret; this._caret({ start: caret, end: caret }) } _caret(position, force) { const $input = this._input(); if (!$input.length) { return } if (arguments.length > 0) { (0, _utils.default)($input, position, force); return } return (0, _utils.default)($input) } _hasSelection() { const caret = this._caret(); return (null === caret || void 0 === caret ? void 0 : caret.start) !== (null === caret || void 0 === caret ? void 0 : caret.end) } _direction(direction) { if (!arguments.length) { return this._typingDirection } this._typingDirection = direction } setForwardDirection() { this._direction("forward") } setBackwardDirection() { this._direction("backward") } isForwardDirection() { return "forward" === this._direction() } _updateMaskInfo() { var _this$_maskRulesChain7, _this$_maskRulesChain8; this._textValue = null === (_this$_maskRulesChain7 = this._maskRulesChain) || void 0 === _this$_maskRulesChain7 ? void 0 : _this$_maskRulesChain7.text(); this._value = null === (_this$_maskRulesChain8 = this._maskRulesChain) || void 0 === _this$_maskRulesChain8 ? void 0 : _this$_maskRulesChain8.value() } _clean() { var _this$_maskStrategy; null === (_this$_maskStrategy = this._maskStrategy) || void 0 === _this$_maskStrategy || _this$_maskStrategy.clean(); super._clean() } _validateMask() { if (!this._maskRulesChain) { return } const { maskInvalidMessage: maskInvalidMessage, value: value } = this.option(); const defaultValidationError = { editorSpecific: true, message: maskInvalidMessage }; const isValid = (0, _string.isEmpty)(value) || this._maskRulesChain.isValid(this._normalizeChainArguments()); const validationError = isValid ? null : defaultValidationError; this.option({ isValid: isValid, validationError: validationError }) } _updateHiddenElement() { this._removeHiddenElement(); const { mask: mask } = this.option(); if (mask) { this._input().removeAttr("name"); this._renderHiddenElement() } const { name: name } = this.option(); this._setSubmitElementName(name) } _updateMaskOption() { this._updateHiddenElement(); this._renderMask(); this._validateMask(); this._refreshValueChangeEvent() } _processEmptyMask(mask) { if (mask) { return } const { value: value } = this.option(); this.option({ text: value, isValid: true, validationError: null }); this.validationRequest.fire({ value: value, editor: this }); this._renderValue() } _optionChanged(args) { switch (args.name) { case "mask": this._updateMaskOption(); this._processEmptyMask(args.value); break; case "maskChar": case "maskRules": case "useMaskedValue": this._updateMaskOption(); break; case "value": this._renderMaskedValue(); this._validateMask(); super._optionChanged(args); this._changedValue = this._input().val(); break; case "maskInvalidMessage": break; case "showMaskMode": this.option({ text: "" }); this._renderValue(); break; default: super._optionChanged(args) } } clear() { const { value: defaultValue } = this._getDefaultOptions(); const { value: value } = this.option(); if (value === defaultValue) { this._renderMaskedValue() } super.clear() } } var _default = exports.default = TextEditorMask;