Browse Source

Merge remote-tracking branch 'original/master'

Ross Hadden 14 years ago
parent
commit
a53f387eb5
2 changed files with 186 additions and 183 deletions
  1. 44 35
      README
  2. 142 148
      jquery.inputmask.js

+ 44 - 35
README

@@ -6,48 +6,46 @@ A definition can have a cardinality and have multiple prevalidators.
 
 Example of some new definitions:
 
-                'd': { //day
-
-                    "validator": "0[1-9]|[12][0-9]|3[01]",
-
-                    "cardinality": 2,
-
-                    "prevalidator": [{ "validator": "[0-3]", "cardinality": 1}]
-
-                },
-
-                'm': { //month
-
-                    "validator": "0[1-9]|1[012]",
-
-                    "cardinality": 2,
-
-                    "prevalidator": [{ "validator": "[01]", "cardinality": 1}]
-
+                 'm': { //month
+                    validator: function(chrs, buffer) {
+                        var dayValue = buffer.join('').substr(0, 3);
+                        return $.inputmask.defaults.aliases['dd/mm/yyyy'].regex.month.test(dayValue + chrs);
+                    },
+                    cardinality: 2,
+                    prevalidator: [{ validator: "[01]", cardinality: 1}]
                 },
-
                 'y': { //year
-
-                    "validator": "(19|20)\\d\\d",
-
-                    "cardinality": 4,
-
-                    "prevalidator": [
-
-                        { "validator": "[12]", "cardinality": 1 },
-
-                        { "validator": "(19|20)", "cardinality": 2 },
-
-                        { "validator": "(19|20)\\d", "cardinality": 3 }
-
+                    validator: function(chrs, buffer) {
+                        if ($.inputmask.defaults.aliases['dd/mm/yyyy'].regex.year.test(chrs)) {
+                            var dayMonthValue = buffer.join('').substr(0, 6);
+                            if (dayMonthValue != "29/02/")
+                                return true;
+                            else {
+                                var year = parseInt(chrs);  //detect leap year
+                                if (year % 4 == 0)
+                                    if (year % 100 == 0)
+                                    if (year % 400 == 0)
+                                    return true;
+                                else return false;
+                                else return true;
+                                else return false;
+                            }
+                        } else return false;
+                    },
+                    cardinality: 4,
+                    prevalidator: [
+                        { validator: "[12]", cardinality: 1 },
+                        { validator: "(19|20)", cardinality: 2 },
+                        { validator: "(19|20)\\d", cardinality: 3 }
                         ]
-
                 }
+            },
+            insertMode: false
 
 These allow for a finer date validation then 99/99/9999 which also allows 33/33/3333 for example.  
 In the jquery.inputmask.extentions.js you find a full date input validation which takes days, months & leap years into account.
 
-Also extra features like mask-repetitions (greedy and non-gready) are included.  In the examples you will find more about them.
+Also extra features like mask-repetitions (greedy and non-gready) and many other additions are included.  In the examples you will find more about them.
 
 
 Usage:
@@ -56,6 +54,7 @@ Include the js-files
 
 <script src="jquery.js" type="text/javascript"></script>
 <script src="jquery.inputmask.js" type="text/javascript"></script>
+<script src="jquery.inputmask.extentions.js" type="text/javascript"></script>
 
 Define your masks:
 
@@ -79,10 +78,12 @@ $(document).ready(function(){
    $("#date").inputmask("d/m/y",{ "placeholder": "dd/mm/yyyy" });
 });
 
-execute a function when the mask is completed
+execute a function when the mask is completed, incomplete or cleared
 
 $(document).ready(function(){
    $("#date").inputmask("d/m/y",{ "oncomplete": function(){ alert('inputmask complete'); } });
+   $("#date").inputmask("d/m/y",{ "onincomplete": function(){ alert('inputmask incomplete'); } });
+   $("#date").inputmask("d/m/y",{ "oncleared": function(){ alert('inputmask cleared'); } });
 });
 
 mask repeat function
@@ -210,3 +211,11 @@ $(document).ready(function(){
    alert($("#test").inputmask("getemptymask"));    => "___-___" 
 });
 
+RTL input 
+
+Just add the dir="rtl" attribute to the input element
+
+<input id="test" dir="rtl" />
+
+-----------------------------------------
+

+ 142 - 148
jquery.inputmask.js

@@ -3,7 +3,7 @@ Input Mask plugin for jquery
 http://github.com/RobinHerbots/jquery.inputmask
 Copyright (c) 2010 Robin Herbots
 Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
-Version: 0.4.7
+Version: 0.5.0
  
 This plugin is based on the masked input plugin written by Josh Bush (digitalbush.com)
 */
@@ -48,7 +48,8 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                 },
                 keyCode: { ALT: 18, BACKSPACE: 8, CAPS_LOCK: 20, COMMA: 188, COMMAND: 91, COMMAND_LEFT: 91, COMMAND_RIGHT: 93, CONTROL: 17, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, INSERT: 45, LEFT: 37, MENU: 93, NUMPAD_ADD: 107, NUMPAD_DECIMAL: 110, NUMPAD_DIVIDE: 111, NUMPAD_ENTER: 108,
                     NUMPAD_MULTIPLY: 106, NUMPAD_SUBTRACT: 109, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SHIFT: 16, SPACE: 32, TAB: 9, UP: 38, WINDOWS: 91
-                }
+                },
+                ignorables: [8, 9, 13, 16, 17, 18, 20, 27, 33, 34, 35, 36, 37, 38, 39, 40, 46, 91, 93, 108]
             },
             val: $.fn.val //store the original jquery val function
         };
@@ -58,7 +59,13 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
             var pasteEventName = $.browser.msie ? 'paste.inputmask' : 'input.inputmask';
 
             var iphone = navigator.userAgent.match(/iphone/i) != null;
-            var android = navigator.userAgent.match(/android/i) != null;
+            var android = navigator.userAgent.match(/android.*mobile safari.*/i) != null;
+            if (android) {
+                var browser = navigator.userAgent.match(/mobile safari.*/i);
+                var version = parseInt(new RegExp(/[0-9]+/).exec(browser));
+                android = version <= 533;
+            }
+            var caretposCorrection = null;
 
             var _val = $.inputmask.val;
             if (opts.patch_val && $.fn.val.inputmaskpatch != true) {
@@ -285,14 +292,15 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
             }
             //pos: from position
             function seekPrevious(buffer, pos) {
-                if (pos <= 0) return 0;
                 var position = pos;
+                if (position <= 0) return 0;
+
                 while (--position > 0 && !isMask(position)) { };
                 return position;
             }
-            //these are needed to handle the non-greedy mask repetitions
+
             function setBufferElement(buffer, position, element) {
-                prepareBuffer(buffer, position);
+                //position = prepareBuffer(buffer, position);
 
                 var test = tests[determineTestPosition(position)];
                 var elem = element;
@@ -308,26 +316,38 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                 buffer[position] = elem;
             }
             function getBufferElement(buffer, position) {
-                prepareBuffer(buffer, position);
+                //position = prepareBuffer(buffer, position);
                 return buffer[position];
             }
 
+            //needed to handle the non-greedy mask repetitions
             function prepareBuffer(buffer, position) {
-                while ((buffer.length <= position || position < 0) && buffer.length < getMaskLength()) {
+                while ((buffer.length < position || position < 0) && buffer.length < getMaskLength()) {
                     var j = 0;
-                    if (opts.numericInput) {
-                        j = determineTestPosition(position);
-                        buffer.unshift(_buffer[j]);
+                    if (isRTL) {
+                        j = _buffer.length - 1;
+                        position = position + _buffer.length;
+                        while (_buffer[j] !== undefined)
+                            buffer.unshift(_buffer[j--]);
+                        break;
                     } else while (_buffer[j] !== undefined) {
                         buffer.push(_buffer[j++]);
                     }
                 }
+
+                return position;
             }
 
             function writeBuffer(input, buffer, caretPos) {
                 _val.call(input, buffer.join(''));
-                if (caretPos != undefined)
-                    caret(input, caretPos);
+                if (caretPos != undefined) {
+                    if (android) {
+                        setTimeout(function() {
+                            caret(input, caretPos);
+                        }, 100);
+                    }
+                    else caret(input, caretPos);
+                }
             };
             function clearBuffer(buffer, start, end) {
                 for (var i = start, maskL = getMaskLength(); i < end && i < maskL; i++) {
@@ -344,62 +364,36 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                 var inputValue = TruncateInput(_val.call(input));
                 clearBuffer(buffer, 0, buffer.length);
                 buffer.length = _buffer.length;
-                var lastMatch = -1, checkPosition = -1, maskL = getMaskLength(), ivl = inputValue.length;
-                if (opts.numericInput) {
-                    lastMatch += maskL;
-                    var p = seekPrevious(buffer, ivl);
-                    for (var ivp = 0; ivp < ivl; ivp++) {
-                        var c = inputValue.charAt(ivp);
-                        if (isValid(p, c, buffer)) {
-                            for (var i = 0; i < maskL; i++) {
-                                if (isMask(i)) {
-                                    SetReTargetPlaceHolder(buffer, i);
-
-                                    var j = seekNext(buffer, i);
-                                    var el = getBufferElement(buffer, j);
-                                    if (el != getPlaceHolder(j)) {
-                                        if (j < getMaskLength() && isValid(i, el, buffer) !== false) {
-                                            setBufferElement(buffer, i, getBufferElement(buffer, j));
-                                        } else {
-                                            if (isMask(i))
-                                                break;
-                                        }
-                                    }
-                                } else
-                                    SetReTargetPlaceHolder(buffer, i);
-                            }
-                            lastMatch = seekPrevious(buffer, maskL);
-                            setBufferElement(buffer, lastMatch, c);
-                        }
-                    }
-                } else {
-                    for (var i = 0; i < ivl; i++) {
-                        for (var pos = checkPosition + 1; pos < maskL; pos++) {
-                            if (isMask(pos)) {
-                                if (isValid(pos, inputValue.charAt(i), buffer) !== false) {
-                                    setBufferElement(buffer, pos, inputValue.charAt(i));
-                                    lastMatch = checkPosition = pos;
-                                } else {
-                                    SetReTargetPlaceHolder(buffer, pos);
-                                    if (isMask(i) && inputValue.charAt(i) == getPlaceHolder(i))
-                                        checkPosition = pos;
-                                }
-                                break;
-                            } else {   //nonmask
+                var lastMatch = -1, checkPosition = -1, maskL = getMaskLength(), ivl = inputValue.length, rtlMatch = ivl == 0 ? maskL : -1;
+                for (var i = 0; i < ivl; i++) {
+                    for (var pos = checkPosition + 1; pos < maskL; pos++) {
+                        if (isMask(pos)) {
+                            var c = inputValue.charAt(i);
+                            if (isValid(pos, c, buffer) !== false) {
+                                setBufferElement(buffer, pos, c);
+                                lastMatch = checkPosition = pos;
+                            } else {
                                 SetReTargetPlaceHolder(buffer, pos);
-                                if (lastMatch == checkPosition) //once outsync the nonmask cannot be the lastmatch
-                                    lastMatch = pos;
-                                checkPosition = pos;
-                                if (inputValue.charAt(i) == getBufferElement(buffer, pos))
-                                    break;
+                                if (isMask(i) && c == getPlaceHolder(i)) {
+                                    checkPosition = pos;
+                                    rtlMatch = pos;
+                                }
                             }
+                            break;
+                        } else {   //nonmask
+                            SetReTargetPlaceHolder(buffer, pos);
+                            if (lastMatch == checkPosition) //once outsync the nonmask cannot be the lastmatch
+                                lastMatch = pos;
+                            checkPosition = pos;
+                            if (inputValue.charAt(i) == getBufferElement(buffer, pos))
+                                break;
                         }
                     }
                 }
                 if (clearInvalid) {
                     writeBuffer(input, buffer);
                 }
-                return seekNext(buffer, lastMatch);
+                return seekNext(buffer, isRTL ? (opts.numericInput ? maskL : rtlMatch) : lastMatch);
             }
 
             function EscapeRegex(str) {
@@ -407,10 +401,9 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                 return str.replace(new RegExp('(\\' + specials.join('|\\') + ')', 'gim'), '\\$1');
             }
             function TruncateInput(input) {
-                return input.replace(new RegExp("(" + EscapeRegex(_buffer.join('')) + ")*$"), "");
+                return isRTL ? input.replace(new RegExp("^(" + EscapeRegex(_buffer.join('')) + ")*"), "") : input.replace(new RegExp("(" + EscapeRegex(_buffer.join('')) + ")*$"), "");
             }
 
-
             //functionality fn
             function setvalue(el, value) {
                 _val.call(el, value);
@@ -446,20 +439,19 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                         range.select();
                     }
                     npt.focus();
-                    //setSelectionRange in android is buggy - so we manually track the position
-                    if(android) input.data('inputmask', $.extend(input.data('inputmask'), { caretpos: { begin: begin, end: end} }));
+                    if (android && end != npt.selectionEnd) caretposCorrection = { begin: begin, end: end };
                 } else {
-                	var caretpos = android ? input.data('inputmask').caretpos : null;
-                	if(caretpos == null){
-                    	if (npt.setSelectionRange) {
-                        	begin = npt.selectionStart;
-                        	end = npt.selectionEnd;
-                    	} else if (document.selection && document.selection.createRange) {
-                        	var range = document.selection.createRange();
-                        	begin = 0 - range.duplicate().moveStart('character', -100000);
-                        	end = begin + range.text.length;
-                    	}
-                    	caretpos = { begin: begin, end: end };
+                    var caretpos = android ? caretposCorrection : null, caretposCorrection = null;
+                    if (caretpos == null) {
+                        if (npt.setSelectionRange) {
+                            begin = npt.selectionStart;
+                            end = npt.selectionEnd;
+                        } else if (document.selection && document.selection.createRange) {
+                            var range = document.selection.createRange();
+                            begin = 0 - range.duplicate().moveStart('character', -100000);
+                            end = begin + range.text.length;
+                        }
+                        caretpos = { begin: begin, end: end };
                     }
                     return caretpos;
                 }
@@ -467,6 +459,7 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
 
             function mask(el) {
                 var input = $(el);
+
                 //store tests & original buffer in the input element - used to get the unmasked value
                 input.data('inputmask', {
                     'tests': tests,
@@ -479,11 +472,19 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                 });
 
                 //init buffer
-                var buffer = _buffer.slice();
-                var undoBuffer = _val.call(input);
-                var ignore = false;              //Variable for ignoring control keys
-                var lastPosition = -1;
-                var firstMaskPos = seekNext(buffer, -1);
+                var buffer = _buffer.slice(),
+                undoBuffer = _val.call(input),
+                skipKeyPressEvent = false, //Safari 5.1.x - modal dialog fires keypress twice workaround
+				ignorable = false,
+                lastPosition = -1,
+                firstMaskPos = seekNext(buffer, -1)
+                isRTL = false;
+                if (el.dir == "rtl" || opts.numericInput) {
+                    el.dir = "ltr"
+                    input.css("text-align", "right");
+                    input.removeAttr("dir");
+                    isRTL = true;
+                }
 
                 //unbind all events - to make sure that no other mask will interfere when re-masking
                 input.unbind(".inputmask");
@@ -505,12 +506,10 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                         if (opts.clearMaskOnLostFocus && _val.call(input) == _buffer.join(''))
                             _val.call(input, '');
                         if ((opts.clearIncomplete || opts.onincomplete) && checkVal(input, buffer, true) != getMaskLength()) {
-                            if (opts.onincomplete)
-                            {
-                            	opts.onincomplete.call(input);
+                            if (opts.onincomplete) {
+                                opts.onincomplete.call(input);
                             }
-                            if (opts.clearIncomplete)
-                            {
+                            if (opts.clearIncomplete) {
                                 if (opts.clearMaskOnLostFocus)
                                     _val.call(input, '');
                                 else {
@@ -529,13 +528,15 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                             _val.call(input, '');
                     }).bind("click.inputmask", function() {
                         var input = $(this);
-                        if(android) input.data('inputmask', $.extend(input.data('inputmask'), { caretpos: null })); //android workaround - see caret fn
                         setTimeout(function() {
                             var selectedCaret = caret(input);
                             if (selectedCaret.begin == selectedCaret.end) {
                                 var clickPosition = selectedCaret.begin;
                                 lastPosition = checkVal(input, buffer, false);
-                                caret(input, clickPosition < lastPosition && (isValid(clickPosition, buffer[clickPosition], buffer) || !isMask(clickPosition)) ? clickPosition : lastPosition);
+                                if (isRTL)
+                                    caret(input, clickPosition > lastPosition && (isValid(clickPosition, buffer[clickPosition], buffer) || !isMask(clickPosition)) ? clickPosition : lastPosition);
+                                else
+                                    caret(input, clickPosition < lastPosition && (isValid(clickPosition, buffer[clickPosition], buffer) || !isMask(clickPosition)) ? clickPosition : lastPosition);
                             }
                         }, 0);
                     }).bind('dblclick.inputmask', function() {
@@ -551,7 +552,7 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                     if (k == opts.keyCode.TAB && input.hasClass('focus.inputmask') && _val.call(input).length == 0) {
                         buffer = _buffer.slice();
                         writeBuffer(input, buffer);
-                        if (!opts.numericInput) caret(input, 0);
+                        if (!isRTL) caret(input, 0);
                     }
                 }).bind(pasteEventName, function() {
                     var input = $(this);
@@ -600,15 +601,15 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                         }
                     }
                     if (c != undefined)
-                        setBufferElement(buffer, seekPrevious(buffer, end), c);
+                        setBufferElement(buffer, isRTL ? end : seekPrevious(buffer, end), c);
 
                     buffer = TruncateInput(buffer.join('')).split('');
                     if (buffer.length == 0) buffer = _buffer.slice();
 
                     return start; //return the used start position
                 }
-                function shiftR(pos, c, full) { //full => behave like a push right ~ do not stop on placeholders
-                    for (var i = pos; i < getMaskLength(); i++) {
+                function shiftR(start, end, c, full) { //full => behave like a push right ~ do not stop on placeholders
+                    for (var i = start; i <= end && i < getMaskLength(); i++) {
                         if (isMask(i)) {
                             var t = getBufferElement(buffer, i);
                             setBufferElement(buffer, i, c);
@@ -627,26 +628,22 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                         } else
                             SetReTargetPlaceHolder(buffer, i);
                     }
+                    var lengthBefore = buffer.length;
+                    buffer = TruncateInput(buffer.join('')).split('');
+                    if (buffer.length == 0) buffer = _buffer.slice();
+
+                    return end - (lengthBefore - buffer.length);  //return new start position
                 };
 
                 function keydownEvent(e) {
-                    var input = $(this);
-
                     //Safari 5.1.x - modal dialog fires keypress twice workaround
-                    input.data('inputmask', $.extend(input.data('inputmask'), { skipKeyPressEvent: false }));
-
-                    var k = e.keyCode;
-                    if(android && (k == opts.keyCode.RIGHT || k == opts.keyCode.LEFT)) //android workaround - see caret fn
-						input.data('inputmask', $.extend(input.data('inputmask'), { caretpos: null }));
+                    skipKeyPressEvent = false;
 
-					var pos = caret(input);
-                    ignore = (k < 16 || (k > 16 && k < 32) || (k > 32 && k < 41));
+                    var input = $(this), k = e.keyCode, pos = caret(input);
 
                     //delete selection before proceeding
-                    if (((pos.end - pos.begin) > 1 || ((pos.end - pos.begin) == 1 && opts.insertMode)) && (!ignore || k == opts.keyCode.BACKSPACE || k == opts.keyCode.DELETE)) {
-
+                    if (((pos.end - pos.begin) > 1 || ((pos.end - pos.begin) == 1 && opts.insertMode)) && (k == opts.keyCode.BACKSPACE || k == opts.keyCode.DELETE))
                         clearBuffer(buffer, pos.begin, pos.end);
-                    }
 
                     //backspace, delete, and escape get special treatment
                     if (k == opts.keyCode.BACKSPACE || k == opts.keyCode.DELETE || (iphone && k == 127)) {//backspace/delete
@@ -654,18 +651,14 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                         if (pos.begin == 0 && pos.end == maskL) {
                             buffer = _buffer.slice();
                             writeBuffer(input, buffer);
-                            if (!opts.numericInput) caret(input, 0);
+                            if (!isRTL) caret(input, firstMaskPos);
                         } else {
                             var beginPos = pos.begin - (k == opts.keyCode.DELETE ? 0 : 1);
-                            beginPos = shiftL(beginPos < 0 ? 0 : beginPos, maskL);
-                            if (opts.numericInput) {
-                                shiftR(0, getPlaceHolder(0), true);
+                            if (isRTL) {
+                                beginPos = shiftR(firstMaskPos, beginPos, getPlaceHolder(0), true);
                                 beginPos = seekNext(buffer, beginPos);
-                            }
+                            } else beginPos = shiftL(beginPos < 0 ? firstMaskPos : beginPos, maskL);
                             writeBuffer(input, buffer, beginPos);
-                            if (!opts.insertMode && k == opts.keyCode.BACKSPACE) {
-                                caret(input, beginPos);
-                            }
                         }
                         if (opts.oncleared && _val.call(input) == _buffer.join(''))
                             opts.oncleared.call(input);
@@ -704,54 +697,55 @@ This plugin is based on the masked input plugin written by Josh Bush (digitalbus
                             return false;
                         }
                     }
+
+                    ignorable = $.inArray(k, opts.ignorables) != -1;
                 }
 
                 function keypressEvent(e) {
+                    //Safari 5.1.x - modal dialog fires keypress twice workaround
+                    if (skipKeyPressEvent) return false;
+                    skipKeyPressEvent = true;
+
                     var input = $(this);
 
-                    //Safari 5.1.x - modal dialog fires keypress twice workaround
-                    if (input.data('inputmask').skipKeyPressEvent) return false;
-                    input.data('inputmask', $.extend(input.data('inputmask'), { skipKeyPressEvent: true }));
-                    
-                    if (ignore) {
-                        ignore = false;
-                        //Fixes Mac FF bug on backspace
-                        return (e.keyCode == opts.keyCode.BACKSPACE) ? false : null;
-                    }
                     e = e || window.event;
-                    var k = e.charCode || e.keyCode || e.which;
-                    var pos = caret($(this));
-                    if (e.ctrlKey || e.altKey || e.metaKey) {//Ignore
+                    var k = e.which || e.charCode || e.keyCode;
+                    if (e.ctrlKey || e.altKey || e.metaKey || ignorable) {//Ignore
                         return true;
-                    } else if ((k >= 32 && k <= 125) || k > 186) {//typeable characters
-                        var c = String.fromCharCode(k);
-                        if (opts.numericInput) {
-                            var posEnd = opts.greedy ? pos.end : (pos.end + 1);
-                            var p = seekPrevious(buffer, posEnd);
-                            if (isValid(p, c, buffer)) {
-                                if (isValid(firstMaskPos, buffer[firstMaskPos], buffer) == false || (opts.greedy === false && buffer.length < getMaskLength())) {
-                                    shiftL(firstMaskPos, posEnd, c);
-                                    writeBuffer(input, buffer, posEnd);
-                                } else if (opts.oncomplete)
-                                    opts.oncomplete.call(input);
+                    } else {
+                        if (k) {
+                            var pos = caret(input), c = String.fromCharCode(k), maskL = getMaskLength();
+                            if (isRTL) {
+                                var p = seekPrevious(buffer, pos.end);
+                                if (isValid(p, c, buffer)) {
+                                    if (isValid(firstMaskPos, buffer[firstMaskPos], buffer) == false || (opts.greedy === false && buffer.length < maskL)) {
+                                        if (buffer[firstMaskPos] != getPlaceHolder(firstMaskPos) && buffer.length < maskL) {
+                                            var offset = prepareBuffer(buffer, -1) + 1;
+											p = p + offset;
+                                            maskL = buffer.length;
+                                        }
+                                        shiftL(firstMaskPos, p, c);
+                                        writeBuffer(input, buffer, opts.numericInput ? maskL : p);
+                                    } else if (opts.oncomplete)
+                                        opts.oncomplete.call(input);
+                                } else if (android) writeBuffer(input, buffer, pos.begin);
                             }
-                        }
-                        else {
-                            var p = seekNext(buffer, pos.begin - 1);
-                            if (isValid(p, c, buffer)) {
-                                if (opts.insertMode == true) shiftR(p, c); else setBufferElement(buffer, p, c);
-                                var next = seekNext(buffer, p);
-                                writeBuffer(input, buffer, next);
-
-                                if (opts.oncomplete && next == getMaskLength())
-                                    opts.oncomplete.call(input);
+                            else {
+                                var p = seekNext(buffer, pos.begin - 1);
+                                prepareBuffer(buffer, p);
+                                if (isValid(p, c, buffer)) {
+                                    if (opts.insertMode == true) shiftR(p, maskL, c); else setBufferElement(buffer, p, c);
+                                    var next = seekNext(buffer, p);
+                                    writeBuffer(input, buffer, next);
+
+                                    if (opts.oncomplete && next == maskL)
+                                        opts.oncomplete.call(input);
+                                } else if (android) writeBuffer(input, buffer, pos.begin);
                             }
+                            return false;
                         }
                     }
-                    return false;
                 }
-
-
             }
         };
     }