Spaces:
Runtime error
Runtime error
| const inpaintAnything_waitForElement = async (parent, selector, exist) => { | |
| return new Promise((resolve) => { | |
| const observer = new MutationObserver(() => { | |
| if (!!parent.querySelector(selector) != exist) { | |
| return; | |
| } | |
| observer.disconnect(); | |
| resolve(undefined); | |
| }); | |
| observer.observe(parent, { | |
| childList: true, | |
| subtree: true, | |
| }); | |
| if (!!parent.querySelector(selector) == exist) { | |
| resolve(undefined); | |
| } | |
| }); | |
| }; | |
| const inpaintAnything_waitForStyle = async (parent, selector, style) => { | |
| return new Promise((resolve) => { | |
| const observer = new MutationObserver(() => { | |
| if (!parent.querySelector(selector) || !parent.querySelector(selector).style[style]) { | |
| return; | |
| } | |
| observer.disconnect(); | |
| resolve(undefined); | |
| }); | |
| observer.observe(parent, { | |
| childList: true, | |
| subtree: true, | |
| attributes: true, | |
| attributeFilter: ["style"], | |
| }); | |
| if (!!parent.querySelector(selector) && !!parent.querySelector(selector).style[style]) { | |
| resolve(undefined); | |
| } | |
| }); | |
| }; | |
| const inpaintAnything_timeout = (ms) => { | |
| return new Promise(function (resolve, reject) { | |
| setTimeout(() => reject("Timeout"), ms); | |
| }); | |
| }; | |
| async function inpaintAnything_clearSamMask() { | |
| const waitForElementToBeInDocument = (parent, selector) => | |
| Promise.race([inpaintAnything_waitForElement(parent, selector, true), inpaintAnything_timeout(1000)]); | |
| const elemId = "#ia_sam_image"; | |
| const targetElement = document.querySelector(elemId); | |
| if (!targetElement) { | |
| return; | |
| } | |
| await waitForElementToBeInDocument(targetElement, "button[aria-label='Clear']"); | |
| targetElement.style.transform = null; | |
| targetElement.style.zIndex = null; | |
| targetElement.style.overflow = "auto"; | |
| const samMaskClear = targetElement.querySelector("button[aria-label='Clear']"); | |
| if (!samMaskClear) { | |
| return; | |
| } | |
| const removeImageButton = targetElement.querySelector("button[aria-label='Remove Image']"); | |
| if (!removeImageButton) { | |
| return; | |
| } | |
| samMaskClear?.click(); | |
| if (typeof inpaintAnything_clearSamMask.clickRemoveImage === "undefined") { | |
| inpaintAnything_clearSamMask.clickRemoveImage = () => { | |
| targetElement.style.transform = null; | |
| targetElement.style.zIndex = null; | |
| }; | |
| } else { | |
| removeImageButton.removeEventListener("click", inpaintAnything_clearSamMask.clickRemoveImage); | |
| } | |
| removeImageButton.addEventListener("click", inpaintAnything_clearSamMask.clickRemoveImage); | |
| } | |
| async function inpaintAnything_clearSelMask() { | |
| const waitForElementToBeInDocument = (parent, selector) => | |
| Promise.race([inpaintAnything_waitForElement(parent, selector, true), inpaintAnything_timeout(1000)]); | |
| const elemId = "#ia_sel_mask"; | |
| const targetElement = document.querySelector(elemId); | |
| if (!targetElement) { | |
| return; | |
| } | |
| await waitForElementToBeInDocument(targetElement, "button[aria-label='Clear']"); | |
| targetElement.style.transform = null; | |
| targetElement.style.zIndex = null; | |
| targetElement.style.overflow = "auto"; | |
| const selMaskClear = targetElement.querySelector("button[aria-label='Clear']"); | |
| if (!selMaskClear) { | |
| return; | |
| } | |
| const removeImageButton = targetElement.querySelector("button[aria-label='Remove Image']"); | |
| if (!removeImageButton) { | |
| return; | |
| } | |
| selMaskClear?.click(); | |
| if (typeof inpaintAnything_clearSelMask.clickRemoveImage === "undefined") { | |
| inpaintAnything_clearSelMask.clickRemoveImage = () => { | |
| targetElement.style.transform = null; | |
| targetElement.style.zIndex = null; | |
| }; | |
| } else { | |
| removeImageButton.removeEventListener("click", inpaintAnything_clearSelMask.clickRemoveImage); | |
| } | |
| removeImageButton.addEventListener("click", inpaintAnything_clearSelMask.clickRemoveImage); | |
| } | |
| async function inpaintAnything_initSamSelMask() { | |
| inpaintAnything_clearSamMask(); | |
| inpaintAnything_clearSelMask(); | |
| } | |
| var uiLoadedCallbacks = []; | |
| function gradioApp() { | |
| const elems = document.getElementsByTagName("gradio-app"); | |
| const elem = elems.length == 0 ? document : elems[0]; | |
| if (elem !== document) { | |
| elem.getElementById = function (id) { | |
| return document.getElementById(id); | |
| }; | |
| } | |
| return elem.shadowRoot ? elem.shadowRoot : elem; | |
| } | |
| function onUiLoaded(callback) { | |
| uiLoadedCallbacks.push(callback); | |
| } | |
| function executeCallbacks(queue) { | |
| for (const callback of queue) { | |
| try { | |
| callback(); | |
| } catch (e) { | |
| console.error("error running callback", callback, ":", e); | |
| } | |
| } | |
| } | |
| onUiLoaded(async () => { | |
| const elementIDs = { | |
| ia_sam_image: "#ia_sam_image", | |
| ia_sel_mask: "#ia_sel_mask", | |
| ia_out_image: "#ia_out_image", | |
| ia_cleaner_out_image: "#ia_cleaner_out_image", | |
| }; | |
| function setStyleHeight(elemId, height) { | |
| const elem = gradioApp().querySelector(elemId); | |
| if (elem) { | |
| if (!elem.style.height) { | |
| elem.style.height = height; | |
| const observer = new MutationObserver(() => { | |
| const divPreview = elem.querySelector(".preview"); | |
| if (divPreview) { | |
| divPreview.classList.remove("fixed-height"); | |
| } | |
| }); | |
| observer.observe(elem, { | |
| childList: true, | |
| attributes: true, | |
| attributeFilter: ["class"], | |
| }); | |
| } | |
| } | |
| } | |
| setStyleHeight(elementIDs.ia_out_image, "520px"); | |
| setStyleHeight(elementIDs.ia_cleaner_out_image, "520px"); | |
| // Default config | |
| const defaultHotkeysConfig = { | |
| canvas_hotkey_reset: "KeyR", | |
| canvas_hotkey_fullscreen: "KeyS", | |
| }; | |
| const elemData = {}; | |
| let activeElement; | |
| function applyZoomAndPan(elemId) { | |
| const targetElement = gradioApp().querySelector(elemId); | |
| if (!targetElement) { | |
| console.log("Element not found"); | |
| return; | |
| } | |
| targetElement.style.transformOrigin = "0 0"; | |
| elemData[elemId] = { | |
| zoomLevel: 1, | |
| panX: 0, | |
| panY: 0, | |
| }; | |
| let fullScreenMode = false; | |
| // Toggle the zIndex of the target element between two values, allowing it to overlap or be overlapped by other elements | |
| function toggleOverlap(forced = "") { | |
| // const zIndex1 = "0"; | |
| const zIndex1 = null; | |
| const zIndex2 = "998"; | |
| targetElement.style.zIndex = targetElement.style.zIndex !== zIndex2 ? zIndex2 : zIndex1; | |
| if (forced === "off") { | |
| targetElement.style.zIndex = zIndex1; | |
| } else if (forced === "on") { | |
| targetElement.style.zIndex = zIndex2; | |
| } | |
| } | |
| /** | |
| * This function fits the target element to the screen by calculating | |
| * the required scale and offsets. It also updates the global variables | |
| * zoomLevel, panX, and panY to reflect the new state. | |
| */ | |
| function fitToElement() { | |
| //Reset Zoom | |
| targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; | |
| // Get element and screen dimensions | |
| const elementWidth = targetElement.offsetWidth; | |
| const elementHeight = targetElement.offsetHeight; | |
| const parentElement = targetElement.parentElement; | |
| const screenWidth = parentElement.clientWidth; | |
| const screenHeight = parentElement.clientHeight; | |
| // Get element's coordinates relative to the parent element | |
| const elementRect = targetElement.getBoundingClientRect(); | |
| const parentRect = parentElement.getBoundingClientRect(); | |
| const elementX = elementRect.x - parentRect.x; | |
| // Calculate scale and offsets | |
| const scaleX = screenWidth / elementWidth; | |
| const scaleY = screenHeight / elementHeight; | |
| const scale = Math.min(scaleX, scaleY); | |
| const transformOrigin = window.getComputedStyle(targetElement).transformOrigin; | |
| const [originX, originY] = transformOrigin.split(" "); | |
| const originXValue = parseFloat(originX); | |
| const originYValue = parseFloat(originY); | |
| const offsetX = (screenWidth - elementWidth * scale) / 2 - originXValue * (1 - scale); | |
| const offsetY = (screenHeight - elementHeight * scale) / 2.5 - originYValue * (1 - scale); | |
| // Apply scale and offsets to the element | |
| targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; | |
| // Update global variables | |
| elemData[elemId].zoomLevel = scale; | |
| elemData[elemId].panX = offsetX; | |
| elemData[elemId].panY = offsetY; | |
| fullScreenMode = false; | |
| toggleOverlap("off"); | |
| } | |
| // Reset the zoom level and pan position of the target element to their initial values | |
| function resetZoom() { | |
| elemData[elemId] = { | |
| zoomLevel: 1, | |
| panX: 0, | |
| panY: 0, | |
| }; | |
| // fixCanvas(); | |
| targetElement.style.transform = `scale(${elemData[elemId].zoomLevel}) translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px)`; | |
| // const canvas = gradioApp().querySelector(`${elemId} canvas[key="interface"]`); | |
| toggleOverlap("off"); | |
| fullScreenMode = false; | |
| // if ( | |
| // canvas && | |
| // parseFloat(canvas.style.width) > 865 && | |
| // parseFloat(targetElement.style.width) > 865 | |
| // ) { | |
| // fitToElement(); | |
| // return; | |
| // } | |
| // targetElement.style.width = ""; | |
| // if (canvas) { | |
| // targetElement.style.height = canvas.style.height; | |
| // } | |
| targetElement.style.width = null; | |
| targetElement.style.height = 480; | |
| } | |
| /** | |
| * This function fits the target element to the screen by calculating | |
| * the required scale and offsets. It also updates the global variables | |
| * zoomLevel, panX, and panY to reflect the new state. | |
| */ | |
| // Fullscreen mode | |
| function fitToScreen() { | |
| const canvas = gradioApp().querySelector(`${elemId} canvas[key="interface"]`); | |
| const img = gradioApp().querySelector(`${elemId} img`); | |
| if (!canvas && !img) return; | |
| // if (canvas.offsetWidth > 862) { | |
| // targetElement.style.width = canvas.offsetWidth + "px"; | |
| // } | |
| if (fullScreenMode) { | |
| resetZoom(); | |
| fullScreenMode = false; | |
| return; | |
| } | |
| //Reset Zoom | |
| targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; | |
| // Get scrollbar width to right-align the image | |
| const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; | |
| // Get element and screen dimensions | |
| const elementWidth = targetElement.offsetWidth; | |
| const elementHeight = targetElement.offsetHeight; | |
| const screenWidth = window.innerWidth - scrollbarWidth; | |
| const screenHeight = window.innerHeight; | |
| // Get element's coordinates relative to the page | |
| const elementRect = targetElement.getBoundingClientRect(); | |
| const elementY = elementRect.y; | |
| const elementX = elementRect.x; | |
| // Calculate scale and offsets | |
| const scaleX = screenWidth / elementWidth; | |
| const scaleY = screenHeight / elementHeight; | |
| const scale = Math.min(scaleX, scaleY); | |
| // Get the current transformOrigin | |
| const computedStyle = window.getComputedStyle(targetElement); | |
| const transformOrigin = computedStyle.transformOrigin; | |
| const [originX, originY] = transformOrigin.split(" "); | |
| const originXValue = parseFloat(originX); | |
| const originYValue = parseFloat(originY); | |
| // Calculate offsets with respect to the transformOrigin | |
| const offsetX = (screenWidth - elementWidth * scale) / 2 - elementX - originXValue * (1 - scale); | |
| const offsetY = (screenHeight - elementHeight * scale) / 2 - elementY - originYValue * (1 - scale); | |
| // Apply scale and offsets to the element | |
| targetElement.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`; | |
| // Update global variables | |
| elemData[elemId].zoomLevel = scale; | |
| elemData[elemId].panX = offsetX; | |
| elemData[elemId].panY = offsetY; | |
| fullScreenMode = true; | |
| toggleOverlap("on"); | |
| } | |
| // Reset zoom when uploading a new image | |
| const fileInput = gradioApp().querySelector(`${elemId} input[type="file"][accept="image/*"].svelte-116rqfv`); | |
| if (fileInput) { | |
| fileInput.addEventListener("click", resetZoom); | |
| } | |
| // Handle keydown events | |
| function handleKeyDown(event) { | |
| // Disable key locks to make pasting from the buffer work correctly | |
| if ( | |
| (event.ctrlKey && event.code === "KeyV") || | |
| (event.ctrlKey && event.code === "KeyC") || | |
| event.code === "F5" | |
| ) { | |
| return; | |
| } | |
| // before activating shortcut, ensure user is not actively typing in an input field | |
| if (event.target.nodeName === "TEXTAREA" || event.target.nodeName === "INPUT") { | |
| return; | |
| } | |
| const hotkeyActions = { | |
| [defaultHotkeysConfig.canvas_hotkey_reset]: resetZoom, | |
| [defaultHotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen, | |
| }; | |
| const action = hotkeyActions[event.code]; | |
| if (action) { | |
| event.preventDefault(); | |
| action(event); | |
| } | |
| } | |
| // Handle events only inside the targetElement | |
| let isKeyDownHandlerAttached = false; | |
| function handleMouseMove() { | |
| if (!isKeyDownHandlerAttached) { | |
| document.addEventListener("keydown", handleKeyDown); | |
| isKeyDownHandlerAttached = true; | |
| activeElement = elemId; | |
| } | |
| } | |
| function handleMouseLeave() { | |
| if (isKeyDownHandlerAttached) { | |
| document.removeEventListener("keydown", handleKeyDown); | |
| isKeyDownHandlerAttached = false; | |
| activeElement = null; | |
| } | |
| } | |
| // Add mouse event handlers | |
| targetElement.addEventListener("mousemove", handleMouseMove); | |
| targetElement.addEventListener("mouseleave", handleMouseLeave); | |
| } | |
| applyZoomAndPan(elementIDs.ia_sam_image); | |
| applyZoomAndPan(elementIDs.ia_sel_mask); | |
| // applyZoomAndPan(elementIDs.ia_out_image); | |
| // applyZoomAndPan(elementIDs.ia_cleaner_out_image); | |
| }); | |
| var executedOnLoaded = false; | |
| document.addEventListener("DOMContentLoaded", function () { | |
| var mutationObserver = new MutationObserver(function () { | |
| if ( | |
| !executedOnLoaded && | |
| gradioApp().querySelector("#ia_sam_image") && | |
| gradioApp().querySelector("#ia_sel_mask") | |
| ) { | |
| executedOnLoaded = true; | |
| executeCallbacks(uiLoadedCallbacks); | |
| } | |
| }); | |
| mutationObserver.observe(gradioApp(), { childList: true, subtree: true }); | |
| }); | |