feat(calculator): Scientific-Modus implementieren

Neuer Taschenrechner-Modus mit 6 wissenschaftlichen Buttons (√, x², xⁿ, π, e, ±),
Formel-Helfer (Kreisfläche, Kreisumfang, °C↔°F, Pythagoras, Prozent) und
erweiterter Tastaturunterstützung (p → Pi, ^ → Potenz).

- calc-scientific.js: IIFE, registriert 'scientific' Mode via Calculator.registerMode()
- newtab.html: Script-Tag nach calculator.js eingefügt (Load-Order eingehalten)
- i18n.js: 15 neue Keys pro Sprache (DE + EN)
- main.css: Styles für .calc-sci-buttons und .calc-formula-* Komponenten
This commit is contained in:
2026-04-16 21:59:10 +02:00
parent f5cebd8d34
commit 2f23c13de1
4 changed files with 374 additions and 0 deletions
+59
View File
@@ -1332,6 +1332,65 @@ body.show-desc .bm-desc { display: block; }
overflow-x: hidden;
}
/* Calculator Scientific Mode */
.calc-sci-buttons {
grid-template-columns: repeat(3, 1fr);
margin-bottom: 4px;
}
.calc-formula-helper {
border-top: 1px solid var(--border);
padding-top: 8px;
margin-top: 4px;
}
.calc-formula-label {
font-size: 9px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 4px;
}
.calc-formula-select {
width: 100%;
padding: 4px 6px;
background: rgba(0,0,0,0.3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 12px;
font-family: 'Rajdhani', sans-serif;
margin-bottom: 6px;
}
.calc-formula-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.calc-formula-row label {
font-size: 11px;
color: var(--text-secondary);
min-width: 50px;
}
.calc-formula-input {
flex: 1;
padding: 4px 6px;
background: rgba(0,0,0,0.3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 12px;
font-family: 'Rajdhani', sans-serif;
}
.calc-formula-result {
font-size: 14px;
color: var(--accent);
font-weight: 600;
font-family: 'Rajdhani', monospace;
text-align: right;
min-height: 20px;
padding: 2px 0;
}
/* ============================================
TIMER WIDGET
============================================ */
+284
View File
@@ -0,0 +1,284 @@
/* =============================================
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':
Calculator._currentExpr += 'sqrt(';
Calculator._updateDisplay();
break;
case 'square':
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;
}
});
})();
+30
View File
@@ -84,6 +84,21 @@ const STRINGS = {
'calculator.history': 'History',
'calculator.error': 'Fehler',
'calculator.tab.standard': 'Standard',
'calculator.tab.scientific': 'Wissenschaftlich',
'calculator.sci.formulas': 'Formel-Helfer',
'calculator.sci.select_formula': 'Formel wählen…',
'calculator.sci.formula.circle_area': 'Kreisfläche (π×r²)',
'calculator.sci.formula.circle_circumference':'Kreisumfang (2πr)',
'calculator.sci.formula.celsius_to_fahrenheit':'°C → °F',
'calculator.sci.formula.fahrenheit_to_celsius':'°F → °C',
'calculator.sci.formula.pythagoras': 'Pythagoras (√(a²+b²))',
'calculator.sci.formula.percentage': 'Prozentwert',
'calculator.sci.field.radius': 'Radius',
'calculator.sci.field.temp': 'Temperatur',
'calculator.sci.field.a': 'Seite a',
'calculator.sci.field.b': 'Seite b',
'calculator.sci.field.value': 'Wert',
'calculator.sci.field.percent': 'Prozent',
// Timer
'timer.title': 'Timer',
@@ -393,6 +408,21 @@ const STRINGS = {
'calculator.history': 'History',
'calculator.error': 'Error',
'calculator.tab.standard': 'Standard',
'calculator.tab.scientific': 'Scientific',
'calculator.sci.formulas': 'Formula Helper',
'calculator.sci.select_formula': 'Choose formula…',
'calculator.sci.formula.circle_area': 'Circle Area (π×r²)',
'calculator.sci.formula.circle_circumference':'Circle Circumference (2πr)',
'calculator.sci.formula.celsius_to_fahrenheit':'°C → °F',
'calculator.sci.formula.fahrenheit_to_celsius':'°F → °C',
'calculator.sci.formula.pythagoras': 'Pythagoras (√(a²+b²))',
'calculator.sci.formula.percentage': 'Percentage',
'calculator.sci.field.radius': 'Radius',
'calculator.sci.field.temp': 'Temperature',
'calculator.sci.field.a': 'Side a',
'calculator.sci.field.b': 'Side b',
'calculator.sci.field.value': 'Value',
'calculator.sci.field.percent': 'Percent',
// Timer
'timer.title': 'Timer',