|
|
|
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"); |
|
|
|
|
|
let selectedImage = null; |
|
|
|
|
|
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"); |
|
|
|
|
|
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"); |
|
|
|
|
|
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 = ""; |
|
} |
|
|
|
|
|
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); |
|
}); |
|
}); |
|
|
|
|
|
hideResultSection(); |
|
|
|
|
|
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] } }); |
|
} |
|
}); |
|
|
|
|
|
resourceCategories.forEach((category) => { |
|
category.addEventListener("click", () => { |
|
resourceCategories.forEach((c) => c.classList.remove("active")); |
|
category.classList.add("active"); |
|
}); |
|
}); |
|
|
|
|
|
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; |
|
|
|
|
|
hideResultSection(); |
|
|
|
|
|
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); |
|
|
|
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; |
|
|
|
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) { |
|
|
|
pages.forEach((page) => { |
|
page.classList.remove("active"); |
|
if (page.id === pageId) { |
|
page.classList.add("active"); |
|
} |
|
}); |
|
|
|
|
|
navButtons.forEach((button) => { |
|
button.classList.toggle("active", button.dataset.page === pageId); |
|
}); |
|
|
|
|
|
window.scrollTo(0, 0); |
|
} |
|
|
|
|
|
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"); |
|
|
|
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(); |
|
} |
|
}); |
|
|
|
|
|
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"; |
|
} |
|
}); |
|
} |
|
}); |
|
|
|
|
|
(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)); |
|
}); |
|
|
|
showResource("do-not-train"); |
|
} |
|
})(); |
|
|