kit.PluginManager.register('mask', function(module, exports, require){ var $ = require('jquery'); var ua = navigator.userAgent, iPhone = /iphone/i.test(ua), chrome = /chrome/i.test(ua), android = /android/i.test(ua), caretTimeoutId; $.mask = { //Predefined character definitions definitions: { '9': "[0-9]", 'a': "[A-Za-z]", '*': "[A-Za-z0-9]" }, autoclear: false, dataName: "rawMaskFn", placeholder: '_' }; $.fn.extend({ //Helper Function for Caret positioning caret: function(begin, end) { var range; if (this.length === 0 || this.is(":hidden") || this.get(0) !== document.activeElement) { return; } if (typeof begin == 'number') { end = (typeof end === 'number') ? end : begin; return this.each(function() { if (this.setSelectionRange) { this.setSelectionRange(begin, end); } else if (this.createTextRange) { range = this.createTextRange(); range.collapse(true); range.moveEnd('character', end); range.moveStart('character', begin); range.select(); } }); } else { if (this[0].setSelectionRange) { begin = this[0].selectionStart; end = this[0].selectionEnd; } else if (document.selection && document.selection.createRange) { range = document.selection.createRange(); begin = 0 - range.duplicate().moveStart('character', -100000); end = begin + range.text.length; } return { begin: begin, end: end }; } }, unmask: function() { return this.trigger("unmask"); }, mask: function(mask, settings) { var triggerEvent = function (el, eventName, options) { var event; if (window.CustomEvent) { event = new CustomEvent(eventName, options); } else { event = document.createEvent('CustomEvent'); event.initCustomEvent(eventName, true, true, options); } el.dispatchEvent(event); }; var input, defs, tests, partialPosition, firstNonMaskPos, lastRequiredNonMaskPos, len, oldVal, actualCaretID; if (!mask && this.length > 0) { input = $(this[0]); var fn = input.data($.mask.dataName); return fn?fn():undefined; } settings = $.extend({ autoclear: $.mask.autoclear, placeholder: $.mask.placeholder, // Load default placeholder completed: null }, settings); defs = $.mask.definitions; tests = []; partialPosition = len = mask.length; firstNonMaskPos = null; mask = String(mask); $.each(mask.split(""), function(i, c) { if (c == '?') { len--; partialPosition = i; } else if (defs[c]) { tests.push(new RegExp(defs[c])); if (firstNonMaskPos === null) { firstNonMaskPos = tests.length - 1; } if(i < partialPosition){ lastRequiredNonMaskPos = tests.length - 1; } } else { tests.push(null); } }); return this.trigger("unmask").each(function() { var input = $(this), buffer = $.map( mask.split(""), function(c, i) { if (c != '?') { return defs[c] ? getPlaceholder(i) : c; } }), defaultBuffer = buffer.join(''), isInput = input.get(0).tagName === 'INPUT', focusText = isInput?input.val():input.text(); function tryFireCompleted(){ if (!settings.completed) { return; } for (var i = firstNonMaskPos; i <= lastRequiredNonMaskPos; i++) { if (tests[i] && buffer[i] === getPlaceholder(i)) { return; } } settings.completed.call(input); } function getPlaceholder(i){ if(i < settings.placeholder.length) { return settings.placeholder.charAt(i); } return settings.placeholder.charAt(0); } function seekNext(pos) { while (++pos < len && !tests[pos]); return pos; } function seekPrev(pos) { while (--pos >= 0 && !tests[pos]); return pos; } function shiftL(begin,end) { var i, j; if (begin<0) { return; } for (i = begin, j = seekNext(end); i < len; i++) { if (tests[i]) { if (j < len && tests[i].test(buffer[j])) { buffer[i] = buffer[j]; buffer[j] = getPlaceholder(j); } else { break; } j = seekNext(j); } } writeBuffer(); input.caret(Math.max(firstNonMaskPos, begin)); } function shiftR(pos) { var i, c, j, t; for (i = pos, c = getPlaceholder(pos); i < len; i++) { if (tests[i]) { j = seekNext(i); t = buffer[i]; buffer[i] = c; if (j < len && tests[j].test(t)) { c = t; } else { break; } } } } function androidInputEvent(e) { var curVal = input.val(); var pos = input.caret(); if (oldVal && oldVal.length && oldVal.length > curVal.length ) { // a deletion or backspace happened checkVal(true); while (pos.begin > 0 && !tests[pos.begin-1]) pos.begin--; if (pos.begin === 0) { while (pos.begin < firstNonMaskPos && !tests[pos.begin]) pos.begin++; } input.caret(pos.begin,pos.begin); } else { var pos2 = checkVal(true); var lastEnteredValue = curVal.charAt(pos.begin); if (pos.begin < len){ if(!tests[pos.begin]){ pos.begin++; if(tests[pos.begin].test(lastEnteredValue)){ pos.begin++; } }else{ if(tests[pos.begin].test(lastEnteredValue)){ pos.begin++; } } } input.caret(pos.begin,pos.begin); } tryFireCompleted(); } function blurEvent(e) { clearInterval(actualCaretID); checkVal(); if (input.val() != focusText) input.change(); } var inputCancel = false; function keydownEvent(e) { if (input.prop("readonly")){ return; } var k = e.which || e.keyCode, pos, begin, end; oldVal = input.val(); //backspace, delete, and escape get special treatment if (k === 8 || k === 46 || (iPhone && k === 127)) { pos = input.caret(); begin = pos.begin; end = pos.end; if (end - begin === 0) { begin=k!==46?seekPrev(begin):(end=seekNext(begin-1)); end=k===46?seekNext(end):end; } clearBuffer(begin, end); shiftL(begin, end - 1); e.preventDefault(); inputCancel = true; triggerEvent(input[0], 'input', { 'bubbles': true }); } else if( k === 13 ) { // enter blurEvent.call(this, e); } else if (k === 27) { // escape input.val(focusText); input.caret(0, checkVal()); e.preventDefault(); } } function keypressEvent(e) { if (input.prop("readonly")){ return; } var k = e.which || e.keyCode, pos = input.caret(), p, c, next; if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore return; } else if ( k && k !== 13 ) { if (pos.end - pos.begin !== 0){ clearBuffer(pos.begin, pos.end); shiftL(pos.begin, pos.end-1); } p = seekNext(pos.begin - 1); if (p < len) { c = String.fromCharCode(k); if (tests[p].test(c)) { shiftR(p); buffer[p] = c; writeBuffer(); next = seekNext(p); if(android){ //Path for CSP Violation on FireFox OS 1.1 var proxy = function() { $.proxy($.fn.caret,input,next)(); }; setTimeout(proxy,0); }else{ input.caret(next); } if(pos.begin <= lastRequiredNonMaskPos){ tryFireCompleted(); } } } e.preventDefault(); inputCancel = true; triggerEvent(input[0], 'input', { 'bubbles': true }); } } function clearBuffer(start, end) { var i; for (i = start; i < end && i < len; i++) { if (tests[i]) { buffer[i] = getPlaceholder(i); } } } function writeBuffer() { if(isInput) { input.val(buffer.join('')); } else { input.text(buffer.join('')); } } function isValid() { var i; var wordLength = 0; for (i = 0; i < tests.length; i++) { if (tests[i]) { wordLength++; } } return (wordLength === getRaw().length); } function checkVal(allow) { //try to place characters where they belong var test = '', lastMatch = -1, i, c, pos; if(isInput) { test = input.val(); } else { test = input.text(); } for (i = 0, pos = 0; i < len; i++) { if (tests[i]) { buffer[i] = getPlaceholder(i); while (pos++ < test.length) { c = test.charAt(pos - 1); if (tests[i].test(c)) { buffer[i] = c; lastMatch = i; break; } } if (pos > test.length) { clearBuffer(i + 1, len); break; } } else { if (buffer[i] === test.charAt(pos)) { pos++; } if( i < partialPosition){ lastMatch = i; } } } if (allow) { writeBuffer(); } else if (lastMatch + 1 < partialPosition) { if (settings.autoclear || buffer.join('') === defaultBuffer) { // Invalid value. Remove it and replace it with the // mask, which is the default behavior. if(isInput) { if(input.val()) input.val(""); } else { if(input.text()) input.text(""); } clearBuffer(0, len); } else { // Invalid value, but we opt to show the value to the // user and allow them to correct their mistake. writeBuffer(); } } else { writeBuffer(); if(isInput) { input.val(input.val().substring(0, lastMatch + 1)); } else { input.text(input.text().substring(0, lastMatch + 1)); } } return (partialPosition ? i : firstNonMaskPos); } function getRaw() { checkVal(); return $.map(buffer, function(c, i) { return tests[i]&&c!=getPlaceholder(i) ? c : null; }).join(''); } input.data($.mask.dataName,function(){ return getRaw(); }); input.data('maskCall', function(){ }); input.data('isValidMask', function(){ return isValid(); }); function focusEvent () { if (input.prop("readonly")){ return; } actualCaretID = setInterval(actualCaret, 100); clearTimeout(caretTimeoutId); var pos; focusText = input.val(); pos = checkVal(); caretTimeoutId = setTimeout(function(){ if(input.get(0) !== document.activeElement){ return; } writeBuffer(); if (pos == mask.replace("?","").length) { //input.caret(0, pos); input.caret(pos); } else { input.caret(pos); } }, 10); } function unmask() { input .off(".mask") .removeData($.mask.dataName); if(isInput) { input.removeData('maskCall'); } delete input[0]['mask']; } function actualCaret(){ var pos = checkVal(true), c = input.caret(); if( c.begin === c.end && c.begin > pos ) { input.caret(pos); } } input .one("unmask", unmask); isInput && input .on("focus.mask", focusEvent) .on("blur.mask", blurEvent) .on("keydown.mask", keydownEvent) .on("keypress.mask", keypressEvent); input .on("input.mask paste.mask", function() { if(inputCancel) { inputCancel = false; return; } if (input.prop("readonly")){ return; } setTimeout(function() { var pos=checkVal(true); input.caret(pos); tryFireCompleted(); }, 0); }); isInput && input .on("keyup.mask", function() { if (input.prop("readonly")){ return; } try { input.data('maskCall')(); } catch { } }); if (chrome && android) { input.off('input.mask'); isInput && input.on('input.mask', androidInputEvent); } checkVal(); //Perform initial check for existing values this.mask = { unmask: unmask, getRaw: getRaw, isValid: isValid, }; }); }, }); });