/* * Input Mask Core * http://github.com/RobinHerbots/jquery.inputmask * Copyright (c) Robin Herbots * Licensed under the MIT license */ import defaults from "./defaults"; import definitions from "./definitions"; import $ from "./dependencyLibs/inputmask.dependencyLib"; import { EventRuler } from "./eventruler"; import window from "./global/window"; import { checkVal, unmaskedvalue } from "./inputHandling"; import { mask } from "./mask"; import { generateMaskSet, analyseMask } from "./mask-lexer"; import { determineLastRequiredPosition, getBuffer, getBufferTemplate, isMask } from "./positioning"; import { isComplete } from "./validation"; import { getMaskTemplate } from "./validation-tests"; const document = window.document, dataKey = "_inputmask_opts"; function Inputmask(alias, options, internal) { // allow instanciating without new if (!(this instanceof Inputmask)) { return new Inputmask(alias, options, internal); } this.dependencyLib = $; this.el = undefined; this.events = {}; this.maskset = undefined; if (internal !== true) { // init options if (Object.prototype.toString.call(alias) === "[object Object]") { options = alias; } else { options = options || {}; if (alias) options.alias = alias; } this.opts = $.extend(true, {}, this.defaults, options); this.noMasksCache = options && options.definitions !== undefined; this.userOptions = options || {}; // user passed options resolveAlias(this.opts.alias, options, this.opts); } // maskscope properties this.refreshValue = false; // indicate a refresh from the inputvalue is needed (form.reset) this.undoValue = undefined; this.$el = undefined; this.skipInputEvent = false; // skip when triggered from within inputmask this.validationEvent = false; this.ignorable = false; // eslint-disable-next-line no-unused-expressions this.maxLength; this.mouseEnter = false; this.clicked = 0; this.originalPlaceholder = undefined; // needed for FF this.isComposing = false; // keydowncode == 229 compositionevent fallback this.hasAlternator = false; } Inputmask.prototype = { dataAttribute: "data-inputmask", // data attribute prefix used for attribute binding // options default defaults, definitions, aliases: {}, // aliases definitions masksCache: {}, i18n: {}, get isRTL() { return this.opts.isRTL || this.opts.numericInput; }, mask: function (elems) { const that = this; if (typeof elems === "string") { elems = document.getElementById(elems) || document.querySelectorAll(elems); } elems = elems.nodeName ? [elems] : Array.isArray(elems) ? elems : [].slice.call(elems); // [].slice as alternate for Array.from (Yandex browser) elems.forEach(function (el, ndx) { const scopedOpts = $.extend(true, {}, that.opts); if ( importAttributeOptions( el, scopedOpts, $.extend(true, {}, that.userOptions), that.dataAttribute ) ) { const maskset = generateMaskSet(scopedOpts, that.noMasksCache); if (maskset !== undefined) { if (el.inputmask !== undefined) { el.inputmask.opts.autoUnmask = true; // force autounmasking when remasking el.inputmask.remove(); } // store inputmask instance on the input with element reference el.inputmask = new Inputmask(undefined, undefined, true); el.inputmask.opts = scopedOpts; el.inputmask.noMasksCache = that.noMasksCache; el.inputmask.userOptions = $.extend(true, {}, that.userOptions); // el.inputmask.isRTL = scopedOpts.isRTL || scopedOpts.numericInput; el.inputmask.el = el; el.inputmask.$el = $(el); el.inputmask.maskset = maskset; $.data(el, dataKey, that.userOptions); mask.call(el.inputmask); } } }); return elems && elems[0] ? elems[0].inputmask || this : this; }, option: function (options, noremask) { // set extra options || retrieve value of a current option if (typeof options === "string") { return this.opts[options]; } else if (typeof options === "object") { $.extend(this.userOptions, options); // user passed options // remask if (this.el && noremask !== true) { this.mask(this.el); } return this; } }, unmaskedvalue: function (value) { this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache); if (this.el === undefined || value !== undefined) { const valueBuffer = ( typeof this.opts.onBeforeMask === "function" ? this.opts.onBeforeMask.call(this, value, this.opts) || value : value ).split(""); checkVal.call(this, undefined, false, false, valueBuffer); if (typeof this.opts.onBeforeWrite === "function") this.opts.onBeforeWrite.call( this, undefined, getBuffer.call(this), 0, this.opts ); } return unmaskedvalue.call(this, this.el); }, remove: function () { if (this.el) { $.data(this.el, dataKey, null); // invalidate // writeout the value const cv = this.opts.autoUnmask ? unmaskedvalue(this.el) : this._valueGet(this.opts.autoUnmask); if (cv !== getBufferTemplate.call(this).join("")) this._valueSet(cv, this.opts.autoUnmask); else this._valueSet(""); // unbind all events EventRuler.off(this.el); // restore the value property let valueProperty; if (Object.getOwnPropertyDescriptor && Object.getPrototypeOf) { valueProperty = Object.getOwnPropertyDescriptor( Object.getPrototypeOf(this.el), "value" ); if (valueProperty) { if (this.__valueGet) { Object.defineProperty(this.el, "value", { get: this.__valueGet, set: this.__valueSet, configurable: true }); } } } else if ( document.__lookupGetter__ && this.el.__lookupGetter__("value") ) { if (this.__valueGet) { this.el.__defineGetter__("value", this.__valueGet); this.el.__defineSetter__("value", this.__valueSet); } } // clear data this.el.inputmask = undefined; } return this.el; }, getemptymask: function () { // return the default (empty) mask value, usefull for setting the default value in validation this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache); return ( this.isRTL ? getBufferTemplate.call(this).reverse() : getBufferTemplate.call(this) ).join(""); }, hasMaskedValue: function () { // check wheter the returned value is masked or not; currently only works reliable when using jquery.val fn to retrieve the value return !this.opts.autoUnmask; }, isComplete: function () { this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache); return isComplete.call(this, getBuffer.call(this)); }, getmetadata: function () { // return mask metadata if exists this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache); if (Array.isArray(this.maskset.metadata)) { let maskTarget = getMaskTemplate.call(this, true, 0, false).join(""); this.maskset.metadata.forEach(function (mtdt) { if (mtdt.mask === maskTarget) { maskTarget = mtdt; return false; } return true; }); return maskTarget; } return this.maskset.metadata; }, isValid: function (value) { this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache); if (value) { const valueBuffer = ( typeof this.opts.onBeforeMask === "function" ? this.opts.onBeforeMask.call(this, value, this.opts) || value : value ).split(""); checkVal.call(this, undefined, true, false, valueBuffer); } else { value = this.isRTL ? getBuffer.call(this).slice().reverse().join("") : getBuffer.call(this).join(""); } let buffer = getBuffer.call(this), rl = determineLastRequiredPosition.call(this), lmib = buffer.length - 1; for (; lmib > rl; lmib--) { if (isMask.call(this, lmib)) break; } buffer.splice(rl, lmib + 1 - rl); return ( isComplete.call(this, buffer) && value === (this.isRTL ? getBuffer.call(this).slice().reverse().join("") : getBuffer.call(this).join("")) ); }, format: function (value, metadata) { this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache); const valueBuffer = ( typeof this.opts.onBeforeMask === "function" ? this.opts.onBeforeMask.call(this, value, this.opts) || value : value ).split(""); checkVal.call(this, undefined, true, false, valueBuffer); const formattedValue = this.isRTL ? getBuffer.call(this).slice().reverse().join("") : getBuffer.call(this).join(""); return metadata ? { value: formattedValue, metadata: this.getmetadata() } : formattedValue; }, setValue: function (value) { if (this.el) { $(this.el).trigger("setvalue", [value]); } }, analyseMask }; function resolveAlias(aliasStr, options, opts) { const aliasDefinition = Inputmask.prototype.aliases[aliasStr]; if (aliasDefinition) { if (aliasDefinition.alias) resolveAlias(aliasDefinition.alias, undefined, opts); // alias is another alias $.extend(true, opts, aliasDefinition); // merge alias definition in the options $.extend(true, opts, options); // reapply extra given options return true; } // alias not found - try as mask else if (opts.mask === null) { opts.mask = aliasStr; } return false; } function importAttributeOptions(npt, opts, userOptions, dataAttribute) { function importOption(option, optionData) { const attrOption = dataAttribute === "" ? option : dataAttribute + "-" + option; optionData = optionData !== undefined ? optionData : npt.getAttribute(attrOption); if (optionData !== null) { if (typeof optionData === "string") { if (option.indexOf("on") === 0) { optionData = window[optionData]; } // get function definition else if (optionData === "false") { optionData = false; } else if (optionData === "true") optionData = true; } userOptions[option] = optionData; } } if (opts.importDataAttributes === true) { let attrOptions = npt.getAttribute(dataAttribute), option, dataoptions, optionData, p; if (attrOptions && attrOptions !== "") { attrOptions = attrOptions.replace(/'/g, '"'); dataoptions = JSON.parse("{" + attrOptions + "}"); } // resolve aliases if (dataoptions) { // pickup alias from dataAttribute optionData = undefined; for (p in dataoptions) { if (p.toLowerCase() === "alias") { optionData = dataoptions[p]; break; } } } importOption("alias", optionData); // pickup alias from dataAttribute-alias if (userOptions.alias) { resolveAlias(userOptions.alias, userOptions, opts); } for (option in opts) { if (dataoptions) { optionData = undefined; for (p in dataoptions) { if (p.toLowerCase() === option.toLowerCase()) { optionData = dataoptions[p]; break; } } } importOption(option, optionData); } } $.extend(true, opts, userOptions); // handle dir=rtl if (npt.dir === "rtl" || opts.rightAlign) { npt.style.textAlign = "right"; } if (npt.dir === "rtl" || opts.numericInput) { npt.dir = "ltr"; npt.removeAttribute("dir"); opts.isRTL = true; } return Object.keys(userOptions).length; } // apply defaults, definitions, aliases Inputmask.extendDefaults = function (options) { $.extend(true, Inputmask.prototype.defaults, options); }; Inputmask.extendDefinitions = function (definition) { $.extend(true, Inputmask.prototype.definitions, definition); }; Inputmask.extendAliases = function (alias) { $.extend(true, Inputmask.prototype.aliases, alias); }; // static fn on inputmask Inputmask.format = function (value, options, metadata) { return Inputmask(options).format(value, metadata); }; Inputmask.unmask = function (value, options) { return Inputmask(options).unmaskedvalue(value); }; Inputmask.isValid = function (value, options) { return Inputmask(options).isValid(value); }; Inputmask.remove = function (elems) { if (typeof elems === "string") { elems = document.getElementById(elems) || document.querySelectorAll(elems); } elems = elems.nodeName ? [elems] : elems; elems.forEach(function (el) { if (el.inputmask) el.inputmask.remove(); }); }; Inputmask.setValue = function (elems, value) { if (typeof elems === "string") { elems = document.getElementById(elems) || document.querySelectorAll(elems); } elems = elems.nodeName ? [elems] : elems; elems.forEach(function (el) { if (el.inputmask) el.inputmask.setValue(value); else $(el).trigger("setvalue", [value]); }); }; Inputmask.dependencyLib = $; // make inputmask available window.Inputmask = Inputmask; export default Inputmask;