casio-fx-master / script.js
drkasi's picture
i eed as casio fx scientific calculator,UI needs to be accurate
1b78809 verified
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 = `
<div class="history-expression">${expression} =</div>
<div class="history-result">${result}</div>
`;
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 = '<div class="text-center text-gray-600 text-xs italic">No calculations</div>';
}
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);
}
}