UNPKG

@true-directive/base

Version:

The set of base classes for the TrueDirective Grid

628 lines (627 loc) 27.5 kB
import { MaskSectionValue } from './mask-section-value.class'; import { MaskValue } from './mask-value.class'; import { Keys } from '../common/keys.class'; // Actions which are initialized by section var MaskSectionAction = /** @class */ (function () { function MaskSectionAction(name) { this.name = name; } MaskSectionAction.NONE = new MaskSectionAction('NONE'); MaskSectionAction.APPLY = new MaskSectionAction('APPLY'); MaskSectionAction.SKIP = new MaskSectionAction('SKIP'); MaskSectionAction.GO_FWD = new MaskSectionAction('GO_FWD'); MaskSectionAction.GO_BACK = new MaskSectionAction('GO_BACK'); MaskSectionAction.GO_BACK_AND_DELETE = new MaskSectionAction('GO_BACK_AND_DELETE'); MaskSectionAction.GO_BACK_AND_BACKSPACE = new MaskSectionAction('GO_BACK_AND_BACKSPACE'); return MaskSectionAction; }()); export { MaskSectionAction }; // Result of user input var MaskResult = /** @class */ (function () { function MaskResult(newValue, action, nextSectionPos) { this.newValue = newValue; this.action = action; this.nextSectionPos = nextSectionPos; } return MaskResult; }()); export { MaskResult }; // Section of pattern var MaskSection = /** @class */ (function () { function MaskSection(settings, section, // Value of the mask delimiter, sectionType) { if (sectionType === void 0) { sectionType = null; } this.settings = settings; this.section = section; this.delimiter = delimiter; this.sectionType = sectionType; } Object.defineProperty(MaskSection.prototype, "length", { // Minimum length of value get: function () { if (this.hasOptions()) { var min_1 = 99; this.sectionType.options.forEach(function (v) { if (v.length < min_1) { min_1 = v.length; } }); return min_1; } return this.section.length; }, enumerable: true, configurable: true }); Object.defineProperty(MaskSection.prototype, "maxLength", { // Maximum length get: function () { if (this.hasOptions()) { var ml_1 = 0; this.sectionType.options.forEach(function (v) { if (v.length > ml_1) { ml_1 = v.length; } }); return ml_1; } if (this.sectionType && this.sectionType.max && ('' + this.sectionType.max).length > this.section.length) { return ('' + this.sectionType.max).length; } else { return this.section.length; } }, enumerable: true, configurable: true }); MaskSection.prototype.isEmptySection = function () { return this.section === ''; }; MaskSection.prototype.hasOptions = function () { return this.sectionType && this.sectionType.options && this.sectionType.options.length > 0; }; MaskSection.prototype.hasRegExp = function () { return this.sectionType && this.sectionType.regExp !== undefined; }; MaskSection.prototype.isNumeric = function () { return this.sectionType && this.sectionType.numeric; }; MaskSection.prototype.numericValue = function (value) { return +value; }; MaskSection.prototype.checkMinMax = function (n) { if (n === null) { return null; } if (!this.sectionType) { return n; } if (this.sectionType.min !== undefined && n < this.sectionType.min) { n = this.sectionType.min; } if (this.sectionType.max !== undefined && n > this.sectionType.max) { n = this.sectionType.max; } return n; }; // String placeholders cleanup method MaskSection.prototype.removePlaceholders = function (txt) { return txt.split(this.settings.placeholder).join(''); }; // Increasing section's value upon Up button press MaskSection.prototype.incValue = function (value) { // Next option if (this.hasOptions()) { var i = this.sectionType.options.indexOf(value); return i < this.sectionType.options.length - 1 ? this.sectionType.options[i + 1] : this.sectionType.options[0]; } // Incrementing numeric value if (this.isNumeric() && this.sectionType.max !== undefined) { var n = this.numericValue(value); if (isNaN(n)) { n = this.sectionType.min === undefined ? 0 : this.sectionType.min; } else { n++; } var res = '' + this.checkMinMax(n); while (res.length < this.length) { res = '0' + res; } return res; } // Returning current value if none found return value; }; // Предыдущее значение секции при нажатии стрелки вниз MaskSection.prototype.decValue = function (value) { // Previous option if (this.hasOptions()) { var i = this.sectionType.options.indexOf(value); return i > 0 ? this.sectionType.options[i - 1] : this.sectionType.options[this.sectionType.options.length - 1]; } // Previous numeric value if (this.isNumeric() && this.sectionType.min !== undefined) { var n = this.numericValue(value); if (isNaN(n)) { n = this.sectionType.min === undefined ? 0 : this.sectionType.max; } else { n--; } var res = '' + this.checkMinMax(n); while (res.length < this.length) { res = '0' + res; } return res; } // Returning current value if none found return value; }; // Auto-correction of section's value MaskSection.prototype.autoCorrectVal = function (s) { if (!this.sectionType) { return s; } if (this.hasOptions()) { var variant = this.sectionType.options.find(function (v) { return v.toLowerCase() === s.toLowerCase(); }); if (!variant) { s = this.sectionType.options.length > 0 ? this.sectionType.options[0] : ''; } return s; } var res = s; if (this.isNumeric()) { var n = this.numericValue(res); // Numeric value is expected if (isNaN(n) || s === '') { return s; // Bad or empty value } // Year if (this.sectionType.datePart === 'yyyy') { if (s.length === 2) { n += n < 50 ? 2000 : 1900; } else { if (s.length !== 4) { return s; } } } // Numeric value recognized. Identifying min and max values. // But only if autoCorrect is enabled. // Otherwise numeric value of defined length is returned if (this.settings.autoCorrect) { n = this.checkMinMax(n); } if (n !== null) { res = '' + n; while (res.length < this.length) { res = '0' + res; } } } while (res.length < this.length) { res += this.settings.placeholder; } return res; }; // Section extracts its value from input using following method MaskSection.prototype.extract = function (maskValue, // Input mask value sectionPos, // Section value's first char index selStart, selLength) { if (selStart === void 0) { selStart = 0; } if (selLength === void 0) { selLength = 0; } var res = new MaskValue(); var len = this.length; var i2 = sectionPos + len; // Variable length brings trouble... if (this.length < this.maxLength) { // ...so here a small block should be placed to identify relative position compared to delimiter or end of line, // but less than MaxLength var i = sectionPos; while (i < maskValue.length && i < (sectionPos + this.maxLength)) { if (this.delimiter !== '' && maskValue[i] === this.delimiter[0]) { break; } i++; } i2 = i; len = i2 - sectionPos; } // Delimiter might not be present... var delimiterStart = i2; var delimiterEnd = delimiterStart; if (this.delimiter !== '') { while (delimiterEnd < maskValue.length) { var c = maskValue[delimiterEnd]; if (c === this.delimiter[delimiterEnd - delimiterStart]) { delimiterEnd++; } else { break; } } } if (i2 > maskValue.length) { i2 = maskValue.length; } if (delimiterEnd > maskValue.length) { delimiterEnd = maskValue.length; } var section = maskValue.substring(sectionPos, i2); if (section.length > this.maxLength) { throw new Error('Invalid value length: ' + section); } var selStart_local = selStart - sectionPos; res.sectionPos = sectionPos; res.before = maskValue.substring(0, sectionPos); // До секции res.section = new MaskSectionValue(section, sectionPos, selStart); // Значение секции res.delimiter = maskValue.substring(delimiterStart, delimiterEnd); // Разделитель res.after = maskValue.substring(delimiterEnd); // После секции res.inSection = selStart_local >= 0 && selStart_local <= res.section.length; return res; }; MaskSection.prototype.skip = function (mv, selStart) { var res = new MaskResult(mv.value(), MaskSectionAction.SKIP, mv.nextSectionPos()); res.selStart = selStart; return res; }; MaskSection.prototype.none = function (mv) { var res = new MaskResult(mv.value(), MaskSectionAction.NONE, mv.nextSectionPos()); return res; }; // Section processing result - button pressing is applied MaskSection.prototype.apply = function (mv, newSectionValue, selStart, direction, isLast) { if (direction === void 0) { direction = 1; } if (isLast === void 0) { isLast = false; } var selStart_local = selStart - mv.sectionPos; // If section is the last and value length is equal to maximum length and carriage is positioned at end of line... // ... we're correcting the value if (isLast && newSectionValue.length === this.maxLength && selStart_local >= newSectionValue.length - 1 && direction > 0) { newSectionValue = this.autoCorrectVal(newSectionValue); mv.delimiter = this.delimiter; } // Updating the value mv.update(newSectionValue, selStart); // Deciding what's next if (direction > 0) { return this.goFwd(mv, selStart, 1, false); // Moving forward } else { if (direction < 0) { return this.goBack(mv, selStart, 1, false, isLast); // Moving backwards } else { // Stand still var res = new MaskResult(mv.value(), MaskSectionAction.APPLY, mv.nextSectionPos()); res.selStart = selStart; res.selLength = this.settings.replaceMode && selStart_local < this.length ? 1 : 0; return res; } } }; // Section processing result - button with delimiter symbol pressing is applied MaskSection.prototype.applyDelimiter = function (mv, selStart) { // autoCorrection is necessary var sv = this.autoCorrectVal(mv.section.value()); mv.update(sv, selStart); mv.delimiter = this.delimiter; // Moving forward var res = new MaskResult(mv.value(), MaskSectionAction.GO_FWD, mv.nextSectionPos()); res.selStart = mv.nextSectionPos(); return res; }; // Moving carriage backward MaskSection.prototype.goBack = function (mv, selStart, selLength, byBackspace, isLast) { if (byBackspace === void 0) { byBackspace = false; } if (isLast === void 0) { isLast = false; } var res = new MaskResult(mv.value(), MaskSectionAction.APPLY, mv.nextSectionPos()); if (selStart === 0 && selLength <= 1) { // Case when first symbol is selected. Carriage position is set to the beginning of the line res.selLength = 0; return res; } // For last section with delimiter it doesn't matter if we are standing before or after delimiter if (isLast && selStart > (mv.sectionPos + this.maxLength) && this.settings.replaceMode) { selStart = mv.sectionPos + this.maxLength; } res.selStart = selStart - 1; res.selLength = this.settings.replaceMode ? 1 : 0; var selStart_local = res.selStart - mv.sectionPos; // If we exceed minimal length... if (selStart_local >= this.length && selStart_local >= mv.section.length) { res.selLength = 0; } var newSelStart_local = res.selStart - mv.sectionPos; if (newSelStart_local < 0 && mv.before !== '') { res.action = byBackspace ? MaskSectionAction.GO_BACK_AND_DELETE : MaskSectionAction.GO_BACK; } if (res.selStart < 0) { res.selStart = 0; } if (res.selLength === 0 && selStart === 0) { res.selLength = 0; } return res; }; // Moving carriage forward MaskSection.prototype.goFwd = function (mv, selStart, selLength, appendDelimiter) { if (appendDelimiter === void 0) { appendDelimiter = false; } var selStart_local = selStart - mv.sectionPos; var res = new MaskResult(mv.value(), MaskSectionAction.APPLY, mv.nextSectionPos()); // replaceMode, selLength === 0 and something is present if (this.settings.replaceMode && selLength === 0 && mv.section.currentChar !== '') { // Carriage stands still and next char is selected res.selStart = selStart; res.selLength = 1; } else { // Moving one char forward if (mv.after !== '' || mv.section.afterChars !== '') { res.selStart = selStart + 1; res.selLength = (mv.section.currentChar !== '' && this.settings.replaceMode) ? 1 : 0; // Section with variable length. If we reached the end of it, then selLength = 0 if (res.selStart - mv.sectionPos >= this.length && mv.section.afterChars === '') { res.selLength = 0; } } else { // Nothing's present next, just positoning the carriage in the end of the section res.selStart = selStart + 1; res.selLength = 0; } } var newSelStart_local = res.selStart - mv.sectionPos; // We're out of bounds if (newSelStart_local > mv.section.length || (newSelStart_local === this.maxLength && mv.after !== '')) { // Trying to apply autoCorrection var v = this.autoCorrectVal(mv.section.value()); mv.delimiter = this.delimiter; // After that carriage's position and next section's position could be different mv.update(v, newSelStart_local); res.newValue = mv.value(); res.nextSectionPos = mv.nextSectionPos(); res.selStart = mv.section.length; // Moving forward to next section res.action = MaskSectionAction.GO_FWD; } return res; }; MaskSection.prototype.isDigit = function (char) { return /\d/.test(char); // char.match(/\d/) !== ''; }; // Checking if Button is applicable to section // We should return: // - if button pressing has been applied // - new section value // - new carriage position // - new selection length MaskSection.prototype.applyKey = function (value, // Mask value keyCode, // Key which has been pressed keyChar, sectionPos, // Section's first symbol index selStart, // Current carriage position selLength, // Number of currently selected chars acceptDelimiterChars, // If symbols of delimiter are acceptable isLast // Is last section ) { if (acceptDelimiterChars === void 0) { acceptDelimiterChars = false; } if (isLast === void 0) { isLast = false; } // Parsing the value var mv = this.extract(value, sectionPos, selStart, selLength); // Cursor's positioned before the section. Doing nothing if (selStart < sectionPos) { return this.none(mv); } // Cursor's positioned after the section. Skipping the section if (!mv.inSection) { if (value.length !== selStart || !isLast) { return this.skip(mv, selStart); } } // Relative carriage position compared to current section's beginning var selStart_local = selStart - sectionPos; // If carriage is positioned at the end of section and section doesn't have a delimiter // And next section contains something // Then we're located not in current section, but in the beginning of the next. Sending a SKIP if (selStart_local === this.maxLength && this.delimiter === '' && mv.after !== '') { return this.skip(mv, selStart); } if (keyChar !== this.delimiter[0] || !acceptDelimiterChars) { if (this.isEmptySection() || // Empty section (selStart === (sectionPos + mv.section.length) && // Our position's the beginning of the section mv.section.length === this.maxLength && // Section value is complete keyChar.length === 1)) { // Adding a delimiter if necessary // Setting a corrected value and asking to move to a next section mv.delimiter = this.delimiter; mv.update(this.autoCorrectVal(mv.section.value()), selStart); return this.skip(mv, mv.nextSectionPos()); // To the beginning of next section } } // Single char if (keyChar.length === 1) { var cValue_1 = mv.section.beforeChars; cValue_1 += keyChar; // Potentially new value // Section values are limited to a list of possible values if (this.hasOptions() && cValue_1 !== mv.section.beforeChars) { var optionFound_1 = null; this.sectionType.options.some(function (variant) { if (selStart_local < variant.length && variant.substring(0, cValue_1.length).toLowerCase() === cValue_1.toLowerCase()) { optionFound_1 = variant; return true; } return false; }); if (optionFound_1 !== null) { return this.apply(mv, optionFound_1, selStart, 1, isLast); } } // Regular expression if (this.hasRegExp()) { // New value will be: var nv = mv.section.value(keyChar); if (this.sectionType.regExp.test(nv)) { // And we can accept it return this.apply(mv, nv, selStart, 1, isLast); } } // Section is configured with char types if (!this.hasRegExp() && !this.hasOptions() && !this.isEmptySection()) { var isOk = false; if (selStart_local < this.maxLength) { if (this.sectionType.numeric && this.isDigit(keyChar)) { isOk = true; } } if (isOk) { return this.apply(mv, mv.section.value(keyChar), selStart, 1, isLast); } } // Delimiter char is entered if (this.delimiter !== '' && keyChar === this.delimiter[0] && acceptDelimiterChars) { // If nothing has been entered then there is no reason to go forward if (this.removePlaceholders(mv.section.value()) === '' && !this.isEmptySection()) { return this.apply(mv, mv.section.value(), selStart, 0); } return this.applyDelimiter(mv, selStart); } } // Delete key if (keyCode === Keys.DELETE) { // Variable length of the section. We've exceeded minimum value if (selStart_local >= this.length && mv.section.afterChars === '') { if (mv.after === '') { mv.delimiter = ''; } return this.apply(mv, mv.section.beforeChars, selStart, 0); } if (mv.after === '' && mv.section.afterChars === '') { // No more data after the char being deleted mv.delimiter = ''; return this.apply(mv, mv.section.beforeChars, selStart, 0); } else { return this.apply(mv, mv.section.value(this.settings.placeholder), selStart, 0); } } // Backspace key if (keyCode === Keys.BACKSPACE) { if (mv.section.length === 0) { return this.goBack(mv, selStart, selLength, true, isLast); } if (mv.section.beforeChars === '') { // Nothing to delete in current section if (mv.before === '') { // is first return this.none(mv); } // Deleting in the previous section return this.goBack(mv, selStart, selLength, true, isLast); } mv.section.beforeChars = mv.section.beforeChars.substring(0, mv.section.beforeChars.length - 1); if ((mv.section.beforeChars.length >= this.length && mv.after === '') || (mv.section.currentChar === '' && mv.after === '')) { // Deleting completely // Delimiter should be deleted too mv.delimiter = ''; } else { // Replacing with a placeholder if (mv.section.beforeChars.length < this.length) { mv.section.beforeChars += this.settings.placeholder; } } return this.apply(mv, mv.section.value(), selStart, -1, isLast); } // Moving backward if (keyCode === Keys.LEFT) { return this.goBack(mv, selStart, selLength, false, isLast); } // Moving forward if (keyCode === Keys.RIGHT) { return this.goFwd(mv, selStart, selLength); } // Incrementing value of the section upon 'Up' key pressing if (keyCode === Keys.UP && this.settings.incDecByArrows) { return this.apply(mv, this.incValue(mv.section.value()), selStart, 0); } // Decrementing value of the section upon 'Down' key pressing if (keyCode === Keys.DOWN && this.settings.incDecByArrows) { return this.apply(mv, this.decValue(mv.section.value()), selStart, 0); } return this.none(mv); }; // If section is empty then fill with first option of list MaskSection.prototype.setDefaultVariant = function (value, sectionPos) { if (!this.settings.defaultOptions) { return value; } if (!this.hasOptions()) { return value; } // Get section value var mv = this.extract(value, sectionPos, 0, 0); // Remove placeholderes var s = this.removePlaceholders(mv.section.value()); // If empty... if (s === '') { var applyResult = this.apply(mv, this.sectionType.options[0], 0, 0); value = applyResult.newValue; } return value; }; // Selcting first symbol of the section MaskSection.prototype.selectFirst = function (value, sectionPos) { var mv = this.extract(value, sectionPos, sectionPos, 0); var res = new MaskResult(mv.value(), MaskSectionAction.APPLY, mv.nextSectionPos()); res.selStart = sectionPos; res.selLength = this.settings.replaceMode ? 1 : 0; if (this.isEmptySection()) { res.selLength = 0; } return res; }; // Selecting last symbol of the section MaskSection.prototype.selectLast = function (value, sectionPos, forDelete) { if (forDelete === void 0) { forDelete = false; } var mv = this.extract(value, sectionPos, sectionPos, 0); var res = new MaskResult(mv.value(), MaskSectionAction.APPLY, mv.nextSectionPos()); if (!this.settings.replaceMode) { // We need to be positioned before the last symbol res.selStart = sectionPos + mv.section.length - 1; res.selLength = 0; return res; } if ((!forDelete && mv.section.length >= this.length && mv.section.length < this.maxLength) || this.isEmptySection()) { // We haven't reached maximum length of the section res.selStart = sectionPos + mv.section.length; res.selLength = 0; } else { // We've reached maxLength - selecting last symbol res.selStart = sectionPos + mv.section.length - 1; res.selLength = 1; } return res; }; // Autocorrection of the value. Returning everything required in order to apply changes to a control MaskSection.prototype.autoCorrect = function (value, sectionPos, selStart, selLength) { // Parsing var mv = this.extract(value, sectionPos, selStart, 0); var v = mv.section.value(); // Correcting the value v = this.autoCorrectVal(v); // Updating the result mv.update(v, selStart); var res = new MaskResult(mv.value(), MaskSectionAction.APPLY, mv.nextSectionPos()); res.selStart = res.newValue.length; res.selLength = 0; return res; }; return MaskSection; }()); export { MaskSection };