-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
351 lines (317 loc) · 12.8 KB
/
script.js
File metadata and controls
351 lines (317 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
document.addEventListener("DOMContentLoaded", () => {
// --- DOM Elements ---
const numberInput = document.getElementById("numberInput");
const binaryInputDisplay = document.getElementById("binaryInputDisplay");
const controlsContainer = document.querySelector(".controls");
const operationButtons = controlsContainer.querySelectorAll("button");
const operandInputContainer = document.getElementById(
"operandInputContainer"
);
const operandInput = document.getElementById("operandInput");
const operandLabel = document.getElementById("operandLabel");
const resultOperationDisplay = document.getElementById(
"resultOperationDisplay"
);
const resultDecimalDisplay = document.getElementById("resultDecimalDisplay");
const resultBinaryDisplay = document.getElementById("resultBinaryDisplay");
const resultOriginalBinaryDisplay = document.getElementById(
"resultOriginalBinaryDisplay"
);
const explanationTitle = document.getElementById("explanationTitle");
const explanationText = document.getElementById("explanationText");
const explanationUsecases = document.getElementById("explanationUsecases");
const explanationCode = document.getElementById("explanationCode");
const themeToggle = document.getElementById("themeToggle");
// --- State ---
let currentNumber = parseInt(numberInput.value, 10) || 0;
let currentOperand = parseInt(operandInput.value, 10) || 0;
let currentOperation = null; // Holds the operator string e.g., '<<'
let currentActiveButton = null;
// --- Operation Explanations ---
const explanations = {
"<<": {
name: "Left Shift (<<)",
description:
"Shifts all bits of the first operand to the left by the number of positions specified by the second operand. New bits shifted in on the right are always zero. Effectively multiplies the number by 2 raised to the power of the shift amount (discarding bits shifted off the left).",
usecases: "Fast multiplication by powers of 2, manipulating bitmasks.",
codeSample: (a, b) =>
`let num = ${a};\nlet shiftAmount = ${b};\nlet result = num << shiftAmount; // ${
a << b
}`,
},
">>": {
name: "Sign-Propagating Right Shift (>>)",
description:
"Shifts all bits of the first operand to the right by the number of positions specified by the second operand. Bits shifted off the right are discarded. Copies of the leftmost bit (the sign bit) are shifted in from the left, preserving the sign of the original number.",
usecases:
"Fast division by powers of 2 (maintaining sign), sign extension.",
codeSample: (a, b) =>
`let num = ${a};\nlet shiftAmount = ${b};\nlet result = num >> shiftAmount; // ${
a >> b
}`,
},
">>>": {
name: "Zero-Fill Right Shift (>>>)",
description:
"Shifts all bits of the first operand to the right by the number of positions specified by the second operand. Bits shifted off the right are discarded. Zeros are always shifted in from the left, regardless of the sign bit. The result is always non-negative.",
usecases:
"Working with bit patterns where sign is irrelevant, ensuring a non-negative result after shifting, common in hashing algorithms or graphics.",
codeSample: (a, b) =>
`let num = ${a};\nlet shiftAmount = ${b};\nlet result = num >>> shiftAmount; // ${
a >>> b
}`,
},
"&": {
name: "Bitwise AND (&)",
description:
"Compares each bit of the first operand to the corresponding bit of the second operand. If both bits are 1, the corresponding result bit is set to 1. Otherwise, the result bit is set to 0.",
usecases:
"Checking if a specific bit (flag) is set, clearing specific bits (masking).",
codeSample: (a, b) =>
`let numA = ${a};\nlet numB = ${b};\nlet result = numA & numB; // ${
a & b
}`,
},
"|": {
name: "Bitwise OR (|)",
description:
"Compares each bit of the first operand to the corresponding bit of the second operand. If either bit is 1, the corresponding result bit is set to 1. Otherwise, the result bit is set to 0.",
usecases: "Setting specific bits (flags), combining permission flags.",
codeSample: (a, b) =>
`let numA = ${a};\nlet numB = ${b};\nlet result = numA | numB; // ${
a | b
}`,
},
"^": {
name: "Bitwise XOR (^)",
description:
"Compares each bit of the first operand to the corresponding bit of the second operand. If the bits are different, the corresponding result bit is set to 1. If the bits are the same, the result bit is set to 0.",
usecases:
"Toggling specific bits (flags), simple encryption/hashing, swapping variables without a temporary variable.",
codeSample: (a, b) =>
`let numA = ${a};\nlet numB = ${b};\nlet result = numA ^ numB; // ${
a ^ b
}`,
},
"~": {
name: "Bitwise NOT (~)",
description:
"Inverts all the bits of the operand. Each 1 becomes a 0, and each 0 becomes a 1. This is a unary operation (takes only one operand). Note that ~x is equivalent to -(x + 1).",
usecases: "Creating bitmasks, inverting all flags.",
codeSample: (a) => `let num = ${a};\nlet result = ~num; // ${~a}`,
},
};
// --- Helper Functions ---
function decToBinary32(dec) {
// Use >>> 0 to treat the number as an unsigned 32-bit integer for binary conversion
// This correctly handles negative numbers in two's complement format for display.
return (dec >>> 0).toString(2).padStart(32, "0");
}
function createGraphicalBinary(binaryString) {
const binaryGraphical = document.createElement("div");
binaryGraphical.classList.add("binary-graphical");
let bitCount = 0; // Counter for bits in the current group
for (let i = 0; i < binaryString.length; i++) {
// Add a separator before every 8 bits, except the first group
if (bitCount === 0 && i !== 0) {
const separator = document.createElement("div");
separator.classList.add("separator");
binaryGraphical.appendChild(separator);
}
const bit = document.createElement("div");
bit.classList.add("bit");
bit.textContent = binaryString[i];
if (binaryString[i] === "1") {
bit.classList.add("one");
}
binaryGraphical.appendChild(bit);
bitCount++;
if (bitCount === 8) {
bitCount = 0; // Reset the counter for the next group
}
}
return binaryGraphical;
}
function updateBinaryInputDisplay() {
const binary = decToBinary32(currentNumber);
const graphicalBinary = createGraphicalBinary(binary);
// Clear previous content
while (binaryInputDisplay.firstChild) {
binaryInputDisplay.removeChild(binaryInputDisplay.firstChild);
}
binaryInputDisplay.appendChild(graphicalBinary);
}
function updateResultDisplay(op, operand, result) {
let opString = "";
if (op === "~") {
opString = `${op} ${currentNumber}`;
} else {
opString = `${currentNumber} ${op} ${operand}`;
}
resultOperationDisplay.textContent = `Operation: ${opString}`;
resultDecimalDisplay.textContent = result;
// Create and display graphical binary representations
const originalBinary = decToBinary32(currentNumber);
const resultBinary = decToBinary32(result);
const originalBinaryGraphical = createGraphicalBinary(originalBinary);
const resultBinaryGraphical = createGraphicalBinary(resultBinary);
// Clear previous content
while (resultOriginalBinaryDisplay.firstChild) {
resultOriginalBinaryDisplay.removeChild(
resultOriginalBinaryDisplay.firstChild
);
}
while (resultBinaryDisplay.firstChild) {
resultBinaryDisplay.removeChild(resultBinaryDisplay.firstChild);
}
// Append the new graphical representations
resultOriginalBinaryDisplay.appendChild(originalBinaryGraphical);
resultBinaryDisplay.appendChild(resultBinaryGraphical);
}
function updateExplanation(op) {
const details = explanations[op];
if (details) {
explanationTitle.textContent = details.name;
explanationText.textContent = details.description;
explanationUsecases.textContent = details.usecases;
if (op === "~") {
explanationCode.textContent = details.codeSample(currentNumber);
} else {
explanationCode.textContent = details.codeSample(
currentNumber,
currentOperand
);
}
} else {
explanationTitle.textContent = "Operation Details";
explanationText.textContent = "Select an operation above.";
explanationUsecases.textContent = "";
explanationCode.textContent = "";
}
}
function performOperation() {
if (!currentOperation) return; // No operation selected
let result;
const num = currentNumber; // Use the current state
const operand = currentOperand; // Use the current state
switch (currentOperation) {
case "<<":
result = num << operand;
break;
case ">>":
result = num >> operand;
break;
case ">>>":
result = num >>> operand;
break;
case "&":
result = num & operand;
break;
case "|":
result = num | operand;
break;
case "^":
result = num ^ operand;
break;
case "~":
result = ~num;
break; // Unary
default:
result = "N/A";
}
if (result !== "N/A") {
updateResultDisplay(currentOperation, operand, result);
updateExplanation(currentOperation); // Refresh explanation with current numbers
}
}
function setActiveButton(button) {
if (currentActiveButton) {
currentActiveButton.classList.remove("active");
}
if (button) {
button.classList.add("active");
currentActiveButton = button;
} else {
currentActiveButton = null;
}
}
function handleOperationButtonClick(event) {
const button = event.target;
const op = button.dataset.op;
const operandType = button.dataset.operandType; // 'shift', 'logical', 'unary'
currentOperation = op; // Set the current operation
setActiveButton(button); // Highlight the button
// Show/hide and label the second operand input
if (operandType === "unary") {
operandInputContainer.style.display = "none";
} else {
operandInputContainer.style.display = "block";
if (operandType === "shift") {
operandLabel.textContent = "Shift by:";
} else {
// logical
operandLabel.textContent = "With:";
}
// Ensure currentOperand is up-to-date when showing the input
currentOperand = parseInt(operandInput.value, 10) || 0;
}
// Perform the calculation immediately
performOperation();
}
// --- Event Listeners ---
numberInput.addEventListener("input", () => {
currentNumber = parseInt(numberInput.value, 10);
// Handle NaN case (e.g., empty input)
if (isNaN(currentNumber)) {
currentNumber = 0;
}
updateBinaryInputDisplay();
// Recalculate if an operation is already selected
if (currentOperation) {
performOperation();
}
});
operandInput.addEventListener("input", () => {
currentOperand = parseInt(operandInput.value, 10);
// Handle NaN or negative shift/operand values (often want non-negative)
if (isNaN(currentOperand) || currentOperand < 0) {
currentOperand = 0;
operandInput.value = 0; // Correct input visually if invalid
}
// Recalculate if an operation is selected that uses the operand
if (currentOperation && currentOperation !== "~") {
performOperation();
}
});
operationButtons.forEach((button) => {
button.addEventListener("click", handleOperationButtonClick);
});
// --- Theme Toggler ---
function applyTheme(theme) {
if (theme === "dark") {
document.body.classList.add("dark-mode");
themeToggle.textContent = "☀️"; // Sun icon for light mode
} else {
document.body.classList.remove("dark-mode");
themeToggle.textContent = "🌓"; // Moon icon for dark mode
}
}
themeToggle.addEventListener("click", () => {
const isDarkMode = document.body.classList.toggle("dark-mode");
const newTheme = isDarkMode ? "dark" : "light";
applyTheme(newTheme);
localStorage.setItem("theme", newTheme);
});
// --- Initial Setup ---
// Load saved theme or use system preference
const savedTheme = localStorage.getItem("theme");
const prefersDark =
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches;
const initialTheme = savedTheme || (prefersDark ? "dark" : "light");
applyTheme(initialTheme);
// Initial display updates
updateBinaryInputDisplay();
// Optionally select a default operation on load?
// operationButtons[0].click(); // Example: Click the first button (Left Shift)
});