Skip to content

Commit a7fe53c

Browse files
authored
Add files via upload
1 parent 5597928 commit a7fe53c

File tree

4 files changed

+311
-0
lines changed

4 files changed

+311
-0
lines changed

calculator.js

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// @ts-check
2+
3+
"use strict";
4+
5+
// ========================================================================== \\
6+
7+
var op = '+', result = 0, isShowResult = true;
8+
9+
const KEY = 'keydown', CLICK = 'click', MAX_CHARS = 23;
10+
const ZERO = '0', DOT = '.', NEG = '-';
11+
12+
const calc = /** @type {HTMLDivElement} */
13+
(document.getElementById('calculator'));
14+
15+
const display = /** @type {HTMLSpanElement} */
16+
(document.getElementById('display'));
17+
18+
// ========================================================================== \\
19+
20+
const keyButtons = /** @type {HTMLCollectionOf<HTMLButtonElement>} */
21+
(document.getElementsByClassName('key'));
22+
23+
calc.addEventListener(KEY, btnKeyEvent);
24+
25+
/**
26+
* Handles a 'keydown' event for all .key `button` elements inside #calculator.
27+
* If a 'key' corresponding to a .key `button`::`textContent` is pressed and
28+
* it's not a 'repeat', it triggers a 'click' event on that `button`.
29+
*
30+
* @param {KeyboardEvent} ev The KeyboardEvent object.
31+
* Callback uses event object's properties 'key', 'keyCode' and 'repeat'.
32+
*
33+
* @description Firstly it checks if backspace key was pressed.
34+
* If so, it invokes function deleteLastChar() to delete previous char entered.
35+
* Secondly it checks if delete key was pressed and invokes btnClearEntryEvent()
36+
* if that's the case, thus behaving the same as the CE's #ce `button`.
37+
* Then it checks if the event is not a 'repeat'.
38+
* If it's not a 'repeat', it iterates over each .key `button`.
39+
* If the 'key' property of the event matches the `textContent` of the
40+
* current `button`, it triggers a 'click' event on that `button`.
41+
* It also prematurely `break` the loop after the first match.
42+
*/
43+
function btnKeyEvent({ key, keyCode, repeat }) {
44+
if (keyCode == 8) deleteLastChar(); // key: "Backspace" (ASCII 8)
45+
46+
else if (keyCode == 46) btnClearEntryEvent(); // key: "Delete" (ASCII 46)
47+
48+
else if (!repeat) for (const btn of keyButtons) if (key == btn.textContent) {
49+
btn.click();
50+
break;
51+
}
52+
}
53+
54+
/**
55+
* Deletes the last entered character from the #display `span` element.
56+
*
57+
* @description If `isShowResult` state is currently set `true` it does nothing.
58+
* If the #display text length is more than 1, it removes last char from it.
59+
* Othewise it changes it back to initial value '0'.
60+
* If final #display is a form of negative zero, sanitize it to just zero "0".
61+
*/
62+
function deleteLastChar() {
63+
if (isShowResult) return;
64+
65+
const { innerText } = display, { length } = innerText;
66+
67+
const s = display.textContent = length > 1 && innerText.slice(0, -1) || ZERO;
68+
69+
if (s == NEG || s == '-0' || s == '-0.') display.textContent = ZERO;
70+
}
71+
72+
// ========================================================================== \\
73+
74+
const commonButtons = /** @type {HTMLCollectionOf<HTMLButtonElement>} */
75+
(document.getElementsByClassName('common'));
76+
77+
for (const btn of commonButtons) btn.addEventListener(CLICK, btnCommonEvent);
78+
79+
/**
80+
* Handles the 'click' event of a .common `button` element. It appends the text
81+
* of the clicked button to the #display `span` element on the webpage.
82+
*
83+
* @this {HTMLButtonElement} `button` .common
84+
*
85+
* @description The function checks if `isShowResult` is true. If it is, it sets
86+
* `isShowResult` to false and sets the text content of the #display `span`
87+
* element to the text content of the clicked button.
88+
* If `isShowResult` is false, the function checks if the length of the text
89+
* content in the #display `span` element is 1 and if the 1st character is '0'.
90+
* If both conditions are true, it sets the text content of the #display `span`
91+
* element to the text content of the clicked button.
92+
* If none of the above conditions are met, it appends the text content of the
93+
* clicked button to the existing text content in the #display `span` element.
94+
*/
95+
function btnCommonEvent() {
96+
const { innerText: { 0: head, length: len } } = display, { innerText } = this;
97+
98+
if (isShowResult) {
99+
isShowResult = false;
100+
display.textContent = innerText;
101+
}
102+
103+
else if (len == 1 && head == ZERO) display.textContent = innerText;
104+
105+
else if (len < MAX_CHARS) display.innerText += innerText;
106+
}
107+
108+
// ========================================================================== \\
109+
110+
document.getElementById('zero')?.addEventListener(CLICK, btnZeroEvent);
111+
112+
/**
113+
* Handles the 'click' event of the .digit #zero `button` element.
114+
*
115+
* @description If the variable `isShowResult` is true, it sets it to false
116+
* and updates the text content of the #display `span` element to a zero ('0').
117+
* If `isShowResult` is false, it checks if the length of the display text is
118+
* greater than 1 or if the first character of the display text is not '0'.
119+
* If either condition is true, append '0' to the display text.
120+
*/
121+
function btnZeroEvent() {
122+
if (isShowResult) {
123+
isShowResult = false;
124+
display.textContent = ZERO;
125+
return;
126+
}
127+
128+
const { innerText: { 0: head, length: len } } = display;
129+
130+
if (len < MAX_CHARS && (len > 1 || head != ZERO)) display.innerText += ZERO;
131+
}
132+
133+
// ========================================================================== \\
134+
135+
document.getElementById('decimal')?.addEventListener(CLICK, btnDotEvent);
136+
137+
/**
138+
* Handles the 'click' event of the .digit #decimal `button` element.
139+
*
140+
* @description If the variable `isShowResult` is true, it sets it to false and
141+
* updates the text content of the #display `span` element to a zero dot ('0.').
142+
* If `isShowResult` is false and the #display `span` element does not already
143+
* contain a dot, the function appends a dot to the existing text content
144+
* of the #display `span` element.
145+
*/
146+
function btnDotEvent() {
147+
if (isShowResult) {
148+
isShowResult = false;
149+
display.textContent = '0.';
150+
return;
151+
}
152+
153+
const { innerText } = display, { length } = innerText;
154+
155+
if (length < MAX_CHARS && !innerText.includes(DOT)) display.innerText += DOT;
156+
}
157+
158+
// ========================================================================== \\
159+
160+
document.getElementById('ac')?.addEventListener(CLICK, btnAllClearEvent);
161+
162+
/**
163+
* Handles the 'click' event of the C's #ac `button` element. It resets the
164+
* #calculator back to its initial state.
165+
*
166+
* @description This function is triggered when the C's #ac `button` is clicked.
167+
* It resets the #calculator to its initial state by setting the operation to
168+
* addition '+', the result to 0, and `isShowResult` to true.
169+
* It also sets the text content of the #display `span` element back to '0'.
170+
*/
171+
function btnAllClearEvent() {
172+
op = '+', result = 0, isShowResult = true;
173+
display.textContent = ZERO;
174+
}
175+
176+
document.getElementById('ce')?.addEventListener(CLICK, btnClearEntryEvent);
177+
178+
/**
179+
* Handles the 'click' event of the CE's #ce `button` element.
180+
* It clears the last entry on the #calculator.
181+
*
182+
* @description It checks if current operator `op` is "=". If it is it calls
183+
* `btnAllClearEvent()`, effectively behaving as the C's #ac `button`.
184+
* Otherwise, it sets the `textContent` of the #display element back to '0',
185+
* clearing the last entry on the #calculator.
186+
*/
187+
function btnClearEntryEvent() {
188+
if (op == '=') btnAllClearEvent();
189+
else display.textContent = ZERO;
190+
}
191+
192+
// ========================================================================== \\
193+
194+
document.getElementById('negate')?.addEventListener(CLICK, btnNegateEvent);
195+
196+
/**
197+
* Handles the 'click' event of the #negate `button` element.
198+
* It negates the current value on the #calculator's #display.
199+
*
200+
* @description This callback triggers when #negate `button` is clicked.
201+
* If the current display value is '0' or '0.', it does nothing.
202+
* If `isShowResult` is true, it multiplies the result by -1.
203+
* Then it checks if current #display value is negative. If it is, it removes
204+
* the negative sign. If it isn't, it adds a '-' sign in front of the current
205+
* display value.
206+
*/
207+
function btnNegateEvent() {
208+
const { innerText } = display, [ head ] = innerText;
209+
210+
if (innerText == ZERO || innerText == '0.') return;
211+
212+
if (isShowResult) result *= -1;
213+
214+
display.textContent = head == NEG && innerText.substr(1) || NEG + innerText;
215+
}
216+
217+
// ========================================================================== \\

favicon.ico

37.2 KB
Binary file not shown.

index.html

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<title>Calculator</title>
6+
<meta charset="utf-8">
7+
8+
<link rel="icon" href="favicon.ico">
9+
<link rel="stylesheet" href="styles.css">
10+
</head>
11+
12+
<body onload="document.getElementById('calculator').focus()">
13+
14+
<script async src="calculator.js"></script>
15+
16+
<div id="calculator" tabindex="-999">
17+
18+
<span id="display">0</span>
19+
20+
<button class="delete" id="ce">CE</button>
21+
<button class="delete" id="ac">C</button>
22+
<button class="operator unary" id="negate">±</button>
23+
<button class="operator dyadic" id="pow">xⁿ</button>
24+
<button class="operator unary" id="sqrt"></button>
25+
26+
<button class="digit common key" id="seven">7</button>
27+
<button class="digit common key" id="eight">8</button>
28+
<button class="digit common key" id="nine">9</button>
29+
<button class="operator dyadic key" id="divide">/</button>
30+
<button class="operator unary" id="percent">%</button>
31+
32+
<button class="digit common key" id="four">4</button>
33+
<button class="digit common key" id="five">5</button>
34+
<button class="digit common key" id="six">6</button>
35+
<button class="operator dyadic key" id="multiply">*</button>
36+
<button class="operator unary" id="reciprocal">1/x</button>
37+
38+
<button class="digit common key" id="one">1</button>
39+
<button class="digit common key" id="two">2</button>
40+
<button class="digit common key" id="three">3</button>
41+
<button class="operator dyadic key" id="minus">-</button>
42+
<button class="operator unary key" id="equals">=</button>
43+
44+
<button class="digit key" id="zero">0</button>
45+
<button class="digit key" id="decimal">.</button>
46+
<button class="operator dyadic key" id="plus">+</button>
47+
48+
</div>
49+
50+
</body>
51+
52+
</html>

styles.css

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#calculator {
2+
display: grid;
3+
grid-template: repeat(6, 1fr) / repeat(5, 1fr);
4+
max-width: 400px;
5+
gap: 10px;
6+
border: 3px solid;
7+
padding: 10px;
8+
}
9+
10+
#calculator button {
11+
font-size: larger;
12+
}
13+
14+
#calculator button:hover {
15+
filter: brightness(1.1);
16+
}
17+
18+
#display {
19+
grid-column: span 5;
20+
border: 2px solid;
21+
text-align: right;
22+
padding: 10px;
23+
font-size: xx-large;
24+
background-color: lightgreen;
25+
}
26+
27+
#equals {
28+
grid-row: span 2;
29+
background-color: gold;
30+
}
31+
32+
#zero {
33+
grid-column: span 2;
34+
}
35+
36+
.delete {
37+
background-color: crimson;
38+
}
39+
40+
.operator {
41+
background-color: lightgray;
42+
}

0 commit comments

Comments
 (0)