(function ($) { "use strict"; if (!$.browser) { $.browser = {}; $.browser.mozilla = /mozilla/.test(navigator.userAgent.toLowerCase()) && !/webkit/.test(navigator.userAgent.toLowerCase()); $.browser.webkit = /webkit/.test(navigator.userAgent.toLowerCase()); $.browser.opera = /opera/.test(navigator.userAgent.toLowerCase()); $.browser.msie = /msie/.test(navigator.userAgent.toLowerCase()); } var methods = { destroy : function () { $(this).unbind(".maskMoney"); if ($.browser.msie) { this.onpaste = null; } return this; }, mask : function (value) { return this.each(function () { var $this = $(this), decimalSize; if (typeof value === "number") { $this.trigger("mask"); decimalSize = $($this.val().split(/\D/)).last()[0].length; value = value.toFixed(decimalSize); $this.val(value); } return $this.trigger("mask"); }); }, unmasked : function () { return this.map(function () { var value = ($(this).val() || "0"), isNegative = value.indexOf("-") !== -1, 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); if (isNegative) { value = "-" + value; } return parseFloat(value); }); }, init : function (settings) { settings = $.extend({ prefix: "", suffix: "", affixesStay: true, thousands: ",", decimal: ".", precision: 2, allowZero: false, allowNegative: false }, settings); return this.each(function () { var $input = $(this), onFocusValue; // data-* api settings = $.extend(settings, $input.data()); function getInputSelection() { var el = $input.get(0), start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange; if (typeof el.selectionStart === "number" && typeof el.selectionEnd === "number") { start = el.selectionStart; end = el.selectionEnd; } else { range = document.selection.createRange(); if (range && range.parentElement() === el) { len = el.value.length; normalizedValue = el.value.replace(/\r\n/g, "\n"); // Create a working TextRange that lives only in the input textInputRange = el.createTextRange(); textInputRange.moveToBookmark(range.getBookmark()); // Check if the start and end of the selection are at the very end // of the input, since moveStart/moveEnd doesn't return what we want // in those cases endRange = el.createTextRange(); endRange.collapse(false); if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { start = end = len; } else { start = -textInputRange.moveStart("character", -len); start += normalizedValue.slice(0, start).split("\n").length - 1; if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { end = len; } else { end = -textInputRange.moveEnd("character", -len); end += normalizedValue.slice(0, end).split("\n").length - 1; } } } } return { start: start, end: end }; } // getInputSelection function canInputMoreNumbers() { var haventReachedMaxLength = !($input.val().length >= $input.attr("maxlength") && $input.attr("maxlength") >= 0), selection = getInputSelection(), start = selection.start, end = selection.end, haveNumberSelected = (selection.start !== selection.end && $input.val().substring(start, end).match(/\d/)) ? true : false, startWithZero = ($input.val().substring(0, 1) === "0"); return haventReachedMaxLength || haveNumberSelected || startWithZero; } function setCursorPosition(pos) { $input.each(function (index, elem) { if (elem.setSelectionRange) { elem.focus(); elem.setSelectionRange(pos, pos); } else if (elem.createTextRange) { var range = elem.createTextRange(); range.collapse(true); range.moveEnd("character", pos); range.moveStart("character", pos); range.select(); } }); } function setSymbol(value) { var operator = ""; if (value.indexOf("-") > -1) { value = value.replace("-", ""); operator = "-"; } return operator + settings.prefix + value + settings.suffix; } 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; if (settings.precision > 0) { decimalPart = onlyNumbers.slice(onlyNumbers.length - settings.precision); leadingZeros = new Array((settings.precision + 1) - decimalPart.length).join(0); newValue += settings.decimal + leadingZeros + decimalPart; } return setSymbol(newValue); } function maskAndPosition(startPos) { var originalLen = $input.val().length, newLen; $input.val(maskValue($input.val())); newLen = $input.val().length; startPos = startPos - (originalLen - newLen); setCursorPosition(startPos); } function mask() { var value = $input.val(); $input.val(maskValue(value)); } function changeSign() { var inputValue = $input.val(); if (settings.allowNegative) { if (inputValue !== "" && inputValue.charAt(0) === "-") { return inputValue.replace("-", ""); } else { return "-" + inputValue; } } else { return inputValue; } } function preventDefault(e) { if (e.preventDefault) { //standard browsers e.preventDefault(); } else { // old internet explorer e.returnValue = false; } } function keypressEvent(e) { e = e || window.event; var key = e.which || e.charCode || e.keyCode, keyPressedChar, selection, startPos, endPos, value; //added to handle an IE "special" event if (key === undefined) { return false; } // any key except the numbers 0-9 if (key < 48 || key > 57) { // -(minus) key if (key === 45) { $input.val(changeSign()); return false; // +(plus) key } else if (key === 43) { $input.val($input.val().replace("-", "")); return false; // enter key or tab key } else if (key === 13 || key === 9) { return true; } 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) return true; } else { // any other key with keycode less than 48 and greater than 57 preventDefault(e); return true; } } 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); return false; } } function keydownEvent(e) { e = e || window.event; var key = e.which || e.charCode || e.keyCode, selection, startPos, endPos, value, lastNumber; //needed to handle an IE "special" event if (key === undefined) { return false; } selection = getInputSelection(); startPos = selection.start; endPos = selection.end; if (key === 8 || key === 46 || key === 63272) { // backspace or delete key (with special case for safari) preventDefault(e); value = $input.val(); // not a selection if (startPos === endPos) { // backspace if (key === 8) { if (settings.suffix === "") { startPos -= 1; } else { // needed to find the position of the last number to be erased lastNumber = value.split("").reverse().join("").search(/\d/); startPos = value.length - lastNumber - 1; endPos = startPos + 1; } //delete } else { endPos += 1; } } $input.val(value.substring(0, startPos) + value.substring(endPos, value.length)); maskAndPosition(startPos); return false; } else if (key === 9) { // tab key return true; } else { // any other key return true; } } function focusEvent() { onFocusValue = $input.val(); mask(); var input = $input.get(0), textRange; if (input.createTextRange) { textRange = input.createTextRange(); textRange.collapse(false); // set the cursor at the end of the input textRange.select(); } } function cutPasteEvent() { setTimeout(function() { mask(); }, 0); } function getDefaultMask() { var n = parseFloat("0") / Math.pow(10, settings.precision); return (n.toFixed(settings.precision)).replace(new RegExp("\\.", "g"), settings.decimal); } function blurEvent(e) { if ($.browser.msie) { keypressEvent(e); } if ($input.val() === "" || $input.val() === setSymbol(getDefaultMask())) { if (!settings.allowZero) { $input.val(""); } else if (!settings.affixesStay) { $input.val(getDefaultMask()); } else { $input.val(setSymbol(getDefaultMask())); } } else { if (!settings.affixesStay) { var newValue = $input.val().replace(settings.prefix, "").replace(settings.suffix, ""); $input.val(newValue); } } if ($input.val() !== onFocusValue) { $input.change(); } } function clickEvent() { var input = $input.get(0), length; if (input.setSelectionRange) { length = $input.val().length; input.setSelectionRange(length, length); } else { $input.val($input.val()); } } $input.unbind(".maskMoney"); $input.bind("keypress.maskMoney", keypressEvent); $input.bind("keydown.maskMoney", keydownEvent); $input.bind("blur.maskMoney", blurEvent); $input.bind("focus.maskMoney", focusEvent); $input.bind("click.maskMoney", clickEvent); $input.bind("cut.maskMoney", cutPasteEvent); $input.bind("paste.maskMoney", cutPasteEvent); $input.bind("mask.maskMoney", mask); }); } }; $.fn.maskMoney = function (method) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === "object" || ! method) { return methods.init.apply(this, arguments); } else { $.error("Method " + method + " does not exist on jQuery.maskMoney"); } }; })(window.jQuery || window.Zepto);