UNPKG

@true-directive/base

Version:

The set of base classes for the TrueDirective Grid

424 lines (423 loc) 20.2 kB
import { MaskSection, MaskSectionAction } from './mask-section.class'; import { MaskSettings } from './mask-settings.class'; import { Keys } from '../common/keys.class'; var Mask = /** @class */ (function () { function Mask() { // Settings this._settings = null; this._separators = ['.', ',']; // Sections with section chars this._singles = '*aAnN#0'; // Locale formats this.localeDateFormat = ''; this.localeTimeHMFormat = ''; this.localeTimeHMSFormat = ''; this.localeDateTimeHMFormat = ''; this.localeDateTimeHMSFormat = ''; // The list of sections this.sections = []; } Object.defineProperty(Mask.prototype, "settings", { // Settings by default get: function () { return this._settings === null ? Mask.defaultSettings : this._settings; }, set: function (o) { this._settings = o; this.sections.forEach(function (s) { return s.settings = o; }); this.updateMask(); }, enumerable: true, configurable: true }); Object.defineProperty(Mask.prototype, "pattern", { get: function () { return this._pattern; }, set: function (v) { this._pattern = v; this.updateMask(); }, enumerable: true, configurable: true }); // Определяем тип секции по шаблону Mask.prototype.selectSectionType = function (s) { // First, look in the settings var res = this.settings.sectionTypes.find(function (i) { return (i.selectors.find(function (sel) { return sel === s; }) !== undefined); }); if (res !== undefined) { return res; } // Then, in predefined section types return Mask.sectionTypes.find(function (i) { return (i.selectors.find(function (sel) { return sel === s; }) !== undefined); }); }; // Определяем, существуют ли типы секций, которые начинаются на заданный символ. Mask.prototype.selectSectionTypeByFirstChar = function (char) { // First, look in the settings var res = this.settings.sectionTypes.find(function (i) { return (i.selectors.find(function (sel) { return sel[0] === char; }) !== null); }); if (res !== null) { return res; } // Then, in predefined section types return Mask.sectionTypes.find(function (i) { return (i.selectors.find(function (sel) { return sel[0] === char; }) !== null); }); }; // Добавляет в список секций пустую секцию, имеющую разделитель Mask.prototype.addEmptySection = function (delimiter) { this.sections.push(new MaskSection(this.settings, '', delimiter)); }; // Добавление секции в список Mask.prototype.addSection = function (section, delimiter) { var sType = this.selectSectionType(section); if (!sType) { // Если секция не распознана - считаем это фиксированным текстом // и для каждого символа создаем пустую секцию с разделителем - этим // символом. Тогда маска будет принимать только их и переходить к // следующей секции for (var i = 0; i < section.length; i++) { this.addEmptySection(section[i]); } // Про разделители тоже нужно не забыть for (var i = 0; i < delimiter.length; i++) { this.addEmptySection(delimiter[i]); } return; } var s = new MaskSection(this.settings, section, delimiter, sType); // Так-то вообще, если разделитель длиннее одного символа, // нам нужно добавить пустые секции для всех символов кроме первого. // Но пока не будем здесь усложнять... s.delimiter = delimiter; this.sections.push(s); }; // Получаем чистое значение без разделителей // Для преобразования из одного шаблона в другой. // Годится только для шаблонов, в котором все секции имеют фиксированную длину Mask.prototype.pureValue = function (value) { if (value === null) { return value; } var sectionPos = 0; var res = ''; this.sections.forEach(function (section) { var v = section.extract(value, sectionPos); res += section.removePlaceholders(v.section.value()); sectionPos = v.nextSectionPos(); }); return res; }; // Применяем чистое значение к шаблону и возвращаем форматированное значение Mask.prototype.applyPureValue = function (value) { // if (value === null) { return value; } var res = ''; var i = 0; this.sections.forEach(function (section) { var l = section.section.length; var s = value.substring(i, i + l); res += s; i += l; if (value.length >= i) { res += section.delimiter; } }); if (this.settings.appendPlaceholders) { res = this.appendPlaceholders(res); } return res; }; // Разбиваем строку маски на секции между разделителями Mask.prototype.updateMask = function () { this.sections = []; var s; // Выбор формата на по локализации switch (this._pattern) { case 'date': { s = this.localeDateFormat; break; } case 'time': case 'timeHM': { s = this.localeTimeHMFormat; break; } case 'timeHMS': { s = this.localeTimeHMSFormat; break; } case 'dateTime': case 'dateTimeHM': { s = this.localeDateTimeHMFormat; break; } case 'dateTimeHMS': { s = this.localeDateTimeHMSFormat; break; } default: s = this._pattern; } if (!s || s.length === 0) { return; } var i = 0; while (i < s.length) { var c = s[i]; var sType = null; var part = ''; if (this._singles.indexOf(c) >= 0) { part = c; sType = this.selectSectionType(c); } else { for (var j = s.length; j >= i; j--) { part = s.substring(i, j); sType = this.selectSectionType(part); if (sType) { break; } } } if (sType) { // Нужно добить разделителем i += part.length; var del = ''; while (Mask.delimiterChars.indexOf(s[i]) >= 0) { del += s[i]; i++; } if (del === '') { // Не найден разделитель if (i < s.length && this.selectSectionTypeByFirstChar(s[i]) === null) { // Если на текущий символ не найдется секции.. // ..то это тоже разделитель del = s[i]; i++; } } this.addSection(part, del); continue; } this.addSection('', c); i++; } }; // Добавляем плэйсхолдеры к значению Mask.prototype.appendPlaceholders = function (value) { var sectionStart = 0; var i = 0; while (i < this.sections.length) { var section = this.sections[i]; var v = section.extract(value, sectionStart); while (v.section.length < section.length) { v.section.append(this.settings.placeholder); } v.delimiter = section.delimiter; // Обновляем значение и позицию следующей секции value = v.value(); sectionStart = v.nextSectionPos(); i++; } return value; }; Mask.prototype.checkMask = function (value) { if (value === null) { return false; } if (value === '' && this.pattern !== '') { return false; } return this.applyMask(value) !== ''; }; // Форматирование строки по маске // Пустая строка будет означать инвалидность Mask.prototype.applyMask = function (value, autoCorrect) { if (autoCorrect === void 0) { autoCorrect = true; } var sectionPos = 0; var res = value; for (var i = 0; i < this.sections.length; i++) { var section = this.sections[i]; var v = section.extract(res, sectionPos); v.delimiter = section.delimiter; var sv = v.section.value(); sv = section.removePlaceholders(sv); if (section.isNumeric()) { // Invalid number value var n = section.numericValue(sv); if (isNaN(n) || sv === '') { return ''; } } if (sv.length < section.length) { if (section.sectionType && section.sectionType.datePart) { var dp = section.sectionType.datePart; if (dp === 'yyyy' && sv.length !== 2) { // For year we can accept value with 2 digits return ''; } if (sv.length < 1) { // For others dateparts we can accept any not empty value return ''; } } else { return ''; } } if (autoCorrect) { sv = section.autoCorrectVal(sv); } res = v.update(sv, 0); sectionPos = v.nextSectionPos(); } res = res.substring(0, sectionPos); return res; }; // Применяем заданный символ к заданному значению в заданном месте Mask.prototype.applyKeyAtPos = function (value, key, char, selStart, selEnd) { if (selEnd === void 0) { selEnd = 0; } var selLength = selEnd - selStart; var sectionStart = 0; var section = null; var prev_section = null; var prev_sectionStart = 0; var acceptDelimiterChars = true; // Добавляем плэйсхолдеры перед обработкой. Чтобы обработчик мог их учитывать // при расчете следующей позиции курсора if (this.settings.appendPlaceholders) { value = this.appendPlaceholders(value); } for (var i = 0; i < this.sections.length; i++) { section = this.sections[i]; // Обработка пользовательского действия var res = section.applyKey(value, key, char, sectionStart, selStart, selLength, acceptDelimiterChars, i === this.sections.length - 1); // Нельзя ничего применить if (res.action === MaskSectionAction.NONE) { return null; } if (this.settings.appendPlaceholders) { // Добавляем еще раз плэйсхолдеры res.newValue = this.appendPlaceholders(res.newValue); } if (res.action === MaskSectionAction.APPLY) { // Готово! return res; } if (res.action === MaskSectionAction.GO_BACK_AND_DELETE && prev_section !== null) { // Идем в конец предыдущей секции var selRes = prev_section.selectLast(res.newValue, prev_sectionStart, true); // И применяем Delete var delRes = prev_section.applyKey(selRes.newValue, Keys.DELETE, '', prev_sectionStart, selRes.selStart, selRes.selLength); return delRes; } if (res.action === MaskSectionAction.GO_BACK_AND_BACKSPACE && prev_section !== null) { // Идем в конец предыдущей секции var selRes = prev_section.selectLast(res.newValue, prev_sectionStart); // И тоже применяем Delete var delRes = prev_section.applyKey(selRes.newValue, Keys.DELETE, '', prev_sectionStart, selRes.selStart, selRes.selLength); return delRes; } if (res.action === MaskSectionAction.GO_BACK && prev_section !== null) { // Идем в конец предыдущей секции return prev_section.selectLast(res.newValue, prev_sectionStart); } // Идем в начало следующей секции if (res.action === MaskSectionAction.GO_FWD) { if (i < this.sections.length - 1) { var next_section = this.sections[i + 1]; var valueWithDefaultVariant = next_section.setDefaultVariant(res.newValue, res.nextSectionPos); return next_section.selectFirst(valueWithDefaultVariant, res.nextSectionPos); } else { // Секция последняя. Скорректируем значение. return section.autoCorrect(res.newValue, sectionStart, res.selStart, res.selLength); } } // К этой секции ничего не применилось. Переходим к следующей секции.. if (res.action === MaskSectionAction.SKIP) { // Запомним положение текущей секции и саму секцию для возврата по BACKSPACE // и стрелке влево if (section !== null && section.section !== '') { // Это условие для того, чтобы нельзя было вернуться на секции // без значащих символов prev_section = section; prev_sectionStart = sectionStart; } // Больше не будем принимать символы разделителей, т.к. // мы его отвергли в одной из предыдущей секций // Пример - +7 921 911 11 11 - в начале строки жмем 7, но + его не принял // Тогда это будет значащий символ уже if (section.section === '' && selStart < res.nextSectionPos) { acceptDelimiterChars = false; } // Даже если мы передали управление следующей секции, значение может // измениться - могут быть добавлены разделители или скорректированы значения value = res.newValue; selStart = res.selStart; sectionStart = res.nextSectionPos; continue; } // Значение кончилось... if (sectionStart > value.length) { return null; } } return null; }; Mask.maskWithPattern = function (intl, pattern) { var mask = new Mask(); mask.setLocale(intl.locale); mask.pattern = pattern; return mask; }; Mask.prototype.setLocale = function (locale) { // Форматы this.localeDateFormat = locale.dateFormat; this.localeTimeHMFormat = locale.timeHMFormat; this.localeTimeHMSFormat = locale.timeHMSFormat; this.localeDateTimeHMFormat = locale.dateTimeHMFormat; this.localeDateTimeHMSFormat = locale.dateTimeHMSFormat; // Разделители this._separators[0] = locale.separators[0]; this._separators[1] = locale.separators[1]; // Устанавливаем короткие названия месяцев this.selectSectionType('mmm').options = locale.shortMonthNames.map(function (el) { return el.toLowerCase(); }); this.selectSectionType('MMM').options = locale.shortMonthNames.map(function (el) { return el.toUpperCase(); }); this.updateMask(); }; // Default settings of all masks Mask.defaultSettings = new MaskSettings('_'); // Delimiters Mask.delimiterChars = " .,()/|-:+ '"; // Predefined section types Mask.sectionTypes = [ // Time components { selectors: ['HH'], numeric: true, min: 0, max: 23, datePart: 'H' }, { selectors: ['h'], numeric: true, min: 1, max: 12, datePart: 'h' }, { selectors: ['hh'], numeric: true, min: 1, max: 12, datePart: 'h' }, { selectors: ['mi', 'MI'], numeric: true, min: 0, max: 59, datePart: 'mi' }, { selectors: ['ss', 'SS'], numeric: true, min: 0, max: 59, datePart: 'ss' }, { selectors: ['TT', 'AM', 'PM'], numeric: false, options: ['AM', 'PM'], datePart: 'tt' }, { selectors: ['tt', 'am', 'pm'], numeric: false, options: ['am', 'pm'], datePart: 'tt' }, { selectors: ['fff'], numeric: true, datePart: 'ms' }, // Date components { selectors: ['d', 'dd', 'DD'], numeric: true, min: 1, max: 31, datePart: 'd' }, { selectors: ['m', 'mm', 'MM'], numeric: true, min: 1, max: 12, datePart: 'm' }, { selectors: ['mmm'], numeric: false, datePart: 'm' }, { selectors: ['MMM'], numeric: false, datePart: 'm' }, { selectors: ['yy', 'YY'], numeric: true, min: 0, max: 99, datePart: 'yy' }, { selectors: ['yyyy', 'YYYY'], numeric: true, min: 0, max: 9999, datePart: 'yyyy' }, // Byte (from 0 to 255) - for ip-address or network mask { selectors: ['b'], numeric: true, min: 0, max: 255 }, // Plus/minus { selectors: ['~'], numeric: false, regExp: /[-+]/ }, // Letter or digit { selectors: ['*'], numeric: false, regExp: /[\d\w]/ }, // Letters { selectors: ['l', 'L'], numeric: false, regExp: /\w/ }, // Digits { selectors: ['n', 'N'], numeric: false, regExp: /\d/ }, ]; return Mask; }()); export { Mask };