Custom tokens and dynamic patterns

Formatting only: no date, card, or identifier validation. These options also work with applyMask, process, and buildMask.

Hexadecimal MAC address

bind(input, 'HH:HH:HH:HH:HH:HH', { tokens: { H: /[0-9A-Fa-f]/ } })Try a1b2c3d4e5f6; g is rejected.

Uppercase identifier

bind(input, 'UUU-999', { tokens: { U: { match: /[a-z]/i, transform: char => char.toUpperCase() } } })Type abc123, then replace a letter in the middle.

Card layout by prefix

bind(input, '9999 9999 9999 9999', { resolveMask: v => v.startsWith('34') || v.startsWith('37') ? '9999 999999 99999' : '9999 9999 9999 9999' })Change 34 to 51: the layout changes while your caret stays at the edit.

Literal token character

bind(input, '\\A-999999')The A is fixed text, not a slot.

Unicode letters

bind(input, 'LLLL', { tokens: { L: /\p{L}/u } })One code point per slot; provisional IME text is preserved until commit.

Token and transform contracts#

A token is a RegExp, a character predicate, or { match, transform? }. Keys are single Unicode code points; backslash is reserved. Overrides of 9, Z, and A apply only to that operation or binding. Definitions are snapshotted on bind; dispose and rebind to change them. Use pure matchers. RegExp g/y flags are ignored on a private copy without changing the caller's lastIndex.

A transform must return exactly one code point; otherwise the engine throws a RangeError. Use an idempotent transform whose output still matches the token. Uppercasing ß to SS is not supported. Normalize with tokens instead of assigning to input.value in a callback so the engine can map the caret through the transformation.

Content-dependent masks#

resolveMask runs once per masking application, before transforms. It receives candidate data accepted by the fallback pattern's slots (or any fallback array member), with complete fallback literal runs at their boundaries and nonmatching characters removed. Escaped runs remain formatting after a segment shrinks. Make the fallback alphabet cover every layout the resolver can return. Return a string or an ordered array; arrays still select by capacity. Resolution is not recursive.

Resolvers always format one continuous identifier, removing old separators before applying the new layout, even with segmented: true. Use static masks or arrays when fields must remain independently editable. eager still applies, and the caret follows the logical data when a layout changes.

Unicode, composition, and length#

Slots match Unicode code points, not grapheme clusters. Combining marks and joined emoji occupy separate slots if accepted; no normalization is performed. Built-in Z and A remain ASCII-only. Caret offsets use UTF-16, as DOM selections do.

Custom-token bindings leave provisional IME text and selection untouched until composition commits, including when the matcher only accepts ASCII. Built-in-only masks keep live formatting during Android autocorrect composition. Bindings with custom tokens or a resolver do not add maxlength; the engine still caps slots. Author-supplied limits remain in place. Disposal removes only attributes added by the binding.