maska
Version:
Simple zero-dependency input mask for Vanilla JS, Vue, Alpine.js and Svelte
3 lines (2 loc) • 7.89 kB
JavaScript
/*! maska v3.1.0 by Alexander Shabunevich | Released under the MIT license */
(function(u,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(u=typeof globalThis<"u"?globalThis:u||self,p(u.Maska={}))})(this,function(u){"use strict";var j=Object.defineProperty;var V=(u,p,M)=>p in u?j(u,p,{enumerable:!0,configurable:!0,writable:!0,value:M}):u[p]=M;var A=(u,p,M)=>V(u,typeof p!="symbol"?p+"":p,M);const p={"#":{pattern:/[0-9]/},"@":{pattern:/[a-zA-Z]/},"*":{pattern:/[a-zA-Z0-9]/}},M=(a,e,s)=>a.replaceAll(e,"").replace(s,".").replace("..",".").replace(/[^.\d]/g,""),I=(a,e,s)=>{var t;return new Intl.NumberFormat(((t=s.number)==null?void 0:t.locale)??"en",{minimumFractionDigits:a,maximumFractionDigits:e,roundingMode:"trunc"})},W=(a,e=!0,s)=>{var E,w,b,g;const t=((E=s.number)==null?void 0:E.unsigned)!==!0&&a.startsWith("-")?"-":"",n=((w=s.number)==null?void 0:w.fraction)??0;let o=I(0,n,s);const m=o.formatToParts(1000.12),h=((b=m.find(r=>r.type==="group"))==null?void 0:b.value)??" ",l=((g=m.find(r=>r.type==="decimal"))==null?void 0:g.value)??".",i=M(a,h,l);if(Number.isNaN(parseFloat(i)))return t;const f=i.split(".");if(f[1]!=null&&f[1].length>=1){const r=f[1].length<=n?f[1].length:n;o=I(r,n,s)}let c=o.format(parseFloat(i));return e?n>0&&i.endsWith(".")&&!i.slice(0,-1).includes(".")&&(c+=l):c=M(c,h,l),t+c};class S{constructor(e={}){A(this,"opts",{});A(this,"memo",new Map);const s={...e};if(s.tokens!=null){s.tokens=s.tokensReplace?{...s.tokens}:{...p,...s.tokens};for(const t of Object.values(s.tokens))typeof t.pattern=="string"&&(t.pattern=new RegExp(t.pattern))}else s.tokens=p;Array.isArray(s.mask)&&(s.mask.length>1?s.mask=[...s.mask].sort((t,n)=>t.length-n.length):s.mask=s.mask[0]??""),s.mask===""&&(s.mask=null),this.opts=s}masked(e){return this.process(String(e),this.findMask(String(e)))}unmasked(e){return this.process(String(e),this.findMask(String(e)),!1)}isEager(){return this.opts.eager===!0}isReversed(){return this.opts.reversed===!0}completed(e){const s=this.findMask(String(e));if(this.opts.mask==null||s==null)return!1;const t=this.process(String(e),s).length;return typeof this.opts.mask=="string"?t>=this.opts.mask.length:t>=s.length}findMask(e){const s=this.opts.mask;if(s==null)return null;if(typeof s=="string")return s;if(typeof s=="function")return s(e);const t=this.process(e,s.slice(-1).pop()??"",!1);return s.find(n=>this.process(e,n,!1).length>=t.length)??""}escapeMask(e){const s=[],t=[];return e.split("").forEach((n,o)=>{n==="!"&&e[o-1]!=="!"?t.push(o-t.length):s.push(n)}),{mask:s.join(""),escaped:t}}process(e,s,t=!0){if(this.opts.number!=null)return W(e,t,this.opts);if(s==null)return e;const n=`v=${e},mr=${s},m=${t?1:0}`;if(this.memo.has(n))return this.memo.get(n);const{mask:o,escaped:m}=this.escapeMask(s),h=[],l=this.opts.tokens!=null?this.opts.tokens:{},i=this.isReversed()?-1:1,f=this.isReversed()?"unshift":"push",c=this.isReversed()?0:o.length-1,E=this.isReversed()?()=>r>-1&&d>-1:()=>r<o.length&&d<e.length,w=y=>!this.isReversed()&&y<=c||this.isReversed()&&y>=c;let b,g=-1,r=this.isReversed()?o.length-1:0,d=this.isReversed()?e.length-1:0,C=!1;for(;E();){const y=o.charAt(r),k=l[y],N=(k==null?void 0:k.transform)!=null?k.transform(e.charAt(d)):e.charAt(d);if(!m.includes(r)&&k!=null?(N.match(k.pattern)!=null?(h[f](N),k.repeated?(g===-1?g=r:r===c&&r!==g&&(r=g-i),c===g&&(r-=i)):k.multiple&&(C=!0,r-=i),r+=i):k.multiple?C&&(r+=i,d-=i,C=!1):N===b?b=void 0:k.optional&&(r+=i,d-=i),d+=i):(t&&!this.isEager()&&h[f](y),N===y&&!this.isEager()?d+=i:b=y,this.isEager()||(r+=i)),this.isEager())for(;w(r)&&(l[o.charAt(r)]==null||m.includes(r));){if(t){if(h[f](o.charAt(r)),e.charAt(d)===o.charAt(r)){r+=i,d+=i;continue}}else o.charAt(r)===e.charAt(d)&&(d+=i);r+=i}}return this.memo.set(n,h.join("")),this.memo.get(n)}}const T=a=>JSON.parse(a.replaceAll("'",'"')),x=(a,e={})=>{const s={...e};a.dataset.maska!=null&&a.dataset.maska!==""&&(s.mask=L(a.dataset.maska)),a.dataset.maskaEager!=null&&(s.eager=v(a.dataset.maskaEager)),a.dataset.maskaReversed!=null&&(s.reversed=v(a.dataset.maskaReversed)),a.dataset.maskaTokensReplace!=null&&(s.tokensReplace=v(a.dataset.maskaTokensReplace)),a.dataset.maskaTokens!=null&&(s.tokens=O(a.dataset.maskaTokens));const t={};return a.dataset.maskaNumberLocale!=null&&(t.locale=a.dataset.maskaNumberLocale),a.dataset.maskaNumberFraction!=null&&(t.fraction=parseInt(a.dataset.maskaNumberFraction)),a.dataset.maskaNumberUnsigned!=null&&(t.unsigned=v(a.dataset.maskaNumberUnsigned)),(a.dataset.maskaNumber!=null||Object.values(t).length>0)&&(s.number=t),s},v=a=>a!==""?!!JSON.parse(a):!0,L=a=>a.startsWith("[")&&a.endsWith("]")?T(a):a,O=a=>{if(a.startsWith("{")&&a.endsWith("}"))return T(a);const e={};return a.split("|").forEach(s=>{const t=s.split(":");e[t[0]]={pattern:new RegExp(t[1]),optional:t[2]==="optional",multiple:t[2]==="multiple",repeated:t[2]==="repeated"}}),e};class P{constructor(e,s={}){A(this,"items",new Map);A(this,"eventAbortController");A(this,"onInput",e=>{if(e instanceof CustomEvent&&e.type==="input"&&!e.isTrusted&&!e.bubbles)return;const s=e.target,t=this.items.get(s),n="inputType"in e&&e.inputType.startsWith("delete"),o=t.isEager(),m=n&&o&&t.unmasked(s.value)===""?"":s.value;this.fixCursor(s,n,()=>this.setValue(s,m))});this.options=s,this.eventAbortController=new AbortController,this.init(this.getInputs(e))}update(e={}){this.options={...e},this.init(Array.from(this.items.keys()))}updateValue(e){e.value!==""&&e.value!==this.processInput(e).masked&&this.setValue(e,e.value)}destroy(){this.eventAbortController.abort(),this.items.clear()}init(e){const s=this.getOptions(this.options);for(const t of e){if(!this.items.has(t)){const{signal:o}=this.eventAbortController;t.addEventListener("input",this.onInput,{capture:!0,signal:o})}const n=new S(x(t,s));this.items.set(t,n),queueMicrotask(()=>this.updateValue(t)),t.selectionStart===null&&n.isEager()&&console.warn("Maska: input of `%s` type is not supported",t.type)}}getInputs(e){return typeof e=="string"?Array.from(document.querySelectorAll(e)):"length"in e?Array.from(e):[e]}getOptions(e){const{onMaska:s,preProcess:t,postProcess:n,...o}=e;return o}fixCursor(e,s,t){const n=e.selectionStart,o=e.value;if(t(),n===null||n===o.length&&!s)return;const m=e.value,h=o.slice(0,n),l=m.slice(0,n),i=this.processInput(e,h).unmasked,f=this.processInput(e,l).unmasked;let c=n;h!==l&&(c+=s?m.length-o.length:i.length-f.length),e.setSelectionRange(c,c)}setValue(e,s){const t=this.processInput(e,s);e.value=t.masked,this.options.onMaska!=null&&(Array.isArray(this.options.onMaska)?this.options.onMaska.forEach(n=>n(t)):this.options.onMaska(t)),e.dispatchEvent(new CustomEvent("maska",{detail:t})),e.dispatchEvent(new CustomEvent("input",{detail:t.masked}))}processInput(e,s){const t=this.items.get(e);let n=s??e.value;this.options.preProcess!=null&&(n=this.options.preProcess(n));let o=t.masked(n);return this.options.postProcess!=null&&(o=this.options.postProcess(o)),{masked:o,unmasked:t.unmasked(n),completed:t.completed(n)}}}const R=new WeakMap,F=a=>{a.directive("maska",(e,s,t)=>{const n=e instanceof HTMLInputElement?e:e.querySelector("input");if(n==null||(n==null?void 0:n.type)==="file")return;let o={};const m=s.expression!==""?t.evaluateLater(s.expression):()=>{};t.effect(()=>{var h;if(m(l=>{o=typeof l=="string"?{mask:l}:{...l}}),s.value!=null){const l=i=>{const f=s.modifiers.includes("unmasked")?i.unmasked:s.modifiers.includes("completed")?i.completed:i.masked,c=t.Alpine.$data(n);s.value in c&&(c[s.value]=f)};o.onMaska=o.onMaska==null?l:Array.isArray(o.onMaska)?[...o.onMaska,l]:[o.onMaska,l]}R.has(n)?(h=R.get(n))==null||h.update(o):R.set(n,new P(n,o))})}).before("model")};document.addEventListener("alpine:init",()=>{window.Alpine.plugin(F)}),u.Mask=S,u.MaskInput=P,u.tokens=p,u.xMaska=F,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})});