| 1 |
import ChangeDetails from '../core/change-details.js'; |
| 2 |
import IMask from '../core/holder.js'; |
| 3 |
import MaskedPattern from './pattern.js'; |
| 4 |
import '../core/utils.js'; |
| 5 |
import './base.js'; |
| 6 |
import '../core/continuous-tail-details.js'; |
| 7 |
import './factory.js'; |
| 8 |
import './pattern/chunk-tail-details.js'; |
| 9 |
import './pattern/cursor.js'; |
| 10 |
import './pattern/fixed-definition.js'; |
| 11 |
import './pattern/input-definition.js'; |
| 12 |
import './regexp.js'; |
| 13 |
|
| 14 |
/** Pattern which accepts ranges */ |
| 15 |
class MaskedRange extends MaskedPattern { |
| 16 |
/** |
| 17 |
Optionally sets max length of pattern. |
| 18 |
Used when pattern length is longer then `to` param length. Pads zeros at start in this case. |
| 19 |
*/ |
| 20 |
|
| 21 |
/** Min bound */ |
| 22 |
|
| 23 |
/** Max bound */ |
| 24 |
|
| 25 |
get _matchFrom() { |
| 26 |
return this.maxLength - String(this.from).length; |
| 27 |
} |
| 28 |
constructor(opts) { |
| 29 |
super(opts); // mask will be created in _update |
| 30 |
} |
| 31 |
updateOptions(opts) { |
| 32 |
super.updateOptions(opts); |
| 33 |
} |
| 34 |
_update(opts) { |
| 35 |
const { |
| 36 |
to = this.to || 0, |
| 37 |
from = this.from || 0, |
| 38 |
maxLength = this.maxLength || 0, |
| 39 |
autofix = this.autofix, |
| 40 |
...patternOpts |
| 41 |
} = opts; |
| 42 |
this.to = to; |
| 43 |
this.from = from; |
| 44 |
this.maxLength = Math.max(String(to).length, maxLength); |
| 45 |
this.autofix = autofix; |
| 46 |
const fromStr = String(this.from).padStart(this.maxLength, '0'); |
| 47 |
const toStr = String(this.to).padStart(this.maxLength, '0'); |
| 48 |
let sameCharsCount = 0; |
| 49 |
while (sameCharsCount < toStr.length && toStr[sameCharsCount] === fromStr[sameCharsCount]) ++sameCharsCount; |
| 50 |
patternOpts.mask = toStr.slice(0, sameCharsCount).replace(/0/g, '\\0') + '0'.repeat(this.maxLength - sameCharsCount); |
| 51 |
super._update(patternOpts); |
| 52 |
} |
| 53 |
get isComplete() { |
| 54 |
return super.isComplete && Boolean(this.value); |
| 55 |
} |
| 56 |
boundaries(str) { |
| 57 |
let minstr = ''; |
| 58 |
let maxstr = ''; |
| 59 |
const [, placeholder, num] = str.match(/^(\D*)(\d*)(\D*)/) || []; |
| 60 |
if (num) { |
| 61 |
minstr = '0'.repeat(placeholder.length) + num; |
| 62 |
maxstr = '9'.repeat(placeholder.length) + num; |
| 63 |
} |
| 64 |
minstr = minstr.padEnd(this.maxLength, '0'); |
| 65 |
maxstr = maxstr.padEnd(this.maxLength, '9'); |
| 66 |
return [minstr, maxstr]; |
| 67 |
} |
| 68 |
doPrepareChar(ch, flags) { |
| 69 |
if (flags === void 0) { |
| 70 |
flags = {}; |
| 71 |
} |
| 72 |
let details; |
| 73 |
[ch, details] = super.doPrepareChar(ch.replace(/\D/g, ''), flags); |
| 74 |
if (!ch) details.skip = !this.isComplete; |
| 75 |
return [ch, details]; |
| 76 |
} |
| 77 |
_appendCharRaw(ch, flags) { |
| 78 |
if (flags === void 0) { |
| 79 |
flags = {}; |
| 80 |
} |
| 81 |
if (!this.autofix || this.value.length + 1 > this.maxLength) return super._appendCharRaw(ch, flags); |
| 82 |
const fromStr = String(this.from).padStart(this.maxLength, '0'); |
| 83 |
const toStr = String(this.to).padStart(this.maxLength, '0'); |
| 84 |
const [minstr, maxstr] = this.boundaries(this.value + ch); |
| 85 |
if (Number(maxstr) < this.from) return super._appendCharRaw(fromStr[this.value.length], flags); |
| 86 |
if (Number(minstr) > this.to) { |
| 87 |
if (!flags.tail && this.autofix === 'pad' && this.value.length + 1 < this.maxLength) { |
| 88 |
return super._appendCharRaw(fromStr[this.value.length], flags).aggregate(this._appendCharRaw(ch, flags)); |
| 89 |
} |
| 90 |
return super._appendCharRaw(toStr[this.value.length], flags); |
| 91 |
} |
| 92 |
return super._appendCharRaw(ch, flags); |
| 93 |
} |
| 94 |
doValidate(flags) { |
| 95 |
const str = this.value; |
| 96 |
const firstNonZero = str.search(/[^0]/); |
| 97 |
if (firstNonZero === -1 && str.length <= this._matchFrom) return true; |
| 98 |
const [minstr, maxstr] = this.boundaries(str); |
| 99 |
return this.from <= Number(maxstr) && Number(minstr) <= this.to && super.doValidate(flags); |
| 100 |
} |
| 101 |
pad(flags) { |
| 102 |
const details = new ChangeDetails(); |
| 103 |
if (this.value.length === this.maxLength) return details; |
| 104 |
const value = this.value; |
| 105 |
const padLength = this.maxLength - this.value.length; |
| 106 |
if (padLength) { |
| 107 |
this.reset(); |
| 108 |
for (let i = 0; i < padLength; ++i) { |
| 109 |
details.aggregate(super._appendCharRaw('0', flags)); |
| 110 |
} |
| 111 |
|
| 112 |
// append tail |
| 113 |
value.split('').forEach(ch => this._appendCharRaw(ch)); |
| 114 |
} |
| 115 |
return details; |
| 116 |
} |
| 117 |
} |
| 118 |
IMask.MaskedRange = MaskedRange; |
| 119 |
|
| 120 |
export { MaskedRange as default }; |
| 121 |
|