UNPKG

di-vue-mask

Version:
651 lines (538 loc) 19.2 kB
/** * di-vue-mask v1.2.1 * (c) 2021 Sergio Rodrigues * @license MIT */ var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var stringMask = createCommonjsModule(function (module, exports) { (function(root, factory) { /* istanbul ignore next */ if (typeof undefined === 'function' && undefined.amd) { // AMD. Register as an anonymous module. undefined([], factory); } else { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } }(commonjsGlobal, function() { var tokens = { '0': {pattern: /\d/, _default: '0'}, '9': {pattern: /\d/, optional: true}, '#': {pattern: /\d/, optional: true, recursive: true}, 'A': {pattern: /[a-zA-Z0-9]/}, 'S': {pattern: /[a-zA-Z]/}, 'U': {pattern: /[a-zA-Z]/, transform: function(c) { return c.toLocaleUpperCase(); }}, 'L': {pattern: /[a-zA-Z]/, transform: function(c) { return c.toLocaleLowerCase(); }}, '$': {escape: true} }; function isEscaped(pattern, pos) { var count = 0; var i = pos - 1; var token = {escape: true}; while (i >= 0 && token && token.escape) { token = tokens[pattern.charAt(i)]; count += token && token.escape ? 1 : 0; i--; } return count > 0 && count % 2 === 1; } function calcOptionalNumbersToUse(pattern, value) { var numbersInP = pattern.replace(/[^0]/g,'').length; var numbersInV = value.replace(/[^\d]/g,'').length; return numbersInV - numbersInP; } function concatChar(text, character, options, token) { if (token && typeof token.transform === 'function') { character = token.transform(character); } if (options.reverse) { return character + text; } return text + character; } function hasMoreTokens(pattern, pos, inc) { var pc = pattern.charAt(pos); var token = tokens[pc]; if (pc === '') { return false; } return token && !token.escape ? true : hasMoreTokens(pattern, pos + inc, inc); } function hasMoreRecursiveTokens(pattern, pos, inc) { var pc = pattern.charAt(pos); var token = tokens[pc]; if (pc === '') { return false; } return token && token.recursive ? true : hasMoreRecursiveTokens(pattern, pos + inc, inc); } function insertChar(text, char, position) { var t = text.split(''); t.splice(position, 0, char); return t.join(''); } function StringMask(pattern, opt) { this.options = opt || {}; this.options = { reverse: this.options.reverse || false, usedefaults: this.options.usedefaults || this.options.reverse }; this.pattern = pattern; } StringMask.prototype.process = function proccess(value) { var this$1 = this; if (!value) { return {result: '', valid: false}; } value = value + ''; var pattern2 = this.pattern; var valid = true; var formatted = ''; var valuePos = this.options.reverse ? value.length - 1 : 0; var patternPos = 0; var optionalNumbersToUse = calcOptionalNumbersToUse(pattern2, value); var escapeNext = false; var recursive = []; var inRecursiveMode = false; var steps = { start: this.options.reverse ? pattern2.length - 1 : 0, end: this.options.reverse ? -1 : pattern2.length, inc: this.options.reverse ? -1 : 1 }; function continueCondition(options) { if (!inRecursiveMode && !recursive.length && hasMoreTokens(pattern2, patternPos, steps.inc)) { // continue in the normal iteration return true; } else if (!inRecursiveMode && recursive.length && hasMoreRecursiveTokens(pattern2, patternPos, steps.inc)) { // continue looking for the recursive tokens // Note: all chars in the patterns after the recursive portion will be handled as static string return true; } else if (!inRecursiveMode) { // start to handle the recursive portion of the pattern inRecursiveMode = recursive.length > 0; } if (inRecursiveMode) { var pc = recursive.shift(); recursive.push(pc); if (options.reverse && valuePos >= 0) { patternPos++; pattern2 = insertChar(pattern2, pc, patternPos); return true; } else if (!options.reverse && valuePos < value.length) { pattern2 = insertChar(pattern2, pc, patternPos); return true; } } return patternPos < pattern2.length && patternPos >= 0; } /** * Iterate over the pattern's chars parsing/matching the input value chars * until the end of the pattern. If the pattern ends with recursive chars * the iteration will continue until the end of the input value. * * Note: The iteration must stop if an invalid char is found. */ for (patternPos = steps.start; continueCondition(this.options); patternPos = patternPos + steps.inc) { // Value char var vc = value.charAt(valuePos); // Pattern char to match with the value char var pc = pattern2.charAt(patternPos); var token = tokens[pc]; if (recursive.length && token && !token.recursive) { // In the recursive portion of the pattern: tokens not recursive must be seen as static chars token = null; } // 1. Handle escape tokens in pattern // go to next iteration: if the pattern char is a escape char or was escaped if (!inRecursiveMode || vc) { if (this$1.options.reverse && isEscaped(pattern2, patternPos)) { // pattern char is escaped, just add it and move on formatted = concatChar(formatted, pc, this$1.options, token); // skip escape token patternPos = patternPos + steps.inc; continue; } else if (!this$1.options.reverse && escapeNext) { // pattern char is escaped, just add it and move on formatted = concatChar(formatted, pc, this$1.options, token); escapeNext = false; continue; } else if (!this$1.options.reverse && token && token.escape) { // mark to escape the next pattern char escapeNext = true; continue; } } // 2. Handle recursive tokens in pattern // go to next iteration: if the value str is finished or // if there is a normal token in the recursive portion of the pattern if (!inRecursiveMode && token && token.recursive) { // save it to repeat in the end of the pattern and handle the value char now recursive.push(pc); } else if (inRecursiveMode && !vc) { // in recursive mode but value is finished. Add the pattern char if it is not a recursive token formatted = concatChar(formatted, pc, this$1.options, token); continue; } else if (!inRecursiveMode && recursive.length > 0 && !vc) { // recursiveMode not started but already in the recursive portion of the pattern continue; } // 3. Handle the value // break iterations: if value is invalid for the given pattern if (!token) { // add char of the pattern formatted = concatChar(formatted, pc, this$1.options, token); if (!inRecursiveMode && recursive.length) { // save it to repeat in the end of the pattern recursive.push(pc); } } else if (token.optional) { // if token is optional, only add the value char if it matchs the token pattern // if not, move on to the next pattern char if (token.pattern.test(vc) && optionalNumbersToUse) { formatted = concatChar(formatted, vc, this$1.options, token); valuePos = valuePos + steps.inc; optionalNumbersToUse--; } else if (recursive.length > 0 && vc) { valid = false; break; } } else if (token.pattern.test(vc)) { // if token isn't optional the value char must match the token pattern formatted = concatChar(formatted, vc, this$1.options, token); valuePos = valuePos + steps.inc; } else if (!vc && token._default && this$1.options.usedefaults) { // if the token isn't optional and has a default value, use it if the value is finished formatted = concatChar(formatted, token._default, this$1.options, token); } else { // the string value don't match the given pattern valid = false; break; } } return {result: formatted, valid: valid}; }; StringMask.prototype.apply = function(value) { return this.process(value).result; }; StringMask.prototype.validate = function(value) { return this.process(value).valid; }; StringMask.process = function(value, pattern, options) { return new StringMask(pattern, options).process(value); }; StringMask.apply = function(value, pattern, options) { return new StringMask(pattern, options).apply(value); }; StringMask.validate = function(value, pattern, options) { return new StringMask(pattern, options).validate(value); }; return StringMask; })); }); var getInputElement = function (el) { var inputEl = el.tagName.toLowerCase() !== 'input' ? el.querySelector('input:not([readonly])') : el; if (!inputEl) { throw new Error('Mask directive requires at least one input'); } return inputEl; }; function createEvent(name) { var event = document.createEvent('HTMLEvents'); event.initEvent(name, true, true); return event; } var filterNumbers = function (v) { return ( v.replace(/\D/g, '') ); }; var filterLetters = function (v) { return ( v.replace(/[^a-zA-Z]/g, '') ); }; var filterAlphanumeric = function (v) { return ( v.replace(/[^a-zA-Z0-9]/g, '') ); }; var parsePreFn = function (arg) { if (typeof arg === 'function') { return arg; } switch (arg) { case 'filter-number': return filterNumbers; case 'filter-letter': return filterLetters; default: return filterAlphanumeric; } }; var parsePostFn = function (arg) { if (typeof arg === 'function') { return arg; } return function (value) { return ( value.trim().replace(/[^0-9]$/, '') ); }; }; var delimiter = '\u00a7'; function masker(fn) { return function (args) { var data = fn(args); var pre = parsePreFn('pre' in data ? data.pre : null); var post = parsePostFn('post' in data ? data.post : null); var formatter = 'pattern' in data && data.pattern ? new stringMask(data.pattern, data.options || {}) : null; var handler = 'handler' in data && typeof data.handler === 'function' ? data.handler : function (value) { return (formatter ? formatter.apply(value) : value); }; return function (str, args) { if ( args === void 0 ) args = {}; args = Object.assign(args, { delimiter: delimiter }); str = pre(str, args); var ref = ( !str.includes(delimiter) ? ("" + delimiter + str) : str ).split(delimiter); var prefix = ref[0]; var value = ref[1]; value = handler(value, args); return post(("" + prefix + value), args); } } } var mask = masker(function (ref) { var pattern = ref.value; return ({ pattern: pattern, pre: filterAlphanumeric, post: function (value) { return ( value.trim().replace(/[^a-zA-Z0-9]$/, '') ); }, }); }); var patterns = { us: '0000-00-00', br: '00/00/0000' }; var date = masker(function (ref) { if ( ref === void 0 ) ref = {}; var locale = ref.locale; if ( locale === void 0 ) locale = null; return ({ pattern: patterns[locale || 'us'], pre: filterNumbers, }); }); var handlers = { get us() { var phone = new stringMask('(000) 000-0000'); return function (value) { return phone.apply(value); }; }, get br() { var phone = new stringMask('(00) 0000-0000'); var phone9 = new stringMask('(00) 9 0000-0000'); var phone0800 = new stringMask('0000-000-0000'); return function (value) { if (value.startsWith('0800'.slice(0, value.length))) { return phone0800.apply(value); } else if (value.length <= 10) { return phone.apply(value); } return phone9.apply(value); } } }; var phone = masker(function (ref) { var locale = ref.locale; var handler = handlers[locale || 'us']; return { pre: filterNumbers, handler: handler, }; }); var config = { us: { thousand: ',', decimal: '.' }, br: { thousand: '.', decimal: ',' } }; var decimal = masker(function (ref) { var locale = ref.locale; var value = ref.value; var conf = config[locale || 'us']; var patternParts = [("#" + (conf.thousand) + "##0")]; var precision = value || 0; if (precision) { patternParts.push( conf.decimal, new Array(precision).fill('0').join('') ); } return { pattern: patternParts.join(''), options: { reverse: true }, pre: function pre(value, ref) { var delimiter = ref.delimiter; if (!value) { return ''; } var sign = value.startsWith('-') ? '-' : ''; var ref$1 = value.split(conf.decimal).map(filterNumbers); var number = ref$1[0]; var fraction = ref$1[1]; if ( fraction === void 0 ) fraction = ''; if (fraction && fraction.length > precision) { number = "" + number + (fraction.slice(0, -precision)); fraction = fraction.slice(-precision); } return [sign, delimiter, Number(number), fraction].join(''); }, post: function post(value) { return value; }, }; }); var number = masker(function () { return { pattern: '#0', options: { reverse: true }, pre: filterNumbers } }); var cpf = masker(function () { return ({ pattern: '000.000.000-00', pre: filterNumbers, }); }); var cnpj = masker(function () { return ({ pattern: '00.000.000/0000-00', pre: filterNumbers, }); }); var cep = masker(function () { return ({ pattern: '00.000-000', pre: filterNumbers, }); }); var cc = masker(function () { return ({ pattern: '0000 0000 0000 0000', pre: filterNumbers, }); }); var model = { bind: function bind(el, ref, vnode) { var value = ref.value; var expression = ref.expression; el = getInputElement(el, vnode); var expressionParts = expression.replace(/\[(\d+)]/, '.$1').split('.'); var updateModelValue = function (val) { var obj = vnode.context; var parts = expressionParts.slice(0); while (parts.length > 1) { obj = obj[parts.shift()]; } return obj[parts.shift()] = val; }; el.addEventListener('input', function (ref) { var target = ref.target; return updateModelValue(target.value); }, false); el.value = value; }, update: function update(el, ref, vnode) { var value = ref.value; var oldValue = ref.oldValue; if (value !== oldValue) { el = getInputElement(el, vnode); el.value = value; } } }; function updater(el, masker) { var currentValue = el.value; var oldValue = el.dataset.value; if (oldValue === currentValue) { return; } var newValue = masker(currentValue, { el: el }); if (newValue === currentValue) { el.dataset.value = currentValue; return; } // Get current cursor position var position = el.selectionEnd; // Find next cursor position if (position === currentValue.length) { position = newValue.length; } else if (position > 0 && position <= newValue.length) { var digit = currentValue.charAt(position - 1); if (digit !== newValue.charAt(position - 1)) { if (digit === newValue.charAt(position)) { position += 1; } else if (digit === newValue.charAt(position - 2)) { position -= 1; } } } el.value = newValue; el.dataset.value = newValue; if (el === document.activeElement) { // Restore cursor position el.setSelectionRange(position, position); } el.dispatchEvent(createEvent('input')); } function make(maskerFn) { var maskerMap = new WeakMap(); var inputMap = new WeakMap(); var eventMap = new WeakMap(); return { bind: function (el, binding) { var masker = maskerFn({ value: binding.value, locale: binding.arg || Object.keys(binding.modifiers)[0] || null, }); var inputEl = getInputElement(el); var eventHandler = function (ref) { var isTrusted = ref.isTrusted; if (isTrusted) { updater(inputEl, masker); } }; maskerMap.set(el, masker); inputMap.set(el, inputEl); eventMap.set(el, eventHandler); inputEl.addEventListener('input', eventHandler); updater(inputEl, masker); }, componentUpdated: function componentUpdated(el) { updater(inputMap.get(el), maskerMap.get(el)); }, unbind: function unbind(el) { el.removeEventListener('input', inputMap.get(el)); maskerMap.delete(el); inputMap.delete(el); eventMap.delete(el); }, }; } var _Vue; function install(Vue) { if (install.installed) { return; } _Vue = Vue; Vue.directive('mask', make(mask)); Vue.directive('maskDate', make(date)); Vue.directive('maskPhone', make(phone)); Vue.directive('maskDecimal', make(decimal)); Vue.directive('maskNumber', make(number)); Vue.directive('maskCpf', make(cpf)); Vue.directive('maskCnpj', make(cnpj)); Vue.directive('maskCep', make(cep)); Vue.directive('maskCc', make(cc)); Vue.directive('maskModel', model); install.installed = true; } var index = { install: install }; if (typeof window !== 'undefined' && window.Vue) { window.Vue.use({ install: install }); } export default index;