imask
Version:
vanilla javascript input mask
1 lines • 300 kB
Source Map (JSON)
{"version":3,"file":"imask.cjs","sources":["../src/core/utils.ts","../src/core/action-details.ts","../src/core/holder.ts","../src/masked/factory.ts","../src/controls/mask-element.ts","../src/controls/html-mask-element.ts","../src/controls/html-input-mask-element.ts","../src/controls/html-contenteditable-mask-element.ts","../src/controls/input-history.ts","../src/controls/input.ts","../src/core/change-details.ts","../src/core/continuous-tail-details.ts","../src/masked/base.ts","../src/masked/pattern/chunk-tail-details.ts","../src/masked/pattern/cursor.ts","../src/masked/pattern/fixed-definition.ts","../src/masked/pattern/input-definition.ts","../src/masked/regexp.ts","../src/masked/pattern.ts","../src/masked/range.ts","../src/masked/date.ts","../src/masked/dynamic.ts","../src/masked/enum.ts","../src/masked/function.ts","../src/masked/number.ts","../src/masked/pipe.ts","../src/masked/repeat.ts","../src/index.ts"],"sourcesContent":["/** Checks if value is string */\nexport\nfunction isString (str: unknown): str is string {\n return typeof str === 'string' || str instanceof String;\n}\n\n/** Checks if value is object */\nexport\nfunction isObject (obj: unknown): obj is object {\n return typeof obj === 'object' && obj != null && obj?.constructor?.name === 'Object';\n}\n\nexport\nfunction pick<T extends Record<string, any>, K extends keyof T, V extends T[keyof T]> (\n obj: T,\n keys: K[] | ((v: V, k: K) => boolean),\n): Pick<T, K> {\n if (Array.isArray(keys)) return pick(obj, (_, k) => keys.includes(k));\n return (Object.entries(obj) as unknown as Array<[K, V]>)\n .reduce((acc, [k, v]) => {\n if (keys(v, k)) acc[k] = v;\n return acc;\n }, {} as any);\n}\n\n/** Direction */\nexport\nconst DIRECTION = {\n NONE: 'NONE',\n LEFT: 'LEFT',\n FORCE_LEFT: 'FORCE_LEFT',\n RIGHT: 'RIGHT',\n FORCE_RIGHT: 'FORCE_RIGHT',\n} as const;\n\n/** Direction */\nexport\ntype Direction = typeof DIRECTION[keyof typeof DIRECTION];\n\nexport\nfunction forceDirection (direction: Direction): Direction {\n switch (direction) {\n case DIRECTION.LEFT:\n return DIRECTION.FORCE_LEFT;\n case DIRECTION.RIGHT:\n return DIRECTION.FORCE_RIGHT;\n default:\n return direction;\n }\n}\n\n/** Escapes regular expression control chars */\nexport\nfunction escapeRegExp (str: string): string {\n return str.replace(/([.*+?^=!:${}()|[\\]/\\\\])/g, '\\\\$1');\n}\n\n// cloned from https://github.com/epoberezkin/fast-deep-equal with small changes\nexport\nfunction objectIncludes (b: any, a: any): boolean {\n if (a === b) return true;\n\n const arrA = Array.isArray(a), arrB = Array.isArray(b);\n let i;\n\n if (arrA && arrB) {\n if (a.length != b.length) return false;\n for (i = 0; i < a.length; i++)\n if (!objectIncludes(a[i], b[i])) return false;\n return true;\n }\n\n if (arrA != arrB) return false;\n\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n const dateA = a instanceof Date, dateB = b instanceof Date;\n if (dateA && dateB) return a.getTime() == b.getTime();\n if (dateA != dateB) return false;\n\n const regexpA = a instanceof RegExp, regexpB = b instanceof RegExp;\n if (regexpA && regexpB) return a.toString() == b.toString();\n if (regexpA != regexpB) return false;\n\n const keys = Object.keys(a);\n // if (keys.length !== Object.keys(b).length) return false;\n\n for (i = 0; i < keys.length; i++)\n if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;\n\n for (i = 0; i < keys.length; i++)\n if(!objectIncludes(b[keys[i]], a[keys[i]])) return false;\n\n return true;\n } else if (a && b && typeof a === 'function' && typeof b === 'function') {\n return a.toString() === b.toString()\n }\n\n return false;\n}\n\n/** Selection range */\nexport\ntype Selection = {\n start: number;\n end: number;\n};\n","import { type Direction, type Selection, DIRECTION } from './utils';\n\nexport\ntype ActionDetailsOptions = Pick<ActionDetails,\n | 'value'\n | 'cursorPos'\n | 'oldValue'\n | 'oldSelection'\n>;\n\n\n/** Provides details of changing input */\nexport default\nclass ActionDetails {\n /** Current input value */\n declare value: string;\n /** Current cursor position */\n declare cursorPos: number;\n /** Old input value */\n declare oldValue: string;\n /** Old selection */\n declare oldSelection: Selection;\n\n constructor (opts: ActionDetailsOptions) {\n Object.assign(this, opts);\n\n // double check if left part was changed (autofilling, other non-standard input triggers)\n while (this.value.slice(0, this.startChangePos) !== this.oldValue.slice(0, this.startChangePos)) {\n --this.oldSelection.start;\n }\n\n if (this.insertedCount) {\n // double check right part\n while (this.value.slice(this.cursorPos) !== this.oldValue.slice(this.oldSelection.end)) {\n if (this.value.length - this.cursorPos < this.oldValue.length - this.oldSelection.end) ++this.oldSelection.end;\n else ++this.cursorPos;\n }\n }\n }\n\n /** Start changing position */\n get startChangePos (): number {\n return Math.min(this.cursorPos, this.oldSelection.start);\n }\n\n /** Inserted symbols count */\n get insertedCount (): number {\n return this.cursorPos - this.startChangePos;\n }\n\n /** Inserted symbols */\n get inserted (): string {\n return this.value.substr(this.startChangePos, this.insertedCount);\n }\n\n /** Removed symbols count */\n get removedCount (): number {\n // Math.max for opposite operation\n return Math.max((this.oldSelection.end - this.startChangePos) ||\n // for Delete\n this.oldValue.length - this.value.length, 0);\n }\n\n /** Removed symbols */\n get removed (): string {\n return this.oldValue.substr(this.startChangePos, this.removedCount);\n }\n\n /** Unchanged head symbols */\n get head (): string {\n return this.value.substring(0, this.startChangePos);\n }\n\n /** Unchanged tail symbols */\n get tail (): string {\n return this.value.substring(this.startChangePos + this.insertedCount);\n }\n\n /** Remove direction */\n get removeDirection (): Direction {\n if (!this.removedCount || this.insertedCount) return DIRECTION.NONE;\n\n // align right if delete at right\n return (\n (this.oldSelection.end === this.cursorPos || this.oldSelection.start === this.cursorPos) &&\n // if not range removed (event with backspace)\n this.oldSelection.end === this.oldSelection.start\n ) ?\n DIRECTION.RIGHT :\n DIRECTION.LEFT;\n }\n}\n","import type { default as _InputMask, InputMaskElement as _InputMaskElement } from '../controls/input';\nimport type { default as _Masked } from '../masked/base';\nimport type { default as _MaskedPattern } from '../masked/pattern';\nimport type { default as _RepeatBlock } from '../masked/repeat';\nimport type { default as _MaskedDate } from '../masked/date';\nimport type { default as _MaskedDynamic } from '../masked/dynamic';\nimport type { default as _MaskedEnum } from '../masked/enum';\nimport type { default as _MaskedRange } from '../masked/range';\nimport type { default as _MaskedNumber } from '../masked/number';\nimport type { default as _MaskedFunction } from '../masked/function';\nimport type { default as _MaskedRegExp } from '../masked/regexp';\nimport type {\n default as _createMask,\n FactoryArg,\n} from '../masked/factory';\nimport type { default as _ChangeDetails } from './change-details';\n\nimport type { default as _MaskElement } from '../controls/mask-element';\nimport type { default as _HTMLMaskElement } from '../controls/html-mask-element';\nimport type { default as _HTMLContenteditableMaskElement } from '../controls/html-contenteditable-mask-element';\nimport type {\n createPipe as _createPipe,\n pipe as _pipe,\n PIPE_TYPE as _PIPE_TYPE\n} from '../masked/pipe';\n\n\n/** Applies mask on element */\nfunction IMask<Opts extends FactoryArg> (el: _InputMaskElement, opts: Opts): _InputMask<Opts> {\n // currently available only for input-like elements\n return new IMask.InputMask(el, opts);\n}\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\ndeclare namespace IMask {\n export let InputMask: typeof _InputMask;\n export let createMask: typeof _createMask;\n export let Masked: typeof _Masked;\n export let MaskedPattern: typeof _MaskedPattern;\n export let RepeatBlock: typeof _RepeatBlock;\n export let MaskedDate: typeof _MaskedDate;\n export let MaskedDynamic: typeof _MaskedDynamic;\n export let MaskedEnum: typeof _MaskedEnum;\n export let MaskedRange: typeof _MaskedRange;\n export let MaskedNumber: typeof _MaskedNumber;\n export let MaskedFunction: typeof _MaskedFunction;\n export let MaskedRegExp: typeof _MaskedRegExp;\n export let ChangeDetails: typeof _ChangeDetails;\n export let MaskElement: typeof _MaskElement;\n export let HTMLMaskElement: typeof _HTMLMaskElement;\n export let HTMLContenteditableMaskElement: typeof _HTMLContenteditableMaskElement;\n export let createPipe: typeof _createPipe;\n export let pipe: typeof _pipe;\n export let PIPE_TYPE: typeof _PIPE_TYPE;\n}\n\nexport default IMask;\n","import { isString, pick, isObject } from '../core/utils';\nimport type Masked from './base';\nimport { type MaskedOptions } from './base';\nimport IMask from '../core/holder';\n\nimport type MaskedRegExp from './regexp';\nimport type MaskedPattern from './pattern';\nimport type MaskedFunction from './function';\nimport type MaskedDate from './date';\nimport type MaskedNumber from './number';\nimport type MaskedDynamic from './dynamic';\nimport type MaskedRange from './range';\nimport type MaskedEnum from './enum';\n\nimport { type MaskedEnumOptions } from './enum';\nimport { type MaskedRangeOptions } from './range';\nimport { type MaskedDynamicOptions } from './dynamic';\nimport { type MaskedPatternOptions } from './pattern';\nimport { type MaskedNumberOptions } from './number';\nimport { type MaskedRegExpOptions } from './regexp';\nimport { type MaskedFunctionOptions } from './function';\nimport { type MaskedDateOptions } from './date';\n\ntype MaskedDateFactoryOptions = Omit<MaskedDateOptions, 'mask'> & { mask: DateConstructor };\n\nexport\ntype FactoryStaticOpts =\n | MaskedDateFactoryOptions\n | MaskedNumberOptions\n | MaskedPatternOptions\n | MaskedDynamicOptions\n | MaskedRegExpOptions\n | MaskedFunctionOptions\n;\n\nexport\ntype AllFactoryStaticOpts =\n & MaskedDateFactoryOptions\n & MaskedNumberOptions\n & MaskedPatternOptions\n & MaskedDynamicOptions\n & MaskedRegExpOptions\n & MaskedFunctionOptions\n & MaskedEnumOptions\n & MaskedRangeOptions\n;\n\nexport\ntype FactoryStaticReturnMasked<Opts extends FactoryStaticOpts> =\n Opts extends MaskedDateFactoryOptions ? MaskedDate :\n Opts extends MaskedNumberOptions ? MaskedNumber :\n Opts extends MaskedPatternOptions ? MaskedPattern :\n Opts extends MaskedDynamicOptions ? MaskedDynamic :\n Opts extends MaskedRegExpOptions ? MaskedRegExp :\n Opts extends MaskedFunctionOptions ? MaskedFunction :\n never\n;\n\nexport\ntype FactoryStaticMaskReturnMasked<Mask extends FactoryStaticOpts['mask']> =\n Mask extends MaskedDateFactoryOptions['mask'] ? MaskedDate :\n Mask extends MaskedNumberOptions['mask'] ? MaskedNumber :\n Mask extends MaskedPatternOptions['mask'] ? MaskedPattern :\n Mask extends MaskedDynamicOptions['mask'] ? MaskedDynamic :\n Mask extends MaskedRegExpOptions['mask'] ? MaskedRegExp :\n Mask extends MaskedFunctionOptions['mask'] ? MaskedFunction :\n never\n;\n\n\nexport\ntype FactoryInstanceOpts =\n | { mask: MaskedDate } & Omit<MaskedDateFactoryOptions, 'mask'>\n | { mask: MaskedNumber } & Omit<MaskedNumberOptions, 'mask'>\n | { mask: MaskedEnum } & Omit<MaskedEnumOptions, 'mask'>\n | { mask: MaskedRange } & Omit<MaskedRangeOptions, 'mask'>\n | { mask: MaskedRegExp } & Omit<MaskedRegExpOptions, 'mask'>\n | { mask: MaskedFunction } & Omit<MaskedFunctionOptions, 'mask'>\n | { mask: MaskedPattern } & Omit<MaskedPatternOptions, 'mask'>\n | { mask: MaskedDynamic } & Omit<MaskedDynamicOptions, 'mask'>\n | { mask: Masked } & Omit<MaskedOptions, 'mask'>\n;\n\nexport\ntype FactoryInstanceReturnMasked<Opts extends FactoryInstanceOpts> = Opts extends { mask: infer M } ? M : never;\n\nexport\ntype FactoryConstructorOpts =\n | { mask: typeof MaskedDate } & Omit<MaskedDateFactoryOptions, 'mask'>\n | { mask: typeof MaskedNumber } & Omit<MaskedNumberOptions, 'mask'>\n | { mask: typeof MaskedEnum } & Omit<MaskedEnumOptions, 'mask'>\n | { mask: typeof MaskedRange } & Omit<MaskedRangeOptions, 'mask'>\n | { mask: typeof MaskedRegExp } & Omit<MaskedRegExpOptions, 'mask'>\n | { mask: typeof MaskedFunction } & Omit<MaskedFunctionOptions, 'mask'>\n | { mask: typeof MaskedPattern } & Omit<MaskedPatternOptions, 'mask'>\n | { mask: typeof MaskedDynamic } & Omit<MaskedDynamicOptions, 'mask'>\n | { mask: typeof Masked } & Omit<MaskedOptions, 'mask'>\n;\n \nexport\ntype FactoryConstructorReturnMasked<Opts extends FactoryConstructorOpts> =\n Opts extends { mask: typeof MaskedDate } ? MaskedDate :\n Opts extends { mask: typeof MaskedNumber } ? MaskedNumber :\n Opts extends { mask: typeof MaskedEnum } ? MaskedEnum :\n Opts extends { mask: typeof MaskedRange } ? MaskedRange :\n Opts extends { mask: typeof MaskedRegExp } ? MaskedRegExp :\n Opts extends { mask: typeof MaskedFunction } ? MaskedFunction :\n Opts extends { mask: typeof MaskedPattern } ? MaskedPattern :\n Opts extends { mask: typeof MaskedDynamic } ? MaskedDynamic :\n Masked\n;\n\nexport\ntype FactoryOpts = FactoryConstructorOpts | FactoryInstanceOpts | FactoryStaticOpts;\n\nexport\ntype FactoryArg = Masked | FactoryOpts | FactoryStaticOpts['mask'];\n\nexport\ntype ExtendFactoryArgOptions<Opts extends { [key: string]: any }> =\n Masked | FactoryOpts & Opts | FactoryStaticOpts['mask']\n;\n\nexport\ntype UpdateStaticOpts<Opts extends FactoryStaticOpts> =\n Opts extends MaskedEnumOptions ? MaskedEnumOptions :\n Opts extends MaskedRangeOptions ? MaskedRangeOptions :\n Opts extends MaskedDynamicOptions ? MaskedDynamicOptions :\n Opts extends MaskedPatternOptions ? MaskedPatternOptions :\n Opts extends MaskedDateOptions ? MaskedDateOptions :\n Opts extends MaskedNumberOptions ? MaskedNumberOptions :\n Opts extends MaskedRegExpOptions ? MaskedRegExpOptions :\n Opts extends MaskedFunctionOptions ? MaskedFunctionOptions :\n never\n;\n\ntype AnyOpts = Record<string, any>;\n\nexport\ntype UpdateInstanceOpts<M extends Masked> =\n M extends MaskedRegExp ? MaskedRegExpOptions :\n M extends MaskedFunction ? MaskedFunctionOptions :\n M extends MaskedDate ? MaskedDateOptions :\n M extends MaskedNumber ? MaskedNumberOptions :\n M extends MaskedDynamic ? MaskedDynamicOptions :\n M extends MaskedRange ? MaskedRangeOptions :\n M extends MaskedEnum ? MaskedEnumOptions :\n M extends MaskedPattern ? MaskedPatternOptions :\n AnyOpts\n;\n\nexport\ntype UpdateConstructorOpts<M extends FactoryConstructorOpts> =\n M extends { mask: typeof MaskedDate } ? MaskedDateOptions :\n M extends { mask: typeof MaskedNumber } ? MaskedNumberOptions :\n M extends { mask: typeof MaskedEnum } ? MaskedEnumOptions :\n M extends { mask: typeof MaskedRange } ? MaskedRangeOptions :\n M extends { mask: typeof MaskedRegExp } ? MaskedRegExpOptions :\n M extends { mask: typeof MaskedFunction } ? MaskedFunctionOptions :\n M extends { mask: typeof MaskedPattern } ? MaskedPatternOptions :\n M extends { mask: typeof MaskedDynamic } ? MaskedDynamicOptions :\n AnyOpts\n;\n\nexport\ntype UpdateStaticMaskOpts<M extends FactoryStaticOpts['mask']> =\n M extends MaskedDateFactoryOptions['mask'] ? MaskedDateOptions :\n M extends MaskedNumberOptions['mask'] ? MaskedNumberOptions :\n M extends MaskedPatternOptions['mask'] ? MaskedPatternOptions :\n M extends MaskedDynamicOptions['mask'] ? MaskedDynamicOptions :\n M extends MaskedRegExpOptions['mask'] ? MaskedRegExpOptions :\n M extends MaskedFunctionOptions['mask'] ? MaskedFunctionOptions :\n never\n;\n\nexport\ntype UpdateOpts<Opts extends FactoryArg> = Partial<\n Opts extends Masked ? UpdateInstanceOpts<Opts> :\n Opts extends FactoryStaticOpts['mask'] ? UpdateStaticMaskOpts<Opts> :\n Opts extends FactoryStaticOpts ? UpdateStaticOpts<Opts> :\n Opts extends FactoryInstanceOpts ? UpdateInstanceOpts<Opts['mask']> :\n Opts extends FactoryConstructorOpts ? UpdateConstructorOpts<Opts> :\n AnyOpts\n>;\n\nexport\ntype FactoryReturnMasked<Opts extends FactoryArg> =\n Opts extends Masked ? Opts :\n Opts extends FactoryStaticOpts['mask'] ? FactoryStaticMaskReturnMasked<Opts> :\n Opts extends FactoryConstructorOpts ? FactoryConstructorReturnMasked<Opts> :\n Opts extends FactoryInstanceOpts ? FactoryInstanceReturnMasked<Opts> :\n Opts extends FactoryStaticOpts ? FactoryStaticReturnMasked<Opts> :\n never\n;\n\n\n// TODO can't use overloads here because of https://github.com/microsoft/TypeScript/issues/50754\n// export function maskedClass(mask: string): typeof MaskedPattern;\n// export function maskedClass(mask: DateConstructor): typeof MaskedDate;\n// export function maskedClass(mask: NumberConstructor): typeof MaskedNumber;\n// export function maskedClass(mask: Array<any> | ArrayConstructor): typeof MaskedDynamic;\n// export function maskedClass(mask: MaskedDate): typeof MaskedDate;\n// export function maskedClass(mask: MaskedNumber): typeof MaskedNumber;\n// export function maskedClass(mask: MaskedEnum): typeof MaskedEnum;\n// export function maskedClass(mask: MaskedRange): typeof MaskedRange;\n// export function maskedClass(mask: MaskedRegExp): typeof MaskedRegExp;\n// export function maskedClass(mask: MaskedFunction): typeof MaskedFunction;\n// export function maskedClass(mask: MaskedPattern): typeof MaskedPattern;\n// export function maskedClass(mask: MaskedDynamic): typeof MaskedDynamic;\n// export function maskedClass(mask: Masked): typeof Masked;\n// export function maskedClass(mask: typeof Masked): typeof Masked;\n// export function maskedClass(mask: typeof MaskedDate): typeof MaskedDate;\n// export function maskedClass(mask: typeof MaskedNumber): typeof MaskedNumber;\n// export function maskedClass(mask: typeof MaskedEnum): typeof MaskedEnum;\n// export function maskedClass(mask: typeof MaskedRange): typeof MaskedRange;\n// export function maskedClass(mask: typeof MaskedRegExp): typeof MaskedRegExp;\n// export function maskedClass(mask: typeof MaskedFunction): typeof MaskedFunction;\n// export function maskedClass(mask: typeof MaskedPattern): typeof MaskedPattern;\n// export function maskedClass(mask: typeof MaskedDynamic): typeof MaskedDynamic;\n// export function maskedClass<Mask extends typeof Masked> (mask: Mask): Mask;\n// export function maskedClass(mask: RegExp): typeof MaskedRegExp;\n// export function maskedClass(mask: (value: string, ...args: any[]) => boolean): typeof MaskedFunction;\n\n/** Get Masked class by mask type */\nexport function maskedClass (mask: Masked | FactoryOpts['mask']): any /* TODO */ {\n if (mask == null) throw new Error('mask property should be defined');\n\n if (mask instanceof RegExp) return IMask.MaskedRegExp;\n if (isString(mask)) return IMask.MaskedPattern;\n if (mask === Date) return IMask.MaskedDate;\n if (mask === Number) return IMask.MaskedNumber;\n if (Array.isArray(mask) || mask === Array) return IMask.MaskedDynamic;\n if (IMask.Masked && (mask as any).prototype instanceof IMask.Masked) return mask;\n if (IMask.Masked && mask instanceof IMask.Masked) return mask.constructor;\n if (mask instanceof Function) return IMask.MaskedFunction;\n\n console.warn('Mask not found for mask', mask); // eslint-disable-line no-console\n return IMask.Masked;\n}\n\ntype MaskedClassOf<M extends Masked> =\n M extends MaskedDate ? typeof MaskedDate :\n M extends MaskedNumber ? typeof MaskedNumber :\n M extends MaskedEnum ? typeof MaskedEnum :\n M extends MaskedRange ? typeof MaskedRange :\n M extends MaskedRegExp ? typeof MaskedRegExp :\n M extends MaskedFunction ? typeof MaskedFunction :\n M extends MaskedPattern ? typeof MaskedPattern :\n M extends MaskedDynamic ? typeof MaskedDynamic :\n any\n;\n\n\ntype NormalizedMaskedOpts<Opts extends Masked> = Omit<Opts, 'mask'> & {\n _mask: Opts,\n mask: MaskedClassOf<Opts>,\n};\n\ntype NormalizedInstanceOpts<Opts extends FactoryInstanceOpts> =\n Omit<Opts['mask'], `_${string}` | 'mask'> &\n NormalizedMaskedOpts<Opts['mask']>\n;\n\nexport\ntype NormalizedOpts<Opts extends FactoryArg> =\n Opts extends FactoryStaticOpts['mask'] ? { mask: Opts } :\n Opts extends Masked ? NormalizedMaskedOpts<Opts> :\n Opts extends FactoryInstanceOpts ? NormalizedInstanceOpts<Opts> :\n Opts extends FactoryStaticOpts | FactoryConstructorOpts ? Opts :\n { mask: Opts }\n;\n\nexport\nfunction normalizeOpts<Opts extends FactoryArg> (opts: Opts): NormalizedOpts<Opts> {\n if (!opts) throw new Error('Options in not defined');\n\n if (IMask.Masked) {\n if ((opts as any).prototype instanceof IMask.Masked) return { mask: opts } as NormalizedOpts<Opts>;\n\n /*\n handle cases like:\n 1) opts = Masked\n 2) opts = { mask: Masked, ...instanceOpts }\n */\n const { mask=undefined, ...instanceOpts } =\n opts instanceof IMask.Masked ? { mask: opts } :\n isObject(opts) && (opts as FactoryInstanceOpts).mask instanceof IMask.Masked ? (opts as FactoryInstanceOpts) : {};\n\n if (mask) {\n const _mask = (mask as Masked).mask;\n\n return {\n ...pick(mask, (_, k: string) => !k.startsWith('_')),\n mask: mask.constructor,\n _mask,\n ...instanceOpts,\n } as NormalizedOpts<Opts>;\n }\n }\n\n if (!isObject(opts)) return { mask: opts } as unknown as NormalizedOpts<Opts>;\n\n return { ...opts } as unknown as NormalizedOpts<Opts>;\n}\n\n// TODO can't use overloads here because of https://github.com/microsoft/TypeScript/issues/50754\n\n// From masked\n// export default function createMask<Opts extends Masked, ReturnMasked=Opts> (opts: Opts): ReturnMasked;\n// // From masked class\n// export default function createMask<Opts extends MaskedOptions<typeof Masked>, ReturnMasked extends Masked=InstanceType<Opts['mask']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedDate>, ReturnMasked extends MaskedDate=MaskedDate<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedNumber>, ReturnMasked extends MaskedNumber=MaskedNumber<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedEnum>, ReturnMasked extends MaskedEnum=MaskedEnum<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedRange>, ReturnMasked extends MaskedRange=MaskedRange<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedRegExp>, ReturnMasked extends MaskedRegExp=MaskedRegExp<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedFunction>, ReturnMasked extends MaskedFunction=MaskedFunction<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedPattern>, ReturnMasked extends MaskedPattern=MaskedPattern<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<typeof MaskedDynamic>, ReturnMasked extends MaskedDynamic=MaskedDynamic<Opts['parent']>> (opts: Opts): ReturnMasked;\n// // From mask opts\n// export default function createMask<Opts extends MaskedOptions<Masked>, ReturnMasked=Opts extends MaskedOptions<infer M> ? M : never> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedNumberOptions, ReturnMasked extends MaskedNumber=MaskedNumber<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedDateFactoryOptions, ReturnMasked extends MaskedDate=MaskedDate<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedEnumOptions, ReturnMasked extends MaskedEnum=MaskedEnum<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedRangeOptions, ReturnMasked extends MaskedRange=MaskedRange<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedPatternOptions, ReturnMasked extends MaskedPattern=MaskedPattern<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedDynamicOptions, ReturnMasked extends MaskedDynamic=MaskedDynamic<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<RegExp>, ReturnMasked extends MaskedRegExp=MaskedRegExp<Opts['parent']>> (opts: Opts): ReturnMasked;\n// export default function createMask<Opts extends MaskedOptions<Function>, ReturnMasked extends MaskedFunction=MaskedFunction<Opts['parent']>> (opts: Opts): ReturnMasked;\n\n/** Creates new {@link Masked} depending on mask type */\nexport default\nfunction createMask<Opts extends FactoryArg> (opts: Opts): FactoryReturnMasked<Opts> {\n if (IMask.Masked && (opts instanceof IMask.Masked)) return opts as FactoryReturnMasked<Opts>;\n const nOpts = normalizeOpts(opts);\n\n const MaskedClass = maskedClass(nOpts.mask);\n 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.`);\n\n if (nOpts.mask === MaskedClass) delete nOpts.mask;\n if ((nOpts as any)._mask) { nOpts.mask = (nOpts as any)._mask; delete (nOpts as any)._mask; }\n return new MaskedClass(nOpts);\n}\n\n\nIMask.createMask = createMask;\n","import IMask from '../core/holder';\n\n\nexport\ntype ElementEvent =\n | 'selectionChange'\n | 'input'\n | 'drop'\n | 'click'\n | 'focus'\n | 'commit'\n;\n\nexport\ntype EventHandlers = { [key in ElementEvent]: (...args: any[]) => void } & {\n undo?: (...args: any[]) => void;\n redo?: (...args: any[]) => void;\n}\n\n/** Generic element API to use with mask */\nexport default\nabstract class MaskElement {\n /** */\n abstract _unsafeSelectionStart: number | null;\n /** */\n abstract _unsafeSelectionEnd: number | null;\n /** */\n abstract value: string;\n\n /** Safely returns selection start */\n get selectionStart (): number {\n let start;\n try {\n start = this._unsafeSelectionStart;\n } catch {}\n\n return start != null ?\n start :\n this.value.length;\n }\n\n /** Safely returns selection end */\n get selectionEnd (): number {\n let end;\n try {\n end = this._unsafeSelectionEnd;\n } catch {}\n\n return end != null ?\n end :\n this.value.length;\n }\n\n /** Safely sets element selection */\n select (start: number, end: number) {\n if (start == null || end == null ||\n start === this.selectionStart && end === this.selectionEnd) return;\n\n try {\n this._unsafeSelect(start, end);\n } catch {}\n }\n\n /** */\n get isActive (): boolean { return false; }\n /** */\n abstract _unsafeSelect (start: number, end: number): void;\n /** */\n abstract bindEvents (handlers: EventHandlers): void;\n /** */\n abstract unbindEvents (): void\n}\n\n\nIMask.MaskElement = MaskElement;\n","import MaskElement, { EventHandlers } from './mask-element';\nimport IMask from '../core/holder';\n\n\nconst KEY_Z = 90;\nconst KEY_Y = 89;\n\n/** Bridge between HTMLElement and {@link Masked} */\nexport default\nabstract class HTMLMaskElement extends MaskElement {\n /** HTMLElement to use mask on */\n declare input: HTMLElement;\n declare _handlers: EventHandlers;\n abstract value: string;\n\n constructor (input: HTMLElement) {\n super();\n this.input = input;\n this._onKeydown = this._onKeydown.bind(this);\n this._onInput = this._onInput.bind(this);\n this._onBeforeinput = this._onBeforeinput.bind(this);\n this._onCompositionEnd = this._onCompositionEnd.bind(this);\n }\n\n get rootElement (): HTMLDocument {\n return (this.input.getRootNode?.() ?? document) as HTMLDocument;\n }\n\n /** Is element in focus */\n get isActive (): boolean {\n return this.input === this.rootElement.activeElement;\n }\n\n /** Binds HTMLElement events to mask internal events */\n override bindEvents (handlers: EventHandlers) {\n this.input.addEventListener('keydown', this._onKeydown as EventListener);\n this.input.addEventListener('input', this._onInput as EventListener);\n this.input.addEventListener('beforeinput', this._onBeforeinput as EventListener);\n this.input.addEventListener('compositionend', this._onCompositionEnd as EventListener);\n this.input.addEventListener('drop', handlers.drop);\n this.input.addEventListener('click', handlers.click);\n this.input.addEventListener('focus', handlers.focus);\n this.input.addEventListener('blur', handlers.commit);\n this._handlers = handlers;\n }\n\n _onKeydown (e: KeyboardEvent) {\n if (this._handlers.redo && (\n (e.keyCode === KEY_Z && e.shiftKey && (e.metaKey || e.ctrlKey)) ||\n (e.keyCode === KEY_Y && e.ctrlKey)\n )) {\n e.preventDefault();\n return this._handlers.redo(e);\n }\n\n if (this._handlers.undo && e.keyCode === KEY_Z && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n return this._handlers.undo(e);\n }\n\n if (!e.isComposing) this._handlers.selectionChange(e);\n }\n\n _onBeforeinput (e: InputEvent) {\n if (e.inputType === 'historyUndo' && this._handlers.undo) {\n e.preventDefault();\n return this._handlers.undo(e);\n }\n\n if (e.inputType === 'historyRedo' && this._handlers.redo) {\n e.preventDefault();\n return this._handlers.redo(e);\n }\n }\n\n _onCompositionEnd (e: CompositionEvent) {\n this._handlers.input(e);\n }\n\n _onInput (e: InputEvent) {\n if (!e.isComposing) this._handlers.input(e);\n }\n\n /** Unbinds HTMLElement events to mask internal events */\n override unbindEvents () {\n this.input.removeEventListener('keydown', this._onKeydown as EventListener);\n this.input.removeEventListener('input', this._onInput as EventListener);\n this.input.removeEventListener('beforeinput', this._onBeforeinput as EventListener);\n this.input.removeEventListener('compositionend', this._onCompositionEnd as EventListener);\n this.input.removeEventListener('drop', this._handlers.drop);\n this.input.removeEventListener('click', this._handlers.click);\n this.input.removeEventListener('focus', this._handlers.focus);\n this.input.removeEventListener('blur', this._handlers.commit);\n this._handlers = {} as EventHandlers;\n }\n}\n\n\nIMask.HTMLMaskElement = HTMLMaskElement;\n","import HTMLMaskElement from './html-mask-element';\nimport IMask from '../core/holder';\n\nexport\ntype InputElement = HTMLTextAreaElement | HTMLInputElement;\n\n/** Bridge between InputElement and {@link Masked} */\nexport default\nclass HTMLInputMaskElement extends HTMLMaskElement {\n /** InputElement to use mask on */\n declare input: InputElement;\n\n constructor (input: InputElement) {\n super(input);\n this.input = input;\n }\n\n /** Returns InputElement selection start */\n override get _unsafeSelectionStart (): number | null {\n return this.input.selectionStart != null ? this.input.selectionStart : this.value.length;\n }\n\n /** Returns InputElement selection end */\n override get _unsafeSelectionEnd (): number | null {\n return this.input.selectionEnd;\n }\n\n /** Sets InputElement selection */\n _unsafeSelect (start: number, end: number) {\n this.input.setSelectionRange(start, end);\n }\n\n override get value (): string {\n return this.input.value;\n }\n override set value (value: string) {\n this.input.value = value;\n }\n}\n\n\nIMask.HTMLMaskElement = HTMLMaskElement;\n","import HTMLMaskElement from './html-mask-element';\nimport IMask from '../core/holder';\n\n\nexport default\nclass HTMLContenteditableMaskElement extends HTMLMaskElement {\n declare input: HTMLElement;\n /** Returns HTMLElement selection start */\n override get _unsafeSelectionStart (): number | null {\n const root = this.rootElement;\n const selection = root.getSelection && root.getSelection();\n const anchorOffset = selection && selection.anchorOffset;\n const focusOffset = selection && selection.focusOffset;\n if (focusOffset == null || anchorOffset == null || anchorOffset < focusOffset) {\n return anchorOffset;\n }\n return focusOffset;\n }\n\n /** Returns HTMLElement selection end */\n override get _unsafeSelectionEnd (): number | null {\n const root = this.rootElement;\n const selection = root.getSelection && root.getSelection();\n const anchorOffset = selection && selection.anchorOffset;\n const focusOffset = selection && selection.focusOffset;\n if (focusOffset == null || anchorOffset == null || anchorOffset > focusOffset) {\n return anchorOffset;\n }\n return focusOffset;\n }\n\n /** Sets HTMLElement selection */\n override _unsafeSelect (start: number, end: number) {\n if (!this.rootElement.createRange) return;\n\n const range = this.rootElement.createRange();\n range.setStart(this.input.firstChild || this.input, start);\n range.setEnd(this.input.lastChild || this.input, end);\n const root = this.rootElement;\n const selection = root.getSelection && root.getSelection();\n if (selection) {\n selection.removeAllRanges();\n selection.addRange(range);\n }\n }\n\n /** HTMLElement value */\n override get value (): string {\n return this.input.textContent || '';\n }\n override set value (value: string) {\n this.input.textContent = value;\n }\n}\n\n\nIMask.HTMLContenteditableMaskElement = HTMLContenteditableMaskElement;\n","import { type Selection } from '../core/utils';\n\n\nexport\ntype InputHistoryState = {\n unmaskedValue: string,\n selection: Selection,\n};\n\n\nexport default\nclass InputHistory {\n static MAX_LENGTH = 100;\n states: InputHistoryState[] = [];\n currentIndex = 0;\n\n get currentState (): InputHistoryState | undefined {\n return this.states[this.currentIndex];\n }\n\n get isEmpty (): boolean {\n return this.states.length === 0;\n }\n\n push (state: InputHistoryState) {\n // if current index points before the last element then remove the future\n if (this.currentIndex < this.states.length - 1) this.states.length = this.currentIndex + 1;\n this.states.push(state);\n if (this.states.length > InputHistory.MAX_LENGTH) this.states.shift();\n this.currentIndex = this.states.length - 1;\n }\n\n go (steps: number): InputHistoryState | undefined {\n this.currentIndex = Math.min(Math.max(this.currentIndex + steps, 0), this.states.length - 1);\n return this.currentState;\n }\n\n undo () {\n return this.go(-1);\n }\n\n redo () {\n return this.go(+1);\n }\n\n clear () {\n this.states.length = 0;\n this.currentIndex = 0;\n }\n}\n","import { DIRECTION, type Selection } from '../core/utils';\nimport ActionDetails from '../core/action-details';\nimport createMask, { type UpdateOpts, maskedClass, type FactoryArg, type FactoryReturnMasked } from '../masked/factory';\nimport Masked from '../masked/base';\nimport MaskElement from './mask-element';\nimport HTMLInputMaskElement, { type InputElement } from './html-input-mask-element';\nimport HTMLContenteditableMaskElement from './html-contenteditable-mask-element';\nimport IMask from '../core/holder';\nimport InputHistory, { type InputHistoryState } from './input-history';\n\n\nexport\ntype InputMaskElement = MaskElement | InputElement | HTMLElement;\n\nexport\ntype InputMaskEventListener = (e?: InputEvent) => void;\n\n/** Listens to element events and controls changes between element and {@link Masked} */\nexport default\nclass InputMask<Opts extends FactoryArg=Record<string, unknown>> {\n /**\n View element\n */\n declare el: MaskElement;\n\n /** Internal {@link Masked} model */\n declare masked: FactoryReturnMasked<Opts>;\n\n declare _listeners: Record<string, Array<InputMaskEventListener>>;\n declare _value: string;\n declare _changingCursorPos: number;\n declare _unmaskedValue: string;\n declare _rawInputValue: string;\n declare _selection: Selection;\n declare _cursorChanging?: ReturnType<typeof setTimeout>;\n declare _historyChanging?: boolean;\n declare _inputEvent?: InputEvent;\n declare history: InputHistory;\n\n constructor (el: InputMaskElement, opts: Opts) {\n this.el =\n (el instanceof MaskElement) ? el :\n (el.isContentEditable && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA') ? new HTMLContenteditableMaskElement(el) :\n new HTMLInputMaskElement(el as InputElement);\n\n this.masked = createMask(opts);\n\n this._listeners = {};\n this._value = '';\n this._unmaskedValue = '';\n this._rawInputValue = '';\n this.history = new InputHistory();\n\n this._saveSelection = this._saveSelection.bind(this);\n this._onInput = this._onInput.bind(this);\n this._onChange = this._onChange.bind(this);\n this._onDrop = this._onDrop.bind(this);\n this._onFocus = this._onFocus.bind(this);\n this._onClick = this._onClick.bind(this);\n this._onUndo = this._onUndo.bind(this);\n this._onRedo = this._onRedo.bind(this);\n this.alignCursor = this.alignCursor.bind(this);\n this.alignCursorFriendly = this.alignCursorFriendly.bind(this);\n\n this._bindEvents();\n\n // refresh\n this.updateValue();\n this._onChange();\n }\n\n maskEquals (mask: any): boolean {\n return mask == null || this.masked?.maskEquals(mask);\n }\n\n /** Masked */\n get mask (): FactoryReturnMasked<Opts>['mask'] {\n return this.masked.mask;\n }\n set mask (mask: any) {\n if (this.maskEquals(mask)) return;\n\n if (!((mask as Masked) instanceof IMask.Masked) && this.masked.constructor === maskedClass(mask as Masked)) {\n // TODO \"any\" no idea\n this.masked.updateOptions({ mask } as any);\n return;\n }\n\n const masked = (mask instanceof IMask.Masked ? mask : createMask({ mask } as Opts)) as FactoryReturnMasked<Opts>;\n masked.unmaskedValue = this.masked.unmaskedValue;\n this.masked = masked;\n }\n\n /** Raw value */\n get value (): string {\n return this._value;\n }\n\n set value (str: string) {\n if (this.value === str) return;\n\n this.masked.value = str;\n this.updateControl('auto');\n }\n\n /** Unmasked value */\n get unmaskedValue (): string {\n return this._unmaskedValue;\n }\n\n set unmaskedValue (str: string) {\n if (this.unmaskedValue === str) return;\n\n this.masked.unmaskedValue = str;\n this.updateControl('auto');\n }\n\n /** Raw input value */\n get rawInputValue (): string {\n return this._rawInputValue;\n }\n\n set rawInputValue (str: string) {\n if (this.rawInputValue === str) return;\n\n this.masked.rawInputValue = str;\n this.updateControl();\n this.alignCursor();\n }\n\n /** Typed unmasked value */\n get typedValue (): FactoryReturnMasked<Opts>['typedValue'] {\n return this.masked.typedValue;\n }\n\n set typedValue (val: FactoryReturnMasked<Opts>['typedValue']) {\n if (this.masked.typedValueEquals(val)) return;\n\n this.masked.typedValue = val;\n this.updateControl('auto');\n }\n\n /** Display value */\n get displayValue (): string {\n return this.masked.displayValue;\n }\n\n /** Starts listening to element events */\n _bindEvents () {\n this.el.bindEvents({\n selectionChange: this._saveSelection,\n input: this._onInput,\n drop: this._onDrop,\n click: this._onClick,\n focus: this._onFocus,\n commit: this._onChange,\n undo: this._onUndo,\n redo: this._onRedo,\n });\n }\n\n /** Stops listening to element events */\n _unbindEvents () {\n if (this.el) this.el.unbindEvents();\n }\n\n /** Fires custom event */\n _fireEvent (ev: string, e?: InputEvent) {\n const listeners = this._listeners[ev];\n if (!listeners) return;\n\n listeners.forEach(l => l(e));\n }\n\n /** Current selection start */\n get selectionStart (): number {\n return this._cursorChanging ?\n this._changingCursorPos :\n\n this.el.selectionStart;\n }\n\n /** Current cursor position */\n get cursorPos (): number {\n return this._cursorChanging ?\n this._changingCursorPos :\n\n this.el.selectionEnd;\n }\n set cursorPos (pos: number) {\n if (!this.el || !this.el.isActive) return;\n\n this.el.select(pos, pos);\n this._saveSelection();\n }\n\n /** Stores current selection */\n _saveSelection (/* ev */) {\n if (this.displayValue !== this.el.value) {\n console.warn('Element value was changed outside of mask. Syncronize mask using `mask.updateValue()` to work properly.'); // eslint-disable-line no-console\n }\n this._selection = {\n start: this.selectionStart,\n end: this.cursorPos,\n };\n }\n\n /** Syncronizes model value from view */\n updateValue () {\n this.masked.value = this.el.value;\n this._value = this.masked.value;\n this._unmaskedValue = this.masked.unmaskedValue;\n this._rawInputValue = this.masked.rawInputValue;\n }\n\n /** Syncronizes view from model value, fires change events */\n updateControl (cursorPos?: number | 'auto') {\n const newUnmaskedValue = this.masked.unmaskedValue;\n const newValue = this.masked.value;\n const newRawInputValue = this.masked.rawInputValue;\n const newDisplayValue = this.displayValue;\n\n const isChanged =\n this.unmaskedValue !== newUnmaskedValue ||\n this.value !== newValue ||\n this._rawInputValue !== newRawInputValue\n ;\n\n this._unmaskedValue = newUnmaskedValue;\n this._value = newValue;\n this._rawInputValue = newRawInputValue;\n\n if (this.el.value !== newDisplayValue) this.el.value = newDisplayValue;\n\n if (cursorPos === 'auto') this.alignCursor();\n else if (cursorPos != null) this.cursorPos = cursorPos;\n\n if (isChanged) this._fireChangeEvents();\n if (!this._historyChanging && (isChanged || this.history.isEmpty)) this.history.push({\n unmaskedValue: newUnmaskedValue,\n selection: { start: this.selectionStart, end: this.cursorPos },\n });\n }\n\n /** Updates options with deep equal check, recreates {@link Masked} model if mask type changes */\n updateOptions(opts: UpdateOpts<Opts>) {\n const { mask, ...restOpts } = opts as any; // TODO types, yes, mask is optional\n\n const updateMask = !this.maskEquals(mask);\n const updateOpts = this.masked.optionsIsChanged(restOpts);\n\n if (updateMask) this.mask = mask;\n if (updateOpts) this.masked.updateOptions(restOpts); // TODO\n\n if (updateMask || updateOpts) this.updateControl();\n }\n\n /** Updates cursor */\n updateCursor (cursorPos: number) {\n if (cursorPos == null) return;\n this.cursorPos = cursorPos;\n\n // also queue change cursor for mobile browsers\n this._delayUpdateCursor(cursorPos);\n }\n\n /** Delays cursor update to support mobile browsers */\n _delayUpdateCursor (cursorPos: number) {\n this._abortUpdateCursor();\n this._changingCursorPos = cursorPos;\n this._cursorChanging = setTimeout(() => {\n if (!this.el) return; // if was destroyed\n this.cursorPos = this._changingCursorPos;\n this._abortUpdateCursor();\n }, 10);\n }\n\n /** Fires custom events */\n _fireChangeEvents () {\n this._fireEvent('accept', this._inputEvent);\n if (this.masked.isComplete) this._fireEvent('complete', this._inputEvent);\n }\n\n /** Aborts delayed cursor update */\n _abortUpdateCursor () {\n if (this._cursorChanging) {\n clearTimeout(this._cursorChanging);\n delete this._cursorChanging;\n }\n }\n\n /** Aligns cursor to nearest available position */\n alignCursor () {\n this.cursorPos = this.masked.nearestInputPos(this.masked.nearestInputPos(this.cursorPos, DIRECTION.LEFT));\n }\n\n /** Aligns cursor only if selection is empty */\n alignCursorFriendly () {\n if (this.selectionStart !== this.cursorPos) return; // skip if range is selected\n this.alignCursor();\n }\n\n /** Adds listener on custom event */\n on (ev: string, handler: InputMaskEventListener): this {\n if (!this._listeners[ev]) this._listeners[ev] = [];\n this._listeners[ev].push(handler);\n return this;\n }\n\n /** Removes custom event listener */\n off (ev: string, handler: InputMaskEventListener): this {\n if (!this._listeners[ev]) return this;\n if (!handler) {\n delete this._listeners[ev];\n return this;\n }\n const hIndex = this._listeners[ev].indexOf(handler);\n if (hIndex >= 0) this._listeners[ev].splice(hIndex, 1);\n return this;\n }\n\n /** Handles view input event */\n _onInput (e: InputEvent): void {\n this._inputEvent = e;\n this._abortUpdateCursor();\n\n const details = new ActionDetails({\n // new state\n value: this.el.value,\n cursorPos: this.cursorPos,\n\n // old state\n oldValue: this.displayValue,\n oldSelection: this._selection,\n });\n\n const oldRawValue = this.masked.rawInputValue;\n\n const offset = this.masked.splice(\n details.startChangePos,\n details.removed.length,\n details.inserted,\n details.removeDirection,\n { input: true, raw: true },\n ).offset;\n\n // force align in remove direction only if no input chars were removed\n // otherwise we still need to align with NONE (to get out from fixed symbols for instance)\n const removeDirection = oldRawValue === this.masked.rawInputValue ?\n details.removeDirection :\n DIRECTION.NONE;\n\n let cursorPos = this.masked.nearestInputPos(\n details.startChangePos + offset,\n removeDirection,\n );\n if (removeDirection !== DIRECTION.NONE) cursorPos = this.masked.nearestInputPos(cursorPos, DIRECTION.NONE);\n\n this.updateControl(cursorPos);\n delete this._inputEvent;\n }\n\n /** Handles view change event and commits model value */\n _onChange () {\n if (this.displayValue !== this.el.value) this.updateValue();\n this.masked.doCommit();\n this.updateControl();\n this._saveSelection();\n }\n\n /** Handles view drop event, prevents by default */\n _onDrop (ev: Event) {\n ev.preventDefault();\n ev.stopPropagation();\n }\n\n /** Restore last selection on focus */\n _onFocus (ev: Event) {\n this.alignCursorFriendly();\n }\n\n /** Restore last selection on focus */\n _onClick (ev: Event) {\n this.alignCursorFriendly();\n }\n\n _onUndo () {\n this._applyHistoryState(this.history.undo());\n }\n\n _onRedo () {\n this._applyHistoryState(this.history.redo());\n }\n\n _applyHistoryState (state: InputHistoryState | undefined) {\n if (!state) return;\n\n this._historyChanging = true;\n this.unmaskedValue = state.unmaskedValue;\n this.el.select(state.selection.start, state.selection.end);\n this._saveSelection();\n this._historyChanging = false;\n }\n\n /** Unbind view events and removes element reference */\n destroy () {\n this._unbindEvents();\n (this._listeners as any).length = 0;\n delete (this as any).el;\n }\n}\n\n\nIMask.InputMask = InputMask;\n","import IMask from \"./holder\";\n\n\nexport\ntype ChangeDetailsOptions = Pick<ChangeDetails,\n | 'inserted'\n | 'tailShift'\n | 'rawInserted'\n | 'skip'\n>;\n\n/** Provides details of changing model value */\nexport default\nclass ChangeDetails {\n /** Inserted symbols */\n declare inserted: string;\n /** Additional offset if any changes occurred before tail */\n declare tailShift: number;\n /** Raw inserted is used by dynamic mask */\n declare rawInserted: string;\n /** Can skip chars */\n declare skip: boolean;\n\n\n static normalize (prep: string | [string, ChangeDetails]): [string, ChangeDetails] {\n return Array.isArray(prep) ? prep : [\n prep,\n new ChangeDetails(),\n ];\n }\n\n constructor (details?: Partial<ChangeDetailsOptions>) {\n Object.assign(this, {\n inserted: '',\n rawInserted: '',\n tailShift: 0,\n skip: false,\n }, details);\n }\n\n /** Aggregate changes */\n aggregate (details: ChangeDetails): this {\n this.inserted += details.inserted;\n this.rawInserted += details.rawInserted;\n this.tailShift += details.tailShift;\n this.skip = this.skip || details.skip;\n\n return this;\n }\n\n /** Total offset considering all changes */\n get offset (): number {\n return this.tailShift + this.inserted.length;\n }\n\n get consumed (): boolean {\n return Boolean(this.rawInserted) || this.skip;\n }\n\n equals (details: ChangeDetails): boolean {\n return this.inserted === details.inserted &&\n this.tailShift === details.tailShift &&\n this.rawInserted === details.rawInserted &&\n this.skip === details.skip\n ;\n }\n}\n\n\nIMask.ChangeDetails = ChangeDetails;\n","import type { TailDetails, AppendTail } from './tail-details';\nimport type ChangeDetails from './change-details';\n\n\ntype ContinuousTailState = Pick<ContinuousTailDetails,\n | 'value'\n | 'from'\n | 'stop'\n>;\n\n/** Provides details of continuous extracted tail */\nexport default\nclass ContinuousTailDetails implements TailDetails {\n /** Tail value as string */\n declare value: string;\n /** Tail start position */\n declare from: number;\n /** Start position */\n declare stop?: number;\n\n constructor (value: string='', from: number=0, stop?: number) {\n this.value = value;\n this.from = from;\n this.stop = stop;\n }\n\