3dd9723271
sqrt und x² transferieren _lastResult in _currentExpr wenn Expression leer. handleNegate Regex akzeptiert auch dezimal-first Zahlen (.5). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
295 lines
8.5 KiB
JavaScript
295 lines
8.5 KiB
JavaScript
/* =============================================
|
|
HELLION NEWTAB — calc-scientific.js
|
|
Scientific-Modus für Calculator Widget
|
|
============================================= */
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
const FORMULAS = [
|
|
{
|
|
key: 'circle_area',
|
|
fields: [{ key: 'radius', default: '' }],
|
|
calc: (vals) => Math.PI * vals.radius * vals.radius
|
|
},
|
|
{
|
|
key: 'circle_circumference',
|
|
fields: [{ key: 'radius', default: '' }],
|
|
calc: (vals) => 2 * Math.PI * vals.radius
|
|
},
|
|
{
|
|
key: 'celsius_to_fahrenheit',
|
|
fields: [{ key: 'temp', default: '' }],
|
|
calc: (vals) => (vals.temp * 9 / 5) + 32
|
|
},
|
|
{
|
|
key: 'fahrenheit_to_celsius',
|
|
fields: [{ key: 'temp', default: '' }],
|
|
calc: (vals) => (vals.temp - 32) * 5 / 9
|
|
},
|
|
{
|
|
key: 'pythagoras',
|
|
fields: [{ key: 'a', default: '' }, { key: 'b', default: '' }],
|
|
calc: (vals) => Math.sqrt(vals.a * vals.a + vals.b * vals.b)
|
|
},
|
|
{
|
|
key: 'percentage',
|
|
fields: [{ key: 'value', default: '' }, { key: 'percent', default: '' }],
|
|
calc: (vals) => vals.value * vals.percent / 100
|
|
}
|
|
];
|
|
|
|
let _keyboardExtHandler = null;
|
|
|
|
function renderSciButtons(container) {
|
|
const grid = document.createElement('div');
|
|
grid.className = 'calc-buttons calc-sci-buttons';
|
|
|
|
const buttons = [
|
|
['√', 'sqrt', 'operator'],
|
|
['x²', 'square', 'operator'],
|
|
['xⁿ', 'power', 'operator'],
|
|
['π', 'pi', 'operator'],
|
|
['e', 'euler', 'operator'],
|
|
['±', 'negate', 'operator']
|
|
];
|
|
|
|
buttons.forEach(([label, value, cls]) => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'calc-btn' + (cls ? ' ' + cls : '');
|
|
btn.textContent = label;
|
|
btn.type = 'button';
|
|
btn.addEventListener('click', () => handleSciKey(value));
|
|
grid.appendChild(btn);
|
|
});
|
|
|
|
container.appendChild(grid);
|
|
}
|
|
|
|
function handleSciKey(key) {
|
|
switch (key) {
|
|
case 'sqrt':
|
|
if (!Calculator._currentExpr && Calculator._lastResult) {
|
|
Calculator._currentExpr = 'sqrt(' + Calculator._lastResult + ')';
|
|
Calculator._lastResult = '';
|
|
Calculator._updateDisplay();
|
|
break;
|
|
}
|
|
Calculator._currentExpr += 'sqrt(';
|
|
Calculator._updateDisplay();
|
|
break;
|
|
case 'square':
|
|
if (!Calculator._currentExpr && Calculator._lastResult) {
|
|
Calculator._currentExpr = Calculator._lastResult;
|
|
Calculator._lastResult = '';
|
|
}
|
|
Calculator._currentExpr += '^2';
|
|
Calculator._updateDisplay();
|
|
break;
|
|
case 'power':
|
|
Calculator._handleKey('^');
|
|
break;
|
|
case 'pi':
|
|
Calculator._currentExpr += '3.14159265359';
|
|
Calculator._updateDisplay();
|
|
break;
|
|
case 'euler':
|
|
Calculator._currentExpr += '2.71828182846';
|
|
Calculator._updateDisplay();
|
|
break;
|
|
case 'negate':
|
|
handleNegate();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function handleNegate() {
|
|
const expr = Calculator._currentExpr;
|
|
if (!expr && Calculator._lastResult) {
|
|
const num = parseFloat(Calculator._lastResult);
|
|
if (!isNaN(num)) {
|
|
Calculator._currentExpr = String(-num);
|
|
Calculator._lastResult = '';
|
|
Calculator._updateDisplay();
|
|
}
|
|
return;
|
|
}
|
|
const match = expr.match(/(-?\d*\.?\d+)$/);
|
|
if (match) {
|
|
const num = parseFloat(match[1]);
|
|
const negated = String(-num);
|
|
Calculator._currentExpr = expr.slice(0, expr.length - match[1].length) + negated;
|
|
Calculator._updateDisplay();
|
|
}
|
|
}
|
|
|
|
function renderFormulaHelper(container) {
|
|
const wrapper = document.createElement('div');
|
|
wrapper.className = 'calc-formula-helper';
|
|
|
|
const label = document.createElement('div');
|
|
label.className = 'calc-formula-label';
|
|
label.textContent = t('calculator.sci.formulas');
|
|
|
|
const select = document.createElement('select');
|
|
select.className = 'calc-formula-select';
|
|
|
|
const emptyOpt = document.createElement('option');
|
|
emptyOpt.value = '';
|
|
emptyOpt.textContent = t('calculator.sci.select_formula');
|
|
select.appendChild(emptyOpt);
|
|
|
|
FORMULAS.forEach((f, i) => {
|
|
const opt = document.createElement('option');
|
|
opt.value = String(i);
|
|
opt.textContent = t('calculator.sci.formula.' + f.key);
|
|
select.appendChild(opt);
|
|
});
|
|
|
|
const inputsContainer = document.createElement('div');
|
|
inputsContainer.className = 'calc-formula-inputs';
|
|
|
|
const resultContainer = document.createElement('div');
|
|
resultContainer.className = 'calc-formula-result';
|
|
|
|
select.addEventListener('change', () => {
|
|
while (inputsContainer.firstChild) {
|
|
inputsContainer.removeChild(inputsContainer.firstChild);
|
|
}
|
|
resultContainer.textContent = '';
|
|
|
|
const idx = parseInt(select.value, 10);
|
|
if (isNaN(idx)) return;
|
|
|
|
const formula = FORMULAS[idx];
|
|
renderFormulaInputs(formula, inputsContainer, resultContainer);
|
|
});
|
|
|
|
wrapper.append(label, select, inputsContainer, resultContainer);
|
|
container.appendChild(wrapper);
|
|
}
|
|
|
|
function renderFormulaInputs(formula, inputsEl, resultEl) {
|
|
const inputs = {};
|
|
|
|
formula.fields.forEach(field => {
|
|
const row = document.createElement('div');
|
|
row.className = 'calc-formula-row';
|
|
|
|
const lbl = document.createElement('label');
|
|
lbl.textContent = t('calculator.sci.field.' + field.key);
|
|
|
|
const inp = document.createElement('input');
|
|
inp.type = 'number';
|
|
inp.className = 'calc-formula-input';
|
|
inp.placeholder = '0';
|
|
inp.step = 'any';
|
|
inputs[field.key] = inp;
|
|
|
|
inp.addEventListener('input', () => {
|
|
recalcFormula(formula, inputs, resultEl);
|
|
});
|
|
|
|
row.append(lbl, inp);
|
|
inputsEl.appendChild(row);
|
|
});
|
|
}
|
|
|
|
function recalcFormula(formula, inputs, resultEl) {
|
|
const vals = {};
|
|
let allValid = true;
|
|
|
|
for (const field of formula.fields) {
|
|
const v = parseFloat(inputs[field.key].value);
|
|
if (isNaN(v)) { allValid = false; break; }
|
|
vals[field.key] = v;
|
|
}
|
|
|
|
if (!allValid) {
|
|
resultEl.textContent = '';
|
|
return;
|
|
}
|
|
|
|
const result = formula.calc(vals);
|
|
if (result === null || !isFinite(result)) {
|
|
resultEl.textContent = t('calculator.error');
|
|
return;
|
|
}
|
|
|
|
resultEl.textContent = '= ' + Calculator._formatResult(result);
|
|
}
|
|
|
|
function bindSciKeyboard(widgetEl) {
|
|
_keyboardExtHandler = (e) => {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
if (e.target.contentEditable === 'true') return;
|
|
|
|
if (e.key === 'p') {
|
|
handleSciKey('pi');
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
} else if (e.key === '^') {
|
|
handleSciKey('power');
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
};
|
|
widgetEl.addEventListener('keydown', _keyboardExtHandler);
|
|
}
|
|
|
|
Calculator.registerMode('scientific', {
|
|
label: '📐',
|
|
shortName: 'Sci',
|
|
titleKey: 'calculator.tab.scientific',
|
|
|
|
render(bodyEl) {
|
|
bodyEl.style.padding = '8px';
|
|
bodyEl.style.display = 'flex';
|
|
bodyEl.style.flexDirection = 'column';
|
|
bodyEl.style.flex = '1';
|
|
bodyEl.style.overflow = 'hidden';
|
|
|
|
const display = document.createElement('div');
|
|
display.className = 'calc-display';
|
|
|
|
const exprEl = document.createElement('div');
|
|
exprEl.className = 'calc-expression';
|
|
Calculator._displayExprEl = exprEl;
|
|
|
|
const resultEl = document.createElement('div');
|
|
resultEl.className = 'calc-result';
|
|
resultEl.textContent = Calculator._lastResult || '0';
|
|
Calculator._displayResultEl = resultEl;
|
|
|
|
display.append(exprEl, resultEl);
|
|
|
|
const sciSection = document.createElement('div');
|
|
renderSciButtons(sciSection);
|
|
|
|
const stdButtons = Calculator._createButtons();
|
|
const historyEl = Calculator._createHistoryPanel();
|
|
|
|
const formulaSection = document.createElement('div');
|
|
renderFormulaHelper(formulaSection);
|
|
|
|
bodyEl.append(display, sciSection, stdButtons, historyEl, formulaSection);
|
|
Calculator._updateDisplay();
|
|
|
|
const entry = WidgetManager._widgets.get(Calculator.WIDGET_ID);
|
|
if (entry) bindSciKeyboard(entry.el);
|
|
},
|
|
|
|
destroy() {
|
|
if (_keyboardExtHandler) {
|
|
const entry = WidgetManager._widgets.get(Calculator.WIDGET_ID);
|
|
if (entry) {
|
|
entry.el.removeEventListener('keydown', _keyboardExtHandler);
|
|
}
|
|
_keyboardExtHandler = null;
|
|
}
|
|
Calculator._displayExprEl = null;
|
|
Calculator._displayResultEl = null;
|
|
}
|
|
});
|
|
})();
|