UNPKG

metro4

Version:

The front-end framework for Build responsive, mobile-first projects on the web with the first front-end component library in Metro Style

211 lines (181 loc) 7.6 kB
/* global Metro */ (function(Metro, $) { 'use strict'; var Utils = Metro.utils; var InputMaskDefaultConfig = { maskPattern: ".", mask: null, maskPlaceholder: "_", maskEditableStart: 0, thresholdInterval: 300, onChar: Metro.noop, onInputMaskCreate: Metro.noop }; Metro.inputMaskSetup = function (options) { InputMaskDefaultConfig = $.extend({}, InputMaskDefaultConfig, options); }; if (typeof window["metroInputMaskSetup"] !== undefined) { Metro.inputMaskSetup(window["metroInputMaskSetup"]); } Metro.Component('input-mask', { init: function( options, elem ) { if ($.device) { if (elem.setAttribute) elem.setAttribute("placeholder", options.mask); console.warn("The component input-mask can't be initialized, because you run it on a mobile device!"); return ; } this._super(elem, options, InputMaskDefaultConfig, { // define instance vars here pattern: null, mask: "", maskArray: [], placeholder: "", length: 0, thresholdTimer: null, id: Utils.elementId("input-mask") }); return this; }, _create: function(){ this._createStructure(); this._createEvents(); this._fireEvent('input-mask-create'); }, _createStructure: function(){ var o = this.options; if (!o.mask) { throw new Error('You must provide a pattern for masked input.') } if (typeof o.maskPlaceholder !== 'string' || o.maskPlaceholder.length > 1) { throw new Error('Mask placeholder should be a single character or an empty string.') } this.placeholder = o.maskPlaceholder; this.mask = (""+o.mask); this.maskArray = this.mask.split(""); this.pattern = new RegExp("^"+o.maskPattern+"+$"); this.length = this.mask.length; this._showValue(); }, _createEvents: function(){ var that = this, element = this.element, o = this.options; var editableStart = o.maskEditableStart; var id = this.id; var checkEditablePosition = function(pos){ if (pos < editableStart) { setPosition(editableStart); return false; } return true; } var checkEditableChar = function(pos){ return pos < that.mask.length && that.mask.charAt(pos) === that.placeholder; } var findNextEditablePosition = function (pos){ var i, a = that.maskArray; for (i = pos; i <= a.length; i++) { if (a[i] === that.placeholder) { return i; } } return pos; } var setPosition = function(pos){ that.elem.setSelectionRange(pos, pos); } var clearThresholdInterval = function(){ clearInterval(that.thresholdTimer); that.thresholdTimer = null; } element.on("change", function(){ if (this.value === "") { this.value = that.mask; setPosition(editableStart); } }, {ns: id}); element.on("focus click", function(){ checkEditablePosition(this.selectionStart); setPosition(findNextEditablePosition(this.selectionStart)); }, {ns: id}); element.on("keydown", function(e){ var pos = this.selectionStart; var val = this.value; var code = e.code, key = e.key; if (code === "ArrowRight" || code === "End") { return true; } else { if (pos >= that.length && (["Backspace", "Home", "ArrowLeft", "ArrowUp"].indexOf(code) === -1)) { // Don't move over mask length e.preventDefault(); } else if (code === "Home" || code === "ArrowUp") { // Goto editable start position e.preventDefault(); setPosition(editableStart); } else if (code === "ArrowLeft") { if (pos - 1 < editableStart) { // Don't move behind a editable start position e.preventDefault(); } } else if (code === "Backspace") { e.preventDefault(); if (pos - 1 >= editableStart) { if (checkEditableChar(pos - 1)) { if (this.value.charAt(pos - 1) !== that.placeholder) { // Replace char if it is not a mask placeholder this.value = val.substr(0, pos - 1) + that.placeholder + val.substr(pos); } } // Move to prev char position setPosition(pos - 1); } } else if (code === "Space") { e.preventDefault(); setPosition(pos + 1); } else if (!that.pattern.test(key)) { e.preventDefault(); } else { e.preventDefault(); if (checkEditableChar(pos)) { this.value = val.substr(0, pos) + (o.onChar === Metro.noop ? key : Utils.exec(o.onChar, [key], this)) + val.substr(pos + 1); setPosition(findNextEditablePosition(pos + 1)); } } } }, {ns: id}); element.on("keyup", function(){ var el = this; clearThresholdInterval(); that.thresholdTimer = setInterval(function(){ clearThresholdInterval(); setPosition(findNextEditablePosition(el.selectionStart)); }, o.thresholdInterval) }, {ns: id}); }, _showValue: function(){ var that = this, elem = this.elem; var a = new Array(this.length); var val; if (!elem.value) { elem.value = this.mask; } else { val = elem.value; $.each(this.maskArray, function(i, v){ if (val[i] !== v && !that.pattern.test(val[i])) { a[i] = that.placeholder; } else { a[i] = val[i]; } }); this.elem.value = a.join(""); } }, destroy: function(){ var element = this.element, id = this.id; element.off("change", {ns: id}); element.off("focus", {ns: id}); element.off("click", {ns: id}); element.off("keydown", {ns: id}); element.off("keyup", {ns: id}); return element; } }); }(Metro, m4q));