/**
 * DevExtreme (cjs/ui/text_box/ui.text_editor.mask.js)
 * Version: 23.2.6 (build 24140-0112)
 * Build date: Sun May 19 2024
 *
 * Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
exports.default = void 0;
var _renderer = _interopRequireDefault(require("../../core/renderer"));
var _utils = _interopRequireDefault(require("./utils.caret"));
var _iterator = require("../../core/utils/iterator");
var _index = require("../../events/utils/index");
var _events_engine = _interopRequireDefault(require("../../events/core/events_engine"));
var _extend = require("../../core/utils/extend");
var _selectors = require("../widget/selectors");
var _type = require("../../core/utils/type");
var _message = _interopRequireDefault(require("../../localization/message"));
var _common = require("../../core/utils/common");
var _string = require("../../core/utils/string");
var _wheel = require("../../events/core/wheel");
var _uiText_editorMask = require("./ui.text_editor.mask.rule");
var _uiText_editor = _interopRequireDefault(require("./ui.text_editor.base"));
var _uiText_editorMask2 = _interopRequireDefault(require("./ui.text_editor.mask.strategy"));

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    }
}
const caret = _utils.default;
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 buildInMaskRules = {
    0: /[0-9]/,
    9: /[0-9\s]/,
    "#": /[-+0-9\s]/,
    L: function(char) {
        return isLiteralChar(char)
    },
    l: function(char) {
        return isLiteralChar(char) || isSpaceChar(char)
    },
    C: /\S/,
    c: /./,
    A: function(char) {
        return isLiteralChar(char) || isNumericChar(char)
    },
    a: function(char) {
        return isLiteralChar(char) || isNumericChar(char) || isSpaceChar(char)
    }
};

function isNumericChar(char) {
    return /[0-9]/.test(char)
}

function isLiteralChar(char) {
    const code = char.charCodeAt();
    return 64 < code && code < 91 || 96 < code && code < 123 || code > 127
}

function isSpaceChar(char) {
    return " " === char
}
const TextEditorMask = _uiText_editor.default.inherit({
    _getDefaultOptions: function() {
        return (0, _extend.extend)(this.callBase(), {
            mask: "",
            maskChar: "_",
            maskRules: {},
            maskInvalidMessage: _message.default.format("validation-mask"),
            useMaskedValue: false,
            showMaskMode: "always"
        })
    },
    _supportedKeys: function() {
        const that = this;
        const keyHandlerMap = {
            del: that._maskStrategy.getHandler("del"),
            enter: that._changeHandler
        };
        const result = that.callBase();
        (0, _iterator.each)(keyHandlerMap, (function(key, callback) {
            const parentHandler = result[key];
            result[key] = function(e) {
                that.option("mask") && callback.call(that, e);
                parentHandler && parentHandler(e)
            }
        }));
        return result
    },
    _getSubmitElement: function() {
        return !this.option("mask") ? this.callBase() : this._$hiddenElement
    },
    _init: function() {
        this.callBase();
        this._initMaskStrategy()
    },
    _initMaskStrategy: function() {
        this._maskStrategy = new _uiText_editorMask2.default(this)
    },
    _initMarkup: function() {
        this._renderHiddenElement();
        this.callBase()
    },
    _attachMouseWheelEventHandlers: function() {
        const hasMouseWheelHandler = this._onMouseWheel !== _common.noop;
        if (!hasMouseWheelHandler) {
            return
        }
        const input = this._input();
        const eventName = (0, _index.addNamespace)(_wheel.name, this.NAME);
        const mouseWheelAction = this._createAction(function(e) {
            const {
                event: event
            } = e;
            if ((0, _selectors.focused)(input) && !(0, _index.isCommandKeyPressed)(event)) {
                this._onMouseWheel(event);
                event.preventDefault();
                event.stopPropagation()
            }
        }.bind(this));
        _events_engine.default.off(input, eventName);
        _events_engine.default.on(input, eventName, (function(e) {
            mouseWheelAction({
                event: e
            })
        }))
    },
    _onMouseWheel: _common.noop,
    _useMaskBehavior() {
        return Boolean(this.option("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._renderMask();
        this.callBase();
        this._attachDropEventHandler();
        this._attachMouseWheelEventHandlers()
    },
    _renderHiddenElement: function() {
        if (this.option("mask")) {
            this._$hiddenElement = (0, _renderer.default)("<input>").attr("type", "hidden").appendTo(this._inputWrapper())
        }
    },
    _removeHiddenElement: function() {
        this._$hiddenElement && this._$hiddenElement.remove()
    },
    _renderMask: function() {
        this.$element().removeClass("dx-texteditor-masked");
        this._maskRulesChain = null;
        this._maskStrategy.detachEvents();
        if (!this.option("mask")) {
            return
        }
        this.$element().addClass("dx-texteditor-masked");
        this._maskStrategy.attachEvents();
        this._parseMask();
        this._renderMaskedValue()
    },
    _changeHandler: function(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: function() {
        this._maskRules = (0, _extend.extend)({}, buildInMaskRules, this.option("maskRules"));
        this._maskRulesChain = this._parseMaskRule(0)
    },
    _parseMaskRule: function(index) {
        const mask = this.option("mask");
        if (index >= mask.length) {
            return new _uiText_editorMask.EmptyMaskRule
        }
        const currentMaskChar = mask[index];
        const isEscapedChar = "\\" === currentMaskChar;
        const result = isEscapedChar ? new _uiText_editorMask.StubMaskRule({
            maskChar: mask[index + 1]
        }) : this._getMaskRule(currentMaskChar);
        result.next(this._parseMaskRule(index + 1 + isEscapedChar));
        return result
    },
    _getMaskRule: function(pattern) {
        let ruleConfig;
        (0, _iterator.each)(this._maskRules, (function(rulePattern, allowedChars) {
            if (rulePattern === pattern) {
                ruleConfig = {
                    pattern: rulePattern,
                    allowedChars: allowedChars
                };
                return false
            }
        }));
        return (0, _type.isDefined)(ruleConfig) ? new _uiText_editorMask.MaskRule((0, _extend.extend)({
            maskChar: this.option("maskChar") || " "
        }, ruleConfig)) : new _uiText_editorMask.StubMaskRule({
            maskChar: pattern
        })
    },
    _renderMaskedValue: function() {
        if (!this._maskRulesChain) {
            return
        }
        const value = this.option("value") || "";
        this._maskRulesChain.clear(this._normalizeChainArguments());
        const chainArgs = {
            length: value.length
        };
        chainArgs[this._isMaskedValueMode() ? "text" : "value"] = value;
        this._handleChain(chainArgs);
        this._displayMask()
    },
    _replaceSelectedText: function(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: function() {
        return this.option("useMaskedValue")
    },
    _displayMask: function(caret) {
        caret = caret || this._caret();
        this._renderValue();
        this._caret(caret)
    },
    _isValueEmpty: function() {
        return (0, _string.isEmpty)(this._value)
    },
    _shouldShowMask: function() {
        const showMaskMode = this.option("showMaskMode");
        if ("onFocus" === showMaskMode) {
            return (0, _selectors.focused)(this._input()) || !this._isValueEmpty()
        }
        return true
    },
    _showMaskPlaceholder: function() {
        if (this._shouldShowMask()) {
            const text = this._maskRulesChain.text();
            this.option("text", text);
            if ("onFocus" === this.option("showMaskMode")) {
                this._renderDisplayText(text)
            }
        }
    },
    _renderValue: function() {
        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 this.callBase()
    },
    _getPreparedValue: function() {
        return this._convertToValue().replace(/\s+$/, "")
    },
    _valueChangeEventHandler: function(e) {
        if (!this._maskRulesChain) {
            this.callBase.apply(this, arguments);
            return
        }
        this._saveValueChangeEvent(e);
        this.option("value", this._getPreparedValue())
    },
    _isControlKeyFired: function(e) {
        return this._isControlKey((0, _index.normalizeKeyName)(e)) || (0, _index.isCommandKeyPressed)(e)
    },
    _handleChain: function(args) {
        const handledCount = this._maskRulesChain.handle(this._normalizeChainArguments(args));
        this._updateMaskInfo();
        return handledCount
    },
    _normalizeChainArguments: function(args) {
        args = args || {};
        args.index = 0;
        args.fullText = this._maskRulesChain.text();
        return args
    },
    _convertToValue: function(text) {
        if (this._isMaskedValueMode()) {
            text = this._replaceMaskCharWithEmpty(text || this._textValue || "")
        } else {
            text = text || this._value || ""
        }
        return text
    },
    _replaceMaskCharWithEmpty: function(text) {
        return text.replace(new RegExp(this.option("maskChar"), "g"), " ")
    },
    _maskKeyHandler: function(e, keyHandler) {
        if (this.option("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 {
            this.setForwardDirection();
            this._adjustCaret();
            this._displayMask();
            this._maskRulesChain.reset();
            raiseInputEvent()
        }
    },
    _handleKey: function(key, direction) {
        this._direction(direction || "forward");
        this._adjustCaret(key);
        this._handleKeyChain(key);
        this._moveCaret()
    },
    _handleSelection: function() {
        if (!this._hasSelection()) {
            return
        }
        const caret = this._caret();
        const emptyChars = new Array(caret.end - caret.start + 1).join(" ");
        this._handleKeyChain(emptyChars)
    },
    _handleKeyChain: function(chars) {
        const caret = this._caret();
        const start = this.isForwardDirection() ? caret.start : caret.start - 1;
        const end = this.isForwardDirection() ? caret.end : caret.end - 1;
        const length = start === end ? 1 : end - start;
        this._handleChain({
            text: chars,
            start: start,
            length: length
        })
    },
    _tryMoveCaretBackward: function() {
        this.setBackwardDirection();
        const currentCaret = this._caret().start;
        this._adjustCaret();
        return !currentCaret || currentCaret !== this._caret().start
    },
    _adjustCaret: function(char) {
        const caretStart = this._caret().start;
        const isForwardDirection = this.isForwardDirection();
        const caret = this._maskRulesChain.adjustedCaret(caretStart, isForwardDirection, char);
        this._caret({
            start: caret,
            end: caret
        })
    },
    _moveCaret: function() {
        const currentCaret = this._caret().start;
        const maskRuleIndex = currentCaret + (this.isForwardDirection() ? 0 : -1);
        const caret = this._maskRulesChain.isAccepted(maskRuleIndex) ? currentCaret + (this.isForwardDirection() ? 1 : -1) : currentCaret;
        this._caret({
            start: caret,
            end: caret
        })
    },
    _caret: function(position, force) {
        const $input = this._input();
        if (!$input.length) {
            return
        }
        if (!arguments.length) {
            return caret($input)
        }
        caret($input, position, force)
    },
    _hasSelection: function() {
        const caret = this._caret();
        return caret.start !== caret.end
    },
    _direction: function(direction) {
        if (!arguments.length) {
            return this._typingDirection
        }
        this._typingDirection = direction
    },
    setForwardDirection: function() {
        this._direction("forward")
    },
    setBackwardDirection: function() {
        this._direction("backward")
    },
    isForwardDirection: function() {
        return "forward" === this._direction()
    },
    _updateMaskInfo() {
        this._textValue = this._maskRulesChain.text();
        this._value = this._maskRulesChain.value()
    },
    _clean: function() {
        this._maskStrategy && this._maskStrategy.clean();
        this.callBase()
    },
    _validateMask: function() {
        if (!this._maskRulesChain) {
            return
        }
        const isValid = (0, _string.isEmpty)(this.option("value")) || this._maskRulesChain.isValid(this._normalizeChainArguments());
        this.option({
            isValid: isValid,
            validationError: isValid ? null : {
                editorSpecific: true,
                message: this.option("maskInvalidMessage")
            }
        })
    },
    _updateHiddenElement: function() {
        this._removeHiddenElement();
        if (this.option("mask")) {
            this._input().removeAttr("name");
            this._renderHiddenElement()
        }
        this._setSubmitElementName(this.option("name"))
    },
    _updateMaskOption: function() {
        this._updateHiddenElement();
        this._renderMask();
        this._validateMask();
        this._refreshValueChangeEvent()
    },
    _processEmptyMask: function(mask) {
        if (mask) {
            return
        }
        const value = this.option("value");
        this.option({
            text: value,
            isValid: true,
            validationError: null
        });
        this.validationRequest.fire({
            value: value,
            editor: this
        });
        this._renderValue()
    },
    _optionChanged: function(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();
                this.callBase(args);
                this._changedValue = this._input().val();
                break;
            case "maskInvalidMessage":
                break;
            case "showMaskMode":
                this.option("text", "");
                this._renderValue();
                break;
            default:
                this.callBase(args)
        }
    },
    clear: function() {
        const {
            value: defaultValue
        } = this._getDefaultOptions();
        if (this.option("value") === defaultValue) {
            this._renderMaskedValue()
        }
        this.callBase()
    }
});
var _default = TextEditorMask;
exports.default = _default;
module.exports = exports.default;
module.exports.default = exports.default;
