class CalcModule { constructor() { this.commands = {}; this.terminal = null; this.units = { 'km': { to: { 'miles': 0.621371, 'meters': 1000, 'feet': 3280.84 } }, 'miles': { to: { 'km': 1.60934, 'meters': 1609.34, 'feet': 5280 } }, 'meters': { to: { 'km': 0.001, 'miles': 0.000621371, 'feet': 3.28084 } }, 'feet': { to: { 'km': 0.0003048, 'miles': 0.000189394, 'meters': 0.3048 } }, 'kg': { to: { 'pounds': 2.20462, 'grams': 1000, 'ounces': 35.274 } }, 'pounds': { to: { 'kg': 0.453592, 'grams': 453.592, 'ounces': 16 } }, 'grams': { to: { 'kg': 0.001, 'pounds': 0.00220462, 'ounces': 0.035274 } }, 'ounces': { to: { 'kg': 0.0283495, 'pounds': 0.0625, 'grams': 28.3495 } }, 'celsius': { to: { 'fahrenheit': 'c2f', 'kelvin': 'c2k' } }, 'fahrenheit': { to: { 'celsius': 'f2c', 'kelvin': 'f2k' } }, 'kelvin': { to: { 'celsius': 'k2c', 'fahrenheit': 'k2f' } }, 'inch': { to: { 'cm': 2.54, 'mm': 25.4, 'feet': 0.0833333 } }, 'cm': { to: { 'inch': 0.393701, 'mm': 10, 'm': 0.01 } }, 'mm': { to: { 'cm': 0.1, 'inch': 0.0393701, 'm': 0.001 } }, 'liters': { to: { 'gallons': 0.264172, 'ml': 1000, 'cups': 4.22675 } }, 'gallons': { to: { 'liters': 3.78541, 'ml': 3785.41, 'cups': 16 } } }; } getHelpInfo() { return { 'calc': 'calc or calc to — Evaluate expressions or convert between units' }; } registerCommands() { this.commands['calc'] = (args) => this.calc(args); } calc(args) { if (!args[0]) return this.terminal.print('Usage: calc or calc to ', 'warning'); const input = args.join(' '); const convertMatch = input.match(/^([+-]?\d*\.?\d+)\s*(\w+)\s+(?:to|in|->)\s+(\w+)$/i); if (convertMatch) { return this.convert(parseFloat(convertMatch[1]), convertMatch[2].toLowerCase(), convertMatch[3].toLowerCase()); } this.evaluate(input); } evaluate(expr) { const sanitized = expr.replace(/[^0-9+\-*/.()%eE\s]/g, ''); if (!sanitized) return this.terminal.print('Invalid expression', 'error'); try { const result = new Function('return (' + sanitized + ')')(); if (typeof result === 'number' && !isNaN(result)) { this.terminal.print(`${sanitized} = ${result}`, 'success'); } else { this.terminal.print('Invalid result', 'error'); } } catch (e) { this.terminal.print(`Error: ${e.message}`, 'error'); } } convert(value, from, to) { if (!this.units[from]) return this.terminal.print(`Unknown unit: ${from}`, 'error'); if (!this.units[from].to[to]) { const avail = Object.keys(this.units[from].to).join(', '); return this.terminal.print(`Cannot convert ${from} to ${to}. Available: ${avail}`, 'warning'); } const factor = this.units[from].to[to]; if (typeof factor === 'string') { let result; if (factor === 'c2f') result = value * 9/5 + 32; else if (factor === 'c2k') result = value + 273.15; else if (factor === 'f2c') result = (value - 32) * 5/9; else if (factor === 'f2k') result = (value - 32) * 5/9 + 273.15; else if (factor === 'k2c') result = value - 273.15; else if (factor === 'k2f') result = (value - 273.15) * 9/5 + 32; else return this.terminal.print('Conversion not implemented', 'error'); this.terminal.print(`${value} ${from} = ${result.toFixed(4)} ${to}`, 'success'); } else { this.terminal.print(`${value} ${from} = ${(value * factor).toFixed(4)} ${to}`, 'success'); } } } if (typeof window.webx11 !== 'undefined' && window.webx11) { webx11.moduleManager.registerModule(new CalcModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/calc.js'); }