259 lines
9.9 KiB
JavaScript
259 lines
9.9 KiB
JavaScript
// Hex calculator functionality
|
|
|
|
export class Calculator {
|
|
constructor() {
|
|
this.state = {
|
|
currentValue: 0,
|
|
previousValue: 0,
|
|
operation: null,
|
|
waitingForOperand: false
|
|
};
|
|
}
|
|
|
|
updateDisplay() {
|
|
const value = Math.max(0, Math.min(Math.floor(this.state.currentValue), Number.MAX_SAFE_INTEGER));
|
|
|
|
const hexStr = '0x' + value.toString(16).toUpperCase();
|
|
const decStr = value.toString(10);
|
|
const binStr = '0b' + value.toString(2);
|
|
|
|
// Update only the calculator inside the currently active tab to avoid duplicate-id issues
|
|
const activeContent = document.querySelector('.tab-content.active');
|
|
if (!activeContent) {
|
|
// Fallback: try to update the first calculator on the page
|
|
const fallbackInput = document.querySelector('.calc-input-field');
|
|
if (fallbackInput) fallbackInput.value = hexStr;
|
|
const fallbackHex = document.querySelector('.hex-value');
|
|
if (fallbackHex) fallbackHex.textContent = hexStr;
|
|
const fallbackDec = document.querySelector('.dec-value');
|
|
if (fallbackDec) fallbackDec.textContent = decStr;
|
|
const fallbackBin = document.querySelector('.bin-value');
|
|
if (fallbackBin) fallbackBin.textContent = binStr;
|
|
return;
|
|
}
|
|
|
|
const input = activeContent.querySelector('.calc-input-field');
|
|
if (input) input.value = hexStr;
|
|
|
|
const hexElement = activeContent.querySelector('.hex-value');
|
|
if (hexElement) hexElement.textContent = hexStr;
|
|
|
|
const decElement = activeContent.querySelector('.dec-value');
|
|
if (decElement) decElement.textContent = decStr;
|
|
|
|
const binElement = activeContent.querySelector('.bin-value');
|
|
if (binElement) binElement.textContent = binStr;
|
|
}
|
|
|
|
input(digit) {
|
|
if (this.state.waitingForOperand) {
|
|
this.state.currentValue = 0;
|
|
this.state.waitingForOperand = false;
|
|
}
|
|
|
|
const digitValue = parseInt(digit, 16);
|
|
if (isNaN(digitValue)) return;
|
|
|
|
const newValue = this.state.currentValue * 16 + digitValue;
|
|
if (newValue <= Number.MAX_SAFE_INTEGER && newValue >= 0) {
|
|
this.state.currentValue = newValue;
|
|
}
|
|
|
|
this.updateDisplay();
|
|
}
|
|
|
|
operation(operation) {
|
|
// If there is a pending operation and we are not waiting for a new operand,
|
|
// evaluate it first so chained operations work as expected.
|
|
if (this.state.operation !== null && !this.state.waitingForOperand) {
|
|
this.equals(false);
|
|
}
|
|
|
|
this.state.previousValue = this.state.currentValue;
|
|
this.state.operation = operation;
|
|
this.state.waitingForOperand = true;
|
|
}
|
|
|
|
equals(updateDisplay = true) {
|
|
if (this.state.operation && this.state.previousValue !== null) {
|
|
const prev = Math.floor(this.state.previousValue);
|
|
const curr = Math.floor(this.state.currentValue);
|
|
|
|
try {
|
|
let result = 0;
|
|
switch (this.state.operation) {
|
|
case '+':
|
|
result = prev + curr;
|
|
break;
|
|
case '-':
|
|
result = prev - curr;
|
|
break;
|
|
case '*':
|
|
result = prev * curr;
|
|
break;
|
|
case '/':
|
|
if (curr === 0) {
|
|
alert('Division durch Null!');
|
|
return;
|
|
}
|
|
result = Math.floor(prev / curr);
|
|
break;
|
|
case 'MOD':
|
|
if (curr === 0) {
|
|
alert('Modulo durch Null!');
|
|
return;
|
|
}
|
|
result = prev % curr;
|
|
break;
|
|
}
|
|
|
|
this.state.currentValue = Math.max(0, Math.min(result, Number.MAX_SAFE_INTEGER));
|
|
|
|
} catch (error) {
|
|
alert('Berechnungsfehler: ' + error.message);
|
|
this.state.currentValue = 0;
|
|
}
|
|
|
|
this.state.operation = null;
|
|
this.state.previousValue = 0;
|
|
this.state.waitingForOperand = true;
|
|
}
|
|
|
|
if (updateDisplay) {
|
|
this.updateDisplay();
|
|
}
|
|
}
|
|
|
|
clear() {
|
|
this.state.currentValue = 0;
|
|
this.state.previousValue = 0;
|
|
this.state.operation = null;
|
|
this.state.waitingForOperand = false;
|
|
this.updateDisplay();
|
|
}
|
|
|
|
backspace() {
|
|
if (!this.state.waitingForOperand) {
|
|
this.state.currentValue = Math.floor(this.state.currentValue / 16);
|
|
this.updateDisplay();
|
|
}
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Event delegation for calculator buttons
|
|
document.addEventListener('click', (e) => {
|
|
// Only handle clicks on calculator buttons
|
|
if (!e.target.classList.contains('calc-btn')) return;
|
|
|
|
const button = e.target;
|
|
|
|
if (button.classList.contains('number')) {
|
|
this.input(button.textContent);
|
|
} else if (button.classList.contains('operation')) {
|
|
this.operation(button.textContent);
|
|
} else if (button.classList.contains('equals')) {
|
|
this.equals();
|
|
} else if (button.classList.contains('clear')) {
|
|
this.clear();
|
|
} else if (button.classList.contains('backspace')) {
|
|
this.backspace();
|
|
}
|
|
});
|
|
|
|
// Keyboard support
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.target.tagName === 'INPUT' && !e.target.readOnly) return;
|
|
|
|
const key = e.key.toUpperCase();
|
|
|
|
if (/^[0-9A-F]$/.test(key)) {
|
|
e.preventDefault();
|
|
this.input(key);
|
|
} else if (key === '+') {
|
|
e.preventDefault();
|
|
this.operation('+');
|
|
} else if (key === '-') {
|
|
e.preventDefault();
|
|
this.operation('-');
|
|
} else if (key === '*') {
|
|
e.preventDefault();
|
|
this.operation('*');
|
|
} else if (key === '/') {
|
|
e.preventDefault();
|
|
this.operation('/');
|
|
} else if (key === 'ENTER' || key === '=') {
|
|
e.preventDefault();
|
|
this.equals();
|
|
} else if (key === 'ESCAPE' || key === 'DELETE') {
|
|
// Use Escape or Delete to clear. Do not use 'C' since it collides with hex digit C.
|
|
e.preventDefault();
|
|
this.clear();
|
|
} else if (key === 'BACKSPACE') {
|
|
e.preventDefault();
|
|
this.backspace();
|
|
}
|
|
});
|
|
}
|
|
|
|
generateHTML() {
|
|
return `
|
|
<div class="calculator-container">
|
|
<div class="calc-display">
|
|
<div class="calc-input">
|
|
<input type="text" class="calc-input-field" placeholder="0x0" readonly>
|
|
</div>
|
|
<div class="calc-conversions">
|
|
<div class="conversion-row">
|
|
<span class="conv-label">HEX:</span>
|
|
<span class="conv-value hex-value">0x0</span>
|
|
</div>
|
|
<div class="conversion-row">
|
|
<span class="conv-label">DEC:</span>
|
|
<span class="conv-value dec-value">0</span>
|
|
</div>
|
|
<div class="conversion-row">
|
|
<span class="conv-label">BIN:</span>
|
|
<span class="conv-value bin-value">0b0</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="calc-buttons">
|
|
<div class="calc-row">
|
|
<button class="calc-btn number">A</button>
|
|
<button class="calc-btn number">B</button>
|
|
<button class="calc-btn number">C</button>
|
|
<button class="calc-btn number">D</button>
|
|
<button class="calc-btn clear">CLR</button>
|
|
</div>
|
|
<div class="calc-row">
|
|
<button class="calc-btn number">E</button>
|
|
<button class="calc-btn number">F</button>
|
|
<button class="calc-btn number">7</button>
|
|
<button class="calc-btn number">8</button>
|
|
<button class="calc-btn operation">/</button>
|
|
</div>
|
|
<div class="calc-row">
|
|
<button class="calc-btn number">4</button>
|
|
<button class="calc-btn number">5</button>
|
|
<button class="calc-btn number">6</button>
|
|
<button class="calc-btn operation">*</button>
|
|
<button class="calc-btn operation">MOD</button>
|
|
</div>
|
|
<div class="calc-row">
|
|
<button class="calc-btn number">1</button>
|
|
<button class="calc-btn number">2</button>
|
|
<button class="calc-btn number">3</button>
|
|
<button class="calc-btn operation">-</button>
|
|
<button class="calc-btn operation">+</button>
|
|
</div>
|
|
<div class="calc-row">
|
|
<button class="calc-btn number wide">0</button>
|
|
<button class="calc-btn number">9</button>
|
|
<button class="calc-btn backspace">⌫</button>
|
|
<button class="calc-btn equals">=</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
} |