// Calculator core module const Calculator = (function() { class Calculator { constructor() { this.clear(); } clear() { this.currentValue = '0'; this.operator = null; this.previousValue = null; this.shouldResetDisplay = false; } getCurrentDisplay() { return this.currentValue; } // Add two numbers add(a, b) { return a + b; } // Subtract two numbers subtract(a, b) { return a - b; } // Multiply two numbers multiply(a, b) { return a * b; } // Divide two numbers (handles division by zero) divide(a, b) { if (b === 0) { return Infinity; } return a / b; } // Handle percentage operations percentage(value, percent = null) { if (percent !== null) { // If two arguments, calculate percentage of value return value * (percent / 100); } else { // If single argument, convert to decimal (e.g., 50% = 0.5) return value / 100; } } // Toggle sign of a number toggleSign(value) { return -value; } // Evaluate an arithmetic expression in standard notation evaluate(expression) { try { // Basic validation to prevent dangerous eval usage // Only allow numbers, operators, parentheses, and decimal points if (!/^[0-9+\-*/(). %]+$/.test(expression)) { return 'Error'; } // Handle percentage expressions by evaluating them properly // Replace 'X%' with '(X/100)' for proper evaluation let processedExpression = expression.replace(/(\d+(\.\d+)?)%/g, (match, number) => { return `(${number}/100)`; }); // Handle expressions like "100 + 10%" by replacing with "100 + (100 * 0.1)" processedExpression = processedExpression.replace(/(\d+(?:\.\d+)?)\s*\+\s*(\d+(?:\.\d+)?)%/g, (match, base, percent) => { return `${base} + (${base} * ${percent}/100)`; }); processedExpression = processedExpression.replace(/(\d+(?:\.\d+)?)\s*-\s*(\d+(?:\.\d+)?)%/g, (match, base, percent) => { return `${base} - (${base} * ${percent}/100)`; }); processedExpression = processedExpression.replace(/(\d+(?:\.\d+)?)\s*\*\s*(\d+(?:\.\d+)?)%/g, (match, base, percent) => { return `${base} * (${percent}/100)`; }); // Use Function constructor instead of eval for safety const result = new Function('return ' + processedExpression)(); // Check for invalid results if (isNaN(result) || !isFinite(result)) { return 'Error'; } return result; } catch (error) { return 'Error'; } } // Process input for UI interactions processInput(value) { if (['+', '-', '*', '/'].includes(value)) { // Handle operator input if (this.operator && !this.shouldResetDisplay) { // If there's already an operator, evaluate the current expression this.calculate(); } this.operator = value; this.previousValue = parseFloat(this.currentValue); this.shouldResetDisplay = true; } else if (value === '=') { this.calculate(); } else if (value === 'C' || value === 'CE') { this.clear(); } else if (value === '±') { // Toggle sign this.currentValue = this.toggleSign(parseFloat(this.currentValue)).toString(); } else if (value === '.') { // Handle decimal point if (this.shouldResetDisplay) { this.currentValue = '0.'; this.shouldResetDisplay = false; } else if (!this.currentValue.includes('.')) { this.currentValue += '.'; } } else if (['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(value)) { // Handle number input if (this.shouldResetDisplay) { this.currentValue = value; this.shouldResetDisplay = false; } else { if (this.currentValue === '0') { this.currentValue = value; } else { this.currentValue += value; } } } } // Perform calculation based on current values calculate() { if (this.operator === null || this.previousValue === null) { return; } const current = parseFloat(this.currentValue); let result; switch (this.operator) { case '+': result = this.add(this.previousValue, current); break; case '-': result = this.subtract(this.previousValue, current); break; case '*': result = this.multiply(this.previousValue, current); break; case '/': result = this.divide(this.previousValue, current); break; default: return; } // Check for invalid results if (isNaN(result) || !isFinite(result)) { this.currentValue = 'Error'; } else { // Round to avoid floating point precision issues this.currentValue = Math.round(result * 100000000) / 100000000; this.currentValue = this.currentValue.toString(); } this.operator = null; this.previousValue = null; this.shouldResetDisplay = true; } } return { Calculator }; })();