vendor: 124,186 bytes, lines 1-3641
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
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.