class ScientificCalculator { constructor(previousOperandTextElement, currentOperandTextElement) { this.previousOperandTextElement = previousOperandTextElement; this.currentOperandTextElement = currentOperandTextElement; this.angleMode = 'DEG'; // DEG or RAD this.shiftMode = false; this.alphaMode = false; this.lastAnswer = null; this.historyListElement = document.getElementById('history-list'); this.clear(); } clear() { this.currentOperand = '0'; this.expression = ''; this.resetScreen = false; this.updateDisplay(); } delete() { if (this.currentOperand === '0' || this.resetScreen) { this.currentOperand = '0'; this.resetScreen = false; return; } this.currentOperand = this.currentOperand.toString().slice(0, -1); if (this.currentOperand === '') this.currentOperand = '0'; this.updateDisplay(); } appendNumber(number) { if (this.resetScreen) { this.currentOperand = number.toString(); this.resetScreen = false; } else { if (this.currentOperand === '0' && number !== '.') { this.currentOperand = number.toString(); } else { this.currentOperand = this.currentOperand.toString() + number.toString(); } } this.updateDisplay(); } appendOperator(operator) { if (this.currentOperand === '' && this.expression === '') return; // Convert current to expression if we have one if (this.currentOperand !== '') { if (this.expression !== '') { this.expression += this.currentOperand; } else { this.expression = this.currentOperand; } this.currentOperand = ''; } // Add operator this.expression += ' ' + operator + ' '; this.updateDisplay(); } appendFunction(func) { if (this.resetScreen) { this.currentOperand = ''; this.resetScreen = false; } if (this.currentOperand === '0') { this.currentOperand = func + '('; } else { this.currentOperand += func + '('; } this.updateDisplay(); } appendConstant(constant) { if (this.resetScreen) { this.currentOperand = ''; this.resetScreen = false; } let value; switch(constant) { case 'pi': value = Math.PI; break; case 'e': value = Math.E; break; default: return; } if (this.currentOperand === '0') { this.currentOperand = value.toString(); } else { this.currentOperand += value.toString(); } this.updateDisplay(); } negate() { if (this.currentOperand === '0') return; if (this.currentOperand.startsWith('-')) { this.currentOperand = this.currentOperand.substring(1); } else { this.currentOperand = '-' + this.currentOperand; } this.updateDisplay(); } toRadians(degrees) { return degrees * (Math.PI / 180); } toDegrees(radians) { return radians * (180 / Math.PI); } computeTrig(func, value) { const radians = this.angleMode === 'DEG' ? this.toRadians(value) : value; switch(func) { case 'sin': return Math.sin(radians); case 'cos': return Math.cos(radians); case 'tan': return Math.tan(radians); default: return NaN; } } computeInverseTrig(func, value) { let result; switch(func) { case 'asin': result = Math.asin(value); break; case 'acos': result = Math.acos(value); break; case 'atan': result = Math.atan(value); break; default: return NaN; } return this.angleMode === 'DEG' ? this.toDegrees(result) : result; } factorial(n) { if (n < 0) return NaN; if (n === 0 || n === 1) return 1; if (n > 170) return Infinity; // Prevent overflow let result = 1; for (let i = 2; i <= n; i++) { result *= i; } return result; } evaluateExpression(expr) { try { // Replace mathematical symbols with JS equivalents let evalExpr = expr .replace(/×/g, '*') .replace(/÷/g, '/') .replace(/−/g, '-') .replace(/sin\(/g, 'Math.sin(') .replace(/cos\(/g, 'Math.cos(') .replace(/tan\(/g, 'Math.tan(') .replace(/log\(/g, 'Math.log10(') .replace(/ln\(/g, 'Math.log(') .replace(/√\(/g, 'Math.sqrt(') .replace(/π/g, Math.PI) .replace(/e(?![x])/g, Math.E); return eval(evalExpr); } catch (e) { return NaN; } } compute() { let fullExpression = this.expression; // Add current operand if exists if (this.currentOperand !== '') { fullExpression += this.currentOperand; } if (fullExpression === '') return; // Handle special functions let result; // Check for standalone trig functions const trigMatch = fullExpression.match(/^(sin|cos|tan)\(([^)]+)\)$/); if (trigMatch) { const value = parseFloat(trigMatch[2]); result = this.computeTrig(trigMatch[1], value); } // Check for square else if (fullExpression.startsWith('x²(')) { const value = parseFloat(fullExpression.slice(4, -1)); result = value * value; } // Check for cube else if (fullExpression.startsWith('x³(')) { const value = parseFloat(fullExpression.slice(4, -1)); result = value * value * value; } // Check for square root else if (fullExpression.startsWith('√(')) { const value = parseFloat(fullExpression.slice(3, -1)); result = Math.sqrt(value); } // Check for power (x^y format) else if (fullExpression.includes('^')) { const parts = fullExpression.split('^'); const base = parseFloat(parts[0]); const exponent = parseFloat(parts[1]); result = Math.pow(base, exponent); } // Check for factorial else if (fullExpression.startsWith('x!(')) { const value = parseFloat(fullExpression.slice(4, -1)); result = this.factorial(value); } // Check for log else if (fullExpression.startsWith('log(')) { const value = parseFloat(fullExpression.slice(5, -1)); result = Math.log10(value); } // Check for natural log else if (fullExpression.startsWith('ln(')) { const value = parseFloat(fullExpression.slice(4, -1)); result = Math.log(value); } // Check for absolute value else if (fullExpression.startsWith('Abs(')) { const value = parseFloat(fullExpression.slice(5, -1)); result = Math.abs(value); } // Check for power of 10 else if (fullExpression.startsWith('10^')) { const value = parseFloat(fullExpression.slice(4)); result = Math.pow(10, value); } // Check for exponential (e^x) else if (fullExpression.startsWith('e^')) { const value = parseFloat(fullExpression.slice(3)); result = Math.exp(value); } // Regular expression evaluation else { result = this.evaluateExpression(fullExpression); } // Handle precision if (!isNaN(result) && isFinite(result)) { result = Math.round(result * 1000000000000) / 1000000000000; // Add to history this.addToHistory(fullExpression, result); this.lastAnswer = result; this.currentOperand = result.toString(); this.expression = ''; this.resetScreen = true; } else { this.currentOperand = 'Error'; this.resetScreen = true; } this.updateDisplay(); } updateDisplay() { let displayValue = this.currentOperand || '0'; // Truncate if too long if (displayValue.length > 15) { const num = parseFloat(displayValue); if (!isNaN(num)) { displayValue = num.toExponential(6); } else { displayValue = displayValue.substring(0, 15); } } this.currentOperandTextElement.innerText = displayValue; this.previousOperandTextElement.innerText = this.expression; } addToHistory(expression, result) { const historyContainer = document.getElementById('history-list'); if (historyContainer.querySelector('.italic')) { historyContainer.innerHTML = ''; } const historyItem = document.createElement('div'); historyItem.className = 'history-item'; historyItem.innerHTML = `
${expression} =
${result}
`; historyItem.addEventListener('click', () => { this.currentOperand = result.toString(); this.resetScreen = true; this.updateDisplay(); }); historyContainer.prepend(historyItem); if (historyContainer.children.length > 20) { historyContainer.lastElementChild.remove(); } } clearHistory() { const historyContainer = document.getElementById('history-list'); historyContainer.innerHTML = '
No calculations
'; } toggleAngleMode() { this.angleMode = this.angleMode === 'DEG' ? 'RAD' : 'DEG'; document.getElementById('angle-mode').innerText = this.angleMode; document.getElementById('toggle-angle').innerText = this.angleMode === 'DEG' ? 'Switch to RAD' : 'Switch to DEG'; } toggleShift() { this.shiftMode = !this.shiftMode; document.querySelector('.shift-btn').classList.toggle('pressed', this.shiftMode); } } // DOM Elements const previousOperandTextElement = document.getElementById('previous-operand'); const currentOperandTextElement = document.getElementById('current-operand'); const calculator = new ScientificCalculator(previousOperandTextElement, currentOperandTextElement); // Number buttons document.querySelectorAll('[data-number]').forEach(button => { button.addEventListener('click', () => { calculator.appendNumber(button.innerText); }); }); // Operator buttons document.querySelectorAll('[data-operation]').forEach(button => { button.addEventListener('click', () => { calculator.appendOperator(button.innerText); }); }); // Function buttons document.querySelectorAll('[data-function]').forEach(button => { button.addEventListener('click', () => { const func = button.getAttribute('data-function'); switch(func) { case 'sin': case 'cos': case 'tan': calculator.appendFunction(func); break; case 'pi': calculator.appendConstant('pi'); break; case 'e': calculator.appendConstant('e'); break; case 'square': calculator.appendFunction('x²'); break; case 'cube': calculator.appendFunction('x³'); break; case 'power': calculator.appendOperator('^'); break; case 'log': calculator.appendFunction('log'); break; case 'ln': calculator.appendFunction('ln'); break; case 'sqrt': calculator.appendFunction('√'); break; case 'factorial': calculator.appendFunction('x!'); break; case 'abs': calculator.appendFunction('Abs'); break; case 'exp': calculator.appendOperator('10^'); break; case 'paren-left': calculator.appendOperator('('); break; case 'paren-right': calculator.appendOperator(')'); break; } }); }); // Action buttons document.querySelector('[data-equals]').addEventListener('click', () => { calculator.compute(); }); document.querySelector('[data-all-clear]').addEventListener('click', () => { calculator.clear(); }); document.querySelectorAll('[data-delete], [data-action="delete-del"]').forEach(button => { button.addEventListener('click', () => { calculator.delete(); }); }); document.querySelector('[data-action="negate"]').addEventListener('click', () => { calculator.negate(); }); document.querySelector('#toggle-angle').addEventListener('click', () => { calculator.toggleAngleMode(); }); document.querySelector('#clear-history').addEventListener('click', () => { calculator.clearHistory(); }); document.querySelector('[data-action="shift"]').addEventListener('click', () => { calculator.toggleShift(); }); document.querySelector('[data-action="mode"]').addEventListener('click', () => { calculator.toggleAngleMode(); }); // Keyboard Support document.addEventListener('keydown', (e) => { if (e.key >= '0' && e.key <= '9') { calculator.appendNumber(e.key); highlightButton(e.key); } if (e.key === '.') { calculator.appendNumber('.'); highlightButton('.'); } if (e.key === '=' || e.key === 'Enter') { e.preventDefault(); calculator.compute(); highlightButton('='); } if (e.key === 'Backspace') { calculator.delete(); highlightButton('DEL'); } if (e.key === 'Escape') { calculator.clear(); highlightButton('AC'); } if (e.key === '+' || e.key === '-' || e.key === '*' || e.key === '/') { let op = e.key; if(op === '/') op = '÷'; if(op === '*') op = '×'; calculator.appendOperator(op); highlightButton(op); } if (e.key === '(' || e.key === ')') { calculator.appendOperator(e.key); highlightButton(e.key); } if (e.key === '^') { calculator.appendOperator('^'); highlightButton('^'); } if (e.key === 's') { calculator.appendFunction('sin'); highlightButtonByFunc('sin'); } if (e.key === 'c') { calculator.appendFunction('cos'); highlightButtonByFunc('cos'); } if (e.key === 't') { calculator.appendFunction('tan'); highlightButtonByFunc('tan'); } if (e.key === 'r') { calculator.appendFunction('√'); highlightButtonByFunc('sqrt'); } if (e.key === 'l') { calculator.appendFunction('log'); highlightButtonByFunc('log'); } if (e.key === 'n') { calculator.appendFunction('ln'); highlightButtonByFunc('ln'); } }); function highlightButton(key) { const buttons = Array.from(document.querySelectorAll('.casio-btn')); const button = buttons.find(btn => btn.querySelector('.main-label')?.textContent === key); if (button) { button.classList.add('pressed'); setTimeout(() => { button.classList.remove('pressed'); }, 100); } } function highlightButtonByFunc(func) { const buttons = Array.from(document.querySelectorAll('.casio-btn')); const button = buttons.find(btn => btn.getAttribute('data-function') === func); if (button) { button.classList.add('pressed'); setTimeout(() => { button.classList.remove('pressed'); }, 100); } }