Browse Source

Rework - numerics

Robin Herbots 12 years ago
parent
commit
fa673e466a
2 changed files with 104 additions and 64 deletions
  1. 85 62
      js/jquery.inputmask.js
  2. 19 2
      js/jquery.inputmask.numeric.extensions.js

+ 85 - 62
js/jquery.inputmask.js

@@ -56,19 +56,19 @@
                     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
                 },
                 //specify keycodes which should not be considered in the keypress event, otherwise the preventDefault will stop their default behavior especially in FF
-                ignorables: [9, 13, 19, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46, 93, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123]
+                ignorables: [9, 13, 19, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46, 93, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123],
+                getMaskLength: function (buffer, greedy, repeat, currentBuffer, opts) {
+                    var calculatedLength = buffer.length;
+                    if (!greedy && repeat > 1) {
+                        calculatedLength += (buffer.length * (repeat - 1));
+                    }
+                    return calculatedLength;
+                }
             },
             val: $.fn.val, //store the original jquery val function
             escapeRegex: function (str) {
                 var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
                 return str.replace(new RegExp('(\\' + specials.join('|\\') + ')', 'gim'), '\\$1');
-            },
-            getMaskLength: function (buffer, greedy, repeat) {
-                var calculatedLength = buffer.length;
-                if (!greedy && repeat > 1) {
-                    calculatedLength += (buffer.length * (repeat - 1));
-                }
-                return calculatedLength;
             }
         };
 
@@ -303,7 +303,7 @@
 
             function isValid(pos, c, buffer, strict) { //strict true ~ no correction or autofill
                 var result = false;
-                if (pos >= 0 && pos < getMaskLength()) {
+                if (pos >= 0 && pos < getMaskLength(buffer)) {
                     var testPos = determineTestPosition(pos), loopend = c ? 1 : 0, chrs = '';
                     for (var i = tests[testPos].cardinality; i > loopend; i--) {
                         chrs += getBufferElement(buffer, testPos - (i - 1));
@@ -334,13 +334,13 @@
                 return opts.placeholder.charAt(pos % opts.placeholder.length);
             }
 
-            function getMaskLength() {
-                return $.inputmask.getMaskLength(_buffer, opts.greedy, opts.repeat);
+            function getMaskLength(currentBuffer) {
+                return opts.getMaskLength(_buffer, opts.greedy, opts.repeat, currentBuffer, opts);
             }
 
             //pos: from position
             function seekNext(buffer, pos) {
-                var maskL = getMaskLength();
+                var maskL = getMaskLength(buffer);
                 if (pos >= maskL) return maskL;
                 var position = pos;
                 while (++position < maskL && !isMask(position)) { };
@@ -382,7 +382,7 @@
             function prepareBuffer(buffer, position, isRTL) {
                 var j;
                 if (isRTL) {
-                    while (position < 0 && buffer.length < getMaskLength()) {
+                    while (position < 0 && buffer.length < getMaskLength(buffer)) {
                         j = _buffer.length - 1;
                         position = _buffer.length;
                         while (_buffer[j] !== undefined) {
@@ -390,7 +390,7 @@
                         }
                     }
                 } else {
-                    while (buffer[position] == undefined && buffer.length < getMaskLength()) {
+                    while (buffer[position] == undefined && buffer.length < getMaskLength(buffer)) {
                         j = 0;
                         while (_buffer[j] !== undefined) { //add a new buffer
                             buffer.push(_buffer[j++]);
@@ -413,7 +413,7 @@
                 }
             };
             function clearBuffer(buffer, start, end) {
-                for (var i = start, maskL = getMaskLength() ; i < end && i < maskL; i++) {
+                for (var i = start, maskL = getMaskLength(buffer) ; i < end && i < maskL; i++) {
                     setBufferElement(buffer, i, getBufferElement(_buffer.slice(), i));
                 }
             };
@@ -427,8 +427,8 @@
                 var isRTL = $(input).data('inputmask')['isRTL'],
                     inputValue = truncateInput(input._valueGet(), isRTL).split('');
 
+                var maskL = getMaskLength(buffer);
                 if (isRTL) { //align inputValue for RTL/numeric input
-                    var maskL = getMaskLength();
                     var inputValueRev = inputValue.reverse(); inputValueRev.length = maskL;
 
                     for (var i = 0; i < maskL; i++) {
@@ -444,7 +444,7 @@
                 }
                 clearBuffer(buffer, 0, buffer.length);
                 buffer.length = _buffer.length;
-                var lastMatch = -1, checkPosition = -1, np, maskL = getMaskLength(), ivl = inputValue.length, rtlMatch = ivl == 0 ? maskL : -1;
+                var lastMatch = -1, checkPosition = -1, np, 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)) {
@@ -586,17 +586,18 @@
                 var $input = $(el);
                 if (!$input.is(":input")) return;
 
+                var buffer = _buffer.slice();
+
                 //correct greedy setting if needed
                 opts.greedy = opts.greedy ? opts.greedy : opts.repeat == 0;
-
                 //handle maxlength attribute
                 var maxLength = $input.prop('maxLength');
-                if (getMaskLength() > maxLength && maxLength > -1) { //FF sets no defined max length to -1 
+                if (getMaskLength(buffer) > maxLength && maxLength > -1) { //FF sets no defined max length to -1 
                     if (maxLength < _buffer.length) _buffer.length = maxLength;
                     if (opts.greedy == false) {
                         opts.repeat = Math.round(maxLength / _buffer.length);
                     }
-                    $input.prop('maxLength', getMaskLength() * 2);
+                    $input.prop('maxLength', getMaskLength(buffer) * 2);
                 }
 
                 //store tests & original buffer in the input element - used to get the unmasked value
@@ -613,13 +614,13 @@
                 patchValueProperty(el);
 
                 //init vars
-                var buffer = _buffer.slice(),
-                undoBuffer = el._valueGet(),
+
+                var undoBuffer = el._valueGet(),
                 skipKeyPressEvent = false, //Safari 5.1.x - modal dialog fires keypress twice workaround
                 ignorable = false,
                 lastPosition = -1,
                 firstMaskPos = seekNext(buffer, -1),
-                lastMaskPos = seekPrevious(buffer, getMaskLength()),
+                lastMaskPos = seekPrevious(buffer, getMaskLength(buffer)),
                 isRTL = false;
                 if (el.dir == "rtl" || opts.numericInput) {
                     el.dir = "ltr"
@@ -699,6 +700,7 @@
                         if (selectedCaret.begin == selectedCaret.end) {
                             var clickPosition = selectedCaret.begin;
                             lastPosition = checkVal(input, buffer, false);
+                            determineInputDirection(input, selectedCaret);
                             if (isRTL)
                                 caret(input, clickPosition > lastPosition && (isValid(clickPosition, buffer[clickPosition], buffer, true) !== false || !isMask(clickPosition)) ? clickPosition : lastPosition);
                             else
@@ -833,16 +835,26 @@
                         }
                     }
                 }
+
+                function determineInputDirection(input, pos) {
+                    //set input direction according the position to the radixPoint
+                    if (opts.numericInput && opts.radixPoint != "") {
+                        var nptStr = input._valueGet();
+                        var radixPosition = nptStr.indexOf(opts.radixPoint);
+                        isRTL = pos.begin <= radixPosition || pos.end <= radixPosition || radixPosition == -1;
+                    }
+                }
+
                 //shift chars to left from start to end and put c at end position if defined
                 function shiftL(start, end, c) {
                     while (!isMask(start) && start - 1 >= 0) start--;
-                    for (var i = start; i < end && i < getMaskLength() ; i++) {
+                    for (var i = start; i < end && i < getMaskLength(buffer) ; i++) {
                         if (isMask(i)) {
                             setReTargetPlaceHolder(buffer, i);
                             var j = seekNext(buffer, i);
                             var p = getBufferElement(buffer, j);
                             if (p != getPlaceHolder(j)) {
-                                if (j < getMaskLength() && isValid(i, p, buffer, true) !== false && tests[determineTestPosition(i)].def == tests[determineTestPosition(j)].def) {
+                                if (j < getMaskLength(buffer) && isValid(i, p, buffer, true) !== false && tests[determineTestPosition(i)].def == tests[determineTestPosition(j)].def) {
                                     setBufferElement(buffer, i, getBufferElement(buffer, j));
                                     setReTargetPlaceHolder(buffer, j); //cleanup next position
                                 } else {
@@ -863,13 +875,13 @@
                     return start; //return the used start position
                 }
                 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++) {
+                    for (var i = start; i <= end && i < getMaskLength(buffer) ; i++) {
                         if (isMask(i)) {
                             var t = getBufferElement(buffer, i);
                             setBufferElement(buffer, i, c);
                             if (t != getPlaceHolder(i)) {
                                 var j = seekNext(buffer, i);
-                                if (j < getMaskLength()) {
+                                if (j < getMaskLength(buffer)) {
                                     if (isValid(j, t, buffer, true) !== false && tests[determineTestPosition(i)].def == tests[determineTestPosition(j)].def)
                                         c = t;
                                     else {
@@ -895,44 +907,54 @@
 
                     var input = this, k = e.keyCode, pos = caret(input);
 
-                    //set input direction according the position to the radixPoint
-                    if (opts.numericInput && opts.radixPoint != "") {
-                        var nptStr = input._valueGet();
-                        var radixPosition = nptStr.indexOf(opts.radixPoint);
-                        if (radixPosition != -1) {
-                            isRTL = pos.begin <= radixPosition || pos.end <= radixPosition;
-                        }
-                    }
+                    determineInputDirection(input, pos);
 
                     //backspace, delete, and escape get special treatment
                     if (k == opts.keyCode.BACKSPACE || k == opts.keyCode.DELETE || (iphone && k == 127)) {//backspace/delete
-                        var maskL = getMaskLength();
-                        if (pos.begin == 0 && pos.end == maskL) {
+                        var maskL = getMaskLength(buffer);
+                        if (pos.begin == 0 && pos.end == maskL) { //remove full selection
                             buffer = _buffer.slice();
                             writeBuffer(input, buffer);
                             caret(input, checkVal(input, buffer, false));
-                        } else if ((pos.end - pos.begin) > 1 || ((pos.end - pos.begin) == 1 && opts.insertMode)) {
+                        } else if ((pos.end - pos.begin) > 1 || ((pos.end - pos.begin) == 1 && opts.insertMode)) { //partial selection
                             clearBuffer(buffer, pos.begin, pos.end);
                             writeBuffer(input, buffer);
                             caret(isRTL ? checkVal(input, buffer, false) : pos.begin);
-                        } else {
-                            var beginPos = pos.begin - (k == opts.keyCode.DELETE ? 0 : 1);
-                            if (beginPos < firstMaskPos && k == opts.keyCode.DELETE) {
-                                beginPos = firstMaskPos;
-                            }
-                            if (beginPos >= firstMaskPos) {
-                                if (opts.numericInput && opts.greedy && k == opts.keyCode.DELETE && buffer[beginPos] == opts.radixPoint) {
-                                    beginPos = seekNext(buffer, beginPos);
-                                    isRTL = false;
-                                } else if (opts.numericInput && opts.greedy && k == opts.keyCode.BACKSPACE && buffer[beginPos] == opts.radixPoint) {
-                                    beginPos--;
-                                    isRTL = true;
+                        } else { //handle delete
+                            var beginPos = pos.begin;
+                            if (k == opts.keyCode.DELETE) {
+                                if (beginPos < firstMaskPos)
+                                    beginPos = firstMaskPos;
+                                if (beginPos < maskL) {
+                                    if (opts.numericInput && opts.radixPoint != "" && buffer[beginPos] == opts.radixPoint) {
+                                        beginPos = seekNext(buffer, beginPos);
+                                        beginPos = shiftL(beginPos, maskL);
+                                    } else {
+                                        if (isRTL) {
+                                            beginPos = shiftR(firstMaskPos, beginPos, getPlaceHolder(beginPos), true);
+                                            beginPos = seekNext(buffer, beginPos);
+                                        } else {
+                                            beginPos = shiftL(beginPos, maskL);
+                                        }
+                                    }
+                                    writeBuffer(input, buffer, beginPos);
+                                }
+                            } else if (k == opts.keyCode.BACKSPACE) { //handle backspace
+                                if (beginPos > firstMaskPos) {
+                                    beginPos -= 1;
+                                    if (opts.numericInput && opts.radixPoint != "" && buffer[beginPos] == opts.radixPoint) {
+                                        beginPos = shiftR(firstMaskPos, beginPos - 1, getPlaceHolder(beginPos), true);
+                                        beginPos++;
+                                    } else {
+                                        if (isRTL) {
+                                            beginPos = shiftR(firstMaskPos, beginPos, getPlaceHolder(beginPos), true);
+                                            beginPos = buffer[beginPos + 1] == opts.radixPoint ? beginPos + 1 : seekNext(buffer, beginPos);
+                                        } else {
+                                            beginPos = shiftL(beginPos, maskL);
+                                        }
+                                    }
+                                    writeBuffer(input, buffer, beginPos);
                                 }
-                                if (isRTL) {
-                                    beginPos = shiftR(firstMaskPos, beginPos, getPlaceHolder(beginPos), true);
-                                    beginPos = (opts.numericInput && opts.greedy && k == opts.keyCode.BACKSPACE && buffer[beginPos + 1] == opts.radixPoint) ? beginPos + 1 : seekNext(buffer, beginPos);
-                                } else beginPos = shiftL(beginPos, maskL);
-                                writeBuffer(input, buffer, beginPos);
                             }
                         }
                         if (input._valueGet() == _buffer.join(''))
@@ -942,7 +964,7 @@
                     } else if (k == opts.keyCode.END || k == opts.keyCode.PAGE_DOWN) { //when END or PAGE_DOWN pressed set position at lastmatch
                         setTimeout(function () {
                             var caretPos = checkVal(input, buffer, false, true);
-                            if (!opts.insertMode && caretPos == getMaskLength() && !e.shiftKey) caretPos--;
+                            if (!opts.insertMode && caretPos == getMaskLength(buffer) && !e.shiftKey) caretPos--;
                             caret(input, e.shiftKey ? pos.begin : caretPos, caretPos);
                         }, 0);
                     } else if (k == opts.keyCode.HOME || k == opts.keyCode.PAGE_UP) {//Home or page_up
@@ -953,7 +975,7 @@
                         caret(input, 0, checkVal(input, buffer));
                     } else if (k == opts.keyCode.INSERT) {//insert
                         opts.insertMode = !opts.insertMode;
-                        caret(input, !opts.insertMode && pos.begin == getMaskLength() ? pos.begin - 1 : pos.begin);
+                        caret(input, !opts.insertMode && pos.begin == getMaskLength(buffer) ? pos.begin - 1 : pos.begin);
                     } else if (e.ctrlKey && k == 88) {
                         setTimeout(function () {
                             caret(input, checkVal(input, buffer, true));
@@ -961,7 +983,7 @@
                     } else if (!opts.insertMode) { //overwritemode
                         if (k == opts.keyCode.RIGHT) {//right
                             var caretPos = pos.begin == pos.end ? pos.end + 1 : pos.end;
-                            caretPos = caretPos < getMaskLength() ? caretPos : pos.end;
+                            caretPos = caretPos < getMaskLength(buffer) ? caretPos : pos.end;
                             caret(input, e.shiftKey ? pos.begin : caretPos, e.shiftKey ? caretPos + 1 : caretPos);
                         } else if (k == opts.keyCode.LEFT) {//left
                             var caretPos = pos.begin - 1;
@@ -983,12 +1005,12 @@
 
                     e = e || window.event;
                     var k = e.which || e.charCode || e.keyCode,
-					    c = String.fromCharCode(k);
+                        c = String.fromCharCode(k);
 
                     if (opts.numericInput && c == opts.radixPoint) {
                         var nptStr = input._valueGet();
                         var radixPosition = nptStr.indexOf(opts.radixPoint);
-                        caret(input, seekNext(buffer, radixPosition != -1 ? radixPosition : getMaskLength()));
+                        caret(input, seekNext(buffer, radixPosition != -1 ? radixPosition : getMaskLength(buffer)));
                     }
 
                     if (e.ctrlKey || e.altKey || e.metaKey || ignorable) {
@@ -997,7 +1019,7 @@
                         if (k) {
                             $input.trigger('input');
 
-                            var pos = caret(input), maskL = getMaskLength(), writeOutBuffer = true;
+                            var pos = caret(input), maskL = getMaskLength(buffer), writeOutBuffer = true;
                             clearBuffer(buffer, pos.begin, pos.end);
 
                             if (isRTL) {
@@ -1007,6 +1029,7 @@
                                         p = np.pos != undefined ? np.pos : p; //set new position from isValid
                                         c = np.c != undefined ? np.c : c; //set new char from isValid
                                     }
+                                    maskL = getMaskLength(buffer); //update masklength to include possible groupSeparator offset
 
                                     var firstUnmaskedPosition = firstMaskPos;
                                     if (opts.insertMode == true) {
@@ -1044,7 +1067,7 @@
                                         c = np.c != undefined ? np.c : c; //set new char from isValid
                                     }
                                     if (opts.insertMode == true) {
-                                        var lastUnmaskedPosition = getMaskLength();
+                                        var lastUnmaskedPosition = getMaskLength(buffer);
                                         var bfrClone = buffer.slice();
                                         while (getBufferElement(bfrClone, lastUnmaskedPosition, true) != getPlaceHolder(lastUnmaskedPosition) && lastUnmaskedPosition >= p) {
                                             lastUnmaskedPosition = lastUnmaskedPosition == 0 ? -1 : seekPrevious(buffer, lastUnmaskedPosition);

+ 19 - 2
js/jquery.inputmask.numeric.extensions.js

@@ -21,17 +21,34 @@ Optional extensions on the jquery.inputmask base
             radixPoint: ".",
             groupSize: 3,
             autoGroup: false,
+            getMaskLength: function (buffer, greedy, repeat, currentBuffer, opts) { //custom getMaskLength to take the groupSeparator into account
+                var calculatedLength = buffer.length;
+
+                if (!greedy && repeat > 1) {
+                    calculatedLength += (buffer.length * (repeat - 1));
+                }
+                
+                var escapedGroupSeparator = $.inputmask.escapeRegex.call(this, opts.groupSeparator);
+                var escapedRadixPoint = $.inputmask.escapeRegex.call(this, opts.radixPoint);
+                var currentBufferStr = currentBuffer.join(''), strippedBufferStr = currentBufferStr.replace(new RegExp(escapedGroupSeparator, "g"), "").replace(new RegExp(escapedRadixPoint), ""),
+                groupOffset = currentBufferStr.length - strippedBufferStr.length;
+                return calculatedLength + groupOffset;
+            },
             postFormat: function (buffer, pos, reformatOnly, opts) {
                 var cbuf = buffer.slice();
                 if (!reformatOnly) cbuf.splice(pos, 0, "?"); //set position indicator
                 var bufVal = cbuf.join('');
                 if (opts.autoGroup || (reformatOnly && bufVal.indexOf(opts.groupSeparator) != -1)) {
                     bufVal = bufVal.replace(new RegExp("\\" + opts.groupSeparator, "g"), '');
+                    radixSplit = bufVal.split(opts.radixPoint);
+                    bufVal = radixSplit[0];
                     var reg = new RegExp('([-\+]?[\\d\?]+)([\\d\?]{' + opts.groupSize + '})');
                     while (reg.test(bufVal)) {
                         bufVal = bufVal.replace(reg, '$1' + opts.groupSeparator + '$2');
                         bufVal = bufVal.replace(opts.groupSeparator + opts.groupSeparator, opts.groupSeparator);
                     }
+                    if (radixSplit.length > 1)
+                        bufVal += opts.radixPoint + radixSplit[1];
                 }
                 buffer.length = bufVal.length; //align the length
                 for (var i = 0, l = bufVal.length; i < l; i++) {
@@ -81,7 +98,7 @@ Optional extensions on the jquery.inputmask base
 
                         cbuf.splice(pos + 1, 0, chrs);
                         var bufferStr = cbuf.join('');
-                        if (opts.autoGroup) //strip groupseparator
+                        if (opts.autoGroup && !strict) //strip groupseparator
                             bufferStr = bufferStr.replace(new RegExp("\\" + opts.groupSeparator, "g"), '');
                         var isValid = opts.regex.number(opts.groupSeparator, opts.groupSize, opts.radixPoint, opts.digits).test(bufferStr);
                         if (!isValid) {
@@ -109,7 +126,7 @@ Optional extensions on the jquery.inputmask base
                             }
                         }
 
-                        if (isValid != false && !strict) {
+                        if (isValid != false && !strict && chrs != opts.radixPoint) {
                             var newPos = opts.postFormat(buffer, pos + 1, false, opts);
                             return { "pos": newPos };
                         }