2
0
mirror of https://github.com/tenrok/maska.git synced 2026-06-17 19:21:21 +03:00

Initial commit

This commit is contained in:
Alexander Shabunevich
2019-09-25 15:22:31 +03:00
commit 0690000a62
14 changed files with 9037 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
import Maska from './maska'
import { isString } from './utils'
function getOpts (mask) {
const opts = {}
if (isString(mask)) {
opts.mask = mask
} else if (mask.mask) {
opts.mask = mask.mask
opts.tokens = mask.tokens ? mask.tokens : {}
}
return opts
}
function needUpdate (mask) {
if (isString(mask.value) && isString(mask.oldValue) && mask.value === mask.oldValue) {
return false
}
if (mask.value && mask.oldValue && mask.value.mask === mask.oldValue.mask) {
return false
}
return true
}
export default function directive (el, mask) {
if (!mask.value) return
if (mask.value && needUpdate(mask)) {
return new Maska(el, getOpts(mask.value))
}
}
+24
View File
@@ -0,0 +1,24 @@
import directive from './directive'
import mask from './mask'
import Maska from './maska'
import tokens from './tokens'
function install (Vue) {
Vue.directive('maska', directive)
}
// Install by default if included from script tag
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(install)
}
function create (el, options) {
return new Maska(el, options)
}
export default install
export {
create,
mask,
directive as maska,
tokens
}
+61
View File
@@ -0,0 +1,61 @@
export default function mask (value, mask, tokens, masked = true) {
let im = 0
let iv = 0
let ret = ''
let rest = ''
while (im < mask.length && iv < value.length) {
let maskChar = mask[im]
const valueChar = value[iv]
const token = tokens[maskChar]
if (token && token.pattern) {
if (token.pattern.test(valueChar)) {
ret += tokenTransform(valueChar, token)
im++
}
iv++
} else if (token && token.repeat) {
const maskCharPrev = mask[im - 1]
const tokenPrev = tokens[maskCharPrev]
if (token && token.repeat && tokenPrev && !tokenPrev.pattern.test(valueChar)) {
im++
} else {
im--
}
} else {
if (token && token.escape) {
im++
maskChar = mask[im]
}
if (masked) ret += maskChar
if (valueChar === maskChar) iv++
im++
}
}
// fix mask that ends with parentesis
while (masked && im < mask.length) { // eslint-disable-line no-unmodified-loop-condition
const maskCharRest = mask[im]
if (tokens[maskCharRest] && !tokens[maskCharRest].repeat) {
rest = ''
break
}
if (!tokens[maskCharRest]) {
rest += maskCharRest
}
im++
}
return ret + rest
}
function tokenTransform (value, token) {
if (token.uppercase) {
return value.toLocaleUpperCase()
} else if (token.lowercase) {
return value.toLocaleLowerCase()
}
return value
}
+61
View File
@@ -0,0 +1,61 @@
import mask from './mask'
import tokens from './tokens'
import { event, findInputElement, fixInputSelection, isString } from './utils'
export default class Maska {
constructor (el, opts = {}) {
if (!el) throw new Error('Maska: no element for mask')
if (opts.tokens) {
for (const i in opts.tokens) {
opts.tokens[i] = { ...opts.tokens[i] }
if (opts.tokens[i].pattern && isString(opts.tokens[i].pattern)) {
opts.tokens[i].pattern = new RegExp(opts.tokens[i].pattern)
}
}
}
this._opts = {
mask: opts.mask,
tokens: { ...tokens, ...opts.tokens }
}
this._el = isString(el) ? document.querySelectorAll(el) : !el.length ? [el] : el
this.init()
}
init () {
for (let i = 0; i < this._el.length; i++) {
const el = findInputElement(this._el[i])
if (!el.dataset.mask && this._opts.mask) {
el.dataset.mask = this._opts.mask
}
this.updateValue(el)
el.addEventListener('input', evt => this.onInput(evt))
}
}
destroy () {
for (let i = 0; i < this._el.length; i++) {
const el = findInputElement(this._el[i])
el.removeEventListener('input', evt => this.onInput(evt))
delete el.dataset.mask
}
}
updateValue (el) {
if (!el.value || !el.dataset.mask) return
const position = el.selectionEnd
const digit = el.value[position - 1]
el.value = mask(el.value, el.dataset.mask, this._opts.tokens)
fixInputSelection(el, position, digit)
el.dispatchEvent(event('input'))
}
onInput (event) {
if (event.isTrusted) {
this.updateValue(event.target)
}
}
}
+10
View File
@@ -0,0 +1,10 @@
/* eslint quote-props: ["error", "consistent"] */
export default {
'#': { pattern: /[0-9]/ },
'X': { pattern: /[0-9a-zA-Z]/ },
'S': { pattern: /[a-zA-Z]/ },
'A': { pattern: /[a-zA-Z]/, uppercase: true },
'a': { pattern: /[a-zA-Z]/, lowercase: true },
'!': { escape: true },
'*': { repeat: true }
}
+31
View File
@@ -0,0 +1,31 @@
/* global HTMLInputElement */
function event (name) {
const event = document.createEvent('Event')
event.initEvent(name, true, true)
return event
}
function findInputElement (el) {
return (el instanceof HTMLInputElement) ? el : el.querySelector('input') || el
}
function fixInputSelection (el, position, digit) {
while (position < el.value.length && el.value.charAt(position - 1) !== digit) {
position++
}
if (el === document.activeElement) {
el.setSelectionRange(position, position)
setTimeout(function () {
el.setSelectionRange(position, position)
}, 0)
}
}
function isString (val) {
return Object.prototype.toString.call(val) === '[object String]'
}
export { event, findInputElement, fixInputSelection, isString }