"use strict"; let overlayLoaded = false; let baseLoaded = false; let running = false; let radius = 200; let softness = 25; let width; let height; let fitScreen = true; let paintMode = false; let scale; document.addEventListener("DOMContentLoaded", e => { document.querySelector("#reset-button").addEventListener("click", reset); document.querySelector("#load-button").addEventListener("click", e => { console.log("Trying to load..."); const baseInput = document.querySelector("#base-url").value; const overlayInput = document.querySelector("#overlay-url").value; let success = true; try { let baseURL = new URL(baseInput) console.log(baseURL); } catch { document.querySelector("#base-url").value = ""; document.querySelector("#base-url").placeholder = "Invalid URL..."; success = false; } try { let overlayURL = new URL(overlayInput) console.log(overlayURL); } catch { document.querySelector("#overlay-url").value = ""; document.querySelector("#overlay-url").placeholder = "Invalid URL..."; success = false; } if (!success) { return; } const overlayImg = document.querySelector("#overlay-img"); const baseImg = document.querySelector("#base-img"); overlayImg.src = overlayInput; baseImg.src = baseInput; setURL(); load(); try { localStorage.setItem("base", baseInput); localStorage.setItem("overlay", overlayInput); } catch { console.error("Couldn't set something in local storage :(") } }); let url = new URL(window.location); const overlay = document.querySelector("#overlay"); document.addEventListener("mousedown", e => { let x = e.clientX - e.target.getBoundingClientRect().x; let y = e.clientY - e.target.getBoundingClientRect().y; updateOverlay([[x,y]], e.buttons % 2 != 0); }); document.addEventListener("mousemove", e => { let x = e.clientX - e.target.getBoundingClientRect().x; let y = e.clientY - e.target.getBoundingClientRect().y; updateOverlay([[x,y]], e.buttons % 2 != 0); }); document.addEventListener("touchstart", e => { let offsetX = e.target.getBoundingClientRect().x; let offsetY = e.target.getBoundingClientRect().y; let touches = []; for (let i=0; i < e.touches.length; i++) { let x = e.touches[i].clientX - offsetX; let y = e.touches[i].clientY - offsetY; touches.push([x,y]); } updateOverlay(touches, true); }); document.addEventListener("touchmove", e => { let offsetX = e.target.getBoundingClientRect().x; let offsetY = e.target.getBoundingClientRect().y; let touches = []; for (let i=0; i < e.touches.length; i++) { let x = e.touches[i].clientX - offsetX; let y = e.touches[i].clientY - offsetY; touches.push([x,y]); } updateOverlay(touches, true); }); document.querySelector("#radius-slider").addEventListener("input", e => { try { radius = parseInt(e.target.value); document.querySelector("#radius-input").value = radius; } catch { console.warn("That wasn't a valid radius: " + e.target.value); } }); document.querySelector("#radius-slider").addEventListener("change", e => { try { radius = parseInt(e.target.value); document.querySelector("#radius-input").value = radius; } catch { console.warn("That wasn't a valid radius: " + e.target.value); } setURL(); }); document.querySelector("#radius-input").addEventListener("input", e => { try { radius = parseInt(e.target.value); document.querySelector("#radius-slider").value = radius; } catch { console.warn("That wasn't a valid radius: " + e.target.value); } }); document.querySelector("#radius-input").addEventListener("change", e => { try { radius = parseInt(e.target.value); document.querySelector("#radius-slider").value = radius; } catch { console.warn("That wasn't a valid radius: " + e.target.value); } setURL(); }); document.querySelector("#softness-slider").addEventListener("input", e => { try { softness = parseInt(e.target.value); document.querySelector("#softness-input").value = softness; } catch { console.warn("That wasn't a valid softness: " + e.target.value); } }); document.querySelector("#softness-slider").addEventListener("change", e => { try { softness = parseInt(e.target.value); document.querySelector("#softness-input").value = softness; } catch { console.warn("That wasn't a valid softness: " + e.target.value); } setURL(); }); document.querySelector("#softness-input").addEventListener("input", e => { try { softness = parseInt(e.target.value); document.querySelector("#softness-slider").value = softness; } catch { console.warn("That wasn't a valid softness: " + e.target.value); } }); document.querySelector("#softness-input").addEventListener("change", e => { try { softness = parseInt(e.target.value); document.querySelector("#softness-slider").value = softness; } catch { console.warn("That wasn't a valid softness: " + e.target.value); } setURL(); }); // see if we have params already; if so, use them! const overlayImg = document.querySelector("#overlay-img"); const baseImg = document.querySelector("#base-img"); const baseInput = document.querySelector("#base-url"); const overlayInput = document.querySelector("#overlay-url"); if (url.searchParams.has("base") && url.searchParams.has("overlay")) { let baseURL = url.searchParams.get("base"); let overlayURL = url.searchParams.get("overlay"); baseImg.src = baseURL; overlayImg.src = overlayURL; baseInput.value = baseURL; overlayInput.value = overlayURL; load(); } else { try { baseInput.value = localStorage.getItem("base"); overlayInput.value = localStorage.getItem("overlay"); } catch { console.error("Couldn't get something from local storage :(") } } if (url.searchParams.has("radius")) { try { radius = parseInt(url.searchParams.get("radius")); document.querySelector("#radius-slider").value = radius; document.querySelector("#radius-input").value = radius; } catch { console.warn("That was a bogus radius..."); } } if (url.searchParams.has("softness")) { try { softness = parseInt(url.searchParams.get("softness")); document.querySelector("#softness-slider").value = softness; document.querySelector("#softness-input").value = softness; } catch { console.warn("That was a bogus softness..."); } } window.addEventListener("resize", e => { if (running) { setup(); } }) document.querySelector("#fullscreen-button").addEventListener("click", function toggleFullScreen() { var doc = window.document; var docEl = doc.documentElement; var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { requestFullScreen.call(docEl); } else { cancelFullScreen.call(doc); } }); document.querySelector("#paint-mode").addEventListener("change", e => { paintMode = e.target.checked; }); document.querySelector("#fit-screen").addEventListener("change", e => { fitScreen = e.target.checked; setup(); }); }); function load() { document.querySelector("#menu").classList.remove("start"); const overlayImg = document.querySelector("#overlay-img"); const baseImg = document.querySelector("#base-img"); overlayImg.addEventListener("load", function overlayLoad() { console.log("The overlay is loaded"); overlayLoaded = true; if (overlayLoaded && baseLoaded) { setup(); } overlayImg.removeEventListener("load", overlayLoad); }) baseImg.addEventListener("load", function baseLoad() { console.log("The base is loaded"); baseLoaded = true; if (overlayLoaded && baseLoaded) { setup(); } baseImg.removeEventListener("load", baseLoad); }) } function reset() { running = false; const overlay = document.querySelector("#overlay"); const base = document.querySelector("#base"); const overlayResized = document.querySelector("#overlay-resized"); const baseResized = document.querySelector("#base-resized"); document.querySelector("#menu").classList.add("start"); overlay.classList.add("hidden"); base.classList.add("hidden"); } function setup() { running = true; const overlay = document.querySelector("#overlay"); const base = document.querySelector("#base"); const overlayResized = document.querySelector("#overlay-resized"); const baseResized = document.querySelector("#base-resized"); overlay.classList.remove("hidden"); base.classList.remove("hidden"); const overlayImg = document.querySelector("#overlay-img"); const baseImg = document.querySelector("#base-img"); /** @type {CanvasRenderingContext2D} */ const overlayCtx = overlay.getContext("2d"); /** @type {CanvasRenderingContext2D} */ const baseCtx = base.getContext("2d"); /** @type {CanvasRenderingContext2D} */ const overlayCtxResized = overlayResized.getContext("2d"); /** @type {CanvasRenderingContext2D} */ const baseCtxResized = baseResized.getContext("2d"); const availableWidth = document.querySelector("#fill-div").getBoundingClientRect().width; const availableHeight = document.querySelector("#fill-div").getBoundingClientRect().height; const scaleW = availableWidth / baseImg.width; const scaleH = availableHeight / baseImg.height; scale = fitScreen ? Math.min(scaleW, scaleH) : 1; width = fitScreen ? Math.floor(availableWidth * scale / scaleW) : baseImg.width; height = fitScreen ? Math.floor(availableHeight * scale / scaleH) : baseImg.height; [baseCtx, baseCtxResized, overlayCtx, overlayCtxResized].forEach(ctx => { ctx.canvas.width = width; ctx.canvas.height = height; ctx.canvas.style.left = fitScreen ? (availableWidth - width) / 2 + "px" : 0; ctx.canvas.style.top = fitScreen ? (availableHeight - height) / 2 + "px" : 0; }); baseCtxResized.drawImage(baseImg, 0, 0, width, height); baseCtx.drawImage(baseResized, 0, 0, width, height); overlayCtxResized.drawImage(overlayImg, 0, 0, width, height); console.log("Done"); } function updateOverlay(points, clicked) { const overlay = document.querySelector("#overlay"); const overlayResized = document.querySelector("#overlay-resized"); /** @type {CanvasRenderingContext2D} */ const overlayCtx = overlay.getContext("2d"); /** @type {CanvasRenderingContext2D} */ const overlayCtxResized = overlay.getContext("2d"); const w = overlayCtx.canvas.width; const h = overlayCtx.canvas.height; overlayCtx.save(); overlayCtx.globalCompositeOperation = "source-over"; if (!paintMode) overlayCtx.clearRect(0, 0, w, h); if (!paintMode || clicked) { points.forEach(point => { const [x,y] = point; overlayCtx.beginPath(); overlayCtx.ellipse(x, y, radius * scale, radius * scale, 0, 0, 2 * Math.PI); const gradient = overlayCtx.createRadialGradient(x, y, 0, x, y, Math.floor(radius * scale)); gradient.addColorStop((100-softness)/100, '#000000FF'); gradient.addColorStop(1, '#00000000'); overlayCtx.fillStyle = gradient; overlayCtx.fill(); }) } overlayCtx.globalCompositeOperation = "source-in"; overlayCtx.drawImage(overlayResized, 0, 0); overlayCtx.restore(); } function setURL() { let shareURL = new URL(window.location); // for some reason, the parser gets confused by urlencoded urls... // so, to get rid of all parameters, we do this let keys = Array.from(shareURL.searchParams.keys()); do { keys = Array.from(shareURL.searchParams.keys()); keys.forEach(key => { shareURL.searchParams.delete(key); }); } while (keys.length > 0) const overlayImg = document.querySelector("#overlay-img"); const baseImg = document.querySelector("#base-img"); shareURL.searchParams.append("base", baseImg.src); shareURL.searchParams.append("overlay", overlayImg.src); shareURL.searchParams.append("radius", radius); shareURL.searchParams.append("softness", softness); window.history.replaceState(null, "X-Ray Viewer", shareURL); }