| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- /**
- * DevExtreme (ui/text_box/ui.text_editor.mask.js)
- * Version: 19.1.16
- * Build date: Tue Oct 18 2022
- *
- * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
- * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
- */
- "use strict";
- var $ = require("../../core/renderer");
- var caret = require("./utils.caret");
- var devices = require("../../core/devices");
- var each = require("../../core/utils/iterator").each;
- var eventUtils = require("../../events/utils");
- var eventsEngine = require("../../events/core/events_engine");
- var extend = require("../../core/utils/extend").extend;
- var focused = require("../widget/selectors").focused;
- var isDefined = require("../../core/utils/type").isDefined;
- var messageLocalization = require("../../localization/message");
- var noop = require("../../core/utils/common").noop;
- var stringUtils = require("../../core/utils/string");
- var wheelEvent = require("../../events/core/wheel");
- var MaskRules = require("./ui.text_editor.mask.rule");
- var TextEditorBase = require("./ui.text_editor.base");
- var DefaultMaskStrategy = require("./ui.text_editor.mask.strategy.default").default;
- var AndroidMaskStrategy = require("./ui.text_editor.mask.strategy.android").default;
- var stubCaret = function() {
- return {}
- };
- var EMPTY_CHAR = " ";
- var ESCAPED_CHAR = "\\";
- var TEXTEDITOR_MASKED_CLASS = "dx-texteditor-masked";
- var FORWARD_DIRECTION = "forward";
- var BACKWARD_DIRECTION = "backward";
- var 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)
- }
- };
- var isNumericChar = function(char) {
- return /[0-9]/.test(char)
- };
- var isLiteralChar = function(char) {
- var code = char.charCodeAt();
- return 64 < code && code < 91 || 96 < code && code < 123 || code > 127
- };
- var isSpaceChar = function(char) {
- return " " === char
- };
- var TextEditorMask = TextEditorBase.inherit({
- _getDefaultOptions: function() {
- return extend(this.callBase(), {
- mask: "",
- maskChar: "_",
- maskRules: {},
- maskInvalidMessage: messageLocalization.format("validation-mask"),
- useMaskedValue: false,
- showMaskMode: "always"
- })
- },
- _supportedKeys: function() {
- var that = this;
- var keyHandlerMap = {
- backspace: that._maskStrategy.getHandler("backspace"),
- del: that._maskStrategy.getHandler("del"),
- enter: that._changeHandler
- };
- var result = that.callBase();
- each(keyHandlerMap, function(key, callback) {
- var 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() {
- var device = devices.real();
- this._maskStrategy = device.android && device.version[0] > 4 ? new AndroidMaskStrategy(this) : new DefaultMaskStrategy(this)
- },
- _initMarkup: function() {
- this._renderHiddenElement();
- this.callBase()
- },
- _attachMouseWheelEventHandlers: function() {
- var hasMouseWheelHandler = this._onMouseWheel !== noop;
- if (!hasMouseWheelHandler) {
- return
- }
- var input = this._input();
- var eventName = eventUtils.addNamespace(wheelEvent.name, this.NAME);
- var mouseWheelAction = this._createAction(function(e) {
- if (focused(input)) {
- var dxEvent = e.event;
- this._onMouseWheel(dxEvent);
- dxEvent.preventDefault();
- dxEvent.stopPropagation()
- }
- }.bind(this));
- eventsEngine.off(input, eventName);
- eventsEngine.on(input, eventName, function(e) {
- mouseWheelAction({
- event: e
- })
- })
- },
- _onMouseWheel: noop,
- _render: function() {
- this.callBase();
- this._renderMask();
- this._attachMouseWheelEventHandlers()
- },
- _renderHiddenElement: function() {
- if (this.option("mask")) {
- this._$hiddenElement = $("<input>").attr("type", "hidden").appendTo(this._inputWrapper())
- }
- },
- _removeHiddenElement: function() {
- this._$hiddenElement && this._$hiddenElement.remove()
- },
- _renderMask: function() {
- this.$element().removeClass(TEXTEDITOR_MASKED_CLASS);
- this._maskRulesChain = null;
- this._maskStrategy.detachEvents();
- if (!this.option("mask")) {
- return
- }
- this.$element().addClass(TEXTEDITOR_MASKED_CLASS);
- this._maskStrategy.attachEvents();
- this._parseMask();
- this._renderMaskedValue()
- },
- _suppressCaretChanging: function(callback, args) {
- var originalCaret = caret;
- caret = stubCaret;
- try {
- callback.apply(this, args)
- } finally {
- caret = originalCaret
- }
- },
- _changeHandler: function(e) {
- var $input = this._input();
- var inputValue = $input.val();
- if (inputValue === this._changedValue) {
- return
- }
- this._changedValue = inputValue;
- var changeEvent = eventUtils.createEvent(e, {
- type: "change"
- });
- eventsEngine.trigger($input, changeEvent)
- },
- _parseMask: function() {
- this._maskRules = extend({}, buildInMaskRules, this.option("maskRules"));
- this._maskRulesChain = this._parseMaskRule(0)
- },
- _parseMaskRule: function(index) {
- var mask = this.option("mask");
- if (index >= mask.length) {
- return new MaskRules.EmptyMaskRule
- }
- var currentMaskChar = mask[index];
- var isEscapedChar = currentMaskChar === ESCAPED_CHAR;
- var result = isEscapedChar ? new MaskRules.StubMaskRule({
- maskChar: mask[index + 1]
- }) : this._getMaskRule(currentMaskChar);
- result.next(this._parseMaskRule(index + 1 + isEscapedChar));
- return result
- },
- _getMaskRule: function(pattern) {
- var ruleConfig;
- each(this._maskRules, function(rulePattern, allowedChars) {
- if (rulePattern === pattern) {
- ruleConfig = {
- pattern: rulePattern,
- allowedChars: allowedChars
- };
- return false
- }
- });
- return isDefined(ruleConfig) ? new MaskRules.MaskRule(extend({
- maskChar: this.option("maskChar")
- }, ruleConfig)) : new MaskRules.StubMaskRule({
- maskChar: pattern
- })
- },
- _renderMaskedValue: function() {
- if (!this._maskRulesChain) {
- return
- }
- var value = this.option("value") || "";
- this._maskRulesChain.clear(this._normalizeChainArguments());
- var 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
- }
- var textBefore = text.slice(0, selection.start);
- var textAfter = text.slice(selection.end);
- var 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 stringUtils.isEmpty(this._value)
- },
- _shouldShowMask: function() {
- var showMaskMode = this.option("showMaskMode");
- if ("onFocus" === showMaskMode) {
- return focused(this._input()) || !this._isValueEmpty()
- }
- return true
- },
- _showMaskPlaceholder: function() {
- if (this._shouldShowMask()) {
- var text = this._maskRulesChain.text();
- this.option("text", text);
- if ("onFocus" === this.option("showMaskMode")) {
- this._renderDisplayText(text)
- }
- }
- },
- _renderValue: function() {
- if (this._maskRulesChain) {
- var text = this._maskRulesChain.text();
- this._showMaskPlaceholder();
- if (this._$hiddenElement) {
- var value = this._maskRulesChain.value();
- var hiddenElementValue = this._isMaskedValueMode() ? text : value;
- this._$hiddenElement.val(!stringUtils.isEmpty(value) ? hiddenElementValue : "")
- }
- }
- return this.callBase()
- },
- _valueChangeEventHandler: function(e) {
- if (!this._maskRulesChain) {
- this.callBase.apply(this, arguments);
- return
- }
- this._saveValueChangeEvent(e);
- this.option("value", this._convertToValue().replace(/\s+$/, ""))
- },
- _isControlKeyFired: function(e) {
- return this._isControlKey(eventUtils.normalizeKeyName(e)) || e.ctrlKey || e.metaKey
- },
- _handleChain: function(args) {
- var handledCount = this._maskRulesChain.handle(this._normalizeChainArguments(args));
- this._value = this._maskRulesChain.value();
- this._textValue = this._maskRulesChain.text();
- 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"), EMPTY_CHAR)
- },
- _maskKeyHandler: function(e, keyHandler) {
- var _this = this;
- if (this.option("readOnly")) {
- return
- }
- this.setForwardDirection();
- e.preventDefault();
- this._handleSelection();
- var previousText = this._input().val();
- var raiseInputEvent = function() {
- if (previousText !== _this._input().val()) {
- _this._maskStrategy.runWithoutEventProcessing(function() {
- return eventsEngine.trigger(_this._input(), "input")
- })
- }
- };
- var 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_DIRECTION);
- this._adjustCaret(key);
- this._handleKeyChain(key);
- this._moveCaret()
- },
- _handleSelection: function() {
- if (!this._hasSelection()) {
- return
- }
- var caret = this._caret();
- var emptyChars = new Array(caret.end - caret.start + 1).join(EMPTY_CHAR);
- this._handleKeyChain(emptyChars)
- },
- _handleKeyChain: function(chars) {
- var caret = this._caret();
- var start = this.isForwardDirection() ? caret.start : caret.start - 1;
- var end = this.isForwardDirection() ? caret.end : caret.end - 1;
- var length = start === end ? 1 : end - start;
- this._handleChain({
- text: chars,
- start: start,
- length: length
- })
- },
- _tryMoveCaretBackward: function() {
- this.setBackwardDirection();
- var currentCaret = this._caret().start;
- this._adjustCaret();
- return !currentCaret || currentCaret !== this._caret().start
- },
- _adjustCaret: function(char) {
- var caret = this._maskRulesChain.adjustedCaret(this._caret().start, this.isForwardDirection(), char);
- this._caret({
- start: caret,
- end: caret
- })
- },
- _moveCaret: function() {
- var currentCaret = this._caret().start;
- var maskRuleIndex = currentCaret + (this.isForwardDirection() ? 0 : -1);
- var caret = this._maskRulesChain.isAccepted(maskRuleIndex) ? currentCaret + (this.isForwardDirection() ? 1 : -1) : currentCaret;
- this._caret({
- start: caret,
- end: caret
- })
- },
- _caret: function(position) {
- var $input = this._input();
- if (!$input.length) {
- return
- }
- if (!arguments.length) {
- return caret($input)
- }
- caret($input, position)
- },
- _hasSelection: function() {
- var 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_DIRECTION)
- },
- setBackwardDirection: function() {
- this._direction(BACKWARD_DIRECTION)
- },
- isForwardDirection: function() {
- return this._direction() === FORWARD_DIRECTION
- },
- _clean: function() {
- this._maskStrategy && this._maskStrategy.clean();
- this.callBase()
- },
- _validateMask: function() {
- if (!this._maskRulesChain) {
- return
- }
- var isValid = stringUtils.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()
- },
- _processEmptyMask: function(mask) {
- if (mask) {
- return
- }
- var value = this.option("value");
- this.option({
- text: value,
- isValid: true
- });
- 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);
- break;
- case "maskInvalidMessage":
- break;
- case "showMaskMode":
- this.option("text", "");
- this._renderValue();
- break;
- default:
- this.callBase(args)
- }
- }
- });
- module.exports = TextEditorMask;
|