function isTouchDevice() { return ( "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 ); } class WindowObject { windowObject; windowManager; windowManagerLabel; windowContent; #orig_mousePosX; #orig_mousePosY; #orig_selfPosX; #orig_selfPosY; #isDragging; constructor(parent, content, srcdoc, w, h) { this.parentElement = parent; this.windowObject = WindowObject.createWindow(this, content, srcdoc, w, h); this.parentElement.appendChild(this.windowObject); WindowObject.raiseWindow(this.windowObject); if (isTouchDevice()) this.maximizeWindow(); this.windowManager = this.windowObject.querySelector(".window-manager"); this.windowManagerLabel = this.windowObject.querySelector( ".window-manager-label" ); this.windowContent = this.windowObject.querySelector(".window-content"); this.windowManager.addEventListener("mousedown", (e) => this.event__dragMouseDown(e) ); this.windowManager.addEventListener("mousemove", (e) => this.event__dragMouseMove(e) ); this.windowManager.addEventListener("mouseup", (e) => this.event__dragMouseUp(e) ); this.windowManager.addEventListener("mouseout", (e) => this.event__dragMouseMove(e) ); this.windowManager.addEventListener("touchstart", (e) => this.event__dragMouseDown(e, true) ); this.windowManager.addEventListener("touchmove", (e) => this.event__dragMouseMove(e, true) ); this.windowManager.addEventListener("touchend", (e) => this.event__dragMouseUp(e, true) ); this.windowObject.addEventListener("mousedown", (e) => WindowObject.raiseWindow(this.windowObject) ); this.windowContent.contentWindow.addEventListener("mousedown", (e) => WindowObject.raiseWindow(this.windowObject) ); window.addEventListener("beforeunload", () => this.destroy()); this.windowContent.addEventListener("load", () => { this.title = this.windowContent.contentWindow.document.title; if (!srcdoc) { const nb = this.windowManager.querySelector(".window-new-button"); nb.addEventListener("click", () => { window.open(this.content); }); nb.addEventListener("touchstart", (e) => e.stopPropagation()); nb.addEventListener("touchend", (e) => e.stopPropagation()); } }); } get title() { return this.windowManagerLabel.textContent; } get content() { return this.windowContent.src; } set title(content) { this.windowManagerLabel.textContent = content; } set content(src) { this.windowContent.src = src; } minimizeWindow() { if (this.windowObject.classList.contains("minimized")) { this.windowObject.classList.remove("minimized"); WindowObject.raiseWindow(this.windowObject); } else { this.windowObject.classList.add("minimized"); this.windowObject.style.zIndex = 50; } } maximizeWindow() { this.windowObject.classList.toggle("maximized"); WindowObject.raiseWindow(this.windowObject); } destroy() { if (this.windowContent.contentWindow != null) { const unloadEvent = new Event("beforeunload"); this.windowContent.contentWindow.dispatchEvent(unloadEvent); } this.windowObject.classList.add("closed"); setTimeout(() => { this.windowObject.remove(); var someWindow = this.parentElement.querySelector( `.window-object:last-child` ); if (someWindow != null) WindowObject.raiseWindow(someWindow); }, 250); } event__dragMouseDown(e, touch) { e.preventDefault(); this.#orig_mousePosX = touch ? e.touches[0].clientX : e.clientX; this.#orig_mousePosY = touch ? e.touches[0].clientY : e.clientY; this.#orig_selfPosX = this.windowObject.offsetLeft; this.#orig_selfPosY = this.windowObject.offsetTop; this.windowContent.style.userSelect = "none"; this.windowContent.style.pointerEvents = "none"; this.windowManager.style.cursor = "move"; this.#isDragging = true; if (this.windowObject.style.zIndex != 100) WindowObject.raiseWindow(this.windowObject); } event__dragMouseUp(e, touch) { e.preventDefault(); this.#orig_mousePosX = 0; this.#orig_mousePosY = 0; this.windowContent.style.userSelect = "auto"; this.windowContent.style.pointerEvents = "auto"; this.windowManager.style.cursor = "default"; this.#isDragging = false; } event__dragMouseMove(e, touch) { e.preventDefault(); if (this.#isDragging) { var cX = touch ? e.touches[0].clientX : e.clientX; var cY = touch ? e.touches[0].clientY : e.clientY; this.windowObject.style.left = this.#orig_selfPosX - (this.#orig_mousePosX - cX) + "px"; this.windowObject.style.top = this.#orig_selfPosY - (this.#orig_mousePosY - cY) + "px"; } } static raiseWindow(windowObj) { windowObj.parentElement.querySelectorAll(".window-object").forEach((x) => { if (x.style.zIndex > 50) x.style.zIndex -= 1; x.classList.add("unfocused"); }); windowObj.style.zIndex = 100; windowObj.classList.remove("unfocused"); windowObj.focus(); } static createWindow(windowRef, content, srcdoc, w, h) { const windowObject = document.createElement("div"); windowObject.classList.add("window-object"); windowObject.style.width = `calc(${w}px + 1.5em)`; windowObject.style.height = `calc(${h}px + 3.5em)`; { const windowManager = document.createElement("div"); windowManager.classList.add("window-manager"); { const windowManagerStart = document.createElement("div"); windowManagerStart.classList.add("window-manager-start"); { const windowManagerLabel = document.createElement("span"); windowManagerLabel.textContent = ""; windowManagerLabel.classList.add("window-manager-label"); windowManagerStart.appendChild(windowManagerLabel); } windowManager.appendChild(windowManagerStart); } { const windowManagerEnd = document.createElement("div"); windowManagerEnd.classList.add("window-manager-end"); if (!srcdoc) { const windowNewButton = document.createElement("button"); windowNewButton.innerHTML = "open_in_new"; windowNewButton.classList.add("window-new-button"); windowManagerEnd.appendChild(windowNewButton); } { const windowMaximizeButton = document.createElement("button"); windowMaximizeButton.innerHTML = "maximize"; windowMaximizeButton.classList.add("window-maximize-button"); windowMaximizeButton.ariaHidden = true; // esoteric operation to screen-reader users windowMaximizeButton.addEventListener("click", () => windowRef.maximizeWindow() ); windowMaximizeButton.addEventListener("touchstart", (e) => e.stopPropagation() ); windowMaximizeButton.addEventListener("touchend", (e) => e.stopPropagation() ); windowManagerEnd.appendChild(windowMaximizeButton); } { const windowDestroyButton = document.createElement("button"); windowDestroyButton.innerHTML = "close"; windowDestroyButton.classList.add("window-destroy-button"); windowDestroyButton.ariaHidden = true; windowDestroyButton.addEventListener("click", () => windowRef.destroy() ); windowDestroyButton.addEventListener("touchstart", (e) => e.stopPropagation() ); windowDestroyButton.addEventListener("touchend", (e) => e.stopPropagation() ); windowManagerEnd.appendChild(windowDestroyButton); } windowManager.appendChild(windowManagerEnd); } windowObject.appendChild(windowManager); } { const windowContent = document.createElement("iframe"); windowContent.classList.add("window-content"); { if (srcdoc) windowContent.srcdoc = content; else windowContent.src = content; } windowObject.appendChild(windowContent); } return windowObject; } }