const framerate = 20; const pixelSize = 100; const maxUpdateRate = 6000; const pixelFuzz = document.getElementById("pixelfuzz"); const foreground = document.getElementById("foreground"); const borders = document.getElementById("borders"); const context = document.getElementById("context"); const cType = document.getElementById("c-type"); const cMod = document.getElementById("c-mod"); const cModText = document.getElementById("c-mod-text"); const cLittleMod = document.getElementById("c-littlemod"); const cValue = document.getElementById("c-value"); const cWarnings = document.getElementById("c-warnings"); const cSave = document.getElementById("c-save"); const pixel = document.createElement("div"); pixel.classList.add("pixel"); pixel.style.width = `${100/pixelSize}vw`; pixel.style.height = pixel.style.width; pixel.setAttribute("datatype", "undefined"); pixel.setAttribute("value", 0); const border = document.createElement("div"); border.classList.add("border"); let mouse = [0,0]; document.addEventListener("mousemove", event => { mouse = [event.clientX, event.clientY]; }); document.addEventListener("touchmove", event => { const touch = event.touches[0]; mouse = [touch.clientX, touch.clientY]; }); let movingContext = false; let originalMouse = [0,0]; let originalPos = [0, 0]; function setColor(type, value) { if (type == "bool") { if (value == 0) { return "#000"; } else if (value == 1) { return "#f00"; } else { return `hsl(${60 * value / 255}, 30%, 50%)`; } } else if (type == "char") { return `hsl(15, 100%, ${50 * value / 255}%)`; } else if (type == "string") { return `hsl(35, 100%, ${50 * value / 255}%)`; } else if (type == "UTF8char") { return `hsl(210, 100%, ${50 * value / 255}%)`; } else if (type == "UTF8string") { return `hsl(190, 100%, ${50 * value / 255}%)`; } else if (type == "UTF16char") { return `hsl(300, 100%, ${50 * value / 255}%)`; } else if (type == "UTF16string") { return `hsl(275, 100%, ${50 * value / 255}%)`; } else if (type == "int8") { return `hsl(75, 100%, ${50 * value / 255}%)`; } else if (type == "int16") { return `hsl(90, 100%, ${50 * value / 255}%)`; } else if (type == "int32") { return `hsl(120, 100%, ${50 * value / 255}%)`; } else if (type == "int64") { return `hsl(170, 100%, ${50 * value / 255}%)`; } else { return `rgb(${value}, ${value}, ${value})`; } } /* * byte/int8 (8b) -> vihertävä keltainen * short/int16 (16b) -> kellertävä vihreä * int/int32 (32b) -> vihreä * long/int64 (64b) -> turkoosi * [EHKÄ] BigInt -> tummankeltaisesta vihreään ja turkoosiin * (float ja double ehkä joskus myöhemmin) * char (8b) -> punertava oranssi * string (char[>2]) -> oranssi * UTF8char (8b-32b) -> alkupään sininen * UTF8string (UTF8char[>2]) -> sinertävä syaani * UTF16char (16b, 32b) -> magenta * UTF16string (UTF16char[>2]) -> violetti * bool (8b) -> musta (false) / punainen (true) / hieman harmaansävyinen punainen-keltainen (true>1) */ function updateColor(cell) { const color = setColor(cell.getAttribute("datatype"), cell.getAttribute("value")); cell.style.backgroundColor = color; cell.style.borderColor = color; } let allocs = []; let editing = null; let autosave; function setAutosave(value, updateAuto=true) { if (updateAuto) { autosave = value; } cSave.disabled = value; } setAutosave(true); function setEditing(alloc) { movingContext = false; const original = editing; editing?.getParent().classList.remove("selected"); editing = alloc; editing?.getParent().classList.add("selected"); if (editing) { const ctxStyles = window.getComputedStyle(context); const ctxSizes = [parseFloat(ctxStyles.width), parseFloat(ctxStyles.height)]; context.style.left = `${Math.min(mouse[0], window.innerWidth - ctxSizes[0])}px`; context.style.top = `${Math.min(mouse[1], window.innerHeight - ctxSizes[1])}px`; context.style.visibility = "visible"; context.style.scale = 1; context.style.opacity = 1; } else { context.style.visibility = ""; context.style.scale = ""; context.style.opacity = ""; } if (editing == original) return; // Don't continue if unchanged else if (editing) { const datatype = editing.getDatatype(); if (datatype == "undefined") { cType.value = "undefined"; updateCMod(); updateCValue(); } else if (datatype == "bool") { cType.value = "bool"; updateCMod(); updateCValue(); } else if (datatype.includes("char") || datatype.includes("string")) { cType.value = "char"; const encoding = updateCMod(); updateCValue(); const cValueText = cValue.querySelector("textarea"); if (datatype.includes("UTF8")) { encoding.value = "UTF-8"; cValueText.value = editing.getString("UTF-8"); } else if (datatype.includes("UTF16")) { encoding.value = "UTF-16"; cValueText.value = editing.getString("UTF-16"); } else { encoding.value = "ASCII"; cValueText.value = editing.getString("ASCII"); } updateCValue(); } else if (datatype.includes("int")) { cType.value = "int"; const type = updateCMod(); updateCValue(); type.value = datatype; } } } function deselectAlloc() { setEditing(null); } function deallocate() { if (editing) { editing.dealloc(); setEditing(null); } } function clearAlloc() { if (editing) { editing.clear(); updateCValue(); } } pixelFuzz.addEventListener("mousedown", deselectAlloc); pixelFuzz.addEventListener("touchstart", deselectAlloc); document.addEventListener("keydown", event => { if (event.key == "Escape") { deselectAlloc(); } }); function contextMouseDown(event) { event.stopPropagation(); if (event instanceof MouseEvent && event.button == 1) { event.preventDefault(); originalPos = [parseFloat(context.style.left), parseFloat(context.style.top)]; originalMouse = [event.clientX, event.clientY]; movingContext = true; context.style.transition = "none"; } // TODO: For mobile } context.addEventListener("mousedown", contextMouseDown); context.addEventListener("touchstart", contextMouseDown); class Allocation { constructor(cells, index) { this._cells = cells ? cells : []; this._parent = border.cloneNode(true); this._index = index; this._unsigned = false; this._parent.addEventListener("mousedown", event => { event.stopPropagation(); }); this._parent.addEventListener("touchstart", event => { event.stopPropagation(); }); this._parent.addEventListener("click", event => { event.stopPropagation(); setEditing(this); }); } getCells() { return this._cells; } getParent() { return this._parent; } getIndex() { return this._index; } getUnsigned() { return this._unsigned; } setUnsigned(boolean) { this._unsigned = boolean; } getDatatype() { return this.getCells()[0].getAttribute("datatype"); } updateIndex() { this._index = allocs.indexOf(this); } setupBorder(appending=false) { const parent = this.getParent(); const cells = this.getCells(); const pos0 = cells[0]; const pos1 = cells[cells.length - 1]; parent.style.left = `calc(${pos0.style.left} + 1.25px)`; parent.style.top = `calc(${pos0.style.top} + 1.25px)`; const computedStyles = [window.getComputedStyle(pos0), window.getComputedStyle(pos1)]; parent.style.width = `${parseFloat(computedStyles[1].left) - parseFloat(computedStyles[0].left) + parseFloat(computedStyles[1].width) - 2.5}px`; parent.style.height = `${parseFloat(computedStyles[1].top) - parseFloat(computedStyles[0].top) + parseFloat(computedStyles[1].height) - 2.5}px`; if (appending) borders.appendChild(parent); } add(cell) { this._cells[this._cells.length] = cell; } clear(datatype=null) { this.getCells().forEach(cell => { if (datatype) { cell.setAttribute("datatype", datatype); } cell.setAttribute("value", 0); }); } convert(datatype="undefined") { this.getCells().forEach(cell => { cell.setAttribute("datatype", datatype); }); } setInt(integer, bytesize=4, firstcell=0, unsigned=false) { let rawInt; if (bytesize == 8) { rawInt = unsigned ? new BigInt64Array([BigInt(integer)]) : new BigUint64Array([BigInt(integer)]); } else if (bytesize == 4) { rawInt = unsigned ? new Uint32Array([integer]) : new Int32Array([integer]); } else if (bytesize == 2) { rawInt = unsigned ? new Uint16Array([integer]) : new Int16Array([integer]); } else { rawInt = unsigned ? new Uint8Array([integer]) : new Int8Array([integer]); } const bytes = new Uint8Array(rawInt.buffer); const amount = Math.min(this.getCells().length - firstcell, bytesize); const intrinsicAmount = Math.min(this.getCells().length, bytesize); for (let i = 0; i < amount; i++) { this.getCells()[firstcell + i].setAttribute("value", bytes[i]); } } setString(string, encoding="UTF-8") { let usableEncoding = encoding.toUpperCase(); let bytes; if (usableEncoding == "ASCII" || usableEncoding == "UTF-8") { bytes = new TextEncoder().encode(string); } else if (usableEncoding == "UTF-16") { const rawBytepairs = []; for (let i = 0; i < string.length; i++) { rawBytepairs.push(string.charCodeAt(i)); } bytes = new Uint8Array(new Uint16Array(rawBytepairs).buffer); } for (let i = 0; i < bytes.length; i++) { this.setInt(bytes[i], 1, i, true); } } getTooLongMsg(string, encoding="UTF-8") { let usableEncoding = encoding.toUpperCase(); let bytes; if (usableEncoding == "ASCII" || usableEncoding == "UTF-8") { bytes = new TextEncoder().encode(string); } else if (usableEncoding == "UTF-16") { const rawBytepairs = []; for (let i = 0; i < string.length; i++) { rawBytepairs.push(string.charCodeAt(i)); } bytes = new Uint8Array(new Uint16Array(rawBytepairs).buffer); } const overflow = bytes.length - this.getCells().length; return (overflow > 0) ? `Warning: String is ${overflow} byte${(overflow > 1) ? "s" : ""} too long, truncating.` : ""; } getInt(bytesize=4, firstcell=0, unsigned=false) { const rawValues = []; const amount = Math.min(this.getCells().length - firstcell, bytesize); for (let i = 0; i < amount; i++) { rawValues.push(parseInt(this.getCells()[firstcell + i].getAttribute("value"))); } const missingValues = (rawValues.length % bytesize == 0) ? 0 : bytesize - rawValues.length % bytesize; for (let i = 0; i < missingValues; i++) { rawValues.push(0); } const values = new Uint8Array(rawValues); if (bytesize == 8) { return unsigned ? new BigUint64Array(values.buffer)[0] : new BigInt64Array(values.buffer)[0]; } else if (bytesize == 4) { return unsigned ? new Uint32Array(values.buffer)[0] : new Int32Array(values.buffer)[0]; } else if (bytesize == 2) { return unsigned ? new Uint16Array(values.buffer)[0] : new Int16Array(values.buffer)[0]; } else { return unsigned ? new Uint8Array(values.buffer)[0] : new Int8Array(values.buffer)[0]; } } getString(encoding="UTF-8") { let usableEncoding = encoding.toUpperCase(); const rawBytes = []; for (let i = 0; i < this.getCells().length; i++) { rawBytes.push(parseInt(this.getCells()[i].getAttribute("value"))); } let rawString = ""; if (usableEncoding == "ASCII") { const decoder = new TextDecoder("UTF-8"); for (let i = 0; i < rawBytes.length; i++) { rawString += (rawBytes[i] > 127) ? "\uFFFD" : decoder.decode(new Uint8Array([rawBytes[i]])); } } else if (usableEncoding == "UTF-8") { rawString = new TextDecoder("UTF-8").decode(new Uint8Array(rawBytes)); } else if (usableEncoding == "UTF-16") { let odd = (rawBytes.length % 2 == 1); if (odd) { rawBytes.push(0); } rawString = new TextDecoder("UTF-16").decode(new Uint8Array(rawBytes)); if (odd) { rawBytes.pop(); } } let string = ""; let stringCache = ""; for (let i = 0; i < rawString.length; i++) { // Truncate trailing NULs stringCache += rawString[i]; if (rawString[i] != '\x00') { string += stringCache; stringCache = ""; } } return string; } isString(encoding="UTF-8") { const value = this.getString(encoding); return (value.length > 1) } dealloc() { this.convert(); this.getCells().forEach(cell => { pixelFuzz.appendChild(cell); }); for (let i = 0; i < allocs.length; i++) { if (allocs[i] === this) allocs.splice(i, 1); break; } this.getParent().remove(); if (editing === this) { setEditing(null); } delete this; } updateColors() { this.getCells().forEach(cell => { updateColor(cell); }) } } let nowString = false; function updateSelectedType() { if (editing) { if (cType.value == "undefined") { editing.convert("undefined"); } else if (cType.value == "bool") { editing.convert("bool"); } else if (cType.value == "char") { const encoding = cMod.querySelector("select"); nowString = editing.isString(encoding.value); if (encoding.value == "ASCII") { editing.convert((nowString) ? "string" : "char"); } else if (encoding.value == "UTF-8") { editing.convert((nowString) ? "UTF8string" : "UTF8char"); } else { editing.convert((nowString) ? "UTF16string" : "UTF16char"); } } else if (cType.value == "int") { const type = cMod.querySelector("select"); editing.convert(type.value); } } } function updateCMod() { if (editing) { setAutosave(true); cMod.innerHTML = ""; cLittleMod.innerHTML = ""; if (cType.value == "undefined") { cModText.textContent = "Modifier:"; const empty = document.createElement("span"); empty.textContent = "undefined"; cMod.appendChild(empty); return empty; } else if (cType.value == "bool") { cModText.textContent = "Modifier:"; const single = document.createElement("span"); single.textContent = "bool"; cMod.appendChild(single); return single; } else if (cType.value == "char") { cModText.textContent = "Encoding:"; const encoding = document.createElement("select"); encoding.innerHTML = ` `; encoding.name = "char-encoding"; encoding.value = "UTF-8"; encoding.addEventListener("input", () => { updateSelectedType(); updateCValue(); }); cMod.appendChild(encoding); return encoding; } else if (cType.value == "int") { cModText.textContent = "Size:"; const type = document.createElement("select"); type.innerHTML = ` `; type.name = "int-type"; type.value = "int32"; type.addEventListener("input", () => { updateSelectedType(); updateCValue(); }); cMod.appendChild(type); const littleModText = document.createElement("span"); littleModText.textContent = "Unsigned:"; const unsigned = document.createElement("input"); unsigned.type = "checkbox"; unsigned.name = "int-unsigned"; unsigned.checked = editing.getUnsigned(); unsigned.addEventListener("input", () => { editing.setUnsigned(unsigned.checked); updateSelectedType(); updateCValue(); }); cLittleMod.appendChild(littleModText); cLittleMod.appendChild(unsigned); return type; } } } function saveCharType() { const textArea = cValue.querySelector("textarea"); if (editing && textArea) { setAutosave(true); editing.clear(); editing.setString(textArea.value, cMod.querySelector("select").value); if (textArea.value.length > 1 && !nowString) { nowString = true; updateSelectedType(); } else if (textArea.value.length <= 1 && nowString) { nowString = false; updateSelectedType(); } } } function updateCValue() { if (!editing) return; const datatype = editing.getDatatype(); cValue.innerHTML = ""; cWarnings.innerHTML = ""; if (datatype == "undefined") { cValue.innerHTML = "^^^
Choose a type to start..."; } else if (datatype == "bool") { for (let i = 0; i < editing.getCells().length; i++) { const boolean = document.createElement("div"); const index = document.createElement("span"); index.textContent = `[${i}]: `; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = `alloc-value-${i}`; checkbox.checked = (editing.getCells()[i].getAttribute("value") >= 1); const textValue = document.createElement("label"); textValue.setAttribute("for", checkbox.id); if (editing.getCells()[i].getAttribute("value") <= 1) { textValue.style.fontWeight = "bold"; textValue.style.fontStyle = "normal"; textValue.textContent = (checkbox.checked) ? "TRUE" : "FALSE"; } else { textValue.style.fontWeight = "normal"; textValue.style.fontStyle = "italic"; textValue.textContent = `true (${editing.getCells()[i].getAttribute("value")})`; } checkbox.addEventListener("input", () => { if (editing) { editing.getCells()[i].setAttribute("value", checkbox.checked ? 1 : 0); updateColor(editing.getCells()[i]); textValue.style.fontWeight = "bold"; textValue.style.fontStyle = "normal"; textValue.textContent = (checkbox.checked) ? "TRUE" : "FALSE"; } }); boolean.appendChild(index); boolean.appendChild(checkbox); boolean.appendChild(textValue); cValue.appendChild(boolean); } } else if (datatype.includes("char") || datatype.includes("string")) { const textArea = document.createElement("textarea"); textArea.placeholder = "Enter value (char/string)"; textArea.name = "alloc-value"; textArea.autocomplete = "off"; textArea.value = editing.getString(cMod.querySelector("select").value); const tooLong = document.createElement("div"); tooLong.classList.add("c-warning"); function rescaleTooLong() { tooLong.style.width = window.getComputedStyle(textArea).width; requestAnimationFrame(rescaleTooLong); } rescaleTooLong(); if (datatype != "char" && datatype != "string") { tooLong.textContent = editing.getTooLongMsg(textArea.value, cMod.querySelector("select").value); } const replacementChars = textArea.value.includes("\uFFFD"); setAutosave(!replacementChars || datatype == "char" || datatype == "string", datatype != "char" || datatype != "string"); textArea.disabled = (replacementChars && (datatype == "char" || datatype == "string")); textArea.addEventListener("input", () => { if (editing) { tooLong.textContent = editing.getTooLongMsg(textArea.value, cMod.querySelector("select").value); if (cType.value == "char" && autosave) { saveCharType(); } } }); cValue.appendChild(textArea); cWarnings.appendChild(tooLong); } else if (datatype.includes("int")) { const byteSize = parseInt(datatype.match(/\d+/)) / 8; for (let i = 0; i < Math.floor(editing.getCells().length / byteSize); i++) { const unsigned = editing.getUnsigned(); const integer = document.createElement("div"); const index = document.createElement("span"); index.textContent = `[${i}]: `; const input = document.createElement("input"); input.type = "text"; input.name = `alloc-value-${i}`; const savedValue = editing.getInt(byteSize, i * byteSize, unsigned).toString(); input.value = savedValue; input.setAttribute("savedvalue", savedValue); if (datatype == "int64") { input.addEventListener("input", () => { if (editing) { input.value = input.value.replace(".", ""); if (unsigned) { input.value = input.value.replace("-", ""); } else { input.value = input.value.replace(/-+/, "-"); } let value; if (isNaN(input.value) && input.value != "-") { input.value = input.getAttribute("savedvalue"); } if (input.value == "-") { value = "-"; } else { value = BigInt(input.value); if ((unsigned && (value < 0n || value > 2n**BigInt(8 * byteSize) - 1n)) || (!unsigned && (value < -(2n**BigInt(8 * byteSize - 1)) || value > 2n**BigInt(8 * byteSize - 1) - 1n))) { input.value = input.getAttribute("savedvalue"); value = BigInt(input.getAttribute("savedvalue")); } editing.setInt(value, byteSize, i * byteSize, unsigned); updateColor(editing.getCells()[i * byteSize]); } input.setAttribute("savedvalue", value.toString()); } }); } else { input.addEventListener("input", () => { if (editing) { input.value = input.value.replace(".", ""); if (unsigned) { input.value = input.value.replace("-", ""); } else { input.value = input.value.replace(/-+/, "-"); } let value; if (isNaN(input.value) && input.value != "-") { input.value = input.getAttribute("savedvalue"); } if (input.value == "-") { value = "-"; } else { value = parseInt(input.value); if (isNaN(value)) { value = 0; } if ((unsigned && (value < 0 || value > 2**(8 * byteSize) - 1)) || (!unsigned && (value < -(2**(8 * byteSize - 1)) || value > 2**(8 * byteSize - 1) - 1))) { input.value = input.getAttribute("savedvalue"); value = parseInt(input.getAttribute("savedvalue")); } editing.setInt(value, byteSize, i * byteSize, unsigned); updateColor(editing.getCells()[i * byteSize]); } input.setAttribute("savedvalue", value.toString()); } }); } integer.appendChild(index); integer.appendChild(input); cValue.appendChild(integer); } } } cType.addEventListener("input", () => { updateCMod(); updateSelectedType(); updateCValue(); }); let cells = []; for (let i = 0; i < pixelSize; i++) { if (cells[i] === undefined) { cells[i] = []; } for (let j = 0; j < pixelSize; j++) { const clone = pixel.cloneNode(true); let untilChange = Math.floor(Math.random() * maxUpdateRate / framerate); clone.style.top = `calc(50vh - 50vw + ${100 / pixelSize * i}vw)`; clone.style.left = `${100 / pixelSize * j}vw`; pixelFuzz.appendChild(clone); cells[i][j] = clone; setInterval(() => { updateColor(clone); untilChange -= 1; if (untilChange <= 0) { if (clone.parentNode.id == "pixelfuzz") { clone.setAttribute("value", Math.floor(Math.random() * 256)); } untilChange = Math.floor(Math.random() * maxUpdateRate / framerate); } if (!editing) { window.getSelection().removeAllRanges(); } }, framerate); } } let initMousePos; let hold = false; const selectionBox = document.createElement("div"); selectionBox.style.background = "unset"; selectionBox.style.outline = "1px solid"; selectionBox.style.outlineColor = "transparent"; selectionBox.style.position = "fixed"; document.body.insertBefore(selectionBox, borders); function mouseDown(event) { if (event instanceof MouseEvent && event.button != 0) return; // Only left click if mouse if (event instanceof MouseEvent) { initMousePos = [event.clientX, event.clientY]; } else if (event instanceof TouchEvent) { initMousePos = [event.touches[0].clientX, event.touches[0].clientY] } hold = true; selectionBox.style.left = `${initMousePos[0]}px`; selectionBox.style.top = `${initMousePos[1]}px`; selectionBox.style.width = 0; selectionBox.style.height = 0; selectionBox.style.outlineColor = "#ff0"; } function mouseUp(event) { if (event instanceof MouseEvent && event.button == 1) { event.preventDefault(); movingContext = false; context.style.transition = ""; } // TODO: For mobile if (!hold) return; // Proceed only if held hold = false; selectionBox.style.outlineColor = "transparent"; let insideBox = []; const selectionPos = [parseFloat(selectionBox.style.left), parseFloat(selectionBox.style.top)]; const selectionSize = [parseFloat(selectionBox.style.width), parseFloat(selectionBox.style.height)]; let exclusive = true; for (let i = 0; i < foreground.children.length; i++) { const child = foreground.children[i]; const positions = [parseFloat(window.getComputedStyle(child).left), parseFloat(window.getComputedStyle(child).top)]; const sizes = [parseFloat(window.getComputedStyle(child).width), parseFloat(window.getComputedStyle(child).height)]; if ( positions[0] <= selectionPos[0] + selectionSize[0] && positions[1] <= selectionPos[1] + selectionSize[1] && positions[0] + sizes[0] >= selectionPos[0] && positions[1] + sizes[1] >= selectionPos[1] ) { exclusive = false; break; } } if (exclusive) { let newAlloc = new Allocation(); allocs.push(newAlloc); for (let i = 0; i < cells.length; i++) { for (let j = 0; j < cells[i].length; j++) { const child = cells[i][j]; if (child.parentNode == pixelFuzz) { const positions = [parseFloat(window.getComputedStyle(child).left), parseFloat(window.getComputedStyle(child).top)]; const sizes = [parseFloat(window.getComputedStyle(child).width), parseFloat(window.getComputedStyle(child).height)]; if ( positions[0] <= selectionPos[0] + selectionSize[0] && positions[1] <= selectionPos[1] + selectionSize[1] && positions[0] + sizes[0] >= selectionPos[0] && positions[1] + sizes[1] >= selectionPos[1] ) { insideBox[insideBox.length] = child; } } } } insideBox.forEach(child => { foreground.appendChild(child); newAlloc.add(child); }); newAlloc.setupBorder(true); } } document.addEventListener("mousedown", mouseDown); document.addEventListener("touchstart", mouseDown); document.addEventListener("mouseup", mouseUp); document.addEventListener("touchend", mouseUp); function resizeSelection(event) { let mousePos; if (event instanceof MouseEvent) { mousePos = [event.clientX, event.clientY]; } else if (event instanceof TouchEvent) { mousePos = [event.touches[0].clientX, event.touches[0].clientY]; } if (hold) { selectionBox.style.width = `${Math.max(mousePos[0] - initMousePos[0], initMousePos[0] - mousePos[0])}px`; selectionBox.style.height = `${Math.max(mousePos[1] - initMousePos[1], initMousePos[1] - mousePos[1])}px`; selectionBox.style.left = `${Math.min(initMousePos[0], mousePos[0])}px`; selectionBox.style.top = `${Math.min(initMousePos[1], mousePos[1])}px`; } } document.addEventListener("mousemove", resizeSelection); document.addEventListener("touchmove", resizeSelection); window.addEventListener("resize", () => { allocs.forEach(alloc => { alloc.setupBorder(); }); }); function frame() { if (movingContext) { context.style.left = `${originalPos[0] + mouse[0] - originalMouse[0]}px`; context.style.top = `${originalPos[1] + mouse[1] - originalMouse[1]}px`; } requestAnimationFrame(frame); } frame();