Files
geek-calc/calculator.js
snowprint 54f427ea21
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
init geek calc
2025-10-04 10:53:41 +08:00

184 lines
6.3 KiB
JavaScript

// 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 };
})();