|  | "use strict";
let overlayLoaded = false;
let baseLoaded = false;
let running = false;
let radius = 200;
let softness = 0;
let width;
let height;
let border = true;
let fitScreen = true;
let paintMode = false;
let firstTime = true;
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 artistLink = document.querySelector("#artist");
        
        let artistURL = document.querySelector("#artist-url").value;
        if (artistURL) {
            artistLink.href = artistURL;
            artistLink.style.removeProperty("display");
        } else {
            artistLink.style.display = "none";
        }
        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");
    
    const artistInput = document.querySelector("#artist-url");
    const artistLink = document.querySelector("#artist");
    if (url.searchParams.has("base") && url.searchParams.has("overlay")) {
        let baseURL = url.searchParams.get("base");
        let overlayURL = url.searchParams.get("overlay");
        let artistURL = null;
        
        if (url.searchParams.has("artist")) {
            artistURL = url.searchParams.get("artist");
        }
        baseImg.src = baseURL;
        overlayImg.src = overlayURL;
        baseInput.value = baseURL;
        overlayInput.value = overlayURL;
        if (artistURL) {
            artistLink.href = artistURL;
            artistInput.value = artistURL;
            artistLink.style.removeProperty("display");
        } else {
            artistLink.style.display = "none";
        }
        firstTime = false;
        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...");
        }
    }
    if (url.searchParams.has("border")) {
        try {
            border = 1 == parseInt(url.searchParams.get("border"));
            document.querySelector("#show-border").checked = border;
        } 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("#show-border").addEventListener("change", e => {
        border = e.target.checked;
        setURL();
    });
    
    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;
    baseLoaded = false;
    overlayLoaded = 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);
    // if we're starting fresh, set the radius value to be a fraction of the image size
    if (firstTime) {
        radius = Math.floor((baseImg.width + baseImg.height) / 10);
        document.querySelector("#radius-input").value = radius;
        document.querySelector("#radius-slider").value = radius;
        firstTime = false;
    }
    // also set up the input ranges
    document.querySelector("#radius-input").max = Math.max(baseImg.width, baseImg.height);
    document.querySelector("#radius-slider").max = Math.max(baseImg.width, baseImg.height);
    console.log("Done");
}
function ease(t, k) {
    return 1 - Math.pow(2, -k * (1 - t));
}
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));
            const maxOpacity = ease(0, 1 / (0.00001 + softness / 100));
            
            const steps = 20;
            for (let t=0 ; t <= steps; t+= 1) {
                let eased = ease(t/steps, 1 / (0.00001 + softness / 100)) / maxOpacity;
                gradient.addColorStop(t/steps, `rgba(0, 0, 0, ${eased}`);
            }
            let eased = ease(0.999, 1 / (0.00001 + softness / 100)) / maxOpacity;
            gradient.addColorStop(0.999, `rgba(0, 0, 0, ${eased}`);
            overlayCtx.fillStyle = gradient;
        
            overlayCtx.fill();
        })
    }
    
    
    overlayCtx.globalCompositeOperation = "source-in";
    overlayCtx.drawImage(overlayResized, 0, 0);
    
    overlayCtx.globalCompositeOperation = "source-over";
    if (!paintMode && border) {
        points.forEach(point => {
            const [x, y] = point;
            overlayCtx.strokeStyle = "#000";
            overlayCtx.lineWidth = 3;
            overlayCtx.beginPath();
            overlayCtx.ellipse(x, y, radius * scale, radius * scale, 0, 0, 2 * Math.PI);
            overlayCtx.stroke();
        });
    }
    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 artistLink = document.querySelector("#artist");
        const overlayImg = document.querySelector("#overlay-img");
        const baseImg = document.querySelector("#base-img");
 
        shareURL.searchParams.append("base", baseImg.src);
        shareURL.searchParams.append("overlay", overlayImg.src);
        if (artistLink.href) {
            shareURL.searchParams.append("artist", artistLink.href);
        }
        
        shareURL.searchParams.append("radius", radius);
        shareURL.searchParams.append("softness", softness);
        if (border) {
            shareURL.searchParams.append("border", 1);
        }
        window.history.replaceState(null, "X-Ray Viewer", shareURL);
}
 |