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