1 /** 2 * @class FormulaCalc 3 * @description Calculator object that calculates based on a given formula and mapped variables. Calculates across multiple rows as well, 4 * updating a single totals value based on a given mathematical operator. 5 * @constructor 6 * @param {Object} config Configuration object, all options of slikcalc.BaseCalc plus the following: 7 * <ul> 8 * <li>formula : Mathematical formula in string form. Variables denoted within the '{}' that map to vars definitions passed in on the addRow method. Example: "{a} + {b} = {c}". "{a} + {b}" is used as the formula, and {c} becomes the position that the eval'd result is placed into.</li> 9 * <li>total.vars : Variables configuration object, see slikcalc.FormulaCalc.addRow() for details. If passed in here, an initial row is added</li> 10 * </ul> 11 */ 12 slikcalc.FormulaCalc = function(config) { 13 this.parent.constructor.call(this, config); 14 config = config || {}; 15 this.formula = config.formula || ''; 16 this.rows = []; 17 this.variables = []; 18 if(config.vars !== undefined) { 19 this.addRow({ vars : config.vars }); 20 } 21 }; 22 slikcalc.extend(slikcalc.FormulaCalc, slikcalc.BaseCalc); 23 24 /** 25 * @description Formula used to calculate the total for a row 26 */ 27 slikcalc.FormulaCalc.prototype.formula = null; 28 29 /** 30 * @description Holds the formula value once it has been parsed, and the equality portion has been removed 31 */ 32 slikcalc.FormulaCalc.prototype.formulaParsed = null; 33 34 /** 35 * @description Property to hold the variable from the formula that specifies where the result goes 36 */ 37 slikcalc.FormulaCalc.prototype.resultVar = null; 38 39 /** 40 * @description Regular Expression to find variables in the formula 41 */ 42 slikcalc.FormulaCalc.prototype.varMatch = /\{(\w)\}/gi; 43 44 /** 45 * @description {Array} Array of row objects for the calculator 46 */ 47 slikcalc.FormulaCalc.prototype.rows = null; 48 49 /** 50 * @description {Array} Array of the variables parsed from the formula 51 */ 52 slikcalc.FormulaCalc.prototype.variables = null; 53 54 /** 55 * @description Method run on page load to parse the formula, and pull out variables within it. 56 */ 57 slikcalc.FormulaCalc.prototype.initialize = function() { 58 this.formulaParsed = this.formula; 59 if(this.formulaParsed.indexOf('=') !== -1) { 60 var formulaSplit = this.formulaParsed.split('='); 61 this.formulaParsed = formulaSplit[0]; 62 this.resultVar = this.varMatch.exec(slikcalc.trim(formulaSplit[1]))[1]; 63 } 64 this.varMatch.lastIndex = 0; 65 while((result = this.varMatch.exec(this.formulaParsed)) !== null) { 66 this.variables.push(result[1]); 67 } 68 this.varMatch.lastIndex = 0; 69 }; 70 71 /** 72 * @description Adds a row to the calculator to be included in the calculations 73 * @param {Object} rowConfig Configuration object for the row being added to the calculator. It may contain the following options: 74 * <ul> 75 * <li>vars : Object containing one to many variable definitions</li> 76 * <li>vars.x : Configuration object for variable 'x' where x represents a variable in the formula</li> 77 * <li>vars.x.id : Element id for the input mappted to variable 'x' in formula</li> 78 * <li>vars.x.defaultValue : (Optional) Value used in place of empty/null for variable 'x'. Defaults to 0</li> 79 * <li>checkbox.id : Element id of checkbox. Required if config[checkbox] included</li> 80 * <li>checkbox.invert : Defaults to false. If true, row is included in total calculcation when un-checked, and omitted when checked</li> 81 * </ul> 82 */ 83 slikcalc.FormulaCalc.prototype.addRow = function(rowConfig) { 84 rowConfig = rowConfig || {}; 85 if(rowConfig.checkbox !== undefined) { 86 slikcalc.addListener(rowConfig.checkbox.id, 'click', this.processCalculation, this); 87 rowConfig.checkbox.invert = rowConfig.checkbox.invert || false; 88 } 89 for(var idx in rowConfig.vars) { 90 if(rowConfig.vars.hasOwnProperty(idx)) { 91 var variable = rowConfig.vars[idx]; 92 variable.defaultValue = variable.defaultValue || 0; 93 rowConfig.registerListeners = rowConfig.registerListeners === true || (this.registerListeners === true && rowConfig.registerListeners !== false); 94 if(rowConfig.registerListeners === true) { 95 slikcalc.addListener(variable.id, 'keyup', this.keyupEvent, this); 96 } 97 } 98 } 99 this.rows.push(rowConfig); 100 }; 101 102 /** 103 * @description Processes the rows and applies the formula to each one. 104 */ 105 slikcalc.FormulaCalc.prototype.calculate = function() { 106 var total = 0.00; 107 for(var idx in this.rows) { 108 if(this.rows.hasOwnProperty(idx)) { 109 var includeRow = true, rowTotal, formulaString = this.formulaParsed; 110 if(this.rows[idx].checkbox !== undefined) { 111 var checkbox = this.rows[idx].checkbox; 112 includeRow = (checkbox.invert !== slikcalc.get(checkbox.id).checked); 113 } 114 for(var varIdx in this.variables) { 115 if(this.variables.hasOwnProperty(varIdx)) { 116 var variableName = this.variables[varIdx]; 117 var variable = this.rows[idx].vars[variableName]; 118 var value = variable.defaultValue; 119 if(slikcalc.get(variable.id) !== null) { 120 value = slikcalc.getValue(variable.id); 121 value = value === '' ? variable.defaultValue : value; 122 value = slikcalc.formatCurrency(value); 123 } 124 var variableRegex = new RegExp("\\{" + variableName + "\\}"); 125 formulaString = formulaString.replace(variableRegex, value); 126 } 127 } 128 rowTotal = slikcalc.formatCurrency(eval(formulaString)); 129 if(this.resultVar !== null) { 130 var resultId = this.rows[idx].vars[this.resultVar].id; 131 slikcalc.setAmount(resultId, rowTotal); 132 } 133 if(includeRow === true && this.totalOperator !== null) { 134 total = this.calculateTotal(total, parseFloat(rowTotal)); 135 } 136 } 137 } 138 if(this.totalId !== null) { 139 slikcalc.setAmount(this.totalId, total); 140 } 141 };