Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ The options that you can set are:
* `precision`: how many decimal places are allowed. default: 2
* `allowZero`: use this setting to prevent users from inputing zero. default: false
* `allowNegative`: use this setting to prevent users from inputing negative values. default: false
* `allowNoDecimal`: use this setting to allow decimal portions to be triggered manually by the user (in conjunction with thousands and decimal properties - make sure they are unique). default: false

__IMPORTANT__: if you try to bind maskMoney to a read only field, nothing will happen, since we ignore completely read only fields. So, if you have a read only field, try to bind maskMoney to it, it will not work. Even if you change the field removing the readonly property, you will need to re-bind maskMoney to make it work.

Expand Down
10 changes: 10 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ <h1>jQuery-maskMoney examples</h1>
var num = $('#demo8').maskMoney('unmasked')[0];
alert('type: '+ typeof(num) + ', value: ' + num)
</pre>

<input type="text" id="demo9"/>
<script type="text/javascript">$("#demo9").maskMoney({ allowNoDecimal: true });</script>
<pre class="brush: js">$("#demo9").maskMoney({ allowNoDecimal: true });</pre>

<input type="text" id="demo10" data-thousands="." data-decimal="," data-prefix="AUD$ " data-allow-no-decimal="true" data-precision="4" />
<script type="text/javascript">$("#demo10").maskMoney();</script>
<pre class="brush: js">$("#demo10").maskMoney();</pre>
<pre class="brush: html"><input type="text" id="demo10" data-thousands="." data-decimal="," data-prefix="AUD$ " data-allow-no-decimal="true" data-precision="4" /></pre>

</body>
<script type="text/javascript">
SyntaxHighlighter.all()
Expand Down
142 changes: 103 additions & 39 deletions src/jquery.maskMoney.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,30 @@
unmasked : function () {
return this.map(function () {
var value = ($(this).val() || "0"),
decimal = $(this).attr("data-decimal"),
precision = $(this).attr("data-precision"),
isNegative = value.indexOf("-") !== -1,
integerPart,
decimalPart;
// get the last position of the array that is a number(coercion makes "" to be evaluated as false)
$(value.split(/\D/).reverse()).each(function (index, element) {
if(element) {
decimalPart = element;
return false;
}
});
value = value.replace(/\D/g, "");
value = value.replace(new RegExp(decimalPart + "$"), "." + decimalPart);

var decimalIndex = value.indexOf(decimal);

integerPart = (decimalIndex > -1 ? value.slice(0, decimalIndex) : value);
decimalPart = (decimalIndex > -1 ? value.slice(decimalIndex + 1) : "");
integerPart = integerPart.replace(/[^0-9]/g, "");
decimalPart = decimalPart.replace(/[^0-9]/g, "");

value = integerPart;

if (decimalIndex > -1) {
value += "." + decimalPart;
}

if (isNegative) {
value = "-" + value;
}
return parseFloat(value);

return parseFloat(parseFloat(value).toFixed(precision));
});
},

Expand All @@ -58,7 +67,8 @@
decimal: ".",
precision: 2,
allowZero: false,
allowNegative: false
allowNegative: false,
allowNoDecimal: false
}, parameters);

return this.each(function () {
Expand All @@ -69,6 +79,11 @@
settings = $.extend({}, parameters);
settings = $.extend(settings, $input.data());

// need this to track decimal symbol
$(this)
.attr("data-decimal", settings.decimal)
.attr("data-precision", settings.precision);

function getInputSelection() {
var el = $input.get(0),
start = 0,
Expand Down Expand Up @@ -122,13 +137,27 @@
} // getInputSelection

function canInputMoreNumbers() {
var haventReachedMaxLength = !($input.val().length >= $input.attr("maxlength") && $input.attr("maxlength") >= 0),
var decimalIndex = $input.val().indexOf(settings.decimal),
haventReachedMaxLength = !($input.val().length >= $input.attr("maxlength") && $input.attr("maxlength") >= 0),
haventReachedDecimalLimit = (!settings.allowNoDecimal || $input.val().length - decimalIndex - 1 < settings.precision),
selection = getInputSelection(),
start = selection.start,
end = selection.end,
haveNumberSelected = (selection.start !== selection.end && $input.val().substring(start, end).match(/\d/)) ? true : false,
inputtingDecimal = decimalIndex > -1 && selection.start > decimalIndex,
startWithZero = ($input.val().substring(0, 1) === "0");
return haventReachedMaxLength || haveNumberSelected || startWithZero;

return (
haveNumberSelected || // allow number replacement
(inputtingDecimal ?
haventReachedDecimalLimit && haventReachedMaxLength :
haventReachedMaxLength || startWithZero
)
);
}

function canInputDecimal() {
return settings.allowNoDecimal && $input.val().indexOf(settings.decimal) < 0;
}

function setCursorPosition(pos) {
Expand Down Expand Up @@ -157,26 +186,53 @@

function maskValue(value) {
var negative = (value.indexOf("-") > -1 && settings.allowNegative) ? "-" : "",
onlyNumbers = value.replace(/[^0-9]/g, ""),
integerPart = onlyNumbers.slice(0, onlyNumbers.length - settings.precision),
newValue,
decimalPart,
leadingZeros;

// remove initial zeros
integerPart = integerPart.replace(/^0*/g, "");
// put settings.thousands every 3 chars
integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, settings.thousands);
if (integerPart === "") {
integerPart = "0";
}
newValue = negative + integerPart;
integerPart,
decimalPart;

var thousandify = function(str) {
return (str
// remove initial zeros
.replace(/^0*/g, "")
// put settings.thousands every 3 chars
.replace(/\B(?=(\d{3})+(?!\d))/g, settings.thousands)
) || "0"; // if empty string, return "0"
};

if (settings.precision > 0) {
// use decimal as delimiter, and treat each portion separately
// only handle decimal if required
if(settings.allowNoDecimal) {
var decimalIndex = value.indexOf(settings.decimal);

integerPart = (decimalIndex > -1 ? value.slice(0, decimalIndex) : value);
decimalPart = (decimalIndex > -1 ? value.slice(decimalIndex + 1) : "");
integerPart = integerPart.replace(/[^0-9]/g, "");
decimalPart = decimalPart.replace(/[^0-9]/g, "").slice(0, settings.precision);

newValue = thousandify(integerPart);

// only replace the decimal part if it exists
if(decimalIndex > -1) {
newValue += settings.decimal + decimalPart;
}
// split the number up according to precision
} else {
var onlyNumbers = value.replace(/[^0-9]/g, "");

integerPart = onlyNumbers.slice(0, onlyNumbers.length - settings.precision);
decimalPart = onlyNumbers.slice(onlyNumbers.length - settings.precision);
leadingZeros = new Array((settings.precision + 1) - decimalPart.length).join(0);
newValue += settings.decimal + leadingZeros + decimalPart;

newValue = thousandify(integerPart);

if(settings.precision > 0) {
var leadingZeros = new Array((settings.precision + 1) - decimalPart.length).join(0);
newValue += settings.decimal + leadingZeros + decimalPart;
}
}

// tack on the negative
newValue = negative + newValue;

return setSymbol(newValue);
}

Expand All @@ -192,7 +248,7 @@

function mask() {
var value = $input.val();
if (settings.precision > 0 && value.indexOf(settings.decimal) < 0) {
if (!settings.allowNoDecimal && settings.precision > 0 && value.indexOf(settings.decimal) < 0) {
value += settings.decimal + new Array(settings.precision+1).join(0);
}
$input.val(maskValue(value));
Expand Down Expand Up @@ -232,6 +288,18 @@
return false;
}

var handlePress = function() {
preventDefault(e);

keyPressedChar = String.fromCharCode(key);
selection = getInputSelection();
startPos = selection.start;
endPos = selection.end;
value = $input.val();
$input.val(value.substring(0, startPos) + keyPressedChar + value.substring(endPos, value.length));
maskAndPosition(startPos + 1);
};

// any key except the numbers 0-9
if (key < 48 || key > 57) {
// -(minus) key
Expand All @@ -245,6 +313,10 @@
// enter key or tab key
} else if (key === 13 || key === 9) {
return true;
// accept the decimal key IF allowNoDecimal is true
} else if (key === settings.decimal.codePointAt() && canInputDecimal()) {
handlePress();
return false;
} else if ($.browser.mozilla && (key === 37 || key === 39) && e.charCode === 0) {
// needed for left arrow key or right arrow key with firefox
// the charCode part is to avoid allowing "%"(e.charCode 0, e.keyCode 37)
Expand All @@ -256,15 +328,7 @@
} else if (!canInputMoreNumbers()) {
return false;
} else {
preventDefault(e);

keyPressedChar = String.fromCharCode(key);
selection = getInputSelection();
startPos = selection.start;
endPos = selection.end;
value = $input.val();
$input.val(value.substring(0, startPos) + keyPressedChar + value.substring(endPos, value.length));
maskAndPosition(startPos + 1);
handlePress();
return false;
}
}
Expand Down
1 change: 1 addition & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<script src="change_test.js"></script>
<script src="typing_test.js"></script>
<script src="cut_paste_test.js"></script>
<script src="optional_decimals_test.js"></script>
</head>
<body>
<div id="qunit"></div>
Expand Down
111 changes: 111 additions & 0 deletions test/optional_decimals_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"use strict";

module("optional decimals");

test("testing basic entry with unenforced decimals",function() {
var input = $("#input1").maskMoney({
allowNoDecimal: true
});

input.trigger("focus");
keypress(input, 1);
keypress(input, 2);
keypress(input, 3);
keypress(input, 4);
keypress(input, 5);
keypress(input, 6);
keypress(input, 7);

equal(input.val(), "1,234,567", "accept the input and format correctly");

// no idea why this doesn't work. so I'm hacking around it to simulate keypress
// keypress(input, ".");
input.val(input.val() + ".");
keypress(input, 8);
keypress(input, 9);
keypress(input, 0);

equal(input.val(), "1,234,567.89", "accept the input and format correctly");
});

test("testing basic entries with unenforced decimals and variable precision",function() {
var input = $("#input1").maskMoney({
allowNoDecimal: true,
precision: 3
});

input.trigger("focus");
keypress(input, 1);
keypress(input, 2);
keypress(input, 3);
keypress(input, 4);
keypress(input, 5);
keypress(input, 6);
keypress(input, 7);

equal(input.val(), "1,234,567", "accept the input and format correctly");

// no idea why this doesn't work. so I'm hacking around it to simulate keypress
// keypress(input, ".");
input.val(input.val() + ".");
keypress(input, 8);
keypress(input, 9);
keypress(input, 0);

equal(input.val(), "1,234,567.890", "accept the input and format correctly");

keypress(input, 1);

equal(input.val(), "1,234,567.890", "accept the input and format correctly");
});

test("testing prefilled entries with unenforced decimals",function() {
var input = $("#input1").maskMoney({
allowNoDecimal: true
});

input.val("1234567.890");
input.trigger("focus");

equal(input.val(), "1,234,567.89", "accept the input and format correctly");
});

test("testing prefilled entries with unenforced decimals and variable precision",function() {
var input = $("#input1").maskMoney({
allowNoDecimal: true,
precision: 3
});

input.val("1234567.890");
input.trigger("focus");

equal(input.val(), "1,234,567.890", "accept the input and format correctly");

input.val("1234567.8901");
input.trigger("focus");

equal(input.val(), "1,234,567.890", "accept the input and format correctly");
});

test("testing unmask of unenforced decimals", function() {
var input = $("#input1").maskMoney({
allowNoDecimal: true
});

input.val("1,234,567");
input.maskMoney("mask");

equal(input.maskMoney("unmasked")[0], 1234567.00, "check appropriate unmasked precision");


input.val("1234567.89");
input.maskMoney("mask");

equal(input.maskMoney("unmasked")[0], 1234567.89);

input.val("1234567.8901234");
input.maskMoney("mask");

equal(input.maskMoney("unmasked")[0], 1234567.89);

});
Loading