213 lines
5.6 KiB
JavaScript
213 lines
5.6 KiB
JavaScript
const MAX_DIGITS = 15;
|
|
|
|
const state = {
|
|
currentValue: "0",
|
|
previousValue: null,
|
|
operator: null,
|
|
shouldResetDisplay: false,
|
|
isError: false,
|
|
lastOperand: null,
|
|
lastOperator: null,
|
|
};
|
|
|
|
const expressionEl = document.getElementById("expression");
|
|
const resultEl = document.getElementById("result");
|
|
|
|
function formatNumber(str) {
|
|
if (str === "Error") return str;
|
|
if (str === "Infinity" || str === "-Infinity") return "Error";
|
|
if (str === "NaN") return "Error";
|
|
const num = parseFloat(str);
|
|
if (!isFinite(num)) return "Error";
|
|
if (Number.isInteger(num) && Math.abs(num) < 1e15) {
|
|
return String(num);
|
|
}
|
|
const fixed = num.toPrecision(12);
|
|
return String(parseFloat(fixed));
|
|
}
|
|
|
|
function updateDisplay() {
|
|
if (state.isError) {
|
|
resultEl.textContent = "Error";
|
|
resultEl.style.color = "#e94560";
|
|
} else {
|
|
resultEl.textContent = state.currentValue;
|
|
resultEl.style.color = "";
|
|
}
|
|
}
|
|
|
|
function clearError() {
|
|
if (state.isError) {
|
|
state.isError = false;
|
|
state.currentValue = "0";
|
|
state.previousValue = null;
|
|
state.operator = null;
|
|
state.lastOperand = null;
|
|
state.lastOperator = null;
|
|
state.shouldResetDisplay = false;
|
|
expressionEl.textContent = "";
|
|
}
|
|
}
|
|
|
|
function appendDigit(digit) {
|
|
clearError();
|
|
if (state.shouldResetDisplay) {
|
|
state.currentValue = digit;
|
|
state.shouldResetDisplay = false;
|
|
} else {
|
|
if (digit === "0" && state.currentValue === "0") return;
|
|
state.currentValue = state.currentValue === "0" ? digit : state.currentValue + digit;
|
|
}
|
|
state.currentValue = state.currentValue.slice(0, MAX_DIGITS);
|
|
updateDisplay();
|
|
}
|
|
|
|
function handleDecimal() {
|
|
clearError();
|
|
if (state.shouldResetDisplay) {
|
|
state.currentValue = "0.";
|
|
state.shouldResetDisplay = false;
|
|
updateDisplay();
|
|
return;
|
|
}
|
|
if (!state.currentValue.includes(".")) {
|
|
state.currentValue += ".";
|
|
}
|
|
updateDisplay();
|
|
}
|
|
|
|
function handleBackspace() {
|
|
clearError();
|
|
if (state.shouldResetDisplay) return;
|
|
if (state.currentValue.length <= 1 || (state.currentValue.length === 2 && state.currentValue.startsWith("-"))) {
|
|
state.currentValue = "0";
|
|
} else {
|
|
state.currentValue = state.currentValue.slice(0, -1);
|
|
}
|
|
updateDisplay();
|
|
}
|
|
|
|
function handleOperator(op) {
|
|
clearError();
|
|
const current = parseFloat(state.currentValue);
|
|
if (state.operator && !state.shouldResetDisplay) {
|
|
compute();
|
|
}
|
|
state.previousValue = current;
|
|
state.operator = op;
|
|
state.shouldResetDisplay = true;
|
|
expressionEl.textContent = `${formatNumber(String(current))} ${op}`;
|
|
}
|
|
|
|
function compute() {
|
|
const prev = state.previousValue;
|
|
const current = parseFloat(state.currentValue);
|
|
if (prev === null) return;
|
|
|
|
let result;
|
|
switch (state.operator) {
|
|
case "+": result = prev + current; break;
|
|
case "-": result = prev - current; break;
|
|
case "*": result = prev * current; break;
|
|
case "/":
|
|
if (current === 0) {
|
|
state.isError = true;
|
|
state.currentValue = "Error";
|
|
state.operator = null;
|
|
state.previousValue = null;
|
|
updateDisplay();
|
|
return;
|
|
}
|
|
result = prev / current;
|
|
break;
|
|
default: return;
|
|
}
|
|
|
|
state.lastOperand = current;
|
|
state.lastOperator = state.operator;
|
|
state.currentValue = formatNumber(String(result));
|
|
state.operator = null;
|
|
state.previousValue = null;
|
|
state.shouldResetDisplay = true;
|
|
expressionEl.textContent = "";
|
|
updateDisplay();
|
|
}
|
|
|
|
function handleEquals() {
|
|
clearError();
|
|
if (state.operator) {
|
|
compute();
|
|
} else if (state.lastOperator !== null && state.lastOperand !== null) {
|
|
state.previousValue = parseFloat(state.currentValue);
|
|
state.operator = state.lastOperator;
|
|
compute();
|
|
}
|
|
}
|
|
|
|
function handleClear() {
|
|
state.currentValue = "0";
|
|
state.previousValue = null;
|
|
state.operator = null;
|
|
state.lastOperand = null;
|
|
state.lastOperator = null;
|
|
state.shouldResetDisplay = false;
|
|
state.isError = false;
|
|
expressionEl.textContent = "";
|
|
resultEl.style.color = "";
|
|
updateDisplay();
|
|
}
|
|
|
|
function handleSign() {
|
|
if (state.isError) return;
|
|
if (state.shouldResetDisplay) return;
|
|
state.currentValue = String(-parseFloat(state.currentValue));
|
|
updateDisplay();
|
|
}
|
|
|
|
function handlePercent() {
|
|
if (state.isError) return;
|
|
const num = parseFloat(state.currentValue);
|
|
if (state.operator && state.previousValue !== null) {
|
|
const adjusted = state.previousValue * (num / 100);
|
|
state.currentValue = formatNumber(String(adjusted));
|
|
} else {
|
|
state.currentValue = formatNumber(String(num / 100));
|
|
}
|
|
updateDisplay();
|
|
}
|
|
|
|
document.getElementById("buttons").addEventListener("click", (e) => {
|
|
const btn = e.target.closest("button");
|
|
if (!btn) return;
|
|
|
|
const action = btn.dataset.action;
|
|
switch (action) {
|
|
case "digit": appendDigit(btn.dataset.value); break;
|
|
case "decimal": handleDecimal(); break;
|
|
case "backspace": handleBackspace(); break;
|
|
case "operator": handleOperator(btn.dataset.value); break;
|
|
case "equals": handleEquals(); break;
|
|
case "clear": handleClear(); break;
|
|
case "sign": handleSign(); break;
|
|
}
|
|
});
|
|
|
|
document.addEventListener("keydown", (e) => {
|
|
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
if (e.key >= "0" && e.key <= "9") {
|
|
appendDigit(e.key);
|
|
return;
|
|
}
|
|
switch (e.key) {
|
|
case ".": handleDecimal(); break;
|
|
case "Backspace": handleBackspace(); break;
|
|
case "Delete": handleClear(); break;
|
|
case "Escape": handleClear(); break;
|
|
case "Enter":
|
|
case "=": handleEquals(); break;
|
|
case "+": handleOperator("+"); break;
|
|
case "-": handleOperator("-"); break;
|
|
case "*": handleOperator("*"); break;
|
|
case "/": handleOperator("/"); break;
|
|
}
|
|
});
|