infer / static /app.js
s-ahal's picture
Update static/app.js
84b2c26 verified
// DOM Elements
const uploadArea = document.getElementById("uploadArea");
const imageInput = document.getElementById("imageInput");
const imagePreview = document.getElementById("imagePreview");
const submitButton = document.getElementById("submitButton");
const errorMessage = document.getElementById("errorMessage");
const resultSection = document.getElementById("result");
const resultStatus = document.getElementById("resultStatus");
const resultDescription = document.getElementById("resultDescription");
const probabilityValue = document.getElementById("probabilityValue");
const probabilityFill = document.getElementById("probabilityFill");
const likelyInfo = document.getElementById("likelyInfo");
const resultImage = document.getElementById("resultImage");
const actionButtons = document.getElementById("actionButtons");
const navButtons = document.querySelectorAll(".nav-button");
const pages = document.querySelectorAll(".page");
const resourceCategories = document.querySelectorAll(".resource-category");
const optInCheckbox = document.getElementById("optInCheckbox");
const contactInfoInput = document.getElementById("contactInfo");
const contactOptin = document.querySelector(".contact-optin");
// State
let selectedImage = null;
// Custom dropdown logic
const dropdownToggle = document.getElementById("dropdownToggle");
const dropdownMenu = document.getElementById("dropdownMenu");
const dropdownOptions = dropdownMenu
? dropdownMenu.querySelectorAll(".dropdown-option")
: [];
const selectedModelSpan = document.getElementById("selectedModel");
let selectedModelValue = "CompVis/stable-diffusion-v1-4";
const customDropdown = document.getElementById("modelDropdown");
// Loading animation
const loadingContainer = document.createElement("div");
loadingContainer.id = "loadingAnimation";
loadingContainer.style.display = "none";
loadingContainer.style.justifyContent = "center";
loadingContainer.style.alignItems = "center";
loadingContainer.style.flexDirection = "column";
loadingContainer.style.textAlign = "center";
loadingContainer.style.position = "absolute";
loadingContainer.style.top = "0";
loadingContainer.style.left = "0";
loadingContainer.style.width = "100%";
loadingContainer.style.height = "100%";
loadingContainer.style.zIndex = "2";
const loadingImg = document.createElement("img");
loadingImg.alt = "Loading...";
loadingImg.style.width = "64px";
loadingImg.style.height = "64px";
const loadingMsg = document.createElement("div");
loadingMsg.className = "loading-message";
loadingMsg.textContent = "Processing";
loadingContainer.appendChild(loadingImg);
loadingContainer.appendChild(loadingMsg);
resultSection.appendChild(loadingContainer);
let loadingFrame = 1;
let loadingInterval = null;
const resultContent = resultSection.querySelector(".result-content");
// About overlay logic
const aboutOverlay = document.getElementById("aboutOverlay");
const aboutCloseBtn = document.getElementById("aboutCloseBtn");
function showLoading() {
resultSection.hidden = false;
resultSection.style.display = "";
if (resultContent) resultContent.style.display = "none";
loadingContainer.style.display = "flex";
loadingFrame = 1;
updateLoadingFrame();
loadingInterval = setInterval(() => {
loadingFrame = (loadingFrame % 16) + 1;
updateLoadingFrame();
}, 60);
}
function hideLoading() {
loadingContainer.style.display = "none";
if (resultContent) resultContent.style.display = "";
if (loadingInterval) clearInterval(loadingInterval);
}
function updateLoadingFrame() {
loadingImg.src = `static/assets/Infer_LoadingAnimation/Property 1=Variant${loadingFrame}.svg`;
}
function hideResultSection() {
resultSection.hidden = true;
resultSection.style.display = "none";
if (likelyInfo) likelyInfo.hidden = true;
if (resultImage) resultImage.hidden = true;
if (resultContent) resultContent.style.display = "";
loadingContainer.style.display = "none";
}
function showResultSection() {
resultSection.hidden = false;
resultSection.style.display = "";
}
// Event Listeners
uploadArea.addEventListener("click", () => imageInput.click());
imageInput.addEventListener("change", handleImageUpload);
submitButton.addEventListener("click", handleSubmit);
navButtons.forEach((button) => {
button.addEventListener("click", () => {
const targetPage = button.dataset.page;
navigateToPage(targetPage);
});
});
// Hide result section initially
hideResultSection();
// Add drag and drop support
uploadArea.addEventListener("dragover", (e) => {
e.preventDefault();
e.stopPropagation();
uploadArea.classList.add("dragover");
});
uploadArea.addEventListener("dragleave", (e) => {
e.preventDefault();
e.stopPropagation();
uploadArea.classList.remove("dragover");
});
uploadArea.addEventListener("drop", (e) => {
e.preventDefault();
e.stopPropagation();
uploadArea.classList.remove("dragover");
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith("image/")) {
handleImageUpload({ target: { files: [file] } });
}
});
// Resource category selection
resourceCategories.forEach((category) => {
category.addEventListener("click", () => {
resourceCategories.forEach((c) => c.classList.remove("active"));
category.classList.add("active");
});
});
// Functions
function handleImageUpload(event) {
const file = event.target.files[0];
if (!file) return;
if (!file.type.startsWith("image/")) {
showError("Please select an image file");
return;
}
selectedImage = file;
errorMessage.textContent = "";
submitButton.disabled = false;
// Hide result section when a new image is selected
hideResultSection();
// Hide contact opt-in on new upload
if (contactOptin) contactOptin.style.display = "none";
const reader = new FileReader();
reader.onload = (e) => {
imagePreview.src = e.target.result;
imagePreview.hidden = false;
document.querySelector(".upload-placeholder").style.display = "none";
};
reader.readAsDataURL(file);
}
async function handleSubmit() {
if (!selectedImage) {
showError("Please select an image first");
return;
}
submitButton.disabled = true;
errorMessage.textContent = "";
hideResultSection();
showLoading();
if (contactOptin) contactOptin.style.display = "none";
try {
const formData = new FormData();
formData.append("image", selectedImage);
formData.append("model", selectedModelValue);
// Only send opt-in if visible and checked
if (
contactOptin &&
contactOptin.style.display !== "none" &&
optInCheckbox &&
optInCheckbox.checked &&
contactInfoInput &&
contactInfoInput.value
) {
formData.append("opt_in", "true");
formData.append("contact_info", contactInfoInput.value);
} else {
formData.append("opt_in", "false");
}
const response = await fetch(
"https://s-ahal-infer.hf.space/api/check-membership",
{
method: "POST",
body: formData,
}
);
if (!response.ok) {
throw new Error(`Server responded with ${response.status}`);
}
const data = await response.json();
displayResult(data);
} catch (error) {
showError(`Error: ${error.message}`);
console.error("Error:", error);
hideLoading();
} finally {
submitButton.disabled = false;
}
}
function displayResult(data) {
const probability = data.probability * 100;
const isLikely = data.probability > 0.5;
resultStatus.textContent = isLikely ? "Likely" : "Unlikely";
const modelName = selectedModelSpan.textContent;
const desc = `This image <strong class='result-description-strong'>${
isLikely ? "probably is" : "is probably not"
}</strong> in the training data for the ${modelName} model.`;
resultDescription.innerHTML = desc;
probabilityValue.innerHTML = `<span class='result-percentage'>${probability.toFixed(
1
)}%</span>`;
probabilityFill.style.width = "0%";
setTimeout(() => {
probabilityFill.style.width = `${probability}%`;
}, 30);
if (imagePreview && resultImage) {
resultImage.src = imagePreview.src;
resultImage.hidden = false;
}
if (isLikely) {
likelyInfo.hidden = false;
let usersText = "";
if (typeof data.likely_count === "number" && data.likely_count > 1) {
usersText = `There are <strong>${
data.likely_count - 1
} other user-tested artworks</strong> that were found to likely be included in this model's training data.`;
}
likelyInfo.querySelector(".likely-users").innerHTML = usersText;
// Show contact opt-in below results
if (contactOptin) contactOptin.style.display = "";
} else {
likelyInfo.hidden = true;
if (contactOptin) contactOptin.style.display = "none";
}
hideLoading();
showResultSection();
}
function showError(message) {
errorMessage.textContent = message;
}
function navigateToPage(pageId) {
// Update active page
pages.forEach((page) => {
page.classList.remove("active");
if (page.id === pageId) {
page.classList.add("active");
}
});
// Update active nav button
navButtons.forEach((button) => {
button.classList.toggle("active", button.dataset.page === pageId);
});
// Scroll to top when changing pages
window.scrollTo(0, 0);
}
// Initialize
navigateToPage("model-select");
function closeDropdown() {
dropdownMenu.hidden = true;
dropdownToggle.setAttribute("aria-expanded", "false");
if (customDropdown) customDropdown.classList.remove("open");
}
function openDropdown() {
dropdownMenu.hidden = false;
dropdownToggle.setAttribute("aria-expanded", "true");
if (customDropdown) customDropdown.classList.add("open");
// Focus the selected option
const selected = dropdownMenu.querySelector(".dropdown-option.selected");
if (selected) selected.focus();
}
dropdownToggle.addEventListener("click", (e) => {
e.stopPropagation();
if (dropdownMenu.hidden) {
openDropdown();
} else {
closeDropdown();
}
});
dropdownOptions.forEach((option) => {
option.setAttribute("tabindex", "0");
option.addEventListener("click", (e) => {
dropdownOptions.forEach((opt) => opt.classList.remove("selected"));
option.classList.add("selected");
selectedModelSpan.textContent = option.textContent;
selectedModelValue = option.getAttribute("data-value");
closeDropdown();
});
option.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
option.click();
} else if (e.key === "ArrowDown") {
e.preventDefault();
let next = option.nextElementSibling;
if (!next) next = dropdownMenu.firstElementChild;
next.focus();
} else if (e.key === "ArrowUp") {
e.preventDefault();
let prev = option.previousElementSibling;
if (!prev) prev = dropdownMenu.lastElementChild;
prev.focus();
} else if (e.key === "Escape") {
closeDropdown();
dropdownToggle.focus();
}
});
});
document.addEventListener("click", (e) => {
if (!dropdownMenu.hidden) {
closeDropdown();
}
});
dropdownToggle.addEventListener("keydown", (e) => {
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
openDropdown();
e.preventDefault();
}
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
closeDropdown();
}
});
// Ensure About overlay logic runs after DOM is loaded
window.addEventListener("DOMContentLoaded", function () {
const aboutOverlay = document.getElementById("aboutOverlay");
const aboutCloseBtn = document.getElementById("aboutCloseBtn");
const aboutLink = Array.from(document.querySelectorAll(".nav-link")).find(
(link) => link.textContent.trim().toLowerCase() === "about"
);
if (aboutLink) {
aboutLink.addEventListener("click", function (e) {
e.preventDefault();
if (aboutOverlay) aboutOverlay.style.display = "flex";
});
}
if (aboutCloseBtn) {
aboutCloseBtn.addEventListener("click", function () {
if (aboutOverlay) aboutOverlay.style.display = "none";
});
}
if (aboutOverlay) {
aboutOverlay.addEventListener("click", function (e) {
if (e.target === aboutOverlay) {
aboutOverlay.style.display = "none";
}
});
}
});
// Resource card logic for resources page
(function () {
const resourceData = {
"do-not-train": {
title: "Do Not Train",
html: `<strong>What It Is:</strong> A simple metadata tag artists can add to their images to declare they don't consent to their work being used for training AI.<br><strong>How To Use:</strong> Add a do-not-train tag to your website's HTML or use platforms that support the tag.<br><strong>Note:</strong> It's not enforceable yet, but signals intent for future protections.<br><a href='https://spawning.ai/rightsholders-faq' class='action-button' target='_blank'><span>Learn more</span><span class='arrow'>⟶</span></a>`,
},
nightshade: {
title: "Nightshade",
html: `<strong>What It Is:</strong> A tool that poisons your artwork's pixels—imperceptibly to humans, but disruptive to training algorithms.<br><strong>Why It Works:</strong> It corrupts how AI models interpret your work, deterring them from using it.<br><a href='https://nightshade.cs.uchicago.edu/' class='action-button' target='_blank'><span>Try it out</span><span class='arrow'>⟶</span></a>`,
},
glaze: {
title: "Glaze",
html: `<strong>What It Is:</strong> A style cloak that protects your artistic fingerprint.<br><strong>What It Does:</strong> Applies a subtle, AI-visible filter that makes your work harder to mimic.<br><strong>Best For:</strong> Artists with a strong visual signature.<br><a href='https://glaze.cs.uchicago.edu/' class='action-button' target='_blank'><span>Try it out</span><span class='arrow'>⟶</span></a>`,
},
copyright: {
title: "Copyright Resources",
html: `<strong>Understand Your Rights:</strong><br>In many countries, your work is protected the moment it's created—but enforcing that is another story.<br><br><u>Includes:</u><ul><li>How to formally register your copyright</li><li>Templates for takedown notices</li><li>Intro to fair use & derivative work laws</li></ul>`,
},
licensing: {
title: "Licensing Tools",
html: `<strong>Put Terms in Writing:</strong><br>Use free or paid licenses to explicitly control how your work can be used.<br><br><u>Options to Explore:</u><ul><li>Creative Commons with "no AI use" clauses</li><li>Personal licensing agreements</li><li>Open-source licenses adapted for artists</li></ul>`,
},
};
const categories = document.querySelectorAll(".resource-category");
const info = document.getElementById("resource-info");
if (categories.length && info) {
function showResource(key) {
categories.forEach((btn) =>
btn.classList.toggle("active", btn.dataset.resource === key)
);
info.innerHTML = `<h3>${resourceData[key].title}</h3><p>${resourceData[key].html}</p>`;
}
categories.forEach((btn) => {
btn.addEventListener("click", () => showResource(btn.dataset.resource));
});
// Show default
showResource("do-not-train");
}
})();