PluginProbe
Booking Calendar / 11.8
Booking Calendar v11.8
11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 All 204 releases
booking / vendors / imask / dist / imask.cjs

imask.cjs in Booking Calendar 11.8, at vendors/imask/dist/imask.cjs

3,636 lines 114.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 'use strict';
2
3 Object.defineProperty(exports, '__esModule', { value: true });
4
5 /** Checks if value is string */
6 function isString(str) {
7 return typeof str === 'string' || str instanceof String;
8 }
9
10 /** Checks if value is object */
11 function isObject(obj) {
12 var _obj$constructor;
13 return typeof obj === 'object' && obj != null && (obj == null || (_obj$constructor = obj.constructor) == null ? void 0 : _obj$constructor.name) === 'Object';
14 }
15 function pick(obj, keys) {
16 if (Array.isArray(keys)) return pick(obj, (_, k) => keys.includes(k));
17 return Object.entries(obj).reduce((acc, _ref) => {
18 let [k, v] = _ref;
19 if (keys(v, k)) acc[k] = v;
20 return acc;
21 }, {});
22 }
23
24 /** Direction */
25 const DIRECTION = {
26 NONE: 'NONE',
27 LEFT: 'LEFT',
28 FORCE_LEFT: 'FORCE_LEFT',
29 RIGHT: 'RIGHT',
30 FORCE_RIGHT: 'FORCE_RIGHT'
31 };
32
33 /** Direction */
34
35 function forceDirection(direction) {
36 switch (direction) {
37 case DIRECTION.LEFT:
38 return DIRECTION.FORCE_LEFT;
39 case DIRECTION.RIGHT:
40 return DIRECTION.FORCE_RIGHT;
41 default:
42 return direction;
43 }
44 }
45
46 /** Escapes regular expression control chars */
47 function escapeRegExp(str) {
48 return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1');
49 }
50
51 // cloned from https://github.com/epoberezkin/fast-deep-equal with small changes
52 function objectIncludes(b, a) {
53 if (a === b) return true;
54 const arrA = Array.isArray(a),
55 arrB = Array.isArray(b);
56 let i;
57 if (arrA && arrB) {
58 if (a.length != b.length) return false;
59 for (i = 0; i < a.length; i++) if (!objectIncludes(a[i], b[i])) return false;
60 return true;
61 }
62 if (arrA != arrB) return false;
63 if (a && b && typeof a === 'object' && typeof b === 'object') {
64 const dateA = a instanceof Date,
65 dateB = b instanceof Date;
66 if (dateA && dateB) return a.getTime() == b.getTime();
67 if (dateA != dateB) return false;
68 const regexpA = a instanceof RegExp,
69 regexpB = b instanceof RegExp;
70 if (regexpA && regexpB) return a.toString() == b.toString();
71 if (regexpA != regexpB) return false;
72 const keys = Object.keys(a);
73 // if (keys.length !== Object.keys(b).length) return false;
74
75 for (i = 0; i < keys.length; i++) if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
76 for (i = 0; i < keys.length; i++) if (!objectIncludes(b[keys[i]], a[keys[i]])) return false;
77 return true;
78 } else if (a && b && typeof a === 'function' && typeof b === 'function') {
79 return a.toString() === b.toString();
80 }
81 return false;
82 }
83
84 /** Selection range */
85
86 /** Provides details of changing input */
87 class ActionDetails {
88 /** Current input value */
89
90 /** Current cursor position */
91
92 /** Old input value */
93
94 /** Old selection */
95
96 constructor(opts) {
97 Object.assign(this, opts);
98
99 // double check if left part was changed (autofilling, other non-standard input triggers)
100 while (this.value.slice(0, this.startChangePos) !== this.oldValue.slice(0, this.startChangePos)) {
101 --this.oldSelection.start;
102 }
103 if (this.insertedCount) {
104 // double check right part
105 while (this.value.slice(this.cursorPos) !== this.oldValue.slice(this.oldSelection.end)) {
106 if (this.value.length - this.cursorPos < this.oldValue.length - this.oldSelection.end) ++this.oldSelection.end;else ++this.cursorPos;
107 }
108 }
109 }
110
111 /** Start changing position */
112 get startChangePos() {
113 return Math.min(this.cursorPos, this.oldSelection.start);
114 }
115
116 /** Inserted symbols count */
117 get insertedCount() {
118 return this.cursorPos - this.startChangePos;
119 }
120
121 /** Inserted symbols */
122 get inserted() {
123 return this.value.substr(this.startChangePos, this.insertedCount);
124 }
125
126 /** Removed symbols count */
127 get removedCount() {
128 // Math.max for opposite operation
129 return Math.max(this.oldSelection.end - this.startChangePos ||
130 // for Delete
131 this.oldValue.length - this.value.length, 0);
132 }
133
134 /** Removed symbols */
135 get removed() {
136 return this.oldValue.substr(this.startChangePos, this.removedCount);
137 }
138
139 /** Unchanged head symbols */
140 get head() {
141 return this.value.substring(0, this.startChangePos);
142 }
143
144 /** Unchanged tail symbols */
145 get tail() {
146 return this.value.substring(this.startChangePos + this.insertedCount);
147 }
148
149 /** Remove direction */
150 get removeDirection() {
151 if (!this.removedCount || this.insertedCount) return DIRECTION.NONE;
152
153 // align right if delete at right
154 return (this.oldSelection.end === this.cursorPos || this.oldSelection.start === this.cursorPos) &&
155 // if not range removed (event with backspace)
156 this.oldSelection.end === this.oldSelection.start ? DIRECTION.RIGHT : DIRECTION.LEFT;
157 }
158 }
159
160 /** Applies mask on element */
161 function IMask(el, opts) {
162 // currently available only for input-like elements
163 return new IMask.InputMask(el, opts);
164 }
165
166 // TODO can't use overloads here because of https://github.com/microsoft/TypeScript/issues/50754
167 // export function maskedClass(mask: string): typeof MaskedPattern;
168 // export function maskedClass(mask: DateConstructor): typeof MaskedDate;
169 // export function maskedClass(mask: NumberConstructor): typeof MaskedNumber;
170 // export function maskedClass(mask: Array<any> | ArrayConstructor): typeof MaskedDynamic;
171 // export function maskedClass(mask: MaskedDate): typeof MaskedDate;
172 // export function maskedClass(mask: MaskedNumber): typeof MaskedNumber;
173 // export function maskedClass(mask: MaskedEnum): typeof MaskedEnum;
174 // export function maskedClass(mask: MaskedRange): typeof MaskedRange;
175 // export function maskedClass(mask: MaskedRegExp): typeof MaskedRegExp;
176 // export function maskedClass(mask: MaskedFunction): typeof MaskedFunction;
177 // export function maskedClass(mask: MaskedPattern): typeof MaskedPattern;
178 // export function maskedClass(mask: MaskedDynamic): typeof MaskedDynamic;
179 // export function maskedClass(mask: Masked): typeof Masked;
180 // export function maskedClass(mask: typeof Masked): typeof Masked;
181 // export function maskedClass(mask: typeof MaskedDate): typeof MaskedDate;
182 // export function maskedClass(mask: typeof MaskedNumber): typeof MaskedNumber;
183 // export function maskedClass(mask: typeof MaskedEnum): typeof MaskedEnum;
184 // export function maskedClass(mask: typeof MaskedRange): typeof MaskedRange;
185 // export function maskedClass(mask: typeof MaskedRegExp): typeof MaskedRegExp;
186 // export function maskedClass(mask: typeof MaskedFunction): typeof MaskedFunction;
187 // export function maskedClass(mask: typeof MaskedPattern): typeof MaskedPattern;
188 // export function maskedClass(mask: typeof MaskedDynamic): typeof MaskedDynamic;
189 // export function maskedClass<Mask extends typeof Masked> (mask: Mask): Mask;
190 // export function maskedClass(mask: RegExp): typeof MaskedRegExp;
191 // export function maskedClass(mask: (value: string, ...args: any[]) => boolean): typeof MaskedFunction;
192
193 /** Get Masked class by mask type */
194 function maskedClass(mask) /* TODO */{
195 if (mask == null) throw new Error('mask property should be defined');
196 if (mask instanceof RegExp) return IMask.MaskedRegExp;
197 if (isString(mask)) return IMask.MaskedPattern;
198 if (mask === Date) return IMask.MaskedDate;
199 if (mask === Number) return IMask.MaskedNumber;
200 if (Array.isArray(mask) || mask === Array) return IMask.MaskedDynamic;
201 if (IMask.Masked && mask.prototype instanceof IMask.Masked) return mask;
202 if (IMask.Masked && mask instanceof IMask.Masked) return mask.constructor;
203 if (mask instanceof Function) return IMask.MaskedFunction;
204 console.warn('Mask not found for mask', mask); // eslint-disable-line no-console
205 return IMask.Masked;
206 }
207 function normalizeOpts(opts) {
208 if (!opts) throw new Error('Options in not defined');
209 if (IMask.Masked) {
210 if (opts.prototype instanceof IMask.Masked) return {
211 mask: opts
212 };
213
214 /*
215 handle cases like:
216 1) opts = Masked
217 2) opts = { mask: Masked, ...instanceOpts }
218 */
219 const {
220 mask = undefined,
221 ...instanceOpts
222 } = opts instanceof IMask.Masked ? {
223 mask: opts
224 } : isObject(opts) && opts.mask instanceof IMask.Masked ? opts : {};
225 if (mask) {
226 const _mask = mask.mask;
227 return {
228 ...pick(mask, (_, k) => !k.startsWith('_')),
229 mask: mask.constructor,
230 _mask,
231 ...instanceOpts
232 };
233 }
234 }
235 if (!isObject(opts)) return {
236 mask: opts
237 };
238 return {
239 ...opts
240 };
241 }
242
243 // TODO can't use overloads here because of https://github.com/microsoft/TypeScript/issues/50754
244
245 // From masked
246 // export default function createMask<Opts extends Masked, ReturnMasked=Opts> (opts: Opts): ReturnMasked;
247 // // From masked class
248 // export default function createMask<Opts extends MaskedOptions<typeof Masked>, ReturnMasked extends Masked=InstanceType<Opts['mask']>> (opts: Opts): ReturnMasked;
249 // export default function createMask<Opts extends MaskedOptions<typeof MaskedDate>, ReturnMasked extends MaskedDate=MaskedDate<Opts['parent']>> (opts: Opts): ReturnMasked;
250 // export default function createMask<Opts extends MaskedOptions<typeof MaskedNumber>, ReturnMasked extends MaskedNumber=MaskedNumber<Opts['parent']>> (opts: Opts): ReturnMasked;
251 // export default function createMask<Opts extends MaskedOptions<typeof MaskedEnum>, ReturnMasked extends MaskedEnum=MaskedEnum<Opts['parent']>> (opts: Opts): ReturnMasked;
252 // export default function createMask<Opts extends MaskedOptions<typeof MaskedRange>, ReturnMasked extends MaskedRange=MaskedRange<Opts['parent']>> (opts: Opts): ReturnMasked;
253 // export default function createMask<Opts extends MaskedOptions<typeof MaskedRegExp>, ReturnMasked extends MaskedRegExp=MaskedRegExp<Opts['parent']>> (opts: Opts): ReturnMasked;
254 // export default function createMask<Opts extends MaskedOptions<typeof MaskedFunction>, ReturnMasked extends MaskedFunction=MaskedFunction<Opts['parent']>> (opts: Opts): ReturnMasked;
255 // export default function createMask<Opts extends MaskedOptions<typeof MaskedPattern>, ReturnMasked extends MaskedPattern=MaskedPattern<Opts['parent']>> (opts: Opts): ReturnMasked;
256 // export default function createMask<Opts extends MaskedOptions<typeof MaskedDynamic>, ReturnMasked extends MaskedDynamic=MaskedDynamic<Opts['parent']>> (opts: Opts): ReturnMasked;
257 // // From mask opts
258 // export default function createMask<Opts extends MaskedOptions<Masked>, ReturnMasked=Opts extends MaskedOptions<infer M> ? M : never> (opts: Opts): ReturnMasked;
259 // export default function createMask<Opts extends MaskedNumberOptions, ReturnMasked extends MaskedNumber=MaskedNumber<Opts['parent']>> (opts: Opts): ReturnMasked;
260 // export default function createMask<Opts extends MaskedDateFactoryOptions, ReturnMasked extends MaskedDate=MaskedDate<Opts['parent']>> (opts: Opts): ReturnMasked;
261 // export default function createMask<Opts extends MaskedEnumOptions, ReturnMasked extends MaskedEnum=MaskedEnum<Opts['parent']>> (opts: Opts): ReturnMasked;
262 // export default function createMask<Opts extends MaskedRangeOptions, ReturnMasked extends MaskedRange=MaskedRange<Opts['parent']>> (opts: Opts): ReturnMasked;
263 // export default function createMask<Opts extends MaskedPatternOptions, ReturnMasked extends MaskedPattern=MaskedPattern<Opts['parent']>> (opts: Opts): ReturnMasked;
264 // export default function createMask<Opts extends MaskedDynamicOptions, ReturnMasked extends MaskedDynamic=MaskedDynamic<Opts['parent']>> (opts: Opts): ReturnMasked;
265 // export default function createMask<Opts extends MaskedOptions<RegExp>, ReturnMasked extends MaskedRegExp=MaskedRegExp<Opts['parent']>> (opts: Opts): ReturnMasked;
266 // export default function createMask<Opts extends MaskedOptions<Function>, ReturnMasked extends MaskedFunction=MaskedFunction<Opts['parent']>> (opts: Opts): ReturnMasked;
267
268 /** Creates new {@link Masked} depending on mask type */
269 function createMask(opts) {
270 if (IMask.Masked && opts instanceof IMask.Masked) return opts;
271 const nOpts = normalizeOpts(opts);
272 const MaskedClass = maskedClass(nOpts.mask);
273 if (!MaskedClass) throw new Error("Masked class is not found for provided mask " + nOpts.mask + ", appropriate module needs to be imported manually before creating mask.");
274 if (nOpts.mask === MaskedClass) delete nOpts.mask;
275 if (nOpts._mask) {
276 nOpts.mask = nOpts._mask;
277 delete nOpts._mask;
278 }
279 return new MaskedClass(nOpts);
280 }
281 IMask.createMask = createMask;
282
283 /** Generic element API to use with mask */
284 class MaskElement {
285 /** */
286
287 /** */
288
289 /** */
290
291 /** Safely returns selection start */
292 get selectionStart() {
293 let start;
294 try {
295 start = this._unsafeSelectionStart;
296 } catch {}
297 return start != null ? start : this.value.length;
298 }
299
300 /** Safely returns selection end */
301 get selectionEnd() {
302 let end;
303 try {
304 end = this._unsafeSelectionEnd;
305 } catch {}
306 return end != null ? end : this.value.length;
307 }
308
309 /** Safely sets element selection */
310 select(start, end) {
311 if (start == null || end == null || start === this.selectionStart && end === this.selectionEnd) return;
312 try {
313 this._unsafeSelect(start, end);
314 } catch {}
315 }
316
317 /** */
318 get isActive() {
319 return false;
320 }
321 /** */
322
323 /** */
324
325 /** */
326 }
327 IMask.MaskElement = MaskElement;
328
329 const KEY_Z = 90;
330 const KEY_Y = 89;
331
332 /** Bridge between HTMLElement and {@link Masked} */
333 class HTMLMaskElement extends MaskElement {
334 /** HTMLElement to use mask on */
335
336 constructor(input) {
337 super();
338 this.input = input;
339 this._onKeydown = this._onKeydown.bind(this);
340 this._onInput = this._onInput.bind(this);
341 this._onBeforeinput = this._onBeforeinput.bind(this);
342 this._onCompositionEnd = this._onCompositionEnd.bind(this);
343 }
344 get rootElement() {
345 var _this$input$getRootNo, _this$input$getRootNo2, _this$input;
346 return (_this$input$getRootNo = (_this$input$getRootNo2 = (_this$input = this.input).getRootNode) == null ? void 0 : _this$input$getRootNo2.call(_this$input)) != null ? _this$input$getRootNo : document;
347 }
348
349 /** Is element in focus */
350 get isActive() {
351 return this.input === this.rootElement.activeElement;
352 }
353
354 /** Binds HTMLElement events to mask internal events */
355 bindEvents(handlers) {
356 this.input.addEventListener('keydown', this._onKeydown);
357 this.input.addEventListener('input', this._onInput);
358 this.input.addEventListener('beforeinput', this._onBeforeinput);
359 this.input.addEventListener('compositionend', this._onCompositionEnd);
360 this.input.addEventListener('drop', handlers.drop);
361 this.input.addEventListener('click', handlers.click);
362 this.input.addEventListener('focus', handlers.focus);
363 this.input.addEventListener('blur', handlers.commit);
364 this._handlers = handlers;
365 }
366 _onKeydown(e) {
367 if (this._handlers.redo && (e.keyCode === KEY_Z && e.shiftKey && (e.metaKey || e.ctrlKey) || e.keyCode === KEY_Y && e.ctrlKey)) {
368 e.preventDefault();
369 return this._handlers.redo(e);
370 }
371 if (this._handlers.undo && e.keyCode === KEY_Z && (e.metaKey || e.ctrlKey)) {
372 e.preventDefault();
373 return this._handlers.undo(e);
374 }
375 if (!e.isComposing) this._handlers.selectionChange(e);
376 }
377 _onBeforeinput(e) {
378 if (e.inputType === 'historyUndo' && this._handlers.undo) {
379 e.preventDefault();
380 return this._handlers.undo(e);
381 }
382 if (e.inputType === 'historyRedo' && this._handlers.redo) {
383 e.preventDefault();
384 return this._handlers.redo(e);
385 }
386 }
387 _onCompositionEnd(e) {
388 this._handlers.input(e);
389 }
390 _onInput(e) {
391 if (!e.isComposing) this._handlers.input(e);
392 }
393
394 /** Unbinds HTMLElement events to mask internal events */
395 unbindEvents() {
396 this.input.removeEventListener('keydown', this._onKeydown);
397 this.input.removeEventListener('input', this._onInput);
398 this.input.removeEventListener('beforeinput', this._onBeforeinput);
399 this.input.removeEventListener('compositionend', this._onCompositionEnd);
400 this.input.removeEventListener('drop', this._handlers.drop);
401 this.input.removeEventListener('click', this._handlers.click);
402 this.input.removeEventListener('focus', this._handlers.focus);
403 this.input.removeEventListener('blur', this._handlers.commit);
404 this._handlers = {};
405 }
406 }
407 IMask.HTMLMaskElement = HTMLMaskElement;
408
409 /** Bridge between InputElement and {@link Masked} */
410 class HTMLInputMaskElement extends HTMLMaskElement {
411 /** InputElement to use mask on */
412
413 constructor(input) {
414 super(input);
415 this.input = input;
416 }
417
418 /** Returns InputElement selection start */
419 get _unsafeSelectionStart() {
420 return this.input.selectionStart != null ? this.input.selectionStart : this.value.length;
421 }
422
423 /** Returns InputElement selection end */
424 get _unsafeSelectionEnd() {
425 return this.input.selectionEnd;
426 }
427
428 /** Sets InputElement selection */
429 _unsafeSelect(start, end) {
430 this.input.setSelectionRange(start, end);
431 }
432 get value() {
433 return this.input.value;
434 }
435 set value(value) {
436 this.input.value = value;
437 }
438 }
439 IMask.HTMLMaskElement = HTMLMaskElement;
440
441 class HTMLContenteditableMaskElement extends HTMLMaskElement {
442 /** Returns HTMLElement selection start */
443 get _unsafeSelectionStart() {
444 const root = this.rootElement;
445 const selection = root.getSelection && root.getSelection();
446 const anchorOffset = selection && selection.anchorOffset;
447 const focusOffset = selection && selection.focusOffset;
448 if (focusOffset == null || anchorOffset == null || anchorOffset < focusOffset) {
449 return anchorOffset;
450 }
451 return focusOffset;
452 }
453
454 /** Returns HTMLElement selection end */
455 get _unsafeSelectionEnd() {
456 const root = this.rootElement;
457 const selection = root.getSelection && root.getSelection();
458 const anchorOffset = selection && selection.anchorOffset;
459 const focusOffset = selection && selection.focusOffset;
460 if (focusOffset == null || anchorOffset == null || anchorOffset > focusOffset) {
461 return anchorOffset;
462 }
463 return focusOffset;
464 }
465
466 /** Sets HTMLElement selection */
467 _unsafeSelect(start, end) {
468 if (!this.rootElement.createRange) return;
469 const range = this.rootElement.createRange();
470 range.setStart(this.input.firstChild || this.input, start);
471 range.setEnd(this.input.lastChild || this.input, end);
472 const root = this.rootElement;
473 const selection = root.getSelection && root.getSelection();
474 if (selection) {
475 selection.removeAllRanges();
476 selection.addRange(range);
477 }
478 }
479
480 /** HTMLElement value */
481 get value() {
482 return this.input.textContent || '';
483 }
484 set value(value) {
485 this.input.textContent = value;
486 }
487 }
488 IMask.HTMLContenteditableMaskElement = HTMLContenteditableMaskElement;
489
490 class InputHistory {
491 constructor() {
492 this.states = [];
493 this.currentIndex = 0;
494 }
495 get currentState() {
496 return this.states[this.currentIndex];
497 }
498 get isEmpty() {
499 return this.states.length === 0;
500 }
501 push(state) {
502 // if current index points before the last element then remove the future
503 if (this.currentIndex < this.states.length - 1) this.states.length = this.currentIndex + 1;
504 this.states.push(state);
505 if (this.states.length > InputHistory.MAX_LENGTH) this.states.shift();
506 this.currentIndex = this.states.length - 1;
507 }
508 go(steps) {
509 this.currentIndex = Math.min(Math.max(this.currentIndex + steps, 0), this.states.length - 1);
510 return this.currentState;
511 }
512 undo() {
513 return this.go(-1);
514 }
515 redo() {
516 return this.go(+1);
517 }
518 clear() {
519 this.states.length = 0;
520 this.currentIndex = 0;
521 }
522 }
523 InputHistory.MAX_LENGTH = 100;
524
525 /** Listens to element events and controls changes between element and {@link Masked} */
526 class InputMask {
527 /**
528 View element
529 */
530
531 /** Internal {@link Masked} model */
532
533 constructor(el, opts) {
534 this.el = el instanceof MaskElement ? el : el.isContentEditable && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA' ? new HTMLContenteditableMaskElement(el) : new HTMLInputMaskElement(el);
535 this.masked = createMask(opts);
536 this._listeners = {};
537 this._value = '';
538 this._unmaskedValue = '';
539 this._rawInputValue = '';
540 this.history = new InputHistory();
541 this._saveSelection = this._saveSelection.bind(this);
542 this._onInput = this._onInput.bind(this);
543 this._onChange = this._onChange.bind(this);
544 this._onDrop = this._onDrop.bind(this);
545 this._onFocus = this._onFocus.bind(this);
546 this._onClick = this._onClick.bind(this);
547 this._onUndo = this._onUndo.bind(this);
548 this._onRedo = this._onRedo.bind(this);
549 this.alignCursor = this.alignCursor.bind(this);
550 this.alignCursorFriendly = this.alignCursorFriendly.bind(this);
551 this._bindEvents();
552
553 // refresh
554 this.updateValue();
555 this._onChange();
556 }
557 maskEquals(mask) {
558 var _this$masked;
559 return mask == null || ((_this$masked = this.masked) == null ? void 0 : _this$masked.maskEquals(mask));
560 }
561
562 /** Masked */
563 get mask() {
564 return this.masked.mask;
565 }
566 set mask(mask) {
567 if (this.maskEquals(mask)) return;
568 if (!(mask instanceof IMask.Masked) && this.masked.constructor === maskedClass(mask)) {
569 // TODO "any" no idea
570 this.masked.updateOptions({
571 mask
572 });
573 return;
574 }
575 const masked = mask instanceof IMask.Masked ? mask : createMask({
576 mask
577 });
578 masked.unmaskedValue = this.masked.unmaskedValue;
579 this.masked = masked;
580 }
581
582 /** Raw value */
583 get value() {
584 return this._value;
585 }
586 set value(str) {
587 if (this.value === str) return;
588 this.masked.value = str;
589 this.updateControl('auto');
590 }
591
592 /** Unmasked value */
593 get unmaskedValue() {
594 return this._unmaskedValue;
595 }
596 set unmaskedValue(str) {
597 if (this.unmaskedValue === str) return;
598 this.masked.unmaskedValue = str;
599 this.updateControl('auto');
600 }
601
602 /** Raw input value */
603 get rawInputValue() {
604 return this._rawInputValue;
605 }
606 set rawInputValue(str) {
607 if (this.rawInputValue === str) return;
608 this.masked.rawInputValue = str;
609 this.updateControl();
610 this.alignCursor();
611 }
612
613 /** Typed unmasked value */
614 get typedValue() {
615 return this.masked.typedValue;
616 }
617 set typedValue(val) {
618 if (this.masked.typedValueEquals(val)) return;
619 this.masked.typedValue = val;
620 this.updateControl('auto');
621 }
622
623 /** Display value */
624 get displayValue() {
625 return this.masked.displayValue;
626 }
627
628 /** Starts listening to element events */
629 _bindEvents() {
630 this.el.bindEvents({
631 selectionChange: this._saveSelection,
632 input: this._onInput,
633 drop: this._onDrop,
634 click: this._onClick,
635 focus: this._onFocus,
636 commit: this._onChange,
637 undo: this._onUndo,
638 redo: this._onRedo
639 });
640 }
641
642 /** Stops listening to element events */
643 _unbindEvents() {
644 if (this.el) this.el.unbindEvents();
645 }
646
647 /** Fires custom event */
648 _fireEvent(ev, e) {
649 const listeners = this._listeners[ev];
650 if (!listeners) return;
651 listeners.forEach(l => l(e));
652 }
653
654 /** Current selection start */
655 get selectionStart() {
656 return this._cursorChanging ? this._changingCursorPos : this.el.selectionStart;
657 }
658
659 /** Current cursor position */
660 get cursorPos() {
661 return this._cursorChanging ? this._changingCursorPos : this.el.selectionEnd;
662 }
663 set cursorPos(pos) {
664 if (!this.el || !this.el.isActive) return;
665 this.el.select(pos, pos);
666 this._saveSelection();
667 }
668
669 /** Stores current selection */
670 _saveSelection( /* ev */
671 ) {
672 if (this.displayValue !== this.el.value) {
673 console.warn('Element value was changed outside of mask. Syncronize mask using `mask.updateValue()` to work properly.'); // eslint-disable-line no-console
674 }
675 this._selection = {
676 start: this.selectionStart,
677 end: this.cursorPos
678 };
679 }
680
681 /** Syncronizes model value from view */
682 updateValue() {
683 this.masked.value = this.el.value;
684 this._value = this.masked.value;
685 this._unmaskedValue = this.masked.unmaskedValue;
686 this._rawInputValue = this.masked.rawInputValue;
687 }
688
689 /** Syncronizes view from model value, fires change events */
690 updateControl(cursorPos) {
691 const newUnmaskedValue = this.masked.unmaskedValue;
692 const newValue = this.masked.value;
693 const newRawInputValue = this.masked.rawInputValue;
694 const newDisplayValue = this.displayValue;
695 const isChanged = this.unmaskedValue !== newUnmaskedValue || this.value !== newValue || this._rawInputValue !== newRawInputValue;
696 this._unmaskedValue = newUnmaskedValue;
697 this._value = newValue;
698 this._rawInputValue = newRawInputValue;
699 if (this.el.value !== newDisplayValue) this.el.value = newDisplayValue;
700 if (cursorPos === 'auto') this.alignCursor();else if (cursorPos != null) this.cursorPos = cursorPos;
701 if (isChanged) this._fireChangeEvents();
702 if (!this._historyChanging && (isChanged || this.history.isEmpty)) this.history.push({
703 unmaskedValue: newUnmaskedValue,
704 selection: {
705 start: this.selectionStart,
706 end: this.cursorPos
707 }
708 });
709 }
710
711 /** Updates options with deep equal check, recreates {@link Masked} model if mask type changes */
712 updateOptions(opts) {
713 const {
714 mask,
715 ...restOpts
716 } = opts; // TODO types, yes, mask is optional
717
718 const updateMask = !this.maskEquals(mask);
719 const updateOpts = this.masked.optionsIsChanged(restOpts);
720 if (updateMask) this.mask = mask;
721 if (updateOpts) this.masked.updateOptions(restOpts); // TODO
722
723 if (updateMask || updateOpts) this.updateControl();
724 }
725
726 /** Updates cursor */
727 updateCursor(cursorPos) {
728 if (cursorPos == null) return;
729 this.cursorPos = cursorPos;
730
731 // also queue change cursor for mobile browsers
732 this._delayUpdateCursor(cursorPos);
733 }
734
735 /** Delays cursor update to support mobile browsers */
736 _delayUpdateCursor(cursorPos) {
737 this._abortUpdateCursor();
738 this._changingCursorPos = cursorPos;
739 this._cursorChanging = setTimeout(() => {
740 if (!this.el) return; // if was destroyed
741 this.cursorPos = this._changingCursorPos;
742 this._abortUpdateCursor();
743 }, 10);
744 }
745
746 /** Fires custom events */
747 _fireChangeEvents() {
748 this._fireEvent('accept', this._inputEvent);
749 if (this.masked.isComplete) this._fireEvent('complete', this._inputEvent);
750 }
751
752 /** Aborts delayed cursor update */
753 _abortUpdateCursor() {
754 if (this._cursorChanging) {
755 clearTimeout(this._cursorChanging);
756 delete this._cursorChanging;
757 }
758 }
759
760 /** Aligns cursor to nearest available position */
761 alignCursor() {
762 this.cursorPos = this.masked.nearestInputPos(this.masked.nearestInputPos(this.cursorPos, DIRECTION.LEFT));
763 }
764
765 /** Aligns cursor only if selection is empty */
766 alignCursorFriendly() {
767 if (this.selectionStart !== this.cursorPos) return; // skip if range is selected
768 this.alignCursor();
769 }
770
771 /** Adds listener on custom event */
772 on(ev, handler) {
773 if (!this._listeners[ev]) this._listeners[ev] = [];
774 this._listeners[ev].push(handler);
775 return this;
776 }
777
778 /** Removes custom event listener */
779 off(ev, handler) {
780 if (!this._listeners[ev]) return this;
781 if (!handler) {
782 delete this._listeners[ev];
783 return this;
784 }
785 const hIndex = this._listeners[ev].indexOf(handler);
786 if (hIndex >= 0) this._listeners[ev].splice(hIndex, 1);
787 return this;
788 }
789
790 /** Handles view input event */
791 _onInput(e) {
792 this._inputEvent = e;
793 this._abortUpdateCursor();
794 const details = new ActionDetails({
795 // new state
796 value: this.el.value,
797 cursorPos: this.cursorPos,
798 // old state
799 oldValue: this.displayValue,
800 oldSelection: this._selection
801 });
802 const oldRawValue = this.masked.rawInputValue;
803 const offset = this.masked.splice(details.startChangePos, details.removed.length, details.inserted, details.removeDirection, {
804 input: true,
805 raw: true
806 }).offset;
807
808 // force align in remove direction only if no input chars were removed
809 // otherwise we still need to align with NONE (to get out from fixed symbols for instance)
810 const removeDirection = oldRawValue === this.masked.rawInputValue ? details.removeDirection : DIRECTION.NONE;
811 let cursorPos = this.masked.nearestInputPos(details.startChangePos + offset, removeDirection);
812 if (removeDirection !== DIRECTION.NONE) cursorPos = this.masked.nearestInputPos(cursorPos, DIRECTION.NONE);
813 this.updateControl(cursorPos);
814 delete this._inputEvent;
815 }
816
817 /** Handles view change event and commits model value */
818 _onChange() {
819 if (this.displayValue !== this.el.value) this.updateValue();
820 this.masked.doCommit();
821 this.updateControl();
822 this._saveSelection();
823 }
824
825 /** Handles view drop event, prevents by default */
826 _onDrop(ev) {
827 ev.preventDefault();
828 ev.stopPropagation();
829 }
830
831 /** Restore last selection on focus */
832 _onFocus(ev) {
833 this.alignCursorFriendly();
834 }
835
836 /** Restore last selection on focus */
837 _onClick(ev) {
838 this.alignCursorFriendly();
839 }
840 _onUndo() {
841 this._applyHistoryState(this.history.undo());
842 }
843 _onRedo() {
844 this._applyHistoryState(this.history.redo());
845 }
846 _applyHistoryState(state) {
847 if (!state) return;
848 this._historyChanging = true;
849 this.unmaskedValue = state.unmaskedValue;
850 this.el.select(state.selection.start, state.selection.end);
851 this._saveSelection();
852 this._historyChanging = false;
853 }
854
855 /** Unbind view events and removes element reference */
856 destroy() {
857 this._unbindEvents();
858 this._listeners.length = 0;
859 delete this.el;
860 }
861 }
862 IMask.InputMask = InputMask;
863
864 /** Provides details of changing model value */
865 class ChangeDetails {
866 /** Inserted symbols */
867
868 /** Additional offset if any changes occurred before tail */
869
870 /** Raw inserted is used by dynamic mask */
871
872 /** Can skip chars */
873
874 static normalize(prep) {
875 return Array.isArray(prep) ? prep : [prep, new ChangeDetails()];
876 }
877 constructor(details) {
878 Object.assign(this, {
879 inserted: '',
880 rawInserted: '',
881 tailShift: 0,
882 skip: false
883 }, details);
884 }
885
886 /** Aggregate changes */
887 aggregate(details) {
888 this.inserted += details.inserted;
889 this.rawInserted += details.rawInserted;
890 this.tailShift += details.tailShift;
891 this.skip = this.skip || details.skip;
892 return this;
893 }
894
895 /** Total offset considering all changes */
896 get offset() {
897 return this.tailShift + this.inserted.length;
898 }
899 get consumed() {
900 return Boolean(this.rawInserted) || this.skip;
901 }
902 equals(details) {
903 return this.inserted === details.inserted && this.tailShift === details.tailShift && this.rawInserted === details.rawInserted && this.skip === details.skip;
904 }
905 }
906 IMask.ChangeDetails = ChangeDetails;
907
908 /** Provides details of continuous extracted tail */
909 class ContinuousTailDetails {
910 /** Tail value as string */
911
912 /** Tail start position */
913
914 /** Start position */
915
916 constructor(value, from, stop) {
917 if (value === void 0) {
918 value = '';
919 }
920 if (from === void 0) {
921 from = 0;
922 }
923 this.value = value;
924 this.from = from;
925 this.stop = stop;
926 }
927 toString() {
928 return this.value;
929 }
930 extend(tail) {
931 this.value += String(tail);
932 }
933 appendTo(masked) {
934 return masked.append(this.toString(), {
935 tail: true
936 }).aggregate(masked._appendPlaceholder());
937 }
938 get state() {
939 return {
940 value: this.value,
941 from: this.from,
942 stop: this.stop
943 };
944 }
945 set state(state) {
946 Object.assign(this, state);
947 }
948 unshift(beforePos) {
949 if (!this.value.length || beforePos != null && this.from >= beforePos) return '';
950 const shiftChar = this.value[0];
951 this.value = this.value.slice(1);
952 return shiftChar;
953 }
954 shift() {
955 if (!this.value.length) return '';
956 const shiftChar = this.value[this.value.length - 1];
957 this.value = this.value.slice(0, -1);
958 return shiftChar;
959 }
960 }
961
962 /** Append flags */
963
964 /** Extract flags */
965
966 // see https://github.com/microsoft/TypeScript/issues/6223
967
968 /** Provides common masking stuff */
969 class Masked {
970 /** */
971
972 /** */
973
974 /** Transforms value before mask processing */
975
976 /** Transforms each char before mask processing */
977
978 /** Validates if value is acceptable */
979
980 /** Does additional processing at the end of editing */
981
982 /** Format typed value to string */
983
984 /** Parse string to get typed value */
985
986 /** Enable characters overwriting */
987
988 /** */
989
990 /** */
991
992 /** */
993
994 /** */
995
996 constructor(opts) {
997 this._value = '';
998 this._update({
999 ...Masked.DEFAULTS,
1000 ...opts
1001 });
1002 this._initialized = true;
1003 }
1004
1005 /** Sets and applies new options */
1006 updateOptions(opts) {
1007 if (!this.optionsIsChanged(opts)) return;
1008 this.withValueRefresh(this._update.bind(this, opts));
1009 }
1010
1011 /** Sets new options */
1012 _update(opts) {
1013 Object.assign(this, opts);
1014 }
1015
1016 /** Mask state */
1017 get state() {
1018 return {
1019 _value: this.value,
1020 _rawInputValue: this.rawInputValue
1021 };
1022 }
1023 set state(state) {
1024 this._value = state._value;
1025 }
1026
1027 /** Resets value */
1028 reset() {
1029 this._value = '';
1030 }
1031 get value() {
1032 return this._value;
1033 }
1034 set value(value) {
1035 this.resolve(value, {
1036 input: true
1037 });
1038 }
1039
1040 /** Resolve new value */
1041 resolve(value, flags) {
1042 if (flags === void 0) {
1043 flags = {
1044 input: true
1045 };
1046 }
1047 this.reset();
1048 this.append(value, flags, '');
1049 this.doCommit();
1050 }
1051 get unmaskedValue() {
1052 return this.value;
1053 }
1054 set unmaskedValue(value) {
1055 this.resolve(value, {});
1056 }
1057 get typedValue() {
1058 return this.parse ? this.parse(this.value, this) : this.unmaskedValue;
1059 }
1060 set typedValue(value) {
1061 if (this.format) {
1062 this.value = this.format(value, this);
1063 } else {
1064 this.unmaskedValue = String(value);
1065 }
1066 }
1067
1068 /** Value that includes raw user input */
1069 get rawInputValue() {
1070 return this.extractInput(0, this.displayValue.length, {
1071 raw: true
1072 });
1073 }
1074 set rawInputValue(value) {
1075 this.resolve(value, {
1076 raw: true
1077 });
1078 }
1079 get displayValue() {
1080 return this.value;
1081 }
1082 get isComplete() {
1083 return true;
1084 }
1085 get isFilled() {
1086 return this.isComplete;
1087 }
1088
1089 /** Finds nearest input position in direction */
1090 nearestInputPos(cursorPos, direction) {
1091 return cursorPos;
1092 }
1093 totalInputPositions(fromPos, toPos) {
1094 if (fromPos === void 0) {
1095 fromPos = 0;
1096 }
1097 if (toPos === void 0) {
1098 toPos = this.displayValue.length;
1099 }
1100 return Math.min(this.displayValue.length, toPos - fromPos);
1101 }
1102
1103 /** Extracts value in range considering flags */
1104 extractInput(fromPos, toPos, flags) {
1105 if (fromPos === void 0) {
1106 fromPos = 0;
1107 }
1108 if (toPos === void 0) {
1109 toPos = this.displayValue.length;
1110 }
1111 return this.displayValue.slice(fromPos, toPos);
1112 }
1113
1114 /** Extracts tail in range */
1115 extractTail(fromPos, toPos) {
1116 if (fromPos === void 0) {
1117 fromPos = 0;
1118 }
1119 if (toPos === void 0) {
1120 toPos = this.displayValue.length;
1121 }
1122 return new ContinuousTailDetails(this.extractInput(fromPos, toPos), fromPos);
1123 }
1124
1125 /** Appends tail */
1126 appendTail(tail) {
1127 if (isString(tail)) tail = new ContinuousTailDetails(String(tail));
1128 return tail.appendTo(this);
1129 }
1130
1131 /** Appends char */
1132 _appendCharRaw(ch, flags) {
1133 if (!ch) return new ChangeDetails();
1134 this._value += ch;
1135 return new ChangeDetails({
1136 inserted: ch,
1137 rawInserted: ch
1138 });
1139 }
1140
1141 /** Appends char */
1142 _appendChar(ch, flags, checkTail) {
1143 if (flags === void 0) {
1144 flags = {};
1145 }
1146 const consistentState = this.state;
1147 let details;
1148 [ch, details] = this.doPrepareChar(ch, flags);
1149 if (ch) {
1150 details = details.aggregate(this._appendCharRaw(ch, flags));
1151
1152 // TODO handle `skip`?
1153
1154 // try `autofix` lookahead
1155 if (!details.rawInserted && this.autofix === 'pad') {
1156 const noFixState = this.state;
1157 this.state = consistentState;
1158 let fixDetails = this.pad(flags);
1159 const chDetails = this._appendCharRaw(ch, flags);
1160 fixDetails = fixDetails.aggregate(chDetails);
1161
1162 // if fix was applied or
1163 // if details are equal use skip restoring state optimization
1164 if (chDetails.rawInserted || fixDetails.equals(details)) {
1165 details = fixDetails;
1166 } else {
1167 this.state = noFixState;
1168 }
1169 }
1170 }
1171 if (details.inserted) {
1172 let consistentTail;
1173 let appended = this.doValidate(flags) !== false;
1174 if (appended && checkTail != null) {
1175 // validation ok, check tail
1176 const beforeTailState = this.state;
1177 if (this.overwrite === true) {
1178 consistentTail = checkTail.state;
1179 for (let i = 0; i < details.rawInserted.length; ++i) {
1180 checkTail.unshift(this.displayValue.length - details.tailShift);
1181 }
1182 }
1183 let tailDetails = this.appendTail(checkTail);
1184 appended = tailDetails.rawInserted.length === checkTail.toString().length;
1185
1186 // not ok, try shift
1187 if (!(appended && tailDetails.inserted) && this.overwrite === 'shift') {
1188 this.state = beforeTailState;
1189 consistentTail = checkTail.state;
1190 for (let i = 0; i < details.rawInserted.length; ++i) {
1191 checkTail.shift();
1192 }
1193 tailDetails = this.appendTail(checkTail);
1194 appended = tailDetails.rawInserted.length === checkTail.toString().length;
1195 }
1196
1197 // if ok, rollback state after tail
1198 if (appended && tailDetails.inserted) this.state = beforeTailState;
1199 }
1200
1201 // revert all if something went wrong
1202 if (!appended) {
1203 details = new ChangeDetails();
1204 this.state = consistentState;
1205 if (checkTail && consistentTail) checkTail.state = consistentTail;
1206 }
1207 }
1208 return details;
1209 }
1210
1211 /** Appends optional placeholder at the end */
1212 _appendPlaceholder() {
1213 return new ChangeDetails();
1214 }
1215
1216 /** Appends optional eager placeholder at the end */
1217 _appendEager() {
1218 return new ChangeDetails();
1219 }
1220
1221 /** Appends symbols considering flags */
1222 append(str, flags, tail) {
1223 if (!isString(str)) throw new Error('value should be string');
1224 const checkTail = isString(tail) ? new ContinuousTailDetails(String(tail)) : tail;
1225 if (flags != null && flags.tail) flags._beforeTailState = this.state;
1226 let details;
1227 [str, details] = this.doPrepare(str, flags);
1228 for (let ci = 0; ci < str.length; ++ci) {
1229 const d = this._appendChar(str[ci], flags, checkTail);
1230 if (!d.rawInserted && !this.doSkipInvalid(str[ci], flags, checkTail)) break;
1231 details.aggregate(d);
1232 }
1233 if ((this.eager === true || this.eager === 'append') && flags != null && flags.input && str) {
1234 details.aggregate(this._appendEager());
1235 }
1236
1237 // append tail but aggregate only tailShift
1238 if (checkTail != null) {
1239 details.tailShift += this.appendTail(checkTail).tailShift;
1240 // TODO it's a good idea to clear state after appending ends
1241 // but it causes bugs when one append calls another (when dynamic dispatch set rawInputValue)
1242 // this._resetBeforeTailState();
1243 }
1244 return details;
1245 }
1246 remove(fromPos, toPos) {
1247 if (fromPos === void 0) {
1248 fromPos = 0;
1249 }
1250 if (toPos === void 0) {
1251 toPos = this.displayValue.length;
1252 }
1253 this._value = this.displayValue.slice(0, fromPos) + this.displayValue.slice(toPos);
1254 return new ChangeDetails();
1255 }
1256
1257 /** Calls function and reapplies current value */
1258 withValueRefresh(fn) {
1259 if (this._refreshing || !this._initialized) return fn();
1260 this._refreshing = true;
1261 const rawInput = this.rawInputValue;
1262 const value = this.value;
1263 const ret = fn();
1264 this.rawInputValue = rawInput;
1265 // append lost trailing chars at the end
1266 if (this.value && this.value !== value && value.indexOf(this.value) === 0) {
1267 this.append(value.slice(this.displayValue.length), {}, '');
1268 this.doCommit();
1269 }
1270 delete this._refreshing;
1271 return ret;
1272 }
1273 runIsolated(fn) {
1274 if (this._isolated || !this._initialized) return fn(this);
1275 this._isolated = true;
1276 const state = this.state;
1277 const ret = fn(this);
1278 this.state = state;
1279 delete this._isolated;
1280 return ret;
1281 }
1282 doSkipInvalid(ch, flags, checkTail) {
1283 return Boolean(this.skipInvalid);
1284 }
1285
1286 /** Prepares string before mask processing */
1287 doPrepare(str, flags) {
1288 if (flags === void 0) {
1289 flags = {};
1290 }
1291 return ChangeDetails.normalize(this.prepare ? this.prepare(str, this, flags) : str);
1292 }
1293
1294 /** Prepares each char before mask processing */
1295 doPrepareChar(str, flags) {
1296 if (flags === void 0) {
1297 flags = {};
1298 }
1299 return ChangeDetails.normalize(this.prepareChar ? this.prepareChar(str, this, flags) : str);
1300 }
1301
1302 /** Validates if value is acceptable */
1303 doValidate(flags) {
1304 return (!this.validate || this.validate(this.value, this, flags)) && (!this.parent || this.parent.doValidate(flags));
1305 }
1306
1307 /** Does additional processing at the end of editing */
1308 doCommit() {
1309 if (this.commit) this.commit(this.value, this);
1310 }
1311 splice(start, deleteCount, inserted, removeDirection, flags) {
1312 if (inserted === void 0) {
1313 inserted = '';
1314 }
1315 if (removeDirection === void 0) {
1316 removeDirection = DIRECTION.NONE;
1317 }
1318 if (flags === void 0) {
1319 flags = {
1320 input: true
1321 };
1322 }
1323 const tailPos = start + deleteCount;
1324 const tail = this.extractTail(tailPos);
1325 const eagerRemove = this.eager === true || this.eager === 'remove';
1326 let oldRawValue;
1327 if (eagerRemove) {
1328 removeDirection = forceDirection(removeDirection);
1329 oldRawValue = this.extractInput(0, tailPos, {
1330 raw: true
1331 });
1332 }
1333 let startChangePos = start;
1334 const details = new ChangeDetails();
1335
1336 // if it is just deletion without insertion
1337 if (removeDirection !== DIRECTION.NONE) {
1338 startChangePos = this.nearestInputPos(start, deleteCount > 1 && start !== 0 && !eagerRemove ? DIRECTION.NONE : removeDirection);
1339
1340 // adjust tailShift if start was aligned
1341 details.tailShift = startChangePos - start;
1342 }
1343 details.aggregate(this.remove(startChangePos));
1344 if (eagerRemove && removeDirection !== DIRECTION.NONE && oldRawValue === this.rawInputValue) {
1345 if (removeDirection === DIRECTION.FORCE_LEFT) {
1346 let valLength;
1347 while (oldRawValue === this.rawInputValue && (valLength = this.displayValue.length)) {
1348 details.aggregate(new ChangeDetails({
1349 tailShift: -1
1350 })).aggregate(this.remove(valLength - 1));
1351 }
1352 } else if (removeDirection === DIRECTION.FORCE_RIGHT) {
1353 tail.unshift();
1354 }
1355 }
1356 return details.aggregate(this.append(inserted, flags, tail));
1357 }
1358 maskEquals(mask) {
1359 return this.mask === mask;
1360 }
1361 optionsIsChanged(opts) {
1362 return !objectIncludes(this, opts);
1363 }
1364 typedValueEquals(value) {
1365 const tval = this.typedValue;
1366 return value === tval || Masked.EMPTY_VALUES.includes(value) && Masked.EMPTY_VALUES.includes(tval) || (this.format ? this.format(value, this) === this.format(this.typedValue, this) : false);
1367 }
1368 pad(flags) {
1369 return new ChangeDetails();
1370 }
1371 }
1372 Masked.DEFAULTS = {
1373 skipInvalid: true
1374 };
1375 Masked.EMPTY_VALUES = [undefined, null, ''];
1376 IMask.Masked = Masked;
1377
1378 class ChunksTailDetails {
1379 /** */
1380
1381 constructor(chunks, from) {
1382 if (chunks === void 0) {
1383 chunks = [];
1384 }
1385 if (from === void 0) {
1386 from = 0;
1387 }
1388 this.chunks = chunks;
1389 this.from = from;
1390 }
1391 toString() {
1392 return this.chunks.map(String).join('');
1393 }
1394 extend(tailChunk) {
1395 if (!String(tailChunk)) return;
1396 tailChunk = isString(tailChunk) ? new ContinuousTailDetails(String(tailChunk)) : tailChunk;
1397 const lastChunk = this.chunks[this.chunks.length - 1];
1398 const extendLast = lastChunk && (
1399 // if stops are same or tail has no stop
1400 lastChunk.stop === tailChunk.stop || tailChunk.stop == null) &&
1401 // if tail chunk goes just after last chunk
1402 tailChunk.from === lastChunk.from + lastChunk.toString().length;
1403 if (tailChunk instanceof ContinuousTailDetails) {
1404 // check the ability to extend previous chunk
1405 if (extendLast) {
1406 // extend previous chunk
1407 lastChunk.extend(tailChunk.toString());
1408 } else {
1409 // append new chunk
1410 this.chunks.push(tailChunk);
1411 }
1412 } else if (tailChunk instanceof ChunksTailDetails) {
1413 if (tailChunk.stop == null) {
1414 // unwrap floating chunks to parent, keeping `from` pos
1415 let firstTailChunk;
1416 while (tailChunk.chunks.length && tailChunk.chunks[0].stop == null) {
1417 firstTailChunk = tailChunk.chunks.shift(); // not possible to be `undefined` because length was checked above
1418 firstTailChunk.from += tailChunk.from;
1419 this.extend(firstTailChunk);
1420 }
1421 }
1422
1423 // if tail chunk still has value
1424 if (tailChunk.toString()) {
1425 // if chunks contains stops, then popup stop to container
1426 tailChunk.stop = tailChunk.blockIndex;
1427 this.chunks.push(tailChunk);
1428 }
1429 }
1430 }
1431 appendTo(masked) {
1432 if (!(masked instanceof IMask.MaskedPattern)) {
1433 const tail = new ContinuousTailDetails(this.toString());
1434 return tail.appendTo(masked);
1435 }
1436 const details = new ChangeDetails();
1437 for (let ci = 0; ci < this.chunks.length; ++ci) {
1438 const chunk = this.chunks[ci];
1439 const lastBlockIter = masked._mapPosToBlock(masked.displayValue.length);
1440 const stop = chunk.stop;
1441 let chunkBlock;
1442 if (stop != null && (
1443 // if block not found or stop is behind lastBlock
1444 !lastBlockIter || lastBlockIter.index <= stop)) {
1445 if (chunk instanceof ChunksTailDetails ||
1446 // for continuous block also check if stop is exist
1447 masked._stops.indexOf(stop) >= 0) {
1448 details.aggregate(masked._appendPlaceholder(stop));
1449 }
1450 chunkBlock = chunk instanceof ChunksTailDetails && masked._blocks[stop];
1451 }
1452 if (chunkBlock) {
1453 const tailDetails = chunkBlock.appendTail(chunk);
1454 details.aggregate(tailDetails);
1455
1456 // get not inserted chars
1457 const remainChars = chunk.toString().slice(tailDetails.rawInserted.length);
1458 if (remainChars) details.aggregate(masked.append(remainChars, {
1459 tail: true
1460 }));
1461 } else {
1462 details.aggregate(masked.append(chunk.toString(), {
1463 tail: true
1464 }));
1465 }
1466 }
1467 return details;
1468 }
1469 get state() {
1470 return {
1471 chunks: this.chunks.map(c => c.state),
1472 from: this.from,
1473 stop: this.stop,
1474 blockIndex: this.blockIndex
1475 };
1476 }
1477 set state(state) {
1478 const {
1479 chunks,
1480 ...props
1481 } = state;
1482 Object.assign(this, props);
1483 this.chunks = chunks.map(cstate => {
1484 const chunk = "chunks" in cstate ? new ChunksTailDetails() : new ContinuousTailDetails();
1485 chunk.state = cstate;
1486 return chunk;
1487 });
1488 }
1489 unshift(beforePos) {
1490 if (!this.chunks.length || beforePos != null && this.from >= beforePos) return '';
1491 const chunkShiftPos = beforePos != null ? beforePos - this.from : beforePos;
1492 let ci = 0;
1493 while (ci < this.chunks.length) {
1494 const chunk = this.chunks[ci];
1495 const shiftChar = chunk.unshift(chunkShiftPos);
1496 if (chunk.toString()) {
1497 // chunk still contains value
1498 // but not shifted - means no more available chars to shift
1499 if (!shiftChar) break;
1500 ++ci;
1501 } else {
1502 // clean if chunk has no value
1503 this.chunks.splice(ci, 1);
1504 }
1505 if (shiftChar) return shiftChar;
1506 }
1507 return '';
1508 }
1509 shift() {
1510 if (!this.chunks.length) return '';
1511 let ci = this.chunks.length - 1;
1512 while (0 <= ci) {
1513 const chunk = this.chunks[ci];
1514 const shiftChar = chunk.shift();
1515 if (chunk.toString()) {
1516 // chunk still contains value
1517 // but not shifted - means no more available chars to shift
1518 if (!shiftChar) break;
1519 --ci;
1520 } else {
1521 // clean if chunk has no value
1522 this.chunks.splice(ci, 1);
1523 }
1524 if (shiftChar) return shiftChar;
1525 }
1526 return '';
1527 }
1528 }
1529
1530 class PatternCursor {
1531 constructor(masked, pos) {
1532 this.masked = masked;
1533 this._log = [];
1534 const {
1535 offset,
1536 index
1537 } = masked._mapPosToBlock(pos) || (pos < 0 ?
1538 // first
1539 {
1540 index: 0,
1541 offset: 0
1542 } :
1543 // last
1544 {
1545 index: this.masked._blocks.length,
1546 offset: 0
1547 });
1548 this.offset = offset;
1549 this.index = index;
1550 this.ok = false;
1551 }
1552 get block() {
1553 return this.masked._blocks[this.index];
1554 }
1555 get pos() {
1556 return this.masked._blockStartPos(this.index) + this.offset;
1557 }
1558 get state() {
1559 return {
1560 index: this.index,
1561 offset: this.offset,
1562 ok: this.ok
1563 };
1564 }
1565 set state(s) {
1566 Object.assign(this, s);
1567 }
1568 pushState() {
1569 this._log.push(this.state);
1570 }
1571 popState() {
1572 const s = this._log.pop();
1573 if (s) this.state = s;
1574 return s;
1575 }
1576 bindBlock() {
1577 if (this.block) return;
1578 if (this.index < 0) {
1579 this.index = 0;
1580 this.offset = 0;
1581 }
1582 if (this.index >= this.masked._blocks.length) {
1583 this.index = this.masked._blocks.length - 1;
1584 this.offset = this.block.displayValue.length; // TODO this is stupid type error, `block` depends on index that was changed above
1585 }
1586 }
1587 _pushLeft(fn) {
1588 this.pushState();
1589 for (this.bindBlock(); 0 <= this.index; --this.index, this.offset = ((_this$block = this.block) == null ? void 0 : _this$block.displayValue.length) || 0) {
1590 var _this$block;
1591 if (fn()) return this.ok = true;
1592 }
1593 return this.ok = false;
1594 }
1595 _pushRight(fn) {
1596 this.pushState();
1597 for (this.bindBlock(); this.index < this.masked._blocks.length; ++this.index, this.offset = 0) {
1598 if (fn()) return this.ok = true;
1599 }
1600 return this.ok = false;
1601 }
1602 pushLeftBeforeFilled() {
1603 return this._pushLeft(() => {
1604 if (this.block.isFixed || !this.block.value) return;
1605 this.offset = this.block.nearestInputPos(this.offset, DIRECTION.FORCE_LEFT);
1606 if (this.offset !== 0) return true;
1607 });
1608 }
1609 pushLeftBeforeInput() {
1610 // cases:
1611 // filled input: 00|
1612 // optional empty input: 00[]|
1613 // nested block: XX<[]>|
1614 return this._pushLeft(() => {
1615 if (this.block.isFixed) return;
1616 this.offset = this.block.nearestInputPos(this.offset, DIRECTION.LEFT);
1617 return true;
1618 });
1619 }
1620 pushLeftBeforeRequired() {
1621 return this._pushLeft(() => {
1622 if (this.block.isFixed || this.block.isOptional && !this.block.value) return;
1623 this.offset = this.block.nearestInputPos(this.offset, DIRECTION.LEFT);
1624 return true;
1625 });
1626 }
1627 pushRightBeforeFilled() {
1628 return this._pushRight(() => {
1629 if (this.block.isFixed || !this.block.value) return;
1630 this.offset = this.block.nearestInputPos(this.offset, DIRECTION.FORCE_RIGHT);
1631 if (this.offset !== this.block.value.length) return true;
1632 });
1633 }
1634 pushRightBeforeInput() {
1635 return this._pushRight(() => {
1636 if (this.block.isFixed) return;
1637
1638 // const o = this.offset;
1639 this.offset = this.block.nearestInputPos(this.offset, DIRECTION.NONE);
1640 // HACK cases like (STILL DOES NOT WORK FOR NESTED)
1641 // aa|X
1642 // aa<X|[]>X_ - this will not work
1643 // if (o && o === this.offset && this.block instanceof PatternInputDefinition) continue;
1644 return true;
1645 });
1646 }
1647 pushRightBeforeRequired() {
1648 return this._pushRight(() => {
1649 if (this.block.isFixed || this.block.isOptional && !this.block.value) return;
1650
1651 // TODO check |[*]XX_
1652 this.offset = this.block.nearestInputPos(this.offset, DIRECTION.NONE);
1653 return true;
1654 });
1655 }
1656 }
1657
1658 class PatternFixedDefinition {
1659 /** */
1660
1661 /** */
1662
1663 /** */
1664
1665 /** */
1666
1667 /** */
1668
1669 /** */
1670
1671 constructor(opts) {
1672 Object.assign(this, opts);
1673 this._value = '';
1674 this.isFixed = true;
1675 }
1676 get value() {
1677 return this._value;
1678 }
1679 get unmaskedValue() {
1680 return this.isUnmasking ? this.value : '';
1681 }
1682 get rawInputValue() {
1683 return this._isRawInput ? this.value : '';
1684 }
1685 get displayValue() {
1686 return this.value;
1687 }
1688 reset() {
1689 this._isRawInput = false;
1690 this._value = '';
1691 }
1692 remove(fromPos, toPos) {
1693 if (fromPos === void 0) {
1694 fromPos = 0;
1695 }
1696 if (toPos === void 0) {
1697 toPos = this._value.length;
1698 }
1699 this._value = this._value.slice(0, fromPos) + this._value.slice(toPos);
1700 if (!this._value) this._isRawInput = false;
1701 return new ChangeDetails();
1702 }
1703 nearestInputPos(cursorPos, direction) {
1704 if (direction === void 0) {
1705 direction = DIRECTION.NONE;
1706 }
1707 const minPos = 0;
1708 const maxPos = this._value.length;
1709 switch (direction) {
1710 case DIRECTION.LEFT:
1711 case DIRECTION.FORCE_LEFT:
1712 return minPos;
1713 case DIRECTION.NONE:
1714 case DIRECTION.RIGHT:
1715 case DIRECTION.FORCE_RIGHT:
1716 default:
1717 return maxPos;
1718 }
1719 }
1720 totalInputPositions(fromPos, toPos) {
1721 if (fromPos === void 0) {
1722 fromPos = 0;
1723 }
1724 if (toPos === void 0) {
1725 toPos = this._value.length;
1726 }
1727 return this._isRawInput ? toPos - fromPos : 0;
1728 }
1729 extractInput(fromPos, toPos, flags) {
1730 if (fromPos === void 0) {
1731 fromPos = 0;
1732 }
1733 if (toPos === void 0) {
1734 toPos = this._value.length;
1735 }
1736 if (flags === void 0) {
1737 flags = {};
1738 }
1739 return flags.raw && this._isRawInput && this._value.slice(fromPos, toPos) || '';
1740 }
1741 get isComplete() {
1742 return true;
1743 }
1744 get isFilled() {
1745 return Boolean(this._value);
1746 }
1747 _appendChar(ch, flags) {
1748 if (flags === void 0) {
1749 flags = {};
1750 }
1751 if (this.isFilled) return new ChangeDetails();
1752 const appendEager = this.eager === true || this.eager === 'append';
1753 const appended = this.char === ch;
1754 const isResolved = appended && (this.isUnmasking || flags.input || flags.raw) && (!flags.raw || !appendEager) && !flags.tail;
1755 const details = new ChangeDetails({
1756 inserted: this.char,
1757 rawInserted: isResolved ? this.char : ''
1758 });
1759 this._value = this.char;
1760 this._isRawInput = isResolved && (flags.raw || flags.input);
1761 return details;
1762 }
1763 _appendEager() {
1764 return this._appendChar(this.char, {
1765 tail: true
1766 });
1767 }
1768 _appendPlaceholder() {
1769 const details = new ChangeDetails();
1770 if (this.isFilled) return details;
1771 this._value = details.inserted = this.char;
1772 return details;
1773 }
1774 extractTail() {
1775 return new ContinuousTailDetails('');
1776 }
1777 appendTail(tail) {
1778 if (isString(tail)) tail = new ContinuousTailDetails(String(tail));
1779 return tail.appendTo(this);
1780 }
1781 append(str, flags, tail) {
1782 const details = this._appendChar(str[0], flags);
1783 if (tail != null) {
1784 details.tailShift += this.appendTail(tail).tailShift;
1785 }
1786 return details;
1787 }
1788 doCommit() {}
1789 get state() {
1790 return {
1791 _value: this._value,
1792 _rawInputValue: this.rawInputValue
1793 };
1794 }
1795 set state(state) {
1796 this._value = state._value;
1797 this._isRawInput = Boolean(state._rawInputValue);
1798 }
1799 pad(flags) {
1800 return this._appendPlaceholder();
1801 }
1802 }
1803
1804 class PatternInputDefinition {
1805 /** */
1806
1807 /** */
1808
1809 /** */
1810
1811 /** */
1812
1813 /** */
1814
1815 /** */
1816
1817 /** */
1818
1819 /** */
1820
1821 constructor(opts) {
1822 const {
1823 parent,
1824 isOptional,
1825 placeholderChar,
1826 displayChar,
1827 lazy,
1828 eager,
1829 ...maskOpts
1830 } = opts;
1831 this.masked = createMask(maskOpts);
1832 Object.assign(this, {
1833 parent,
1834 isOptional,
1835 placeholderChar,
1836 displayChar,
1837 lazy,
1838 eager
1839 });
1840 }
1841 reset() {
1842 this.isFilled = false;
1843 this.masked.reset();
1844 }
1845 remove(fromPos, toPos) {
1846 if (fromPos === void 0) {
1847 fromPos = 0;
1848 }
1849 if (toPos === void 0) {
1850 toPos = this.value.length;
1851 }
1852 if (fromPos === 0 && toPos >= 1) {
1853 this.isFilled = false;
1854 return this.masked.remove(fromPos, toPos);
1855 }
1856 return new ChangeDetails();
1857 }
1858 get value() {
1859 return this.masked.value || (this.isFilled && !this.isOptional ? this.placeholderChar : '');
1860 }
1861 get unmaskedValue() {
1862 return this.masked.unmaskedValue;
1863 }
1864 get rawInputValue() {
1865 return this.masked.rawInputValue;
1866 }
1867 get displayValue() {
1868 return this.masked.value && this.displayChar || this.value;
1869 }
1870 get isComplete() {
1871 return Boolean(this.masked.value) || this.isOptional;
1872 }
1873 _appendChar(ch, flags) {
1874 if (flags === void 0) {
1875 flags = {};
1876 }
1877 if (this.isFilled) return new ChangeDetails();
1878 const state = this.masked.state;
1879 // simulate input
1880 let details = this.masked._appendChar(ch, this.currentMaskFlags(flags));
1881 if (details.inserted && this.doValidate(flags) === false) {
1882 details = new ChangeDetails();
1883 this.masked.state = state;
1884 }
1885 if (!details.inserted && !this.isOptional && !this.lazy && !flags.input) {
1886 details.inserted = this.placeholderChar;
1887 }
1888 details.skip = !details.inserted && !this.isOptional;
1889 this.isFilled = Boolean(details.inserted);
1890 return details;
1891 }
1892 append(str, flags, tail) {
1893 // TODO probably should be done via _appendChar
1894 return this.masked.append(str, this.currentMaskFlags(flags), tail);
1895 }
1896 _appendPlaceholder() {
1897 if (this.isFilled || this.isOptional) return new ChangeDetails();
1898 this.isFilled = true;
1899 return new ChangeDetails({
1900 inserted: this.placeholderChar
1901 });
1902 }
1903 _appendEager() {
1904 return new ChangeDetails();
1905 }
1906 extractTail(fromPos, toPos) {
1907 return this.masked.extractTail(fromPos, toPos);
1908 }
1909 appendTail(tail) {
1910 return this.masked.appendTail(tail);
1911 }
1912 extractInput(fromPos, toPos, flags) {
1913 if (fromPos === void 0) {
1914 fromPos = 0;
1915 }
1916 if (toPos === void 0) {
1917 toPos = this.value.length;
1918 }
1919 return this.masked.extractInput(fromPos, toPos, flags);
1920 }
1921 nearestInputPos(cursorPos, direction) {
1922 if (direction === void 0) {
1923 direction = DIRECTION.NONE;
1924 }
1925 const minPos = 0;
1926 const maxPos = this.value.length;
1927 const boundPos = Math.min(Math.max(cursorPos, minPos), maxPos);
1928 switch (direction) {
1929 case DIRECTION.LEFT:
1930 case DIRECTION.FORCE_LEFT:
1931 return this.isComplete ? boundPos : minPos;
1932 case DIRECTION.RIGHT:
1933 case DIRECTION.FORCE_RIGHT:
1934 return this.isComplete ? boundPos : maxPos;
1935 case DIRECTION.NONE:
1936 default:
1937 return boundPos;
1938 }
1939 }
1940 totalInputPositions(fromPos, toPos) {
1941 if (fromPos === void 0) {
1942 fromPos = 0;
1943 }
1944 if (toPos === void 0) {
1945 toPos = this.value.length;
1946 }
1947 return this.value.slice(fromPos, toPos).length;
1948 }
1949 doValidate(flags) {
1950 return this.masked.doValidate(this.currentMaskFlags(flags)) && (!this.parent || this.parent.doValidate(this.currentMaskFlags(flags)));
1951 }
1952 doCommit() {
1953 this.masked.doCommit();
1954 }
1955 get state() {
1956 return {
1957 _value: this.value,
1958 _rawInputValue: this.rawInputValue,
1959 masked: this.masked.state,
1960 isFilled: this.isFilled
1961 };
1962 }
1963 set state(state) {
1964 this.masked.state = state.masked;
1965 this.isFilled = state.isFilled;
1966 }
1967 currentMaskFlags(flags) {
1968 var _flags$_beforeTailSta;
1969 return {
1970 ...flags,
1971 _beforeTailState: (flags == null || (_flags$_beforeTailSta = flags._beforeTailState) == null ? void 0 : _flags$_beforeTailSta.masked) || (flags == null ? void 0 : flags._beforeTailState)
1972 };
1973 }
1974 pad(flags) {
1975 return new ChangeDetails();
1976 }
1977 }
1978 PatternInputDefinition.DEFAULT_DEFINITIONS = {
1979 '0': /\d/,
1980 'a': /[\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/,
1981 // http://stackoverflow.com/a/22075070
1982 '*': /./
1983 };
1984
1985 /** Masking by RegExp */
1986 class MaskedRegExp extends Masked {
1987 /** */
1988
1989 /** Enable characters overwriting */
1990
1991 /** */
1992
1993 /** */
1994
1995 /** */
1996
1997 updateOptions(opts) {
1998 super.updateOptions(opts);
1999 }
2000 _update(opts) {
2001 const mask = opts.mask;
2002 if (mask) opts.validate = value => value.search(mask) >= 0;
2003 super._update(opts);
2004 }
2005 }
2006 IMask.MaskedRegExp = MaskedRegExp;
2007
2008 /** Pattern mask */
2009 class MaskedPattern extends Masked {
2010 /** */
2011
2012 /** */
2013
2014 /** Single char for empty input */
2015
2016 /** Single char for filled input */
2017
2018 /** Show placeholder only when needed */
2019
2020 /** Enable characters overwriting */
2021
2022 /** */
2023
2024 /** */
2025
2026 /** */
2027
2028 constructor(opts) {
2029 super({
2030 ...MaskedPattern.DEFAULTS,
2031 ...opts,
2032 definitions: Object.assign({}, PatternInputDefinition.DEFAULT_DEFINITIONS, opts == null ? void 0 : opts.definitions)
2033 });
2034 }
2035 updateOptions(opts) {
2036 super.updateOptions(opts);
2037 }
2038 _update(opts) {
2039 opts.definitions = Object.assign({}, this.definitions, opts.definitions);
2040 super._update(opts);
2041 this._rebuildMask();
2042 }
2043 _rebuildMask() {
2044 const defs = this.definitions;
2045 this._blocks = [];
2046 this.exposeBlock = undefined;
2047 this._stops = [];
2048 this._maskedBlocks = {};
2049 const pattern = this.mask;
2050 if (!pattern || !defs) return;
2051 let unmaskingBlock = false;
2052 let optionalBlock = false;
2053 for (let i = 0; i < pattern.length; ++i) {
2054 if (this.blocks) {
2055 const p = pattern.slice(i);
2056 const bNames = Object.keys(this.blocks).filter(bName => p.indexOf(bName) === 0);
2057 // order by key length
2058 bNames.sort((a, b) => b.length - a.length);
2059 // use block name with max length
2060 const bName = bNames[0];
2061 if (bName) {
2062 const {
2063 expose,
2064 repeat,
2065 ...bOpts
2066 } = normalizeOpts(this.blocks[bName]); // TODO type Opts<Arg & Extra>
2067 const blockOpts = {
2068 lazy: this.lazy,
2069 eager: this.eager,
2070 placeholderChar: this.placeholderChar,
2071 displayChar: this.displayChar,
2072 overwrite: this.overwrite,
2073 autofix: this.autofix,
2074 ...bOpts,
2075 repeat,
2076 parent: this
2077 };
2078 const maskedBlock = repeat != null ? new IMask.RepeatBlock(blockOpts /* TODO */) : createMask(blockOpts);
2079 if (maskedBlock) {
2080 this._blocks.push(maskedBlock);
2081 if (expose) this.exposeBlock = maskedBlock;
2082
2083 // store block index
2084 if (!this._maskedBlocks[bName]) this._maskedBlocks[bName] = [];
2085 this._maskedBlocks[bName].push(this._blocks.length - 1);
2086 }
2087 i += bName.length - 1;
2088 continue;
2089 }
2090 }
2091 let char = pattern[i];
2092 let isInput = (char in defs);
2093 if (char === MaskedPattern.STOP_CHAR) {
2094 this._stops.push(this._blocks.length);
2095 continue;
2096 }
2097 if (char === '{' || char === '}') {
2098 unmaskingBlock = !unmaskingBlock;
2099 continue;
2100 }
2101 if (char === '[' || char === ']') {
2102 optionalBlock = !optionalBlock;
2103 continue;
2104 }
2105 if (char === MaskedPattern.ESCAPE_CHAR) {
2106 ++i;
2107 char = pattern[i];
2108 if (!char) break;
2109 isInput = false;
2110 }
2111 const def = isInput ? new PatternInputDefinition({
2112 isOptional: optionalBlock,
2113 lazy: this.lazy,
2114 eager: this.eager,
2115 placeholderChar: this.placeholderChar,
2116 displayChar: this.displayChar,
2117 ...normalizeOpts(defs[char]),
2118 parent: this
2119 }) : new PatternFixedDefinition({
2120 char,
2121 eager: this.eager,
2122 isUnmasking: unmaskingBlock
2123 });
2124 this._blocks.push(def);
2125 }
2126 }
2127 get state() {
2128 return {
2129 ...super.state,
2130 _blocks: this._blocks.map(b => b.state)
2131 };
2132 }
2133 set state(state) {
2134 if (!state) {
2135 this.reset();
2136 return;
2137 }
2138 const {
2139 _blocks,
2140 ...maskedState
2141 } = state;
2142 this._blocks.forEach((b, bi) => b.state = _blocks[bi]);
2143 super.state = maskedState;
2144 }
2145 reset() {
2146 super.reset();
2147 this._blocks.forEach(b => b.reset());
2148 }
2149 get isComplete() {
2150 return this.exposeBlock ? this.exposeBlock.isComplete : this._blocks.every(b => b.isComplete);
2151 }
2152 get isFilled() {
2153 return this._blocks.every(b => b.isFilled);
2154 }
2155 get isFixed() {
2156 return this._blocks.every(b => b.isFixed);
2157 }
2158 get isOptional() {
2159 return this._blocks.every(b => b.isOptional);
2160 }
2161 doCommit() {
2162 this._blocks.forEach(b => b.doCommit());
2163 super.doCommit();
2164 }
2165 get unmaskedValue() {
2166 return this.exposeBlock ? this.exposeBlock.unmaskedValue : this._blocks.reduce((str, b) => str += b.unmaskedValue, '');
2167 }
2168 set unmaskedValue(unmaskedValue) {
2169 if (this.exposeBlock) {
2170 const tail = this.extractTail(this._blockStartPos(this._blocks.indexOf(this.exposeBlock)) + this.exposeBlock.displayValue.length);
2171 this.exposeBlock.unmaskedValue = unmaskedValue;
2172 this.appendTail(tail);
2173 this.doCommit();
2174 } else super.unmaskedValue = unmaskedValue;
2175 }
2176 get value() {
2177 return this.exposeBlock ? this.exposeBlock.value :
2178 // TODO return _value when not in change?
2179 this._blocks.reduce((str, b) => str += b.value, '');
2180 }
2181 set value(value) {
2182 if (this.exposeBlock) {
2183 const tail = this.extractTail(this._blockStartPos(this._blocks.indexOf(this.exposeBlock)) + this.exposeBlock.displayValue.length);
2184 this.exposeBlock.value = value;
2185 this.appendTail(tail);
2186 this.doCommit();
2187 } else super.value = value;
2188 }
2189 get typedValue() {
2190 return this.exposeBlock ? this.exposeBlock.typedValue : super.typedValue;
2191 }
2192 set typedValue(value) {
2193 if (this.exposeBlock) {
2194 const tail = this.extractTail(this._blockStartPos(this._blocks.indexOf(this.exposeBlock)) + this.exposeBlock.displayValue.length);
2195 this.exposeBlock.typedValue = value;
2196 this.appendTail(tail);
2197 this.doCommit();
2198 } else super.typedValue = value;
2199 }
2200 get displayValue() {
2201 return this._blocks.reduce((str, b) => str += b.displayValue, '');
2202 }
2203 appendTail(tail) {
2204 return super.appendTail(tail).aggregate(this._appendPlaceholder());
2205 }
2206 _appendEager() {
2207 var _this$_mapPosToBlock;
2208 const details = new ChangeDetails();
2209 let startBlockIndex = (_this$_mapPosToBlock = this._mapPosToBlock(this.displayValue.length)) == null ? void 0 : _this$_mapPosToBlock.index;
2210 if (startBlockIndex == null) return details;
2211
2212 // TODO test if it works for nested pattern masks
2213 if (this._blocks[startBlockIndex].isFilled) ++startBlockIndex;
2214 for (let bi = startBlockIndex; bi < this._blocks.length; ++bi) {
2215 const d = this._blocks[bi]._appendEager();
2216 if (!d.inserted) break;
2217 details.aggregate(d);
2218 }
2219 return details;
2220 }
2221 _appendCharRaw(ch, flags) {
2222 if (flags === void 0) {
2223 flags = {};
2224 }
2225 const blockIter = this._mapPosToBlock(this.displayValue.length);
2226 const details = new ChangeDetails();
2227 if (!blockIter) return details;
2228 for (let bi = blockIter.index, block; block = this._blocks[bi]; ++bi) {
2229 var _flags$_beforeTailSta;
2230 const blockDetails = block._appendChar(ch, {
2231 ...flags,
2232 _beforeTailState: (_flags$_beforeTailSta = flags._beforeTailState) == null || (_flags$_beforeTailSta = _flags$_beforeTailSta._blocks) == null ? void 0 : _flags$_beforeTailSta[bi]
2233 });
2234 details.aggregate(blockDetails);
2235 if (blockDetails.consumed) break; // go next char
2236 }
2237 return details;
2238 }
2239 extractTail(fromPos, toPos) {
2240 if (fromPos === void 0) {
2241 fromPos = 0;
2242 }
2243 if (toPos === void 0) {
2244 toPos = this.displayValue.length;
2245 }
2246 const chunkTail = new ChunksTailDetails();
2247 if (fromPos === toPos) return chunkTail;
2248 this._forEachBlocksInRange(fromPos, toPos, (b, bi, bFromPos, bToPos) => {
2249 const blockChunk = b.extractTail(bFromPos, bToPos);
2250 blockChunk.stop = this._findStopBefore(bi);
2251 blockChunk.from = this._blockStartPos(bi);
2252 if (blockChunk instanceof ChunksTailDetails) blockChunk.blockIndex = bi;
2253 chunkTail.extend(blockChunk);
2254 });
2255 return chunkTail;
2256 }
2257 extractInput(fromPos, toPos, flags) {
2258 if (fromPos === void 0) {
2259 fromPos = 0;
2260 }
2261 if (toPos === void 0) {
2262 toPos = this.displayValue.length;
2263 }
2264 if (flags === void 0) {
2265 flags = {};
2266 }
2267 if (fromPos === toPos) return '';
2268 let input = '';
2269 this._forEachBlocksInRange(fromPos, toPos, (b, _, fromPos, toPos) => {
2270 input += b.extractInput(fromPos, toPos, flags);
2271 });
2272 return input;
2273 }
2274 _findStopBefore(blockIndex) {
2275 let stopBefore;
2276 for (let si = 0; si < this._stops.length; ++si) {
2277 const stop = this._stops[si];
2278 if (stop <= blockIndex) stopBefore = stop;else break;
2279 }
2280 return stopBefore;
2281 }
2282
2283 /** Appends placeholder depending on laziness */
2284 _appendPlaceholder(toBlockIndex) {
2285 const details = new ChangeDetails();
2286 if (this.lazy && toBlockIndex == null) return details;
2287 const startBlockIter = this._mapPosToBlock(this.displayValue.length);
2288 if (!startBlockIter) return details;
2289 const startBlockIndex = startBlockIter.index;
2290 const endBlockIndex = toBlockIndex != null ? toBlockIndex : this._blocks.length;
2291 this._blocks.slice(startBlockIndex, endBlockIndex).forEach(b => {
2292 if (!b.lazy || toBlockIndex != null) {
2293 var _blocks2;
2294 details.aggregate(b._appendPlaceholder((_blocks2 = b._blocks) == null ? void 0 : _blocks2.length));
2295 }
2296 });
2297 return details;
2298 }
2299
2300 /** Finds block in pos */
2301 _mapPosToBlock(pos) {
2302 let accVal = '';
2303 for (let bi = 0; bi < this._blocks.length; ++bi) {
2304 const block = this._blocks[bi];
2305 const blockStartPos = accVal.length;
2306 accVal += block.displayValue;
2307 if (pos <= accVal.length) {
2308 return {
2309 index: bi,
2310 offset: pos - blockStartPos
2311 };
2312 }
2313 }
2314 }
2315 _blockStartPos(blockIndex) {
2316 return this._blocks.slice(0, blockIndex).reduce((pos, b) => pos += b.displayValue.length, 0);
2317 }
2318 _forEachBlocksInRange(fromPos, toPos, fn) {
2319 if (toPos === void 0) {
2320 toPos = this.displayValue.length;
2321 }
2322 const fromBlockIter = this._mapPosToBlock(fromPos);
2323 if (fromBlockIter) {
2324 const toBlockIter = this._mapPosToBlock(toPos);
2325 // process first block
2326 const isSameBlock = toBlockIter && fromBlockIter.index === toBlockIter.index;
2327 const fromBlockStartPos = fromBlockIter.offset;
2328 const fromBlockEndPos = toBlockIter && isSameBlock ? toBlockIter.offset : this._blocks[fromBlockIter.index].displayValue.length;
2329 fn(this._blocks[fromBlockIter.index], fromBlockIter.index, fromBlockStartPos, fromBlockEndPos);
2330 if (toBlockIter && !isSameBlock) {
2331 // process intermediate blocks
2332 for (let bi = fromBlockIter.index + 1; bi < toBlockIter.index; ++bi) {
2333 fn(this._blocks[bi], bi, 0, this._blocks[bi].displayValue.length);
2334 }
2335
2336 // process last block
2337 fn(this._blocks[toBlockIter.index], toBlockIter.index, 0, toBlockIter.offset);
2338 }
2339 }
2340 }
2341 remove(fromPos, toPos) {
2342 if (fromPos === void 0) {
2343 fromPos = 0;
2344 }
2345 if (toPos === void 0) {
2346 toPos = this.displayValue.length;
2347 }
2348 const removeDetails = super.remove(fromPos, toPos);
2349 this._forEachBlocksInRange(fromPos, toPos, (b, _, bFromPos, bToPos) => {
2350 removeDetails.aggregate(b.remove(bFromPos, bToPos));
2351 });
2352 return removeDetails;
2353 }
2354 nearestInputPos(cursorPos, direction) {
2355 if (direction === void 0) {
2356 direction = DIRECTION.NONE;
2357 }
2358 if (!this._blocks.length) return 0;
2359 const cursor = new PatternCursor(this, cursorPos);
2360 if (direction === DIRECTION.NONE) {
2361 // -------------------------------------------------
2362 // NONE should only go out from fixed to the right!
2363 // -------------------------------------------------
2364 if (cursor.pushRightBeforeInput()) return cursor.pos;
2365 cursor.popState();
2366 if (cursor.pushLeftBeforeInput()) return cursor.pos;
2367 return this.displayValue.length;
2368 }
2369
2370 // FORCE is only about a|* otherwise is 0
2371 if (direction === DIRECTION.LEFT || direction === DIRECTION.FORCE_LEFT) {
2372 // try to break fast when *|a
2373 if (direction === DIRECTION.LEFT) {
2374 cursor.pushRightBeforeFilled();
2375 if (cursor.ok && cursor.pos === cursorPos) return cursorPos;
2376 cursor.popState();
2377 }
2378
2379 // forward flow
2380 cursor.pushLeftBeforeInput();
2381 cursor.pushLeftBeforeRequired();
2382 cursor.pushLeftBeforeFilled();
2383
2384 // backward flow
2385 if (direction === DIRECTION.LEFT) {
2386 cursor.pushRightBeforeInput();
2387 cursor.pushRightBeforeRequired();
2388 if (cursor.ok && cursor.pos <= cursorPos) return cursor.pos;
2389 cursor.popState();
2390 if (cursor.ok && cursor.pos <= cursorPos) return cursor.pos;
2391 cursor.popState();
2392 }
2393 if (cursor.ok) return cursor.pos;
2394 if (direction === DIRECTION.FORCE_LEFT) return 0;
2395 cursor.popState();
2396 if (cursor.ok) return cursor.pos;
2397 cursor.popState();
2398 if (cursor.ok) return cursor.pos;
2399 return 0;
2400 }
2401 if (direction === DIRECTION.RIGHT || direction === DIRECTION.FORCE_RIGHT) {
2402 // forward flow
2403 cursor.pushRightBeforeInput();
2404 cursor.pushRightBeforeRequired();
2405 if (cursor.pushRightBeforeFilled()) return cursor.pos;
2406 if (direction === DIRECTION.FORCE_RIGHT) return this.displayValue.length;
2407
2408 // backward flow
2409 cursor.popState();
2410 if (cursor.ok) return cursor.pos;
2411 cursor.popState();
2412 if (cursor.ok) return cursor.pos;
2413 return this.nearestInputPos(cursorPos, DIRECTION.LEFT);
2414 }
2415 return cursorPos;
2416 }
2417 totalInputPositions(fromPos, toPos) {
2418 if (fromPos === void 0) {
2419 fromPos = 0;
2420 }
2421 if (toPos === void 0) {
2422 toPos = this.displayValue.length;
2423 }
2424 let total = 0;
2425 this._forEachBlocksInRange(fromPos, toPos, (b, _, bFromPos, bToPos) => {
2426 total += b.totalInputPositions(bFromPos, bToPos);
2427 });
2428 return total;
2429 }
2430
2431 /** Get block by name */
2432 maskedBlock(name) {
2433 return this.maskedBlocks(name)[0];
2434 }
2435
2436 /** Get all blocks by name */
2437 maskedBlocks(name) {
2438 const indices = this._maskedBlocks[name];
2439 if (!indices) return [];
2440 return indices.map(gi => this._blocks[gi]);
2441 }
2442 pad(flags) {
2443 const details = new ChangeDetails();
2444 this._forEachBlocksInRange(0, this.displayValue.length, b => details.aggregate(b.pad(flags)));
2445 return details;
2446 }
2447 }
2448 MaskedPattern.DEFAULTS = {
2449 ...Masked.DEFAULTS,
2450 lazy: true,
2451 placeholderChar: '_'
2452 };
2453 MaskedPattern.STOP_CHAR = '`';
2454 MaskedPattern.ESCAPE_CHAR = '\\';
2455 MaskedPattern.InputDefinition = PatternInputDefinition;
2456 MaskedPattern.FixedDefinition = PatternFixedDefinition;
2457 IMask.MaskedPattern = MaskedPattern;
2458
2459 /** Pattern which accepts ranges */
2460 class MaskedRange extends MaskedPattern {
2461 /**
2462 Optionally sets max length of pattern.
2463 Used when pattern length is longer then `to` param length. Pads zeros at start in this case.
2464 */
2465
2466 /** Min bound */
2467
2468 /** Max bound */
2469
2470 get _matchFrom() {
2471 return this.maxLength - String(this.from).length;
2472 }
2473 constructor(opts) {
2474 super(opts); // mask will be created in _update
2475 }
2476 updateOptions(opts) {
2477 super.updateOptions(opts);
2478 }
2479 _update(opts) {
2480 const {
2481 to = this.to || 0,
2482 from = this.from || 0,
2483 maxLength = this.maxLength || 0,
2484 autofix = this.autofix,
2485 ...patternOpts
2486 } = opts;
2487 this.to = to;
2488 this.from = from;
2489 this.maxLength = Math.max(String(to).length, maxLength);
2490 this.autofix = autofix;
2491 const fromStr = String(this.from).padStart(this.maxLength, '0');
2492 const toStr = String(this.to).padStart(this.maxLength, '0');
2493 let sameCharsCount = 0;
2494 while (sameCharsCount < toStr.length && toStr[sameCharsCount] === fromStr[sameCharsCount]) ++sameCharsCount;
2495 patternOpts.mask = toStr.slice(0, sameCharsCount).replace(/0/g, '\\0') + '0'.repeat(this.maxLength - sameCharsCount);
2496 super._update(patternOpts);
2497 }
2498 get isComplete() {
2499 return super.isComplete && Boolean(this.value);
2500 }
2501 boundaries(str) {
2502 let minstr = '';
2503 let maxstr = '';
2504 const [, placeholder, num] = str.match(/^(\D*)(\d*)(\D*)/) || [];
2505 if (num) {
2506 minstr = '0'.repeat(placeholder.length) + num;
2507 maxstr = '9'.repeat(placeholder.length) + num;
2508 }
2509 minstr = minstr.padEnd(this.maxLength, '0');
2510 maxstr = maxstr.padEnd(this.maxLength, '9');
2511 return [minstr, maxstr];
2512 }
2513 doPrepareChar(ch, flags) {
2514 if (flags === void 0) {
2515 flags = {};
2516 }
2517 let details;
2518 [ch, details] = super.doPrepareChar(ch.replace(/\D/g, ''), flags);
2519 if (!ch) details.skip = !this.isComplete;
2520 return [ch, details];
2521 }
2522 _appendCharRaw(ch, flags) {
2523 if (flags === void 0) {
2524 flags = {};
2525 }
2526 if (!this.autofix || this.value.length + 1 > this.maxLength) return super._appendCharRaw(ch, flags);
2527 const fromStr = String(this.from).padStart(this.maxLength, '0');
2528 const toStr = String(this.to).padStart(this.maxLength, '0');
2529 const [minstr, maxstr] = this.boundaries(this.value + ch);
2530 if (Number(maxstr) < this.from) return super._appendCharRaw(fromStr[this.value.length], flags);
2531 if (Number(minstr) > this.to) {
2532 if (!flags.tail && this.autofix === 'pad' && this.value.length + 1 < this.maxLength) {
2533 return super._appendCharRaw(fromStr[this.value.length], flags).aggregate(this._appendCharRaw(ch, flags));
2534 }
2535 return super._appendCharRaw(toStr[this.value.length], flags);
2536 }
2537 return super._appendCharRaw(ch, flags);
2538 }
2539 doValidate(flags) {
2540 const str = this.value;
2541 const firstNonZero = str.search(/[^0]/);
2542 if (firstNonZero === -1 && str.length <= this._matchFrom) return true;
2543 const [minstr, maxstr] = this.boundaries(str);
2544 return this.from <= Number(maxstr) && Number(minstr) <= this.to && super.doValidate(flags);
2545 }
2546 pad(flags) {
2547 const details = new ChangeDetails();
2548 if (this.value.length === this.maxLength) return details;
2549 const value = this.value;
2550 const padLength = this.maxLength - this.value.length;
2551 if (padLength) {
2552 this.reset();
2553 for (let i = 0; i < padLength; ++i) {
2554 details.aggregate(super._appendCharRaw('0', flags));
2555 }
2556
2557 // append tail
2558 value.split('').forEach(ch => this._appendCharRaw(ch));
2559 }
2560 return details;
2561 }
2562 }
2563 IMask.MaskedRange = MaskedRange;
2564
2565 const DefaultPattern = 'd{.}`m{.}`Y';
2566
2567 // Make format and parse required when pattern is provided
2568
2569 /** Date mask */
2570 class MaskedDate extends MaskedPattern {
2571 static extractPatternOptions(opts) {
2572 const {
2573 mask,
2574 pattern,
2575 ...patternOpts
2576 } = opts;
2577 return {
2578 ...patternOpts,
2579 mask: isString(mask) ? mask : pattern
2580 };
2581 }
2582
2583 /** Pattern mask for date according to {@link MaskedDate#format} */
2584
2585 /** Start date */
2586
2587 /** End date */
2588
2589 /** Format typed value to string */
2590
2591 /** Parse string to get typed value */
2592
2593 constructor(opts) {
2594 super(MaskedDate.extractPatternOptions({
2595 ...MaskedDate.DEFAULTS,
2596 ...opts
2597 }));
2598 }
2599 updateOptions(opts) {
2600 super.updateOptions(opts);
2601 }
2602 _update(opts) {
2603 const {
2604 mask,
2605 pattern,
2606 blocks,
2607 ...patternOpts
2608 } = {
2609 ...MaskedDate.DEFAULTS,
2610 ...opts
2611 };
2612 const patternBlocks = Object.assign({}, MaskedDate.GET_DEFAULT_BLOCKS());
2613 // adjust year block
2614 if (opts.min) patternBlocks.Y.from = opts.min.getFullYear();
2615 if (opts.max) patternBlocks.Y.to = opts.max.getFullYear();
2616 if (opts.min && opts.max && patternBlocks.Y.from === patternBlocks.Y.to) {
2617 patternBlocks.m.from = opts.min.getMonth() + 1;
2618 patternBlocks.m.to = opts.max.getMonth() + 1;
2619 if (patternBlocks.m.from === patternBlocks.m.to) {
2620 patternBlocks.d.from = opts.min.getDate();
2621 patternBlocks.d.to = opts.max.getDate();
2622 }
2623 }
2624 Object.assign(patternBlocks, this.blocks, blocks);
2625 super._update({
2626 ...patternOpts,
2627 mask: isString(mask) ? mask : pattern,
2628 blocks: patternBlocks
2629 });
2630 }
2631 doValidate(flags) {
2632 const date = this.date;
2633 return super.doValidate(flags) && (!this.isComplete || this.isDateExist(this.value) && date != null && (this.min == null || this.min <= date) && (this.max == null || date <= this.max));
2634 }
2635
2636 /** Checks if date is exists */
2637 isDateExist(str) {
2638 return this.format(this.parse(str, this), this).indexOf(str) >= 0;
2639 }
2640
2641 /** Parsed Date */
2642 get date() {
2643 return this.typedValue;
2644 }
2645 set date(date) {
2646 this.typedValue = date;
2647 }
2648 get typedValue() {
2649 return this.isComplete ? super.typedValue : null;
2650 }
2651 set typedValue(value) {
2652 super.typedValue = value;
2653 }
2654 maskEquals(mask) {
2655 return mask === Date || super.maskEquals(mask);
2656 }
2657 optionsIsChanged(opts) {
2658 return super.optionsIsChanged(MaskedDate.extractPatternOptions(opts));
2659 }
2660 }
2661 MaskedDate.GET_DEFAULT_BLOCKS = () => ({
2662 d: {
2663 mask: MaskedRange,
2664 from: 1,
2665 to: 31,
2666 maxLength: 2
2667 },
2668 m: {
2669 mask: MaskedRange,
2670 from: 1,
2671 to: 12,
2672 maxLength: 2
2673 },
2674 Y: {
2675 mask: MaskedRange,
2676 from: 1900,
2677 to: 9999
2678 }
2679 });
2680 MaskedDate.DEFAULTS = {
2681 ...MaskedPattern.DEFAULTS,
2682 mask: Date,
2683 pattern: DefaultPattern,
2684 format: (date, masked) => {
2685 if (!date) return '';
2686 const day = String(date.getDate()).padStart(2, '0');
2687 const month = String(date.getMonth() + 1).padStart(2, '0');
2688 const year = date.getFullYear();
2689 return [day, month, year].join('.');
2690 },
2691 parse: (str, masked) => {
2692 const [day, month, year] = str.split('.').map(Number);
2693 return new Date(year, month - 1, day);
2694 }
2695 };
2696 IMask.MaskedDate = MaskedDate;
2697
2698 /** Dynamic mask for choosing appropriate mask in run-time */
2699 class MaskedDynamic extends Masked {
2700 constructor(opts) {
2701 super({
2702 ...MaskedDynamic.DEFAULTS,
2703 ...opts
2704 });
2705 this.currentMask = undefined;
2706 }
2707 updateOptions(opts) {
2708 super.updateOptions(opts);
2709 }
2710 _update(opts) {
2711 super._update(opts);
2712 if ('mask' in opts) {
2713 this.exposeMask = undefined;
2714 // mask could be totally dynamic with only `dispatch` option
2715 this.compiledMasks = Array.isArray(opts.mask) ? opts.mask.map(m => {
2716 const {
2717 expose,
2718 ...maskOpts
2719 } = normalizeOpts(m);
2720 const masked = createMask({
2721 overwrite: this._overwrite,
2722 eager: this._eager,
2723 skipInvalid: this._skipInvalid,
2724 ...maskOpts
2725 });
2726 if (expose) this.exposeMask = masked;
2727 return masked;
2728 }) : [];
2729
2730 // this.currentMask = this.doDispatch(''); // probably not needed but lets see
2731 }
2732 }
2733 _appendCharRaw(ch, flags) {
2734 if (flags === void 0) {
2735 flags = {};
2736 }
2737 const details = this._applyDispatch(ch, flags);
2738 if (this.currentMask) {
2739 details.aggregate(this.currentMask._appendChar(ch, this.currentMaskFlags(flags)));
2740 }
2741 return details;
2742 }
2743 _applyDispatch(appended, flags, tail) {
2744 if (appended === void 0) {
2745 appended = '';
2746 }
2747 if (flags === void 0) {
2748 flags = {};
2749 }
2750 if (tail === void 0) {
2751 tail = '';
2752 }
2753 const prevValueBeforeTail = flags.tail && flags._beforeTailState != null ? flags._beforeTailState._value : this.value;
2754 const inputValue = this.rawInputValue;
2755 const insertValue = flags.tail && flags._beforeTailState != null ? flags._beforeTailState._rawInputValue : inputValue;
2756 const tailValue = inputValue.slice(insertValue.length);
2757 const prevMask = this.currentMask;
2758 const details = new ChangeDetails();
2759 const prevMaskState = prevMask == null ? void 0 : prevMask.state;
2760
2761 // clone flags to prevent overwriting `_beforeTailState`
2762 this.currentMask = this.doDispatch(appended, {
2763 ...flags
2764 }, tail);
2765
2766 // restore state after dispatch
2767 if (this.currentMask) {
2768 if (this.currentMask !== prevMask) {
2769 // if mask changed reapply input
2770 this.currentMask.reset();
2771 if (insertValue) {
2772 this.currentMask.append(insertValue, {
2773 raw: true
2774 });
2775 details.tailShift = this.currentMask.value.length - prevValueBeforeTail.length;
2776 }
2777 if (tailValue) {
2778 details.tailShift += this.currentMask.append(tailValue, {
2779 raw: true,
2780 tail: true
2781 }).tailShift;
2782 }
2783 } else if (prevMaskState) {
2784 // Dispatch can do something bad with state, so
2785 // restore prev mask state
2786 this.currentMask.state = prevMaskState;
2787 }
2788 }
2789 return details;
2790 }
2791 _appendPlaceholder() {
2792 const details = this._applyDispatch();
2793 if (this.currentMask) {
2794 details.aggregate(this.currentMask._appendPlaceholder());
2795 }
2796 return details;
2797 }
2798 _appendEager() {
2799 const details = this._applyDispatch();
2800 if (this.currentMask) {
2801 details.aggregate(this.currentMask._appendEager());
2802 }
2803 return details;
2804 }
2805 appendTail(tail) {
2806 const details = new ChangeDetails();
2807 if (tail) details.aggregate(this._applyDispatch('', {}, tail));
2808 return details.aggregate(this.currentMask ? this.currentMask.appendTail(tail) : super.appendTail(tail));
2809 }
2810 currentMaskFlags(flags) {
2811 var _flags$_beforeTailSta, _flags$_beforeTailSta2;
2812 return {
2813 ...flags,
2814 _beforeTailState: ((_flags$_beforeTailSta = flags._beforeTailState) == null ? void 0 : _flags$_beforeTailSta.currentMaskRef) === this.currentMask && ((_flags$_beforeTailSta2 = flags._beforeTailState) == null ? void 0 : _flags$_beforeTailSta2.currentMask) || flags._beforeTailState
2815 };
2816 }
2817 doDispatch(appended, flags, tail) {
2818 if (flags === void 0) {
2819 flags = {};
2820 }
2821 if (tail === void 0) {
2822 tail = '';
2823 }
2824 return this.dispatch(appended, this, flags, tail);
2825 }
2826 doValidate(flags) {
2827 return super.doValidate(flags) && (!this.currentMask || this.currentMask.doValidate(this.currentMaskFlags(flags)));
2828 }
2829 doPrepare(str, flags) {
2830 if (flags === void 0) {
2831 flags = {};
2832 }
2833 let [s, details] = super.doPrepare(str, flags);
2834 if (this.currentMask) {
2835 let currentDetails;
2836 [s, currentDetails] = super.doPrepare(s, this.currentMaskFlags(flags));
2837 details = details.aggregate(currentDetails);
2838 }
2839 return [s, details];
2840 }
2841 doPrepareChar(str, flags) {
2842 if (flags === void 0) {
2843 flags = {};
2844 }
2845 let [s, details] = super.doPrepareChar(str, flags);
2846 if (this.currentMask) {
2847 let currentDetails;
2848 [s, currentDetails] = super.doPrepareChar(s, this.currentMaskFlags(flags));
2849 details = details.aggregate(currentDetails);
2850 }
2851 return [s, details];
2852 }
2853 reset() {
2854 var _this$currentMask;
2855 (_this$currentMask = this.currentMask) == null || _this$currentMask.reset();
2856 this.compiledMasks.forEach(m => m.reset());
2857 }
2858 get value() {
2859 return this.exposeMask ? this.exposeMask.value : this.currentMask ? this.currentMask.value : '';
2860 }
2861 set value(value) {
2862 if (this.exposeMask) {
2863 this.exposeMask.value = value;
2864 this.currentMask = this.exposeMask;
2865 this._applyDispatch();
2866 } else super.value = value;
2867 }
2868 get unmaskedValue() {
2869 return this.exposeMask ? this.exposeMask.unmaskedValue : this.currentMask ? this.currentMask.unmaskedValue : '';
2870 }
2871 set unmaskedValue(unmaskedValue) {
2872 if (this.exposeMask) {
2873 this.exposeMask.unmaskedValue = unmaskedValue;
2874 this.currentMask = this.exposeMask;
2875 this._applyDispatch();
2876 } else super.unmaskedValue = unmaskedValue;
2877 }
2878 get typedValue() {
2879 return this.exposeMask ? this.exposeMask.typedValue : this.currentMask ? this.currentMask.typedValue : '';
2880 }
2881 set typedValue(typedValue) {
2882 if (this.exposeMask) {
2883 this.exposeMask.typedValue = typedValue;
2884 this.currentMask = this.exposeMask;
2885 this._applyDispatch();
2886 return;
2887 }
2888 let unmaskedValue = String(typedValue);
2889
2890 // double check it
2891 if (this.currentMask) {
2892 this.currentMask.typedValue = typedValue;
2893 unmaskedValue = this.currentMask.unmaskedValue;
2894 }
2895 this.unmaskedValue = unmaskedValue;
2896 }
2897 get displayValue() {
2898 return this.currentMask ? this.currentMask.displayValue : '';
2899 }
2900 get isComplete() {
2901 var _this$currentMask2;
2902 return Boolean((_this$currentMask2 = this.currentMask) == null ? void 0 : _this$currentMask2.isComplete);
2903 }
2904 get isFilled() {
2905 var _this$currentMask3;
2906 return Boolean((_this$currentMask3 = this.currentMask) == null ? void 0 : _this$currentMask3.isFilled);
2907 }
2908 remove(fromPos, toPos) {
2909 const details = new ChangeDetails();
2910 if (this.currentMask) {
2911 details.aggregate(this.currentMask.remove(fromPos, toPos))
2912 // update with dispatch
2913 .aggregate(this._applyDispatch());
2914 }
2915 return details;
2916 }
2917 get state() {
2918 var _this$currentMask4;
2919 return {
2920 ...super.state,
2921 _rawInputValue: this.rawInputValue,
2922 compiledMasks: this.compiledMasks.map(m => m.state),
2923 currentMaskRef: this.currentMask,
2924 currentMask: (_this$currentMask4 = this.currentMask) == null ? void 0 : _this$currentMask4.state
2925 };
2926 }
2927 set state(state) {
2928 const {
2929 compiledMasks,
2930 currentMaskRef,
2931 currentMask,
2932 ...maskedState
2933 } = state;
2934 if (compiledMasks) this.compiledMasks.forEach((m, mi) => m.state = compiledMasks[mi]);
2935 if (currentMaskRef != null) {
2936 this.currentMask = currentMaskRef;
2937 this.currentMask.state = currentMask;
2938 }
2939 super.state = maskedState;
2940 }
2941 extractInput(fromPos, toPos, flags) {
2942 return this.currentMask ? this.currentMask.extractInput(fromPos, toPos, flags) : '';
2943 }
2944 extractTail(fromPos, toPos) {
2945 return this.currentMask ? this.currentMask.extractTail(fromPos, toPos) : super.extractTail(fromPos, toPos);
2946 }
2947 doCommit() {
2948 if (this.currentMask) this.currentMask.doCommit();
2949 super.doCommit();
2950 }
2951 nearestInputPos(cursorPos, direction) {
2952 return this.currentMask ? this.currentMask.nearestInputPos(cursorPos, direction) : super.nearestInputPos(cursorPos, direction);
2953 }
2954 get overwrite() {
2955 return this.currentMask ? this.currentMask.overwrite : this._overwrite;
2956 }
2957 set overwrite(overwrite) {
2958 this._overwrite = overwrite;
2959 }
2960 get eager() {
2961 return this.currentMask ? this.currentMask.eager : this._eager;
2962 }
2963 set eager(eager) {
2964 this._eager = eager;
2965 }
2966 get skipInvalid() {
2967 return this.currentMask ? this.currentMask.skipInvalid : this._skipInvalid;
2968 }
2969 set skipInvalid(skipInvalid) {
2970 this._skipInvalid = skipInvalid;
2971 }
2972 get autofix() {
2973 return this.currentMask ? this.currentMask.autofix : this._autofix;
2974 }
2975 set autofix(autofix) {
2976 this._autofix = autofix;
2977 }
2978 maskEquals(mask) {
2979 return Array.isArray(mask) ? this.compiledMasks.every((m, mi) => {
2980 if (!mask[mi]) return;
2981 const {
2982 mask: oldMask,
2983 ...restOpts
2984 } = mask[mi];
2985 return objectIncludes(m, restOpts) && m.maskEquals(oldMask);
2986 }) : super.maskEquals(mask);
2987 }
2988 typedValueEquals(value) {
2989 var _this$currentMask5;
2990 return Boolean((_this$currentMask5 = this.currentMask) == null ? void 0 : _this$currentMask5.typedValueEquals(value));
2991 }
2992 }
2993 /** Currently chosen mask */
2994 /** Currently chosen mask */
2995 /** Compliled {@link Masked} options */
2996 /** Chooses {@link Masked} depending on input value */
2997 MaskedDynamic.DEFAULTS = {
2998 ...Masked.DEFAULTS,
2999 dispatch: (appended, masked, flags, tail) => {
3000 if (!masked.compiledMasks.length) return;
3001 const inputValue = masked.rawInputValue;
3002
3003 // simulate input
3004 const inputs = masked.compiledMasks.map((m, index) => {
3005 const isCurrent = masked.currentMask === m;
3006 const startInputPos = isCurrent ? m.displayValue.length : m.nearestInputPos(m.displayValue.length, DIRECTION.FORCE_LEFT);
3007 if (m.rawInputValue !== inputValue) {
3008 m.reset();
3009 m.append(inputValue, {
3010 raw: true
3011 });
3012 } else if (!isCurrent) {
3013 m.remove(startInputPos);
3014 }
3015 m.append(appended, masked.currentMaskFlags(flags));
3016 m.appendTail(tail);
3017 return {
3018 index,
3019 weight: m.rawInputValue.length,
3020 totalInputPositions: m.totalInputPositions(0, Math.max(startInputPos, m.nearestInputPos(m.displayValue.length, DIRECTION.FORCE_LEFT)))
3021 };
3022 });
3023
3024 // pop masks with longer values first
3025 inputs.sort((i1, i2) => i2.weight - i1.weight || i2.totalInputPositions - i1.totalInputPositions);
3026 return masked.compiledMasks[inputs[0].index];
3027 }
3028 };
3029 IMask.MaskedDynamic = MaskedDynamic;
3030
3031 /** Pattern which validates enum values */
3032 class MaskedEnum extends MaskedPattern {
3033 constructor(opts) {
3034 super({
3035 ...MaskedEnum.DEFAULTS,
3036 ...opts
3037 }); // mask will be created in _update
3038 }
3039 updateOptions(opts) {
3040 super.updateOptions(opts);
3041 }
3042 _update(opts) {
3043 const {
3044 enum: enum_,
3045 ...eopts
3046 } = opts;
3047 if (enum_) {
3048 const lengths = enum_.map(e => e.length);
3049 const requiredLength = Math.min(...lengths);
3050 const optionalLength = Math.max(...lengths) - requiredLength;
3051 eopts.mask = '*'.repeat(requiredLength);
3052 if (optionalLength) eopts.mask += '[' + '*'.repeat(optionalLength) + ']';
3053 this.enum = enum_;
3054 }
3055 super._update(eopts);
3056 }
3057 _appendCharRaw(ch, flags) {
3058 if (flags === void 0) {
3059 flags = {};
3060 }
3061 const matchFrom = Math.min(this.nearestInputPos(0, DIRECTION.FORCE_RIGHT), this.value.length);
3062 const matches = this.enum.filter(e => this.matchValue(e, this.unmaskedValue + ch, matchFrom));
3063 if (matches.length) {
3064 if (matches.length === 1) {
3065 this._forEachBlocksInRange(0, this.value.length, (b, bi) => {
3066 const mch = matches[0][bi];
3067 if (bi >= this.value.length || mch === b.value) return;
3068 b.reset();
3069 b._appendChar(mch, flags);
3070 });
3071 }
3072 const d = super._appendCharRaw(matches[0][this.value.length], flags);
3073 if (matches.length === 1) {
3074 matches[0].slice(this.unmaskedValue.length).split('').forEach(mch => d.aggregate(super._appendCharRaw(mch)));
3075 }
3076 return d;
3077 }
3078 return new ChangeDetails({
3079 skip: !this.isComplete
3080 });
3081 }
3082 extractTail(fromPos, toPos) {
3083 if (fromPos === void 0) {
3084 fromPos = 0;
3085 }
3086 if (toPos === void 0) {
3087 toPos = this.displayValue.length;
3088 }
3089 // just drop tail
3090 return new ContinuousTailDetails('', fromPos);
3091 }
3092 remove(fromPos, toPos) {
3093 if (fromPos === void 0) {
3094 fromPos = 0;
3095 }
3096 if (toPos === void 0) {
3097 toPos = this.displayValue.length;
3098 }
3099 if (fromPos === toPos) return new ChangeDetails();
3100 const matchFrom = Math.min(super.nearestInputPos(0, DIRECTION.FORCE_RIGHT), this.value.length);
3101 let pos;
3102 for (pos = fromPos; pos >= 0; --pos) {
3103 const matches = this.enum.filter(e => this.matchValue(e, this.value.slice(matchFrom, pos), matchFrom));
3104 if (matches.length > 1) break;
3105 }
3106 const details = super.remove(pos, toPos);
3107 details.tailShift += pos - fromPos;
3108 return details;
3109 }
3110 get isComplete() {
3111 return this.enum.indexOf(this.value) >= 0;
3112 }
3113 }
3114 /** Match enum value */
3115 MaskedEnum.DEFAULTS = {
3116 ...MaskedPattern.DEFAULTS,
3117 matchValue: (estr, istr, matchFrom) => estr.indexOf(istr, matchFrom) === matchFrom
3118 };
3119 IMask.MaskedEnum = MaskedEnum;
3120
3121 /** Masking by custom Function */
3122 class MaskedFunction extends Masked {
3123 /** */
3124
3125 /** Enable characters overwriting */
3126
3127 /** */
3128
3129 /** */
3130
3131 /** */
3132
3133 updateOptions(opts) {
3134 super.updateOptions(opts);
3135 }
3136 _update(opts) {
3137 super._update({
3138 ...opts,
3139 validate: opts.mask
3140 });
3141 }
3142 }
3143 IMask.MaskedFunction = MaskedFunction;
3144
3145 var _MaskedNumber;
3146 /** Number mask */
3147 class MaskedNumber extends Masked {
3148 /** Single char */
3149
3150 /** Single char */
3151
3152 /** Array of single chars */
3153
3154 /** */
3155
3156 /** */
3157
3158 /** Digits after point */
3159
3160 /** Flag to remove leading and trailing zeros in the end of editing */
3161
3162 /** Flag to pad trailing zeros after point in the end of editing */
3163
3164 /** Enable characters overwriting */
3165
3166 /** */
3167
3168 /** */
3169
3170 /** */
3171
3172 /** Format typed value to string */
3173
3174 /** Parse string to get typed value */
3175
3176 constructor(opts) {
3177 super({
3178 ...MaskedNumber.DEFAULTS,
3179 ...opts
3180 });
3181 }
3182 updateOptions(opts) {
3183 super.updateOptions(opts);
3184 }
3185 _update(opts) {
3186 super._update(opts);
3187 this._updateRegExps();
3188 }
3189 _updateRegExps() {
3190 const start = '^' + (this.allowNegative ? '[+|\\-]?' : '');
3191 const mid = '\\d*';
3192 const end = (this.scale ? "(" + escapeRegExp(this.radix) + "\\d{0," + this.scale + "})?" : '') + '$';
3193 this._numberRegExp = new RegExp(start + mid + end);
3194 this._mapToRadixRegExp = new RegExp("[" + this.mapToRadix.map(escapeRegExp).join('') + "]", 'g');
3195 this._thousandsSeparatorRegExp = new RegExp(escapeRegExp(this.thousandsSeparator), 'g');
3196 }
3197 _removeThousandsSeparators(value) {
3198 return value.replace(this._thousandsSeparatorRegExp, '');
3199 }
3200 _insertThousandsSeparators(value) {
3201 // https://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
3202 const parts = value.split(this.radix);
3203 parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, this.thousandsSeparator);
3204 return parts.join(this.radix);
3205 }
3206 doPrepareChar(ch, flags) {
3207 if (flags === void 0) {
3208 flags = {};
3209 }
3210 const [prepCh, details] = super.doPrepareChar(this._removeThousandsSeparators(this.scale && this.mapToRadix.length && (
3211 /*
3212 radix should be mapped when
3213 1) input is done from keyboard = flags.input && flags.raw
3214 2) unmasked value is set = !flags.input && !flags.raw
3215 and should not be mapped when
3216 1) value is set = flags.input && !flags.raw
3217 2) raw value is set = !flags.input && flags.raw
3218 */
3219 flags.input && flags.raw || !flags.input && !flags.raw) ? ch.replace(this._mapToRadixRegExp, this.radix) : ch), flags);
3220 if (ch && !prepCh) details.skip = true;
3221 if (prepCh && !this.allowPositive && !this.value && prepCh !== '-') details.aggregate(this._appendChar('-'));
3222 return [prepCh, details];
3223 }
3224 _separatorsCount(to, extendOnSeparators) {
3225 if (extendOnSeparators === void 0) {
3226 extendOnSeparators = false;
3227 }
3228 let count = 0;
3229 for (let pos = 0; pos < to; ++pos) {
3230 if (this._value.indexOf(this.thousandsSeparator, pos) === pos) {
3231 ++count;
3232 if (extendOnSeparators) to += this.thousandsSeparator.length;
3233 }
3234 }
3235 return count;
3236 }
3237 _separatorsCountFromSlice(slice) {
3238 if (slice === void 0) {
3239 slice = this._value;
3240 }
3241 return this._separatorsCount(this._removeThousandsSeparators(slice).length, true);
3242 }
3243 extractInput(fromPos, toPos, flags) {
3244 if (fromPos === void 0) {
3245 fromPos = 0;
3246 }
3247 if (toPos === void 0) {
3248 toPos = this.displayValue.length;
3249 }
3250 [fromPos, toPos] = this._adjustRangeWithSeparators(fromPos, toPos);
3251 return this._removeThousandsSeparators(super.extractInput(fromPos, toPos, flags));
3252 }
3253 _appendCharRaw(ch, flags) {
3254 if (flags === void 0) {
3255 flags = {};
3256 }
3257 const prevBeforeTailValue = flags.tail && flags._beforeTailState ? flags._beforeTailState._value : this._value;
3258 const prevBeforeTailSeparatorsCount = this._separatorsCountFromSlice(prevBeforeTailValue);
3259 this._value = this._removeThousandsSeparators(this.value);
3260 const oldValue = this._value;
3261 this._value += ch;
3262 const num = this.number;
3263 let accepted = !isNaN(num);
3264 let skip = false;
3265 if (accepted) {
3266 let fixedNum;
3267 if (this.min != null && this.min < 0 && this.number < this.min) fixedNum = this.min;
3268 if (this.max != null && this.max > 0 && this.number > this.max) fixedNum = this.max;
3269 if (fixedNum != null) {
3270 if (this.autofix) {
3271 this._value = this.format(fixedNum, this).replace(MaskedNumber.UNMASKED_RADIX, this.radix);
3272 skip || (skip = oldValue === this._value && !flags.tail); // if not changed on tail it's still ok to proceed
3273 } else {
3274 accepted = false;
3275 }
3276 }
3277 accepted && (accepted = Boolean(this._value.match(this._numberRegExp)));
3278 }
3279 let appendDetails;
3280 if (!accepted) {
3281 this._value = oldValue;
3282 appendDetails = new ChangeDetails();
3283 } else {
3284 appendDetails = new ChangeDetails({
3285 inserted: this._value.slice(oldValue.length),
3286 rawInserted: skip ? '' : ch,
3287 skip
3288 });
3289 }
3290 this._value = this._insertThousandsSeparators(this._value);
3291 const beforeTailValue = flags.tail && flags._beforeTailState ? flags._beforeTailState._value : this._value;
3292 const beforeTailSeparatorsCount = this._separatorsCountFromSlice(beforeTailValue);
3293 appendDetails.tailShift += (beforeTailSeparatorsCount - prevBeforeTailSeparatorsCount) * this.thousandsSeparator.length;
3294 return appendDetails;
3295 }
3296 _findSeparatorAround(pos) {
3297 if (this.thousandsSeparator) {
3298 const searchFrom = pos - this.thousandsSeparator.length + 1;
3299 const separatorPos = this.value.indexOf(this.thousandsSeparator, searchFrom);
3300 if (separatorPos <= pos) return separatorPos;
3301 }
3302 return -1;
3303 }
3304 _adjustRangeWithSeparators(from, to) {
3305 const separatorAroundFromPos = this._findSeparatorAround(from);
3306 if (separatorAroundFromPos >= 0) from = separatorAroundFromPos;
3307 const separatorAroundToPos = this._findSeparatorAround(to);
3308 if (separatorAroundToPos >= 0) to = separatorAroundToPos + this.thousandsSeparator.length;
3309 return [from, to];
3310 }
3311 remove(fromPos, toPos) {
3312 if (fromPos === void 0) {
3313 fromPos = 0;
3314 }
3315 if (toPos === void 0) {
3316 toPos = this.displayValue.length;
3317 }
3318 [fromPos, toPos] = this._adjustRangeWithSeparators(fromPos, toPos);
3319 const valueBeforePos = this.value.slice(0, fromPos);
3320 const valueAfterPos = this.value.slice(toPos);
3321 const prevBeforeTailSeparatorsCount = this._separatorsCount(valueBeforePos.length);
3322 this._value = this._insertThousandsSeparators(this._removeThousandsSeparators(valueBeforePos + valueAfterPos));
3323 const beforeTailSeparatorsCount = this._separatorsCountFromSlice(valueBeforePos);
3324 return new ChangeDetails({
3325 tailShift: (beforeTailSeparatorsCount - prevBeforeTailSeparatorsCount) * this.thousandsSeparator.length
3326 });
3327 }
3328 nearestInputPos(cursorPos, direction) {
3329 if (!this.thousandsSeparator) return cursorPos;
3330 switch (direction) {
3331 case DIRECTION.NONE:
3332 case DIRECTION.LEFT:
3333 case DIRECTION.FORCE_LEFT:
3334 {
3335 const separatorAtLeftPos = this._findSeparatorAround(cursorPos - 1);
3336 if (separatorAtLeftPos >= 0) {
3337 const separatorAtLeftEndPos = separatorAtLeftPos + this.thousandsSeparator.length;
3338 if (cursorPos < separatorAtLeftEndPos || this.value.length <= separatorAtLeftEndPos || direction === DIRECTION.FORCE_LEFT) {
3339 return separatorAtLeftPos;
3340 }
3341 }
3342 break;
3343 }
3344 case DIRECTION.RIGHT:
3345 case DIRECTION.FORCE_RIGHT:
3346 {
3347 const separatorAtRightPos = this._findSeparatorAround(cursorPos);
3348 if (separatorAtRightPos >= 0) {
3349 return separatorAtRightPos + this.thousandsSeparator.length;
3350 }
3351 }
3352 }
3353 return cursorPos;
3354 }
3355 doCommit() {
3356 if (this.value) {
3357 const number = this.number;
3358 let validnum = number;
3359
3360 // check bounds
3361 if (this.min != null) validnum = Math.max(validnum, this.min);
3362 if (this.max != null) validnum = Math.min(validnum, this.max);
3363 if (validnum !== number) this.unmaskedValue = this.format(validnum, this);
3364 let formatted = this.value;
3365 if (this.normalizeZeros) formatted = this._normalizeZeros(formatted);
3366 if (this.padFractionalZeros && this.scale > 0) formatted = this._padFractionalZeros(formatted);
3367 this._value = formatted;
3368 }
3369 super.doCommit();
3370 }
3371 _normalizeZeros(value) {
3372 const parts = this._removeThousandsSeparators(value).split(this.radix);
3373
3374 // remove leading zeros
3375 parts[0] = parts[0].replace(/^(\D*)(0*)(\d*)/, (match, sign, zeros, num) => sign + num);
3376 // add leading zero
3377 if (value.length && !/\d$/.test(parts[0])) parts[0] = parts[0] + '0';
3378 if (parts.length > 1) {
3379 parts[1] = parts[1].replace(/0*$/, ''); // remove trailing zeros
3380 if (!parts[1].length) parts.length = 1; // remove fractional
3381 }
3382 return this._insertThousandsSeparators(parts.join(this.radix));
3383 }
3384 _padFractionalZeros(value) {
3385 if (!value) return value;
3386 const parts = value.split(this.radix);
3387 if (parts.length < 2) parts.push('');
3388 parts[1] = parts[1].padEnd(this.scale, '0');
3389 return parts.join(this.radix);
3390 }
3391 doSkipInvalid(ch, flags, checkTail) {
3392 if (flags === void 0) {
3393 flags = {};
3394 }
3395 const dropFractional = this.scale === 0 && ch !== this.thousandsSeparator && (ch === this.radix || ch === MaskedNumber.UNMASKED_RADIX || this.mapToRadix.includes(ch));
3396 return super.doSkipInvalid(ch, flags, checkTail) && !dropFractional;
3397 }
3398 get unmaskedValue() {
3399 return this._removeThousandsSeparators(this._normalizeZeros(this.value)).replace(this.radix, MaskedNumber.UNMASKED_RADIX);
3400 }
3401 set unmaskedValue(unmaskedValue) {
3402 super.unmaskedValue = unmaskedValue;
3403 }
3404 get typedValue() {
3405 return this.parse(this.unmaskedValue, this);
3406 }
3407 set typedValue(n) {
3408 this.rawInputValue = this.format(n, this).replace(MaskedNumber.UNMASKED_RADIX, this.radix);
3409 }
3410
3411 /** Parsed Number */
3412 get number() {
3413 return this.typedValue;
3414 }
3415 set number(number) {
3416 this.typedValue = number;
3417 }
3418 get allowNegative() {
3419 return this.min != null && this.min < 0 || this.max != null && this.max < 0;
3420 }
3421 get allowPositive() {
3422 return this.min != null && this.min > 0 || this.max != null && this.max > 0;
3423 }
3424 typedValueEquals(value) {
3425 // handle 0 -> '' case (typed = 0 even if value = '')
3426 // for details see https://github.com/uNmAnNeR/imaskjs/issues/134
3427 return (super.typedValueEquals(value) || MaskedNumber.EMPTY_VALUES.includes(value) && MaskedNumber.EMPTY_VALUES.includes(this.typedValue)) && !(value === 0 && this.value === '');
3428 }
3429 }
3430 _MaskedNumber = MaskedNumber;
3431 MaskedNumber.UNMASKED_RADIX = '.';
3432 MaskedNumber.EMPTY_VALUES = [...Masked.EMPTY_VALUES, 0];
3433 MaskedNumber.DEFAULTS = {
3434 ...Masked.DEFAULTS,
3435 mask: Number,
3436 radix: ',',
3437 thousandsSeparator: '',
3438 mapToRadix: [_MaskedNumber.UNMASKED_RADIX],
3439 min: Number.MIN_SAFE_INTEGER,
3440 max: Number.MAX_SAFE_INTEGER,
3441 scale: 2,
3442 normalizeZeros: true,
3443 padFractionalZeros: false,
3444 parse: Number,
3445 format: n => n.toLocaleString('en-US', {
3446 useGrouping: false,
3447 maximumFractionDigits: 20
3448 })
3449 };
3450 IMask.MaskedNumber = MaskedNumber;
3451
3452 /** Mask pipe source and destination types */
3453 const PIPE_TYPE = {
3454 MASKED: 'value',
3455 UNMASKED: 'unmaskedValue',
3456 TYPED: 'typedValue'
3457 };
3458 /** Creates new pipe function depending on mask type, source and destination options */
3459 function createPipe(arg, from, to) {
3460 if (from === void 0) {
3461 from = PIPE_TYPE.MASKED;
3462 }
3463 if (to === void 0) {
3464 to = PIPE_TYPE.MASKED;
3465 }
3466 const masked = createMask(arg);
3467 return value => masked.runIsolated(m => {
3468 m[from] = value;
3469 return m[to];
3470 });
3471 }
3472
3473 /** Pipes value through mask depending on mask type, source and destination options */
3474 function pipe(value, mask, from, to) {
3475 return createPipe(mask, from, to)(value);
3476 }
3477 IMask.PIPE_TYPE = PIPE_TYPE;
3478 IMask.createPipe = createPipe;
3479 IMask.pipe = pipe;
3480
3481 /** Pattern mask */
3482 class RepeatBlock extends MaskedPattern {
3483 get repeatFrom() {
3484 var _ref;
3485 return (_ref = Array.isArray(this.repeat) ? this.repeat[0] : this.repeat === Infinity ? 0 : this.repeat) != null ? _ref : 0;
3486 }
3487 get repeatTo() {
3488 var _ref2;
3489 return (_ref2 = Array.isArray(this.repeat) ? this.repeat[1] : this.repeat) != null ? _ref2 : Infinity;
3490 }
3491 constructor(opts) {
3492 super(opts);
3493 }
3494 updateOptions(opts) {
3495 super.updateOptions(opts);
3496 }
3497 _update(opts) {
3498 var _ref3, _ref4, _this$_blocks;
3499 const {
3500 repeat,
3501 ...blockOpts
3502 } = normalizeOpts(opts); // TODO type
3503 this._blockOpts = Object.assign({}, this._blockOpts, blockOpts);
3504 const block = createMask(this._blockOpts);
3505 this.repeat = (_ref3 = (_ref4 = repeat != null ? repeat : block.repeat) != null ? _ref4 : this.repeat) != null ? _ref3 : Infinity; // TODO type
3506
3507 super._update({
3508 mask: 'm'.repeat(Math.max(this.repeatTo === Infinity && ((_this$_blocks = this._blocks) == null ? void 0 : _this$_blocks.length) || 0, this.repeatFrom)),
3509 blocks: {
3510 m: block
3511 },
3512 eager: block.eager,
3513 overwrite: block.overwrite,
3514 skipInvalid: block.skipInvalid,
3515 lazy: block.lazy,
3516 placeholderChar: block.placeholderChar,
3517 displayChar: block.displayChar
3518 });
3519 }
3520 _allocateBlock(bi) {
3521 if (bi < this._blocks.length) return this._blocks[bi];
3522 if (this.repeatTo === Infinity || this._blocks.length < this.repeatTo) {
3523 this._blocks.push(createMask(this._blockOpts));
3524 this.mask += 'm';
3525 return this._blocks[this._blocks.length - 1];
3526 }
3527 }
3528 _appendCharRaw(ch, flags) {
3529 if (flags === void 0) {
3530 flags = {};
3531 }
3532 const details = new ChangeDetails();
3533 for (let bi = (_this$_mapPosToBlock$ = (_this$_mapPosToBlock = this._mapPosToBlock(this.displayValue.length)) == null ? void 0 : _this$_mapPosToBlock.index) != null ? _this$_mapPosToBlock$ : Math.max(this._blocks.length - 1, 0), block, allocated;
3534 // try to get a block or
3535 // try to allocate a new block if not allocated already
3536 block = (_this$_blocks$bi = this._blocks[bi]) != null ? _this$_blocks$bi : allocated = !allocated && this._allocateBlock(bi); ++bi) {
3537 var _this$_mapPosToBlock$, _this$_mapPosToBlock, _this$_blocks$bi, _flags$_beforeTailSta;
3538 const blockDetails = block._appendChar(ch, {
3539 ...flags,
3540 _beforeTailState: (_flags$_beforeTailSta = flags._beforeTailState) == null || (_flags$_beforeTailSta = _flags$_beforeTailSta._blocks) == null ? void 0 : _flags$_beforeTailSta[bi]
3541 });
3542 if (blockDetails.skip && allocated) {
3543 // remove the last allocated block and break
3544 this._blocks.pop();
3545 this.mask = this.mask.slice(1);
3546 break;
3547 }
3548 details.aggregate(blockDetails);
3549 if (blockDetails.consumed) break; // go next char
3550 }
3551 return details;
3552 }
3553 _trimEmptyTail(fromPos, toPos) {
3554 var _this$_mapPosToBlock2, _this$_mapPosToBlock3;
3555 if (fromPos === void 0) {
3556 fromPos = 0;
3557 }
3558 const firstBlockIndex = Math.max(((_this$_mapPosToBlock2 = this._mapPosToBlock(fromPos)) == null ? void 0 : _this$_mapPosToBlock2.index) || 0, this.repeatFrom, 0);
3559 let lastBlockIndex;
3560 if (toPos != null) lastBlockIndex = (_this$_mapPosToBlock3 = this._mapPosToBlock(toPos)) == null ? void 0 : _this$_mapPosToBlock3.index;
3561 if (lastBlockIndex == null) lastBlockIndex = this._blocks.length - 1;
3562 let removeCount = 0;
3563 for (let blockIndex = lastBlockIndex; firstBlockIndex <= blockIndex; --blockIndex, ++removeCount) {
3564 if (this._blocks[blockIndex].unmaskedValue) break;
3565 }
3566 if (removeCount) {
3567 this._blocks.splice(lastBlockIndex - removeCount + 1, removeCount);
3568 this.mask = this.mask.slice(removeCount);
3569 }
3570 }
3571 reset() {
3572 super.reset();
3573 this._trimEmptyTail();
3574 }
3575 remove(fromPos, toPos) {
3576 if (fromPos === void 0) {
3577 fromPos = 0;
3578 }
3579 if (toPos === void 0) {
3580 toPos = this.displayValue.length;
3581 }
3582 const removeDetails = super.remove(fromPos, toPos);
3583 this._trimEmptyTail(fromPos, toPos);
3584 return removeDetails;
3585 }
3586 totalInputPositions(fromPos, toPos) {
3587 if (fromPos === void 0) {
3588 fromPos = 0;
3589 }
3590 if (toPos == null && this.repeatTo === Infinity) return Infinity;
3591 return super.totalInputPositions(fromPos, toPos);
3592 }
3593 get state() {
3594 return super.state;
3595 }
3596 set state(state) {
3597 this._blocks.length = state._blocks.length;
3598 this.mask = this.mask.slice(0, this._blocks.length);
3599 super.state = state;
3600 }
3601 }
3602 IMask.RepeatBlock = RepeatBlock;
3603
3604 try {
3605 globalThis.IMask = IMask;
3606 } catch {}
3607
3608 exports.ChangeDetails = ChangeDetails;
3609 exports.ChunksTailDetails = ChunksTailDetails;
3610 exports.DIRECTION = DIRECTION;
3611 exports.HTMLContenteditableMaskElement = HTMLContenteditableMaskElement;
3612 exports.HTMLInputMaskElement = HTMLInputMaskElement;
3613 exports.HTMLMaskElement = HTMLMaskElement;
3614 exports.InputMask = InputMask;
3615 exports.MaskElement = MaskElement;
3616 exports.Masked = Masked;
3617 exports.MaskedDate = MaskedDate;
3618 exports.MaskedDynamic = MaskedDynamic;
3619 exports.MaskedEnum = MaskedEnum;
3620 exports.MaskedFunction = MaskedFunction;
3621 exports.MaskedNumber = MaskedNumber;
3622 exports.MaskedPattern = MaskedPattern;
3623 exports.MaskedRange = MaskedRange;
3624 exports.MaskedRegExp = MaskedRegExp;
3625 exports.PIPE_TYPE = PIPE_TYPE;
3626 exports.PatternFixedDefinition = PatternFixedDefinition;
3627 exports.PatternInputDefinition = PatternInputDefinition;
3628 exports.RepeatBlock = RepeatBlock;
3629 exports.createMask = createMask;
3630 exports.createPipe = createPipe;
3631 exports.default = IMask;
3632 exports.forceDirection = forceDirection;
3633 exports.normalizeOpts = normalizeOpts;
3634 exports.pipe = pipe;
3635 //# sourceMappingURL=imask.cjs.map
3636