// Get the container to use as a canvas const container = document.getElementById("container"); const editor = document.getElementById("editor"); const backgroundImage = document.getElementById("background-image"); const imageTarget = document.getElementById("target"); const backgroundContainer = document.getElementById("background-container"); let backgroundWidth; let backgroundHeight; window.addEventListener("load", () => { targetSize(); }); window.addEventListener("resize", (e) => { targetSize(); }); function targetSize() { backgroundWidth = backgroundImage.width; backgroundHeight = backgroundImage.height; // cringe imageTarget.style.width = backgroundWidth + "px"; imageTarget.style.height = backgroundHeight + "px"; } // List of labels let labels = []; let labelsObj = []; let closing = false; // Start is where the mouse is pressed, End is where the mouse is released let startX; let startY; let endX; let endY; // Minimum size for the label to be created let minimumSizeX = 5; let minimumSizeY = 5; // Boolean for showing the editor during drawing let showEditor = false; let editorX = 0; let editorY = 0; function percentagePosition(e, target) { let targetRect = target.getBoundingClientRect(); let x = e.clientX - targetRect.left; let y = e.clientY - targetRect.top; return { x: (x / backgroundWidth) * 100, y: (y / backgroundHeight) * 100 }; } window.addEventListener("mousedown", (e) => { let percentage = percentagePosition(e, imageTarget); if (e.target.tagName !== "BUTTON" && e.target.tagName !== "TEXTAREA") { startX = percentage.x; startY = percentage.y; editorX = e.x; editorY = e.y; // activate the editor showEditor = true; editor.classList.add("show-editor"); } }); // Store the coordinates and trigger the function window.addEventListener("mouseup", (e) => { if (e.target.tagName !== "BUTTON" && e.target.tagName !== "TEXTAREA") { let percentage = percentagePosition(e, imageTarget); endX = percentage.x; endY = percentage.y; // disable the editor showEditor = false; editor.classList.remove("show-editor"); editor.style.width = 0; editor.style.height = 0; // draw label drawLabel(); } }); // Edit the editor box using transform instead of left / top in order to be more efficient // (but still with width and height ehm idk if this affects the performance a lot) // (and it is something we must care of because this event is called like every frame that the mouse is dragged) window.addEventListener("mousemove", (e) => { if (showEditor) { let minX = Math.min(editorX, e.x); let minY = Math.min(editorY, e.y); let maxX = Math.max(editorX, e.x); let maxY = Math.max(editorY, e.y); let width = maxX - minX; let height = maxY - minY; // Apply a different class when the sizes pass the minimum size // (i don't know if is good made like this) if (width > minimumSizeX && height > minimumSizeY) { editor.classList.add("can-draw"); } else { editor.classList.remove("can-draw"); } editor.style.transform = `translate(${minX}px, ${minY}px)`; editor.style.width = `${maxX - minX}px`; editor.style.height = `${maxY - minY}px`; } }); // Store the coordinates and trigger the function // container.addEventListener("mousedown", (e) => { // // Avoid inserting a new label if the user is clicking on a close button) // if (e.target.tagName !== "BUTTON" && e.target.tagName !== "TEXTAREA") { // startX = e.x; // startY = e.y; // // activate the editor // showEditor = true; // editor.classList.add("show-editor"); // } // }); // container.addEventListener("mouseup", (e) => { // if (e.target.tagName !== "BUTTON" && e.target.tagName !== "TEXTAREA") { // endX = e.x; // endY = e.y; // // disable the editor // showEditor = false; // editor.classList.remove("show-editor"); // editor.style.width = 0; // editor.style.height = 0; // // draw label // drawLabel(); // } // }); // Edit the editor box using transform instead of left / top in order to be more efficient // (but still with width and height ehm idk if this affects the performance a lot) // (and it is something we must care of because this event is called like every frame that the mouse is dragged) // container.addEventListener("mousemove", (e) => { // if (showEditor) { // let minX = Math.min(startX, e.x); // let minY = Math.min(startY, e.y); // let maxX = Math.max(startX, e.x); // let maxY = Math.max(startY, e.y); // let width = maxX - minX; // let height = maxY - minY; // // Apply a different class when the sizes pass the minimum size // // (i don't know if is good made like this) // if (width > minimumSizeX && height > minimumSizeY) { // editor.classList.add("can-draw"); // } else { // editor.classList.remove("can-draw"); // } // editor.style.transform = `translate(${minX}px, ${minY}px)`; // editor.style.width = `${maxX - minX}px`; // editor.style.height = `${maxY - minY}px`; // } // }); // Check the mouse direction and create the Label // The origin points of the label (because is positioned with top left) are always the lowest x and y values // The width and height are the greater x and y values (because width and height cannot be negative) function drawLabel() { let minX = Math.min(startX, endX); let minY = Math.min(startY, endY); let maxX = Math.max(startX, endX); let maxY = Math.max(startY, endY); let width = maxX - minX; let height = maxY - minY; if (width > minimumSizeX && height > minimumSizeY) { // Create a label and push it into the array of labels let temporaryLabel = createLabel(minX, minY, width, height, labels.length); temporaryLabel.classList.add("temporary"); let form = document.createElement("form"); let input = document.createElement("textarea"); input.placeholder = "Describe this area"; let insert = document.createElement("button"); insert.innerHTML = "Insert"; let cancel = document.createElement("button"); cancel.innerHTML = "x"; form.appendChild(input); form.appendChild(insert); form.appendChild(cancel); temporaryLabel.appendChild(form); imageTarget.appendChild(temporaryLabel); new Promise(function (resolve, reject) { // then if the user click insert and there is a value in the input-- > resolve the promise and return the text input to create the label, // if the user click cancel-- > reject the promise and don't create the label input.focus(); // Insert button insert.addEventListener("click", (e) => { e.preventDefault(); if (input.value) { resolve(); } }); // Cancel button cancel.addEventListener("click", (e) => { e.preventDefault(); reject(); }); }) .then(() => { // Create the label let label = createLabel(minX, minY, width, height, labels.length); // Add the text input to the label let text = document.createElement("p"); text.classList.add("label--text"); text.innerHTML = input.value; label.appendChild(text); let labelObj = { position: { x: minX, y: minY, }, size: { width: width, height: height, }, index: labels.length, text: input.value, timestamp: Date.now(), userID: userID, }; uploadLabel(labelObj); labelsObj.push(labelObj); labels.push(label); imageTarget.appendChild(label); createLabelTranscription(input.value); }) .catch((e) => {}) .then(() => { imageTarget.removeChild(temporaryLabel); }); } } // Create the label element function createLabel(x, y, width, height, index) { let label = document.createElement("div"); label.classList.add("label"); label.style.left = `${x}%`; label.style.top = `${y}%`; label.style.width = `${width}%`; label.style.height = `${height}%`; // data attribute index maybe we will need it later maybe not // with the index number of the label label.setAttribute("data-index", index); // Insert the number in the label let labelNumber = document.createElement("p"); labelNumber.classList.add("label--number"); labelNumber.innerHTML = index + 1; label.appendChild(labelNumber); // Add a button for deleting the label // TODO: reactive numbering oh no let close = document.createElement("button"); close.classList.add("label--close"); close.innerHTML = "x"; close.addEventListener("click", (e) => { label.remove(); }); label.appendChild(close); return label; } const transcriptionPanel = document.getElementById("transcription-panel"); const transcriptionList = transcriptionPanel.querySelector("ol"); function createLabelTranscription(text) { let transcription = document.createElement("li"); transcription.innerHTML = text; transcriptionList.appendChild(transcription); } function uploadLabel(obj) { fetch("https://hub.xpub.nl/soupboat/collecting-labels/", { method: "POST", // or 'PUT' headers: { "Content-Type": "application/json", }, body: JSON.stringify(obj), }) .then((response) => response.json()) .then((data) => { console.log("Success:", data); }) .catch((error) => { console.error("Error:", error); }); } const load = document.getElementById("load-labels"); load.addEventListener("click", loadLabels); function loadLabels() { load.removeEventListener("click", loadLabels); fetch("./labels.json") .then((response) => { return response.json(); }) .then((data) => { labelsObj = [labelsObj, ...labels]; data.labels.forEach((label, index) => { let labelElement = createLabel( label.position.x, label.position.y, label.size.width, label.size.height, index ); // THIS IS TEMPORARY labelElement.style.backgroundColor = `hsla(${Math.floor( (label.userID / 10000000000) * 255 )}, 100%, 75%, 0.2)`; // Add the text input to the label let text = document.createElement("p"); text.classList.add("label--text"); text.innerHTML = label.text; labelElement.appendChild(text); labelElement.classList.add("loaded"); imageTarget.appendChild(labelElement); createLabelTranscription(label.text); }); }); }