Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Modern Glass Calculator</title> | |
| <script src="https://unpkg.com/@phosphor-icons/web"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --primary-bg: #0f172a; | |
| --glass-bg: rgba(255, 255, 255, 0.05); | |
| --glass-border: rgba(255, 255, 255, 0.1); | |
| --text-primary: #f8fafc; | |
| --text-secondary: #94a3b8; | |
| --accent-color: #3b82f6; | |
| --accent-hover: #2563eb; | |
| --operator-color: #f59e0b; | |
| --operator-hover: #d97706; | |
| --danger-color: #ef4444; | |
| --danger-hover: #dc2626; | |
| --shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); | |
| --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| [data-theme="light"] { | |
| --primary-bg: #f1f5f9; | |
| --glass-bg: rgba(255, 255, 255, 0.7); | |
| --glass-border: rgba(0, 0, 0, 0.1); | |
| --text-primary: #1e293b; | |
| --text-secondary: #64748b; | |
| --shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: var(--primary-bg); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| overflow: hidden; | |
| position: relative; | |
| transition: var(--transition); | |
| } | |
| /* Animated Background */ | |
| .bg-animation { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| background: | |
| radial-gradient(circle at 20% 50%, rgba(59, 130, 246, 0.15) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 80%, rgba(245, 158, 11, 0.15) 0%, transparent 50%), | |
| radial-gradient(circle at 50% 50%, rgba(239, 68, 68, 0.1) 0%, transparent 50%); | |
| filter: blur(60px); | |
| animation: bgMove 20s ease infinite; | |
| } | |
| @keyframes bgMove { | |
| 0%, 100% { transform: translate(0, 0) scale(1); } | |
| 33% { transform: translate(30px, -30px) scale(1.1); } | |
| 66% { transform: translate(-20px, 20px) scale(0.9); } | |
| } | |
| /* Header */ | |
| .header { | |
| position: absolute; | |
| top: 2rem; | |
| left: 0; | |
| width: 100%; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 0 2rem; | |
| z-index: 10; | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| color: var(--text-primary); | |
| font-weight: 600; | |
| font-size: 1.25rem; | |
| } | |
| .brand i { | |
| font-size: 1.5rem; | |
| color: var(--accent-color); | |
| } | |
| .anycoder-link { | |
| color: var(--text-secondary); | |
| text-decoration: none; | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| padding: 0.5rem 1rem; | |
| background: var(--glass-bg); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 9999px; | |
| backdrop-filter: blur(10px); | |
| transition: var(--transition); | |
| } | |
| .anycoder-link:hover { | |
| color: var(--accent-color); | |
| border-color: var(--accent-color); | |
| transform: translateY(-2px); | |
| } | |
| /* Main Container */ | |
| .calculator-wrapper { | |
| display: flex; | |
| gap: 1.5rem; | |
| align-items: flex-start; | |
| max-width: 1000px; | |
| width: 100%; | |
| padding: 1rem; | |
| perspective: 1000px; | |
| } | |
| /* Calculator Body */ | |
| .calculator { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 2rem; | |
| padding: 1.5rem; | |
| box-shadow: var(--shadow); | |
| width: 100%; | |
| max-width: 400px; | |
| transform-style: preserve-3d; | |
| transition: var(--transition); | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| } | |
| .calculator:hover { | |
| transform: translateY(-5px) rotateX(2deg); | |
| box-shadow: 0 30px 60px -12px rgba(0, 0, 0, 0.6); | |
| } | |
| /* Display */ | |
| .display-container { | |
| background: rgba(0, 0, 0, 0.2); | |
| border-radius: 1rem; | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| text-align: right; | |
| position: relative; | |
| overflow: hidden; | |
| border: 1px solid var(--glass-border); | |
| } | |
| [data-theme="light"] .display-container { | |
| background: rgba(255, 255, 255, 0.5); | |
| } | |
| .history-display { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 0.875rem; | |
| color: var(--text-secondary); | |
| min-height: 1.5rem; | |
| margin-bottom: 0.5rem; | |
| opacity: 0.8; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| .main-display { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: clamp(2rem, 5vw, 3rem); | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| word-wrap: break-word; | |
| word-break: break-all; | |
| line-height: 1.2; | |
| } | |
| /* Controls Bar */ | |
| .controls { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1rem; | |
| gap: 0.5rem; | |
| } | |
| .mode-toggle { | |
| display: flex; | |
| gap: 0.5rem; | |
| background: rgba(0, 0, 0, 0.2); | |
| padding: 0.25rem; | |
| border-radius: 0.75rem; | |
| border: 1px solid var(--glass-border); | |
| } | |
| .mode-btn { | |
| background: transparent; | |
| border: none; | |
| color: var(--text-secondary); | |
| padding: 0.5rem 1rem; | |
| border-radius: 0.5rem; | |
| cursor: pointer; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| transition: var(--transition); | |
| font-family: inherit; | |
| } | |
| .mode-btn.active { | |
| background: var(--accent-color); | |
| color: white; | |
| } | |
| .icon-btn { | |
| width: 2.5rem; | |
| height: 2.5rem; | |
| border-radius: 0.75rem; | |
| border: 1px solid var(--glass-border); | |
| background: rgba(0, 0, 0, 0.2); | |
| color: var(--text-primary); | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: var(--transition); | |
| font-size: 1.25rem; | |
| } | |
| .icon-btn:hover { | |
| background: var(--accent-color); | |
| color: white; | |
| transform: scale(1.1); | |
| } | |
| /* Button Grid */ | |
| .buttons-grid { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 0.75rem; | |
| } | |
| .btn { | |
| aspect-ratio: 1; | |
| border: none; | |
| border-radius: 1rem; | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| font-family: inherit; | |
| cursor: pointer; | |
| background: rgba(255, 255, 255, 0.05); | |
| color: var(--text-primary); | |
| border: 1px solid var(--glass-border); | |
| transition: var(--transition); | |
| position: relative; | |
| overflow: hidden; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| user-select: none; | |
| } | |
| .btn::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 0; | |
| height: 0; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.2); | |
| transform: translate(-50%, -50%); | |
| transition: width 0.6s, height 0.6s; | |
| } | |
| .btn:active::before { | |
| width: 300%; | |
| height: 300%; | |
| } | |
| .btn:hover { | |
| transform: translateY(-2px); | |
| background: rgba(255, 255, 255, 0.1); | |
| box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.3); | |
| } | |
| .btn:active { | |
| transform: translateY(0); | |
| } | |
| .btn.operator { | |
| background: rgba(245, 158, 11, 0.1); | |
| color: var(--operator-color); | |
| border-color: rgba(245, 158, 11, 0.3); | |
| } | |
| .btn.operator:hover { | |
| background: var(--operator-color); | |
| color: white; | |
| } | |
| .btn.equals { | |
| background: var(--accent-color); | |
| color: white; | |
| grid-column: span 2; | |
| aspect-ratio: auto; | |
| border-color: var(--accent-color); | |
| } | |
| .btn.equals:hover { | |
| background: var(--accent-hover); | |
| box-shadow: 0 10px 20px -5px rgba(59, 130, 246, 0.4); | |
| } | |
| .btn.danger { | |
| background: rgba(239, 68, 68, 0.1); | |
| color: var(--danger-color); | |
| border-color: rgba(239, 68, 68, 0.3); | |
| } | |
| .btn.danger:hover { | |
| background: var(--danger-color); | |
| color: white; | |
| } | |
| .btn.function { | |
| font-size: 1rem; | |
| color: var(--text-secondary); | |
| } | |
| /* Scientific Buttons (Hidden by default) */ | |
| .scientific { | |
| display: none; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 0.75rem; | |
| margin-bottom: 0.75rem; | |
| animation: slideDown 0.3s ease; | |
| } | |
| .scientific.active { | |
| display: grid; | |
| } | |
| @keyframes slideDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* History Sidebar */ | |
| .history-panel { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 2rem; | |
| padding: 1.5rem; | |
| width: 300px; | |
| height: 600px; | |
| overflow-y: auto; | |
| box-shadow: var(--shadow); | |
| transform: translateX(20px); | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: var(--transition); | |
| position: absolute; | |
| right: -320px; | |
| top: 0; | |
| } | |
| .history-panel.active { | |
| transform: translateX(0); | |
| opacity: 1; | |
| visibility: visible; | |
| position: relative; | |
| right: 0; | |
| } | |
| .history-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1rem; | |
| padding-bottom: 1rem; | |
| border-bottom: 1px solid var(--glass-border); | |
| } | |
| .history-title { | |
| color: var(--text-primary); | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .clear-history { | |
| background: none; | |
| border: none; | |
| color: var(--danger-color); | |
| cursor: pointer; | |
| font-size: 0.875rem; | |
| padding: 0.25rem 0.5rem; | |
| border-radius: 0.5rem; | |
| transition: var(--transition); | |
| } | |
| .clear-history:hover { | |
| background: rgba(239, 68, 68, 0.1); | |
| } | |
| .history-item { | |
| padding: 0.75rem; | |
| margin-bottom: 0.5rem; | |
| background: rgba(0, 0, 0, 0.2); | |
| border-radius: 0.75rem; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| border: 1px solid transparent; | |
| } | |
| [data-theme="light"] .history-item { | |
| background: rgba(255, 255, 255, 0.5); | |
| } | |
| .history-item:hover { | |
| border-color: var(--accent-color); | |
| transform: translateX(5px); | |
| } | |
| .history-expr { | |
| font-size: 0.75rem; | |
| color: var(--text-secondary); | |
| margin-bottom: 0.25rem; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| .history-result { | |
| font-size: 1rem; | |
| color: var(--text-primary); | |
| font-weight: 600; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| .empty-history { | |
| text-align: center; | |
| color: var(--text-secondary); | |
| padding: 2rem; | |
| font-size: 0.875rem; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 768px) { | |
| .header { | |
| padding: 0 1rem; | |
| top: 1rem; | |
| } | |
| .calculator-wrapper { | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 0.5rem; | |
| } | |
| .calculator { | |
| max-width: 100%; | |
| padding: 1rem; | |
| border-radius: 1.5rem; | |
| } | |
| .history-panel { | |
| width: 100%; | |
| height: auto; | |
| max-height: 300px; | |
| position: static; | |
| transform: translateY(20px); | |
| right: auto; | |
| margin-top: 1rem; | |
| } | |
| .history-panel.active { | |
| transform: translateY(0); | |
| } | |
| .buttons-grid { | |
| gap: 0.5rem; | |
| } | |
| .btn { | |
| font-size: 1.125rem; | |
| } | |
| .scientific { | |
| gap: 0.5rem; | |
| } | |
| } | |
| /* Ripple Effect */ | |
| .ripple { | |
| position: absolute; | |
| border-radius: 50%; | |
| transform: scale(0); | |
| animation: ripple 0.6s linear; | |
| background-color: rgba(255, 255, 255, 0.3); | |
| pointer-events: none; | |
| } | |
| @keyframes ripple { | |
| to { | |
| transform: scale(4); | |
| opacity: 0; | |
| } | |
| } | |
| /* Scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--glass-border); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--text-secondary); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="bg-animation"></div> | |
| <header class="header"> | |
| <div class="brand"> | |
| <i class="ph ph-calculator"></i> | |
| <span>GlassCalc</span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank"> | |
| <i class="ph ph-code"></i> | |
| Built with anycoder | |
| </a> | |
| </header> | |
| <div class="calculator-wrapper"> | |
| <div class="calculator"> | |
| <div class="display-container"> | |
| <div class="history-display" id="historyDisplay"></div> | |
| <div class="main-display" id="mainDisplay">0</div> | |
| </div> | |
| <div class="controls"> | |
| <div class="mode-toggle"> | |
| <button class="mode-btn active" onclick="setMode('standard')">Standard</button> | |
| <button class="mode-btn" onclick="setMode('scientific')">Scientific</button> | |
| </div> | |
| <div style="display: flex; gap: 0.5rem;"> | |
| <button class="icon-btn" onclick="toggleTheme()" title="Toggle Theme"> | |
| <i class="ph ph-moon" id="themeIcon"></i> | |
| </button> | |
| <button class="icon-btn" onclick="toggleHistory()" title="History"> | |
| <i class="ph ph-clock-counter-clockwise"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="scientific" id="scientificPad"> | |
| <button class="btn function" onclick="appendFunction('sin')">sin</button> | |
| <button class="btn function" onclick="appendFunction('cos')">cos</button> | |
| <button class="btn function" onclick="appendFunction('tan')">tan</button> | |
| <button class="btn function" onclick="appendFunction('log')">log</button> | |
| <button class="btn function" onclick="appendFunction('ln')">ln</button> | |
| <button class="btn function" onclick="appendChar('^')">x^y</button> | |
| <button class="btn function" onclick="appendChar('√')">√</button> | |
| <button class="btn function" onclick="appendChar('π')">π</button> | |
| <button class="btn function" onclick="appendChar('(')">(</button> | |
| <button class="btn function" onclick="appendChar(')')">)</button> | |
| <button class="btn function" onclick="appendChar('!')">n!</button> | |
| <button class="btn function" onclick="appendChar('e')">e</button> | |
| </div> | |
| <div class="buttons-grid"> | |
| <button class="btn danger" onclick="clearAll()">AC</button> | |
| <button class="btn danger" onclick="clearEntry()">CE</button> | |
| <button class="btn operator" onclick="appendChar('%')">%</button> | |
| <button class="btn operator" onclick="appendChar('/')">÷</button> | |
| <button class="btn" onclick="appendChar('7')">7</button> | |
| <button class="btn" onclick="appendChar('8')">8</button> | |
| <button class="btn" onclick="appendChar('9')">9</button> | |
| <button class="btn operator" onclick="appendChar('*')">×</button> | |
| <button class="btn" onclick="appendChar('4')">4</button> | |
| <button class="btn" onclick="appendChar('5')">5</button> | |
| <button class="btn" onclick="appendChar('6')">6</button> | |
| <button class="btn operator" onclick="appendChar('-')">−</button> | |
| <button class="btn" onclick="appendChar('1')">1</button> | |
| <button class="btn" onclick="appendChar('2')">2</button> | |
| <button class="btn" onclick="appendChar('3')">3</button> | |
| <button class="btn operator" onclick="appendChar('+')">+</button> | |
| <button class="btn" onclick="toggleSign()">±</button> | |
| <button class="btn" onclick="appendChar('0')">0</button> | |
| <button class="btn" onclick="appendChar('.')">.</button> | |
| <button class="btn equals" onclick="calculate()">=</button> | |
| </div> | |
| </div> | |
| <div class="history-panel" id="historyPanel"> | |
| <div class="history-header"> | |
| <div class="history-title"> | |
| <i class="ph ph-clock-counter-clockwise"></i> | |
| History | |
| </div> | |
| <button class="clear-history" onclick="clearHistory()"> | |
| <i class="ph ph-trash"></i> Clear | |
| </button> | |
| </div> | |
| <div id="historyList"> | |
| <div class="empty-history">No calculations yet</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentInput = '0'; | |
| let history = []; | |
| let shouldResetScreen = false; | |
| let isScientific = false; | |
| const mainDisplay = document.getElementById('mainDisplay'); | |
| const historyDisplay = document.getElementById('historyDisplay'); | |
| const historyPanel = document.getElementById('historyPanel'); | |
| const historyList = document.getElementById('historyList'); | |
| const scientificPad = document.getElementById('scientificPad'); | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| loadTheme(); | |
| createRippleEffect(); | |
| }); | |
| // Keyboard support | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key >= '0' && e.key <= '9') appendChar(e.key); | |
| if (e.key === '.') appendChar('.'); | |
| if (e.key === '+') appendChar('+'); | |
| if (e.key === '-') appendChar('-'); | |
| if (e.key === '*') appendChar('*'); | |
| if (e.key === '/') appendChar('/'); | |
| if (e.key === '%') appendChar('%'); | |
| if (e.key === '(') appendChar('('); | |
| if (e.key === ')') appendChar(')'); | |
| if (e.key === '^') appendChar('^'); | |
| if (e.key === 'Enter' || e.key === '=') calculate(); | |
| if (e.key === 'Escape') clearAll(); | |
| if (e.key === 'Backspace') backspace(); | |
| }); | |
| function updateDisplay() { | |
| mainDisplay.textContent = currentInput; | |
| // Add animation class | |
| mainDisplay.style.animation = 'none'; | |
| setTimeout(() => { | |
| mainDisplay.style.animation = 'pulse 0.2s ease'; | |
| }, 10); | |
| } | |
| function appendChar(char) { | |
| if (shouldResetScreen) { | |
| currentInput = '0'; | |
| shouldResetScreen = false; | |
| } | |
| if (currentInput === '0' && !isOperator(char) && char !== '.') { | |
| currentInput = char; | |
| } else if (currentInput === '0' && char === '.') { | |
| currentInput = '0.'; | |
| } else { | |
| currentInput += char; | |
| } | |
| updateDisplay(); | |
| } | |
| function appendFunction(func) { | |
| if (shouldResetScreen) { | |
| currentInput = '0'; | |
| shouldResetScreen = false; | |
| } | |
| if (currentInput === '0') { | |
| currentInput = func + '('; | |
| } else { | |
| currentInput += func + '('; | |
| } | |
| updateDisplay(); | |
| } | |
| function isOperator(char) { | |
| return ['+', '-', '*', '/', '%', '^'].includes(char); | |
| } | |
| function clearAll() { | |
| currentInput = '0'; | |
| historyDisplay.textContent = ''; | |
| updateDisplay(); | |
| playSound('clear'); | |
| } | |
| function clearEntry() { | |
| currentInput = '0'; | |
| updateDisplay(); | |
| } | |
| function backspace() { | |
| if (currentInput.length > 1) { | |
| currentInput = currentInput.slice(0, -1); | |
| } else { | |
| currentInput = '0'; | |
| } | |
| updateDisplay(); | |
| } | |
| function toggleSign() { | |
| if (currentInput !== '0') { | |
| if (currentInput.startsWith('-')) { | |
| currentInput = currentInput.slice(1); | |
| } else { | |
| currentInput = '-' + currentInput; | |
| } | |
| updateDisplay(); | |
| } | |
| } | |
| function calculate() { | |
| try { | |
| let expression = currentInput | |
| .replace(/×/g, '*') | |
| .replace(/÷/g, '/') | |
| .replace(/−/g, '-') | |
| .replace(/π/g, Math.PI) | |
| .replace(/e/g, Math.E) | |
| .replace(/√/g, 'Math.sqrt') | |
| .replace(/\^/g, '**'); | |
| // Handle factorial | |
| expression = expression.replace(/(\d+)!/g, (match, num) => factorial(parseInt(num))); | |
| // Handle scientific functions | |
| expression = expression.replace(/sin\(/g, 'Math.sin('); | |
| expression = expression.replace(/cos\(/g, 'Math.cos('); | |
| expression = expression.replace(/tan\(/g, 'Math.tan('); | |
| expression = expression.replace(/log\(/g, 'Math.log10('); | |
| expression = expression.replace(/ln\(/g, 'Math.log('); | |
| // Convert degrees to radians for trig functions | |
| expression = expression.replace(/Math\.(sin|cos|tan)\(([^)]+)\)/g, (match, func, arg) => { | |
| return `Math.${func}((${arg}) * Math.PI / 180)`; | |
| }); | |
| const result = eval(expression); | |
| if (!isFinite(result) || isNaN(result)) { | |
| throw new Error('Invalid result'); | |
| } | |
| const formattedResult = parseFloat(result.toFixed(8)).toString(); | |
| // Add to history | |
| addToHistory(currentInput, formattedResult); | |
| historyDisplay.textContent = currentInput + ' ='; | |
| currentInput = formattedResult; | |
| shouldResetScreen = true; | |
| updateDisplay(); | |
| playSound('success'); | |
| } catch (error) { | |
| currentInput = 'Error'; | |
| shouldResetScreen = true; | |
| updateDisplay(); | |
| playSound('error'); | |
| setTimeout(() => { | |
| currentInput = '0'; | |
| updateDisplay(); | |
| }, 1500); | |
| } | |
| } | |
| function factorial(n) { | |
| if (n < 0) return NaN; | |
| if (n === 0 || n === 1) return 1; | |
| let result = 1; | |
| for (let i = 2; i <= n; i++) result *= i; | |
| return result; | |
| } | |
| function addToHistory(expr, result) { | |
| history.unshift({ expression: expr, result: result }); | |
| if (history.length > 50) history.pop(); | |
| renderHistory(); | |
| } | |
| function renderHistory() { | |
| if (history.length === 0) { | |
| historyList.innerHTML = '<div class="empty-history">No calculations yet</div>'; | |
| return; | |
| } | |
| historyList.innerHTML = history.map((item, index) => ` | |
| <div class="history-item" onclick="loadFromHistory('${item.result}')"> | |
| <div class="history-expr">${item.expression} =</div> | |
| <div class="history-result">${item.result}</div> | |
| </div> | |
| `).join(''); | |
| } | |
| function loadFromHistory(value) { | |
| currentInput = value; | |
| updateDisplay(); | |
| } | |
| function clearHistory() { | |
| history = []; | |
| renderHistory(); | |
| } | |
| function toggleHistory() { | |
| historyPanel.classList.toggle('active'); | |
| } | |
| function setMode(mode) { | |
| const buttons = document.querySelectorAll('.mode-btn'); | |
| buttons.forEach(btn => btn.classList.remove('active')); | |
| event.target.classList.add('active'); | |
| if (mode === 'scientific') { | |
| scientificPad.classList.add('active'); | |
| isScientific = true; | |
| } else { | |
| scientificPad.classList.remove('active'); | |
| isScientific = false; | |
| } | |
| } | |
| function toggleTheme() { | |
| const html = document.documentElement; | |
| const icon = document.getElementById('themeIcon'); | |
| if (html.getAttribute('data-theme') === 'light') { | |
| html.setAttribute('data-theme', 'dark'); | |
| icon.classList.replace('ph-sun', 'ph-moon'); | |
| localStorage.setItem('theme', 'dark'); | |
| } else { | |
| html.setAttribute('data-theme', 'light'); | |
| icon.classList.replace('ph-moon', 'ph-sun'); | |
| localStorage.setItem('theme', 'light'); | |
| } | |
| } | |
| function loadTheme() { | |
| const savedTheme = localStorage.getItem('theme') || 'dark'; | |
| const icon = document.getElementById('themeIcon'); | |
| document.documentElement.setAttribute('data-theme', savedTheme); | |
| if (savedTheme === 'light') { | |
| icon.classList.replace('ph-moon', 'ph-sun'); | |
| } | |
| } | |
| function createRippleEffect() { | |
| document.querySelectorAll('.btn').forEach(button => { | |
| button.addEventListener('click', function(e) { | |
| const ripple = document.createElement('span'); | |
| ripple.classList.add('ripple'); | |
| const rect = this.getBoundingClientRect(); | |
| const size = Math.max(rect.width, rect.height); | |
| const x = e.clientX - rect.left - size / 2; | |
| const y = e.clientY - rect.top - size / 2; | |
| ripple.style.width = ripple.style.height = size + 'px'; | |
| ripple.style.left = x + 'px'; | |
| ripple.style.top = y + 'px'; | |
| this.appendChild(ripple); | |
| setTimeout(() => ripple.remove(), 600); | |
| }); | |
| }); | |
| } | |
| // Simple sound effects using Web Audio API | |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| function playSound(type) { | |
| if (audioContext.state === 'suspended') { | |
| audioContext.resume(); | |
| } | |
| const oscillator = audioContext.createOscillator(); | |
| const gainNode = audioContext.createGain(); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(audioContext.destination); | |
| if (type === 'success') { | |
| oscillator.frequency.setValueAtTime(800, audioContext.currentTime); | |
| oscillator.frequency.exponentialRampToValueAtTime(1200, audioContext.currentTime + 0.1); | |
| gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); | |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); | |
| oscillator.start(audioContext.currentTime); | |
| oscillator.stop(audioContext.currentTime + 0.1); | |
| } else if (type === 'error') { | |
| oscillator.frequency.setValueAtTime(200, audioContext.currentTime); | |
| gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); | |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2); | |
| oscillator.start(audioContext.currentTime); | |
| oscillator.stop(audioContext.currentTime + 0.2); | |
| } else if (type === 'clear') { | |
| oscillator.frequency.setValueAtTime(600, audioContext.currentTime); | |
| gainNode.gain.setValueAtTime(0.05, audioContext.currentTime); | |
| gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.05); | |
| oscillator.start(audioContext.currentTime); | |
| oscillator.stop(audioContext.currentTime + 0.05); | |
| } | |
| } | |
| // Add pulse animation to display | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.02); } | |
| 100% { transform: scale(1); } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| </script> | |
| </body> | |
| </html> |