1const tableLeftOffset = 1028;
2const tableTopOffset = 128;
3const baseElementSize = 80;
4const initElementSize = 10;
5const elementSpacing = 16;
6const easeStrength = 4;
7const mouseInfluenceDistance = 200;
8const mouseInfluencePower = 0.5;
9const influenceScale = 1.2;
10
11var currentlySelectedElement = -1;
12
13const elementPositions = [
14 [0, 0],
15 [17, 0],
16 [0, 1],
17 [1, 1],
18 [12, 1],
19 [13, 1],
20 [14, 1],
21 [15, 1],
22 [16, 1],
23 [17, 1],
24 [0, 2],
25 [1, 2],
26 [12, 2],
27 [13, 2],
28 [14, 2],
29 [15, 2],
30 [16, 2],
31 [17, 2],
32 [0, 3],
33 [1, 3],
34 [2, 3],
35 [3, 3],
36 [4, 3],
37 [5, 3],
38 [6, 3],
39 [7, 3],
40 [8, 3],
41 [9, 3],
42 [10, 3],
43 [11, 3],
44 [12, 3],
45 [13, 3],
46 [14, 3],
47 [15, 3],
48 [16, 3],
49 [17, 3],
50 [0, 4],
51 [1, 4],
52 [2, 4],
53 [3, 4],
54 [4, 4],
55 [5, 4],
56 [6, 4],
57 [7, 4],
58 [8, 4],
59 [9, 4],
60 [10, 4],
61 [11, 4],
62 [12, 4],
63 [13, 4],
64 [14, 4],
65 [15, 4],
66 [16, 4],
67 [17, 4],
68 [0, 5],
69 [1, 5],
70 [3, 8],
71 [4, 8],
72 [5, 8],
73 [6, 8],
74 [7, 8],
75 [8, 8],
76 [9, 8],
77 [10, 8],
78 [11, 8],
79 [12, 8],
80 [13, 8],
81 [14, 8],
82 [15, 8],
83 [16, 8],
84 [17, 8],
85 [3, 5],
86 [4, 5],
87 [5, 5],
88 [6, 5],
89 [7, 5],
90 [8, 5],
91 [9, 5],
92 [10, 5],
93 [11, 5],
94 [12, 5],
95 [13, 5],
96 [14, 5],
97 [15, 5],
98 [16, 5],
99 [17, 5],
100 [0, 6],
101 [1, 6],
102 [3, 9],
103 [4, 9],
104 [5, 9],
105 [6, 9],
106 [7, 9],
107 [8, 9],
108 [9, 9],
109 [10, 9],
110 [11, 9],
111 [12, 9],
112 [13, 9],
113 [14, 9],
114 [15, 9],
115 [16, 9],
116 [17, 9],
117 [3, 6],
118 [4, 6],
119 [5, 6],
120 [6, 6],
121 [7, 6],
122 [8, 6],
123 [9, 6],
124 [10, 6],
125 [11, 6],
126 [12, 6],
127 [13, 6],
128 [14, 6],
129 [15, 6],
130 [16, 6],
131 [17, 6],
132];
133var elementSizes = Array(elementPositions.length).fill(initElementSize);
134var elementLeftOffsets = Array(elementPositions.length).fill(0);
135var elementTopOffsets = Array(elementPositions.length).fill(0);
136
137var mouseX = 0;
138var mouseY = 0;
139
140function generateElementPropertyHTMLCode(name, value, unit) {
141 return `<div class="element-property-container">
142 <div class="element-property-name">${name}</div>
143 <div class="element-property-value">${value}${unit}</div>
144</div>`;
145}
146
147function formatUnit(unitString) {
148 if (unitString !== undefined) {
149 if (unitString.kelvin !== undefined) {
150 return " " + unitString.kelvin;
151 }
152 return " " + unitString;
153 }
154 return "";
155}
156
157function formatValue(valueString) {
158 if (valueString !== undefined) {
159 return valueString;
160 }
161 return "NaN";
162}
163
164function formattedCPKColor(hex_color) {
165 colorString = "#" + hex_color;
166 var r = parseInt(colorString.substr(1, 2), 16); // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10).
167 var g = parseInt(colorString.substr(3, 2), 16);
168 var b = parseInt(colorString.substr(5, 2), 16);
169 (r /= 255), (g /= 255), (b /= 255);
170 var max = Math.max(r, g, b),
171 min = Math.min(r, g, b);
172 var h,
173 s,
174 l = (max + min) / 2;
175
176 if (max == min) {
177 h = s = 0; // achromatic
178 } else {
179 var d = max - min;
180 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
181 switch (max) {
182 case r:
183 h = (g - b) / d + (g < b ? 6 : 0);
184 break;
185 case g:
186 h = (b - r) / d + 2;
187 break;
188 case b:
189 h = (r - g) / d + 4;
190 break;
191 }
192 h /= 6;
193 }
194
195 return `hsl(${h * 360}deg ${s * 100}% 78%)`;
196}
197
198function displayElementProperties(elementIndex) {
199 if (currentlySelectedElement != elementIndex) {
200 currentlySelectedElement = elementIndex;
201 properties = [
202 "abundance.universe",
203 "allotropes",
204 "atomic_mass",
205 "atomic_number",
206 "block",
207 "boiling-point",
208 "classifications.dot_numbers",
209 "conductivity.electric",
210 "conductivity.thermal",
211 "critical_pressure",
212 "critical_temperature",
213 "crystal_structure",
214 "density.liquid",
215 "density.stp",
216 "discovered",
217 "electron_configuration",
218 "electronegativity_pauling",
219 "gas_phase",
220 "group",
221 "hardness.mohs",
222 "heat.fusion",
223 "heat.molar",
224 "heat.vaporization",
225 "isotopes_known",
226 "isotopes_stable",
227 "isotopic_abundances",
228 "melting_point",
229 "molar_volume",
230 "quantum_numbers",
231 "radius.empirical",
232 "refractive_index",
233 "speed_of_sound",
234 ];
235 document.getElementById("element-data").innerHTML = "";
236 document.getElementById("element-data").innerHTML = `
237 <div id="element-name"></div>
238 <div id="element-appearance"></div>
239 <div id="element-properties">
240 </div>`;
241 document.body.style.backgroundColor = formattedCPKColor(
242 pTable[elementIndex].cpk_hex
243 );
244 document.getElementById("element-name").innerHTML =
245 pTable[elementIndex].name;
246 document.getElementById("element-appearance").innerHTML =
247 pTable[elementIndex].appearance;
248 document.getElementById("element-properties").innerHTML = "";
249 for (const [key, value] of Object.entries(properties)) {
250 if (value.includes(".")) {
251 propertySplit = value.split(".");
252 propertyName = propertySplit[0] + `(${propertySplit[1]})`;
253 try {
254 propertyValue =
255 pTable[elementIndex][propertySplit[0]][propertySplit[1]];
256 propertyUnit = formatUnit(
257 pTableUnits[propertySplit[0]][propertySplit[1]]
258 );
259 } catch {
260 propertyValue = undefined;
261 propertyUnit = "";
262 }
263 document.getElementById("element-properties").innerHTML +=
264 generateElementPropertyHTMLCode(
265 propertyName,
266 formatValue(propertyValue),
267 propertyUnit
268 );
269 } else {
270 if (value == "discovered") {
271 document.getElementById("element-properties").innerHTML +=
272 generateElementPropertyHTMLCode(
273 value,
274 "by " +
275 formatValue(pTable[elementIndex][value].by) +
276 " in " +
277 formatValue(pTable[elementIndex][value].year),
278 ""
279 );
280 } else {
281 document.getElementById("element-properties").innerHTML +=
282 generateElementPropertyHTMLCode(
283 value,
284 formatValue(pTable[elementIndex][value]),
285 formatUnit(pTableUnits[value])
286 );
287 }
288 }
289 }
290 }
291}
292
293function drawElement(elementIndex) {
294 elementLeftOffsets[elementIndex] =
295 tableLeftOffset +
296 Math.pow(
297 elementPositions[elementIndex][0] * (baseElementSize + elementSpacing) +
298 elementPositions[elementIndex][1] * 100,
299 1.7
300 ) /
301 100;
302 elementTopOffsets[elementIndex] =
303 tableTopOffset +
304 Math.pow(
305 elementPositions[elementIndex][1] * (baseElementSize + elementSpacing),
306 1.2
307 ) /
308 2;
309 elementSizes[elementIndex] =
310 4 *
311 (elementPositions[elementIndex][0] * 2 + elementPositions[elementIndex][1]);
312 document.body.innerHTML += `
313 <div class="table-element" id="element-index-${elementIndex}" style="left: ${elementLeftOffsets[elementIndex]}px; top: ${elementTopOffsets[elementIndex]}px;">${pTable[elementIndex].symbol}<div class="element-name">${pTable[elementIndex].name}</div><div class="element-z-number">${pTable[elementIndex].atomic_number}</div></div>
314 `;
315}
316var oldTime = new Date();
317function updateElements() {
318 var deltaTime = new Date() - oldTime;
319 for (elementIndex in elementPositions) {
320 refLeftOffset =
321 tableLeftOffset +
322 elementPositions[elementIndex][0] * (baseElementSize + elementSpacing);
323 refTopOffset =
324 tableTopOffset +
325 elementPositions[elementIndex][1] * (baseElementSize + elementSpacing);
326 elementLeftOffsets[elementIndex] =
327 elementLeftOffsets[elementIndex] +
328 (((refLeftOffset - elementLeftOffsets[elementIndex]) / easeStrength) *
329 deltaTime) /
330 100;
331 elementTopOffsets[elementIndex] =
332 elementTopOffsets[elementIndex] +
333 (((refTopOffset - elementTopOffsets[elementIndex]) / easeStrength) *
334 deltaTime) /
335 100;
336 distFromMouse = Math.sqrt(
337 (refLeftOffset + baseElementSize / 2 - mouseX) ** 2 +
338 (refTopOffset + baseElementSize / 2 - mouseY) ** 2
339 );
340 targetSize =
341 baseElementSize +
342 elementSpacing /
343 (influenceScale +
344 (distFromMouse / mouseInfluenceDistance) ** mouseInfluencePower);
345 elementSizes[elementIndex] =
346 elementSizes[elementIndex] +
347 (((targetSize - elementSizes[elementIndex]) / easeStrength) * deltaTime) /
348 100;
349 document.getElementById(`element-index-${elementIndex}`).style.width =
350 elementSizes[elementIndex] + "px";
351 document.getElementById(`element-index-${elementIndex}`).style.height =
352 elementSizes[elementIndex] + "px";
353 document.getElementById(`element-index-${elementIndex}`).style.left =
354 elementLeftOffsets[elementIndex] +
355 (baseElementSize - elementSizes[elementIndex]) / 2 +
356 "px";
357 document.getElementById(`element-index-${elementIndex}`).style.top =
358 elementTopOffsets[elementIndex] +
359 (baseElementSize - elementSizes[elementIndex]) / 2 +
360 "px";
361 }
362 oldTime = new Date();
363 window.requestAnimationFrame(updateElements);
364}
365
366for (elementIndex in elementPositions) {
367 drawElement(elementIndex);
368}
369
370document.body.addEventListener("mousemove", (event) => {
371 mouseX = event.clientX;
372 mouseY = event.clientY;
373 el = document.elementFromPoint(mouseX, mouseY);
374 if (el.parentNode.className == "table-element") {
375 displayElementProperties(parseInt(el.parentNode.id.split("-index-")[1]));
376 }
377 if (el.className == "table-element") {
378 displayElementProperties(parseInt(el.id.split("-index-")[1]));
379 }
380});
381
382displayElementProperties(1);
383
384window.requestAnimationFrame(updateElements);