import { iphone, mobile } from "./environment"; import { EventHandlers } from "./eventhandlers"; import { EventRuler } from "./eventruler"; import { applyInputValue, clearOptionalTail, writeBuffer } from "./inputHandling"; import { caret, getBuffer, getBufferTemplate, getLastValidPosition, resetMaskSet, seekNext } from "./positioning"; import { isComplete } from "./validation"; export { mask }; // todo put on the prototype? function mask() { const inputmask = this, opts = this.opts, el = this.el, $ = this.dependencyLib; function isElementTypeSupported(input, opts) { function patchValueProperty(npt) { let valueGet, valueSet; function patchValhook(type) { if ( $.valHooks && ($.valHooks[type] === undefined || $.valHooks[type].inputmaskpatch !== true) ) { const valhookGet = $.valHooks[type] && $.valHooks[type].get ? $.valHooks[type].get : function (elem) { return elem.value; }, valhookSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function (elem, value) { elem.value = value; return elem; }; $.valHooks[type] = { get: function (elem) { if (elem.inputmask) { if (elem.inputmask.opts.autoUnmask) { return elem.inputmask.unmaskedvalue(); } else { const result = valhookGet(elem); return getLastValidPosition.call( inputmask, undefined, undefined, elem.inputmask.maskset.validPositions ) !== -1 || opts.nullable !== true ? result : ""; } } else { return valhookGet(elem); } }, set: function (elem, value) { const result = valhookSet(elem, value); if (elem.inputmask) { applyInputValue(elem, value); } return result; }, inputmaskpatch: true }; } } function getter() { if (this.inputmask) { return this.inputmask.opts.autoUnmask ? this.inputmask.unmaskedvalue() : getLastValidPosition.call(inputmask) !== -1 || opts.nullable !== true ? (this.inputmask.shadowRoot || this.ownerDocument) .activeElement === this && opts.clearMaskOnLostFocus ? (inputmask.isRTL ? clearOptionalTail .call(inputmask, getBuffer.call(inputmask).slice()) .reverse() : clearOptionalTail.call( inputmask, getBuffer.call(inputmask).slice() ) ).join("") : valueGet.call(this) : ""; } else { return valueGet.call(this); } } function setter(value) { valueSet.call(this, value); if (this.inputmask) { applyInputValue(this, value); } } function installNativeValueSetFallback(npt) { EventRuler.on(npt, "mouseenter", function () { const input = this, value = input.inputmask._valueGet(true), bufferValue = ( input.inputmask.isRTL ? getBuffer.call(input.inputmask).slice().reverse() : getBuffer.call(input.inputmask) ).join(""); if (value != bufferValue) { applyInputValue(input, value); } }); } if (!npt.inputmask.__valueGet) { if (opts.noValuePatching !== true) { if (Object.getOwnPropertyDescriptor) { const valueProperty = Object.getPrototypeOf ? Object.getOwnPropertyDescriptor( Object.getPrototypeOf(npt), "value" ) : undefined; if (valueProperty && valueProperty.get && valueProperty.set) { valueGet = valueProperty.get; valueSet = valueProperty.set; Object.defineProperty(npt, "value", { get: getter, set: setter, configurable: true }); } else if (npt.tagName.toLowerCase() !== "input") { valueGet = function () { return this.textContent; }; valueSet = function (value) { this.textContent = value; }; Object.defineProperty(npt, "value", { get: getter, set: setter, configurable: true }); } } else if ( document.__lookupGetter__ && npt.__lookupGetter__("value") ) { valueGet = npt.__lookupGetter__("value"); valueSet = npt.__lookupSetter__("value"); npt.__defineGetter__("value", getter); npt.__defineSetter__("value", setter); } npt.inputmask.__valueGet = valueGet; // store native property getter npt.inputmask.__valueSet = valueSet; // store native property setter } npt.inputmask._valueGet = function (overruleRTL) { return inputmask.isRTL && overruleRTL !== true ? valueGet.call(this.el).split("").reverse().join("") : valueGet.call(this.el); }; npt.inputmask._valueSet = function (value, overruleRTL) { // null check is needed for IE8 => otherwise converts to "null" valueSet.call( this.el, value === null || value === undefined ? "" : overruleRTL !== true && inputmask.isRTL ? value.split("").reverse().join("") : value ); }; if (valueGet === undefined) { // jquery.val fallback valueGet = function () { return this.value; }; valueSet = function (value) { this.value = value; }; patchValhook(npt.type); installNativeValueSetFallback(npt); } } } // if (input.tagName.toLowerCase() !== "textarea") { // opts.ignorables.push(keys.Enter); // } let elementType = input.getAttribute("type"), isSupported = (input.tagName.toLowerCase() === "input" && opts.supportsInputType.includes(elementType)) || input.isContentEditable || input.tagName.toLowerCase() === "textarea"; if (!isSupported) { if (input.tagName.toLowerCase() === "input") { let el = document.createElement("input"); el.setAttribute("type", elementType); isSupported = el.type === "text"; // apply mask only if the type is not natively supported el = null; } else { isSupported = "partial"; } } if (isSupported !== false) { patchValueProperty(input); } else { input.inputmask = undefined; } return isSupported; } // unbind all events - to make sure that no other mask will interfere when re-masking EventRuler.off(el); const isSupported = isElementTypeSupported(el, opts); if (isSupported !== false) { inputmask.originalPlaceholder = el.placeholder; // read maxlength prop from el inputmask.maxLength = el !== undefined ? el.maxLength : undefined; if (inputmask.maxLength === -1) inputmask.maxLength = undefined; if ("inputMode" in el && el.getAttribute("inputmode") === null) { el.inputMode = opts.inputmode; el.setAttribute("inputmode", opts.inputmode); } if (isSupported === true) { opts.showMaskOnFocus = opts.showMaskOnFocus && ["cc-number", "cc-exp"].indexOf(el.autocomplete) === -1; if (iphone) { // selecting the caret shows as a selection on iphone opts.insertModeVisual = false; // disable autocorrect el.setAttribute("autocorrect", "off"); } // bind events EventRuler.on(el, "submit", EventHandlers.submitEvent); EventRuler.on(el, "reset", EventHandlers.resetEvent); EventRuler.on(el, "blur", EventHandlers.blurEvent); EventRuler.on(el, "focus", EventHandlers.focusEvent); EventRuler.on(el, "invalid", EventHandlers.invalidEvent); EventRuler.on(el, "click", EventHandlers.clickEvent); EventRuler.on(el, "mouseleave", EventHandlers.mouseleaveEvent); EventRuler.on(el, "mouseenter", EventHandlers.mouseenterEvent); EventRuler.on(el, "paste", EventHandlers.pasteEvent); EventRuler.on(el, "cut", EventHandlers.cutEvent); EventRuler.on(el, "complete", opts.oncomplete); EventRuler.on(el, "incomplete", opts.onincomplete); EventRuler.on(el, "cleared", opts.oncleared); if (opts.inputEventOnly !== true) { EventRuler.on(el, "keydown", EventHandlers.keyEvent); } if (mobile || opts.inputEventOnly) { el.removeAttribute("maxLength"); } EventRuler.on(el, "input", EventHandlers.inputFallBackEvent); // EventRuler.on(el, "beforeinput", EventHandlers.beforeInputEvent); //https://github.com/w3c/input-events - to implement } EventRuler.on(el, "setvalue", EventHandlers.setValueEvent); // apply mask inputmask.applyMaskHook === undefined || inputmask.applyMaskHook(); getBufferTemplate.call(inputmask).join(""); // initialize the buffer and getmasklength inputmask.undoValue = inputmask._valueGet(true); const activeElement = (el.inputmask.shadowRoot || el.ownerDocument) .activeElement; if ( el.inputmask._valueGet(true) !== "" || opts.clearMaskOnLostFocus === false || activeElement === el ) { applyInputValue(el, el.inputmask._valueGet(true), opts); let buffer = getBuffer.call(inputmask).slice(); if (isComplete.call(inputmask, buffer) === false) { if (opts.clearIncomplete) { resetMaskSet.call(inputmask, false); } } if (opts.clearMaskOnLostFocus && activeElement !== el) { if (getLastValidPosition.call(inputmask) === -1) { buffer = []; } else { clearOptionalTail.call(inputmask, buffer); } } if ( opts.clearMaskOnLostFocus === false || (opts.showMaskOnFocus && activeElement === el) || el.inputmask._valueGet(true) !== "" ) { writeBuffer(el, buffer); } if (activeElement === el) { // position the caret when in focus caret.call( inputmask, el, seekNext.call(inputmask, getLastValidPosition.call(inputmask)) ); } } } }