├── favicon.ico ├── screenshot └── screenshot.png ├── LICENCE ├── index.html ├── README.md ├── style.css └── script.js /favicon.ico: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /screenshot/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YALDAKHOSHPEY/Calculator_pro/HEAD/screenshot/screenshot.png -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 [Yaldakhoshpey] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Advanced Scientific Calculator 7 | 8 | 9 | 10 | 11 |
12 |
13 |
0
14 |
15 |
16 | 17 | 33 |
34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧮 Advanced Scientific Calculator 2 | 3 | A beautiful and fully functional scientific calculator built with HTML, CSS, and JavaScript. 4 | It includes a scientific expression evaluator, a calculation history panel, and a stopwatch timer — all inside a responsive and visually modern UI. 5 | 6 | --- 7 | 8 | ## ✨ Features 9 | 10 | - ✅ Scientific functions: sin, cos, tan, log, ln, √, ^, !, π, e, mod, abs, exp, and more 11 | - ✅ Calculation history (up to 20 operations) 12 | - ✅ Stopwatch timer with start/stop and reset 13 | - ✅ Red/black modern theme with yellow hover effects 14 | - ✅ Responsive layout for mobile and desktop 15 | - ✅ Built without any external libraries — pure HTML/CSS/JavaScript 16 | 17 | --- 18 | 19 | ## 📸 Screenshots 20 | 21 | ![Calculator Screenshot](./screenshot/screenshot.png) 22 | 23 | --- 24 | 25 | ## Live Demo 26 | 27 | [Click here ☺️](https://yaldakhoshpey.github.io/Calculator_pro/) 28 | 29 | ## 📁 Project Structure 30 | 31 | ``` text 32 | advanced-calculator/ 33 | ├── index.html # Main HTML structure 34 | ├── style.css # Calculator & layout styling 35 | ├── app.js # Calculator logic & interactions 36 | ├── screenshot.png # UI screenshot for README preview 37 | ├── README.md # Project documentation 38 | └── assets/ # (Optional) icons, images, fonts etc. 39 | ``` 40 | --- 41 | 42 | ## 📄 License 43 | 44 | This project is licensed under the [MIT License](./LICENSE). 45 | 46 | --- 47 | 48 | **made by *Yalda Khoshpey*** 49 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* Reset & base */ 2 | * { 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 9 | background: #121212; 10 | color: #eee; 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | min-height: 100vh; 15 | padding: 20px; 16 | } 17 | 18 | .container { 19 | display: flex; 20 | gap: 30px; 21 | flex-wrap: wrap; 22 | max-width: 1000px; 23 | width: 100%; 24 | } 25 | 26 | /* Calculator */ 27 | .calculator { 28 | background: linear-gradient(145deg, #b71c1c, #7f0000); 29 | border-radius: 25px; 30 | box-shadow: 0 0 30px #b71c1caa; 31 | padding: 25px; 32 | width: 350px; 33 | display: flex; 34 | flex-direction: column; 35 | } 36 | 37 | .display { 38 | background: #2a0000; 39 | border-radius: 15px; 40 | padding: 20px; 41 | font-size: 2.5rem; 42 | color: #ffebee; 43 | text-align: right; 44 | min-height: 60px; 45 | overflow-x: auto; 46 | font-weight: 700; 47 | user-select: none; 48 | margin-bottom: 25px; 49 | } 50 | 51 | .buttons { 52 | display: grid; 53 | grid-template-columns: repeat(6, 1fr); 54 | gap: 15px; 55 | } 56 | 57 | button { 58 | background: linear-gradient(145deg, #e53935, #b71c1c); 59 | border: none; 60 | border-radius: 15px; 61 | padding: 18px 0; 62 | font-size: 1.1rem; 63 | color: #fff; 64 | cursor: pointer; 65 | box-shadow: 0 5px 10px #7f0000cc; 66 | transition: all 0.25s ease; 67 | user-select: none; 68 | } 69 | 70 | button:hover { 71 | background: linear-gradient(145deg, #ffeb3b, #fbc02d); 72 | color: #121212; 73 | box-shadow: 0 6px 15px #ffeb3bcc; 74 | transform: scale(1.1); 75 | } 76 | 77 | button:active { 78 | transform: scale(0.95); 79 | } 80 | 81 | button.equal { 82 | grid-column: span 6; 83 | background: linear-gradient(145deg, #ffb300, #ff6f00); 84 | font-weight: 700; 85 | color: #121212; 86 | box-shadow: 0 5px 15px #ff6f00cc; 87 | } 88 | 89 | button.equal:hover { 90 | background: linear-gradient(145deg, #fff176, #ffca28); 91 | box-shadow: 0 6px 18px #fff176cc; 92 | } 93 | 94 | /* Sidebar */ 95 | .sidebar { 96 | background: #1b1b1b; 97 | border-radius: 25px; 98 | padding: 25px; 99 | width: 280px; 100 | box-shadow: 0 0 30px #b71c1caa; 101 | display: flex; 102 | flex-direction: column; 103 | gap: 30px; 104 | user-select: none; 105 | } 106 | 107 | .sidebar h2 { 108 | margin-top: 0; 109 | margin-bottom: 15px; 110 | font-weight: 700; 111 | color: #ff5252; 112 | text-align: center; 113 | } 114 | 115 | /* History */ 116 | .history { 117 | max-height: 280px; 118 | overflow-y: scroll; 119 | } 120 | 121 | #historyList { 122 | list-style: none; 123 | padding-left: 0; 124 | margin: 0 0 15px 0; 125 | max-height: 200px; 126 | overflow-y: auto; 127 | border: 1px solid #7f0000; 128 | border-radius: 10px; 129 | background: #2a0000; 130 | } 131 | 132 | #historyList li { 133 | padding: 8px 12px; 134 | border-bottom: 1px solid #7f0000; 135 | font-size: 0.95rem; 136 | color: #f44336; 137 | } 138 | 139 | #historyList li:last-child { 140 | border-bottom: none; 141 | } 142 | 143 | #clearHistoryBtn { 144 | width: 100%; 145 | background: #b71c1c; 146 | border: none; 147 | border-radius: 12px; 148 | padding: 10px; 149 | font-weight: 700; 150 | cursor: pointer; 151 | color: #fff; 152 | transition: all 0.25s ease; 153 | } 154 | 155 | #clearHistoryBtn:hover { 156 | background: #ffeb3b; 157 | color: #121212; 158 | } 159 | 160 | /* Timer */ 161 | .timer { 162 | text-align: center; 163 | } 164 | 165 | #timerDisplay { 166 | font-size: 2rem; 167 | font-weight: 700; 168 | background: #2a0000; 169 | border-radius: 15px; 170 | padding: 15px 0; 171 | color: #ffeb3b; 172 | margin-bottom: 15px; 173 | user-select: none; 174 | } 175 | 176 | .timer-buttons button { 177 | background: #b71c1c; 178 | border: none; 179 | color: white; 180 | font-weight: 700; 181 | padding: 10px 18px; 182 | margin: 0 10px; 183 | border-radius: 12px; 184 | cursor: pointer; 185 | transition: all 0.25s ease; 186 | min-width: 80px; 187 | } 188 | 189 | .timer-buttons button:hover { 190 | background: #ffeb3b; 191 | color: #121212; 192 | } 193 | 194 | .timer-buttons button:active { 195 | transform: scale(0.95); 196 | } 197 | 198 | /* Responsive */ 199 | @media (max-width: 800px) { 200 | .container { 201 | flex-direction: column; 202 | align-items: center; 203 | } 204 | .calculator, .sidebar { 205 | width: 100%; 206 | max-width: 400px; 207 | } 208 | .buttons { 209 | grid-template-columns: repeat(4, 1fr); 210 | } 211 | button.equal { 212 | grid-column: span 4; 213 | } 214 | } 215 | 216 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const buttons = [ 2 | 'C', '←', '(', ')', 'π', 'e', 3 | 'sin', 'cos', 'tan', 'log', 'ln', '√', 4 | '7', '8', '9', '/', '^', '%', 5 | '4', '5', '6', '*', 'exp', '!', 6 | '1', '2', '3', '-', 'abs', 'mod', 7 | '0', '.', '+', '=' 8 | ]; 9 | 10 | const display = document.getElementById('display'); 11 | const buttonsContainer = document.getElementById('buttons'); 12 | const historyList = document.getElementById('historyList'); 13 | const clearHistoryBtn = document.getElementById('clearHistoryBtn'); 14 | 15 | let input = ''; 16 | let history = []; 17 | 18 | // Timer 19 | const timerDisplay = document.getElementById('timerDisplay'); 20 | const startStopBtn = document.getElementById('startStopBtn'); 21 | const resetBtn = document.getElementById('resetBtn'); 22 | 23 | let timerInterval = null; 24 | let timerStart = null; 25 | let elapsed = 0; 26 | 27 | // Factorial 28 | function factorial(n) { 29 | if (n < 0) return NaN; 30 | if (n === 0 || n === 1) return 1; 31 | let res = 1; 32 | for (let i = 2; i <= n; i++) res *= i; 33 | return res; 34 | } 35 | 36 | // Display 37 | function updateDisplay() { 38 | display.textContent = input || '0'; 39 | } 40 | 41 | // History 42 | function addHistory(entry) { 43 | history.push(entry); 44 | if (history.length > 20) history.shift(); // limit to 20 45 | renderHistory(); 46 | } 47 | 48 | function renderHistory() { 49 | historyList.innerHTML = ''; 50 | history.slice().reverse().forEach(item => { 51 | const li = document.createElement('li'); 52 | li.textContent = item; 53 | historyList.appendChild(li); 54 | }); 55 | } 56 | 57 | clearHistoryBtn.addEventListener('click', () => { 58 | history = []; 59 | renderHistory(); 60 | }); 61 | 62 | // Calculation 63 | function calculate(expr) { 64 | try { 65 | let replaced = expr 66 | .replace(/π/g, Math.PI) 67 | .replace(/e/g, Math.E) 68 | .replace(/√/g, 'Math.sqrt') 69 | .replace(/sin/g, 'Math.sin') 70 | .replace(/cos/g, 'Math.cos') 71 | .replace(/tan/g, 'Math.tan') 72 | .replace(/log/g, 'Math.log10') 73 | .replace(/ln/g, 'Math.log') 74 | .replace(/exp/g, 'Math.exp') 75 | .replace(/abs/g, 'Math.abs') 76 | .replace(/mod/g, '%') 77 | .replace(/\^/g, '**'); 78 | 79 | // Replace factorials 80 | while (replaced.includes('!')) { 81 | replaced = replaced.replace(/(\d+)!/, (_, num) => factorial(parseInt(num))); 82 | } 83 | 84 | // Percent handling 85 | replaced = replaced.replace(/(\d+)%/g, '($1/100)'); 86 | 87 | const result = Function('"use strict"; return (' + replaced + ')')(); 88 | 89 | if ( 90 | result === undefined || 91 | result === Infinity || 92 | Number.isNaN(result) 93 | ) { 94 | return 'Error'; 95 | } 96 | 97 | return Math.round(result * 1e12) / 1e12; 98 | } catch (e) { 99 | return 'Error'; 100 | } 101 | } 102 | 103 | // Button handling 104 | function handleClick(val) { 105 | if (val === 'C') { 106 | input = ''; 107 | } else if (val === '←') { 108 | input = input.slice(0, -1); 109 | } else if (val === '=') { 110 | const res = calculate(input); 111 | addHistory(`${input} = ${res}`); 112 | input = res.toString(); 113 | } else { 114 | if (input === 'Error') input = ''; 115 | input += val; 116 | } 117 | updateDisplay(); 118 | } 119 | 120 | // Create buttons 121 | buttons.forEach(btn => { 122 | const button = document.createElement('button'); 123 | button.textContent = btn; 124 | if (btn === '=') button.classList.add('equal'); 125 | button.addEventListener('click', () => handleClick(btn)); 126 | buttonsContainer.appendChild(button); 127 | }); 128 | 129 | updateDisplay(); 130 | renderHistory(); // Initial rendering of history 131 | 132 | // --- TIMER --- 133 | function formatTime(ms) { 134 | const totalSeconds = Math.floor(ms / 1000); 135 | const h = Math.floor(totalSeconds / 3600).toString().padStart(2, '0'); 136 | const m = Math.floor((totalSeconds % 3600) / 60).toString().padStart(2, '0'); 137 | const s = (totalSeconds % 60).toString().padStart(2, '0'); 138 | return `${h}:${m}:${s}`; 139 | } 140 | 141 | function startTimer() { 142 | if (timerInterval) return; 143 | timerStart = Date.now() - elapsed; 144 | timerInterval = setInterval(() => { 145 | elapsed = Date.now() - timerStart; 146 | timerDisplay.textContent = formatTime(elapsed); 147 | }, 1000); 148 | startStopBtn.textContent = 'Stop'; 149 | } 150 | 151 | function stopTimer() { 152 | if (!timerInterval) return; 153 | clearInterval(timerInterval); 154 | timerInterval = null; 155 | startStopBtn.textContent = 'Start'; 156 | } 157 | 158 | startStopBtn.addEventListener('click', () => { 159 | if (timerInterval) stopTimer(); 160 | else startTimer(); 161 | }); 162 | resetBtn.addEventListener('click', () => { 163 | stopTimer(); 164 | elapsed = 0; 165 | timerDisplay.textContent = '00:00:00'; 166 | }); 167 | --------------------------------------------------------------------------------