(function ($) { /** * @param {node} element The HTML element. * @param {number} [opts.delta] Increment/decrement by the value of delta. * @param {number} [opts.min] The minimum value allowed. * @param {number} [otps.max] The maxiumum value allowed. * @param {function} [opts.parseFn] A custom parse function. * @param {function} [opts.formatFn] A custom format function. */ $.arrowIncrement = function (element, opts) { var that = this; this.opts = $.extend({}, opts); this.$element = $(element).keydown(function (e) { if (e.keyCode === 38) { // up that.increment(); } else if (e.keyCode === 40) { // down that.decrement(); } }); }; /** * @param {boolean} decrement Decrement the value instead. */ $.arrowIncrement.prototype.increment = function (decrement) { var value = this.$element.val(), parsed, computed; // Parse the value if (this.opts.parseFn) { parsed = this.opts.parseFn(value); } else { parsed = $.arrowIncrement.parse(value); } if (isNaN(parsed)) { return; } computed = $.arrowIncrement.compute(parsed, decrement, this.opts); // Apply formatting function if (this.opts.formatFn) { computed = this.opts.formatFn(computed); } this.$element.val(computed).change(); }; $.arrowIncrement.prototype.decrement = function () { this.increment(true); }; /** * @static * @param {number} value The value to increment. * @param {boolean} decrement Decrement the value instead. * @param {number} [opts.delta] Increment/decrement by the value of delta. * @param {number} [opts.min] The minimum value allowed. * @param {number} [otps.max] The maxiumum value allowed. * @return {number} The incremented value. */ $.arrowIncrement.compute = function (value, decrement, opts) { var computed, decimals, delta = 1, hasMin = opts && typeof opts.min === 'number', hasMax = opts && typeof opts.max === 'number'; // check for delta option if (opts && typeof opts.delta == 'number') { delta = opts.delta; } if (decrement) { // return if already less than the minimum if (hasMin && value < opts.min) { return value; } computed = value - delta; } else { // return if already more than the maximum if (hasMax && value > opts.max) { return value; } computed = value + delta; } // Correct floating point errors by rounding to the smallest decimal decimals = Math.max( $.arrowIncrement.decimals(value), $.arrowIncrement.decimals(delta) ); computed = +computed.toFixed(decimals); // If max and min overlap, max takes precedence if (hasMin && computed < opts.min) { computed = opts.min; } if (hasMax && computed > opts.max) { computed = opts.max; } return computed; }; /** * @static * @param {number} value How many decimals places for this value. * @return {number} The number of decimal places. */ $.arrowIncrement.decimals = function (value) { var str = '' + value, index = str.indexOf('.'); if (index >= 0) { return str.length - 1 - str.indexOf('.'); } else { return 0; } }; /** * @static * @param {string} value The input value. * @return {number} The input value as a number. */ $.arrowIncrement.parse = function (value) { var parsed = value.match(/^(\D*?)(\d*(,\d{3})*(\.\d+)?)\D*$/); if (parsed && parsed[2]) { if (parsed[1] && parsed[1].indexOf('-') >= 0) { return -parsed[2].replace(',', ''); } else { return +parsed[2].replace(',', ''); } } return NaN; }; // Add to jQuery $.fn.arrowIncrement = function (opts) { return this.each(function () { (new $.arrowIncrement(this, opts)); }); }; }(jQuery));