first commit
This commit is contained in:
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* jQuery Calculation Plug-in
|
||||
*
|
||||
* Copyright (c) 2007 Dan G. Switzer, II
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* Revision: 11
|
||||
* Version: 0.4.07
|
||||
*
|
||||
* Revision History
|
||||
* v0.4.07
|
||||
* - Added trim to parseNumber to fix issue with whitespace in elements
|
||||
*
|
||||
* v0.4.06
|
||||
* - Added support for calc() "format" callback so that if return value
|
||||
* is null, then value is not updated
|
||||
* - Added jQuery.isFunction() check for calc() callbacks
|
||||
*
|
||||
* v0.4.05
|
||||
* - Added support to the sum() & calc() method for automatically fixing precision
|
||||
* issues (will detect the max decimal spot in the number and fix to that
|
||||
* depth)
|
||||
*
|
||||
* v0.4.04
|
||||
* - Fixed bug #5420 by adding the defaults.cleanseNumber handler; you can
|
||||
* override this function to handle stripping number of extra digits
|
||||
*
|
||||
* v0.4.02
|
||||
* - Fixed bug where bind parameter was not being detecting if you specified
|
||||
* a string in method like sum(), avg(), etc.
|
||||
*
|
||||
* v0.4a
|
||||
* - Fixed bug in aggregate functions so that a string is passed to jQuery's
|
||||
* text() method (since numeric zero is interpetted as false)
|
||||
*
|
||||
* v0.4
|
||||
* - Added support for -$.99 values
|
||||
* - Fixed regex so that decimal values without leading zeros are correctly
|
||||
* parsed
|
||||
* - Removed defaults.comma setting
|
||||
* - Changed secondary regex that cleans additional formatting from parsed
|
||||
* number
|
||||
*
|
||||
* v0.3
|
||||
* - Refactored the aggregate methods (since they all use the same core logic)
|
||||
* to use the $.extend() method
|
||||
* - Added support for negative numbers in the regex)
|
||||
* - Added min/max aggregate methods
|
||||
* - Added defaults.onParseError and defaults.onParseClear methods to add logic for
|
||||
* parsing errors
|
||||
*
|
||||
* v0.2
|
||||
* - Fixed bug in sMethod in calc() (was using getValue, should have been setValue)
|
||||
* - Added arguments for sum() to allow auto-binding with callbacks
|
||||
* - Added arguments for avg() to allow auto-binding with callbacks
|
||||
*
|
||||
* v0.1a
|
||||
* - Added semi-colons after object declaration (for min protection)
|
||||
*
|
||||
* v0.1
|
||||
* - First public release
|
||||
*
|
||||
*/
|
||||
(function($){
|
||||
|
||||
// set the defaults
|
||||
var defaults = {
|
||||
// regular expression used to detect numbers, if you want to force the field to contain
|
||||
// numbers, you can add a ^ to the beginning or $ to the end of the regex to force the
|
||||
// the regex to match the entire string: /^(-|-\$)?(\d+(,\d{3})*(\.\d{1,})?|\.\d{1,})$/g
|
||||
reNumbers: /(-|-\$)?(\d+(,\d{3})*(\.\d{1,})?|\.\d{1,})/g
|
||||
// this function is used in the parseNumber() to cleanse up any found numbers
|
||||
// the function is intended to remove extra information found in a number such
|
||||
// as extra commas and dollar signs. override this function to strip European values
|
||||
, cleanseNumber: function (v){
|
||||
// cleanse the number one more time to remove extra data (like commas and dollar signs)
|
||||
// use this for European numbers: v.replace(/[^0-9,\-]/g, "").replace(/,/g, ".")
|
||||
return v.replace(/[^0-9.\-]/g, "");
|
||||
}
|
||||
// should the Field plug-in be used for getting values of :input elements?
|
||||
, useFieldPlugin: (!!$.fn.getValue)
|
||||
// a callback function to run when an parsing error occurs
|
||||
, onParseError: null
|
||||
// a callback function to run once a parsing error has cleared
|
||||
, onParseClear: null
|
||||
};
|
||||
|
||||
// set default options
|
||||
$.Calculation = {
|
||||
version: "0.4.07",
|
||||
setDefaults: function(options){
|
||||
$.extend(defaults, options);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* jQuery.fn.parseNumber()
|
||||
*
|
||||
* returns Array - detects the DOM element and returns it's value. input
|
||||
* elements return the field value, other DOM objects
|
||||
* return their text node
|
||||
*
|
||||
* NOTE: Breaks the jQuery chain, since it returns a Number.
|
||||
*
|
||||
* Examples:
|
||||
* $("input[name^='price']").parseNumber();
|
||||
* > This would return an array of potential number for every match in the selector
|
||||
*
|
||||
*/
|
||||
// the parseNumber() method -- break the chain
|
||||
$.fn.parseNumber = function(options){
|
||||
var aValues = [];
|
||||
options = $.extend(options, defaults);
|
||||
|
||||
this.each(
|
||||
function (){
|
||||
var
|
||||
// get a pointer to the current element
|
||||
$el = $(this),
|
||||
// determine what method to get it's value
|
||||
sMethod = ($el.is(":input") ? (defaults.useFieldPlugin ? "getValue" : "val") : "text"),
|
||||
// parse the string and get the first number we find
|
||||
v = $.trim($el[sMethod]()).match(defaults.reNumbers, "");
|
||||
|
||||
// if the value is null, use 0
|
||||
if( v == null ){
|
||||
v = 0; // update value
|
||||
// if there's a error callback, execute it
|
||||
if( jQuery.isFunction(options.onParseError) ) options.onParseError.apply($el, [sMethod]);
|
||||
$.data($el[0], "calcParseError", true);
|
||||
// otherwise we take the number we found and remove any commas
|
||||
} else {
|
||||
// clense the number one more time to remove extra data (like commas and dollar signs)
|
||||
v = options.cleanseNumber.apply(this, [v[0]]);
|
||||
// if there's a clear callback, execute it
|
||||
if( $.data($el[0], "calcParseError") && jQuery.isFunction(options.onParseClear) ){
|
||||
options.onParseClear.apply($el, [sMethod]);
|
||||
// clear the error flag
|
||||
$.data($el[0], "calcParseError", false);
|
||||
}
|
||||
}
|
||||
aValues.push(parseFloat(v, 10));
|
||||
}
|
||||
);
|
||||
|
||||
// return an array of values
|
||||
return aValues;
|
||||
};
|
||||
|
||||
/*
|
||||
* jQuery.fn.calc()
|
||||
*
|
||||
* returns Number - performance a calculation and updates the field
|
||||
*
|
||||
* Examples:
|
||||
* $("input[name='price']").calc();
|
||||
* > This would return the sum of all the fields named price
|
||||
*
|
||||
*/
|
||||
// the calc() method
|
||||
$.fn.calc = function(expr, vars, cbFormat, cbDone){
|
||||
var
|
||||
// create a pointer to the jQuery object
|
||||
$this = this
|
||||
// the value determine from the expression
|
||||
, exprValue = ""
|
||||
// track the precision to use
|
||||
, precision = 0
|
||||
// a pointer to the current jQuery element
|
||||
, $el
|
||||
// store an altered copy of the vars
|
||||
, parsedVars = {}
|
||||
// temp variable
|
||||
, tmp
|
||||
// the current method to use for updating the value
|
||||
, sMethod
|
||||
// a hash to store the local variables
|
||||
, _
|
||||
// track whether an error occured in the calculation
|
||||
, bIsError = false;
|
||||
|
||||
// look for any jQuery objects and parse the results into numbers
|
||||
for( var k in vars ){
|
||||
// replace the keys in the expression
|
||||
expr = expr.replace( (new RegExp("(" + k + ")", "g")), "_.$1");
|
||||
if( !!vars[k] && !!vars[k].jquery ){
|
||||
parsedVars[k] = vars[k].parseNumber();
|
||||
} else {
|
||||
parsedVars[k] = vars[k];
|
||||
}
|
||||
}
|
||||
|
||||
this.each(
|
||||
function (i, el){
|
||||
var p, len;
|
||||
// get a pointer to the current element
|
||||
$el = $(this);
|
||||
// determine what method to get it's value
|
||||
sMethod = ($el.is(":input") ? (defaults.useFieldPlugin ? "setValue" : "val") : "text");
|
||||
|
||||
// initialize the hash vars
|
||||
_ = {};
|
||||
for( var k in parsedVars ){
|
||||
if( typeof parsedVars[k] == "number" ){
|
||||
_[k] = parsedVars[k];
|
||||
} else if( typeof parsedVars[k] == "string" ){
|
||||
_[k] = parseFloat(parsedVars[k], 10);
|
||||
} else if( !!parsedVars[k] && (parsedVars[k] instanceof Array) ) {
|
||||
// if the length of the array is the same as number of objects in the jQuery
|
||||
// object we're attaching to, use the matching array value, otherwise use the
|
||||
// value from the first array item
|
||||
tmp = (parsedVars[k].length == $this.length) ? i : 0;
|
||||
_[k] = parsedVars[k][tmp];
|
||||
}
|
||||
|
||||
// if we're not a number, make it 0
|
||||
if( isNaN(_[k]) ) _[k] = 0;
|
||||
|
||||
// check for decimals and check the precision
|
||||
p = _[k].toString().match(/\.\d+$/gi);
|
||||
len = (p) ? p[0].length-1 : 0;
|
||||
|
||||
// track the highest level of precision
|
||||
if( len > precision ) precision = len;
|
||||
}
|
||||
|
||||
|
||||
// try the calculation
|
||||
try {
|
||||
exprValue = eval( expr );
|
||||
|
||||
// fix any the precision errors
|
||||
if( precision ) exprValue = Number(exprValue.toFixed(Math.max(precision, 4)));
|
||||
|
||||
// if there's a format callback, call it now
|
||||
if( jQuery.isFunction(cbFormat) ){
|
||||
// get return value
|
||||
var tmp = cbFormat.apply(this, [exprValue])
|
||||
// if we have a returned value (it's null null) use it
|
||||
if( !!tmp ) exprValue = tmp;
|
||||
}
|
||||
|
||||
// if there's an error, capture the error output
|
||||
} catch(e){
|
||||
exprValue = e;
|
||||
bIsError = true;
|
||||
}
|
||||
|
||||
// update the value
|
||||
$el[sMethod](exprValue.toString());
|
||||
}
|
||||
);
|
||||
|
||||
// if there's a format callback, call it now
|
||||
if( jQuery.isFunction(cbDone) ) cbDone.apply(this, [this]);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/*
|
||||
* Define all the core aggregate functions. All of the following methods
|
||||
* have the same functionality, but they perform different aggregate
|
||||
* functions.
|
||||
*
|
||||
* If this methods are called without any arguments, they will simple
|
||||
* perform the specified aggregate function and return the value. This
|
||||
* will break the jQuery chain.
|
||||
*
|
||||
* However, if you invoke the method with any arguments then a jQuery
|
||||
* object is returned, which leaves the chain intact.
|
||||
*
|
||||
*
|
||||
* jQuery.fn.sum()
|
||||
* returns Number - the sum of all fields
|
||||
*
|
||||
* jQuery.fn.avg()
|
||||
* returns Number - the avg of all fields
|
||||
*
|
||||
* jQuery.fn.min()
|
||||
* returns Number - the minimum value in the field
|
||||
*
|
||||
* jQuery.fn.max()
|
||||
* returns Number - the maximum value in the field
|
||||
*
|
||||
* Examples:
|
||||
* $("input[name='price']").sum();
|
||||
* > This would return the sum of all the fields named price
|
||||
*
|
||||
* $("input[name='price1'], input[name='price2'], input[name='price3']").sum();
|
||||
* > This would return the sum of all the fields named price1, price2 or price3
|
||||
*
|
||||
* $("input[name^=sum]").sum("keyup", "#totalSum");
|
||||
* > This would update the element with the id "totalSum" with the sum of all the
|
||||
* > fields whose name started with "sum" anytime the keyup event is triggered on
|
||||
* > those field.
|
||||
*
|
||||
* NOTE: The syntax above is valid for any of the aggregate functions
|
||||
*
|
||||
*/
|
||||
$.each(["sum", "avg", "min", "max"], function (i, method){
|
||||
$.fn[method] = function (bind, selector){
|
||||
// if no arguments, then return the result of the aggregate function
|
||||
if( arguments.length == 0 )
|
||||
return math[method](this.parseNumber());
|
||||
|
||||
// if the selector is an options object, get the options
|
||||
var bSelOpt = selector && (selector.constructor == Object) && !(selector instanceof jQuery);
|
||||
|
||||
// configure the options for this method
|
||||
var opt = bind && bind.constructor == Object ? bind : {
|
||||
bind: bind || "keyup"
|
||||
, selector: (!bSelOpt) ? selector : null
|
||||
, oncalc: null
|
||||
};
|
||||
|
||||
// if the selector is an options object, extend the options
|
||||
if( bSelOpt ) opt = jQuery.extend(opt, selector);
|
||||
|
||||
// if the selector exists, make sure it's a jQuery object
|
||||
if( !!opt.selector ) opt.selector = $(opt.selector);
|
||||
|
||||
var self = this
|
||||
, sMethod
|
||||
, doCalc = function (){
|
||||
// preform the aggregate function
|
||||
var value = math[method](self.parseNumber(opt));
|
||||
// check to make sure we have a selector
|
||||
if( !!opt.selector ){
|
||||
// determine how to set the value for the selector
|
||||
sMethod = (opt.selector.is(":input") ? (defaults.useFieldPlugin ? "setValue" : "val") : "text");
|
||||
// update the value
|
||||
opt.selector[sMethod](value.toString());
|
||||
}
|
||||
// if there's a callback, run it now
|
||||
if( jQuery.isFunction(opt.oncalc) ) opt.oncalc.apply(self, [value, opt]);
|
||||
};
|
||||
|
||||
// perform the aggregate function now, to ensure init values are updated
|
||||
doCalc();
|
||||
|
||||
// bind the doCalc function to run each time a key is pressed
|
||||
return self.bind(opt.bind, doCalc);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Mathmatical functions
|
||||
*/
|
||||
var math = {
|
||||
// sum an array
|
||||
sum: function (a){
|
||||
var total = 0, precision = 0;
|
||||
|
||||
// loop through the value and total them
|
||||
$.each(a, function (i, v){
|
||||
// check for decimals and check the precision
|
||||
var p = v.toString().match(/\.\d+$/gi), len = (p) ? p[0].length-1 : 0;
|
||||
// track the highest level of precision
|
||||
if( len > precision ) precision = len;
|
||||
// we add 0 to the value to ensure we get a numberic value
|
||||
total += v;
|
||||
});
|
||||
|
||||
// fix any the precision errors
|
||||
if( precision ) total = Number(total.toFixed(precision));
|
||||
|
||||
// return the values as a comma-delimited string
|
||||
return total;
|
||||
},
|
||||
// average an array
|
||||
avg: function (a){
|
||||
// return the values as a comma-delimited string
|
||||
return math.sum(a)/a.length;
|
||||
},
|
||||
// lowest number in array
|
||||
min: function (a){
|
||||
return Math.min.apply(Math, a);
|
||||
},
|
||||
// highest number in array
|
||||
max: function (a){
|
||||
return Math.max.apply(Math, a);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user