PluginProbe
Booking Calendar / 11.8
Booking Calendar v11.8
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 10.11.2 All 203 releases
booking / vendors / imask / dist / imask.js

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

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