website/ts/index.ts
2025-01-05 16:10:40 -05:00

272 lines
9.2 KiB
TypeScript

let WINDOWS: { [key: string]: _winConf } = {};
let MOUSE_MOVE_PROCESSING: {
[key: string]: {
callback: (x: number, y: number) => void,
mouseUp: boolean
}
} = {};
let globalIncrement: number = 1;
function escapeHTML(string: string): string {
return string.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;");
}
function incrementZIndex(windowID: string, focus: boolean=false): void {
WINDOWS[windowID].element.style.zIndex = String(globalIncrement);
WINDOWS[windowID].zIndex = globalIncrement;
globalIncrement++;
if (focus) {
WINDOWS[windowID].element.focus();
}
}
function createWindow(config: _winInitConf): void {
if (document.getElementById(config.id)) {
incrementZIndex(config.id);
return;
}
// 1 - border
// 10 - padding
// 35 - header
let _windowPaddingX: number = 1*2 + 10*2;
let _windowPaddingY: number = 1*2 + 10*2 + 35;
config.width = config.width || 600;
config.height = config.height || 400;
config.minWidth = config.minWidth || 200;
config.minHeight = config.minHeight || 200;
let realWidth: number = Math.max(config.minWidth, Math.min(config.width, innerWidth - _windowPaddingX - 20));
let realHeight: number = Math.max(config.minHeight, Math.min(config.height, innerHeight - _windowPaddingY - 20));
let posX: number = config.posX || Math.round((innerWidth / 2) - ((realWidth + _windowPaddingX) / 2));
let posY: number = config.posY || Math.round((innerHeight / 2) - ((realHeight + _windowPaddingY) / 2));
let wO: HTMLDivElement = document.createElement("div");
wO.classList.add("window-outer");
let w: HTMLDivElement = document.createElement("div");
w.classList.add("window");
w.style.width = `${realWidth}px`;
w.style.height = `${realHeight}px`;
w.innerHTML = config.content;
let wH: HTMLDivElement = document.createElement("div");
wH.classList.add("window-header");
wH.innerHTML = `
<i class="window-header-button blank"></i>
<i class="window-header-button blank"></i>
<i class="window-header-button blank"></i>
<strong class="window-header-title">${config.title}</strong>
<i data-no-move class="window-header-button minimize"></i>
<i data-no-move class="window-header-button fullscreen"></i>
<i data-no-move class="window-header-button close"></i>
`;
let wC: HTMLDivElement | HTMLLabelElement;
let wI: HTMLInputElement = null;
if (config.typeable !== false) {
function syncInputs(): void {
setTimeout(function(): void {
let text: string = wI.value;
let cursor: number = wI.selectionStart;
let el: HTMLElement = wC.querySelector("[data-type-area]");
if (!el) { return; }
if (cursor == text.length) {
el.innerHTML = `${escapeHTML(text)}<i class="cursor">&nbsp;</i>`;
} else {
el.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`;
}
}, 1);
}
function setCursor(): void {
setTimeout((): void => {
wI.setSelectionRange(wI.value.length, wI.value.length);
syncInputs();
}, 0);
}
wI = document.createElement("input");
wI.classList.add("window-input");
wI.id = `${config.id}__input`;
wI.oninput = (event: KeyboardEvent): void => {
syncInputs();
w.scrollTop = w.scrollHeight;
};
wI.onkeydown = (event: KeyboardEvent): void => {
if (event.key == "Enter") {
commandManager(config.id, wI.value.trim());
w.scrollTop = w.scrollHeight;
wI.value = "";
} else {
syncInputs();
}
};
wI.onfocus = setCursor;
wI.onclick = setCursor;
wC = document.createElement("label");
wC.htmlFor = `${config.id}__input`;
} else {
wC = document.createElement("div");
}
wC.classList.add("window-container");
wC.tabIndex = 0;
wC.style.left = `${posX}px`;
wC.style.top = `${posY}px`;
wC.id = config.id;
wC.style.zIndex = String(globalIncrement);
wC.style.width = `${realWidth + _windowPaddingX - 2}px`;
wO.append(w);
wC.append(wH, wO);
document.body.append(wC);
if (config.typeable !== false) {
wC.append(wI);
wI.focus();
} else {
wC.focus();
}
WINDOWS[config.id] = {
element: wC,
height: realHeight,
width: realWidth,
posX: posX,
posY: posY,
fullscreen: false,
zIndex: globalIncrement,
vars: {}
};
function mouseMoveEvent(x: number, y: number): void {
WINDOWS[config.id].posX = Math.max(0, Math.min(innerWidth - WINDOWS[config.id].width - _windowPaddingX, x - WINDOWS[config.id].vars.mouseOffsetX));
WINDOWS[config.id].posY = Math.max(0, Math.min(innerHeight - WINDOWS[config.id].height - _windowPaddingY, y - WINDOWS[config.id].vars.mouseOffsetY));
wC.style.left = `${WINDOWS[config.id].posX}px`;
wC.style.top = `${WINDOWS[config.id].posY}px`;
}
// wC.addEventListener("mousedown", function(): void { incrementZIndex(config.id); });
wC.addEventListener("focus", function(): void { incrementZIndex(config.id); });
for (const link of wC.querySelectorAll("a")) {
link.addEventListener("focus", function(): void { incrementZIndex(config.id); });
}
wH.addEventListener("mousedown", function(e: MouseEvent): void {
if ((e.target as HTMLElement).dataset.noMove !== undefined) {
return;
}
WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX;
WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY;
MOUSE_MOVE_PROCESSING[config.id] = {
callback: mouseMoveEvent,
mouseUp: true
};
});
wH.querySelector(".close").addEventListener("click", function(): void {
delete WINDOWS[config.id];
delete windowInformation[config.id];
delete MOUSE_MOVE_PROCESSING[config.id];
wC.remove();
if (typeof config.onDestroy === "function") {
config.onDestroy();
}
});
wH.querySelector(".fullscreen").addEventListener("click", function(): void {
if (WINDOWS[config.id].fullscreen) {
WINDOWS[config.id].posX = WINDOWS[config.id].vars.oldPosX;
WINDOWS[config.id].posY = WINDOWS[config.id].vars.oldPosY;
WINDOWS[config.id].width = WINDOWS[config.id].vars.oldWidth;
WINDOWS[config.id].height = WINDOWS[config.id].vars.oldHeight;
WINDOWS[config.id].fullscreen = false;
delete WINDOWS[config.id].vars.oldPosX;
delete WINDOWS[config.id].vars.oldPosY;
delete WINDOWS[config.id].vars.oldWidth;
delete WINDOWS[config.id].vars.oldHeight;
wC.style.left = `${WINDOWS[config.id].posX}px`;
wC.style.top = `${WINDOWS[config.id].posY}px`;
wC.style.width = `${WINDOWS[config.id].width + _windowPaddingX - 2}px`;
w.style.width = `${WINDOWS[config.id].width}px`;
w.style.height = `${WINDOWS[config.id].height}px`;
} else {
WINDOWS[config.id].vars.oldPosX = WINDOWS[config.id].posX;
WINDOWS[config.id].vars.oldPosY = WINDOWS[config.id].posY;
WINDOWS[config.id].vars.oldWidth = WINDOWS[config.id].width;
WINDOWS[config.id].vars.oldHeight = WINDOWS[config.id].height;
WINDOWS[config.id].fullscreen = true;
WINDOWS[config.id].posX = 0;
WINDOWS[config.id].posY = 0;
WINDOWS[config.id].width = innerWidth;
WINDOWS[config.id].height = innerHeight;
wC.style.left = "0px";
wC.style.top = "0px";
wC.style.width = `${WINDOWS[config.id].width}px`;
w.style.width = `${WINDOWS[config.id].width - _windowPaddingX}px`;
w.style.height = `${WINDOWS[config.id].height - _windowPaddingY}px`;
}
});
globalIncrement++;
}
window.addEventListener("mousemove", function(e: MouseEvent): void {
for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) {
MOUSE_MOVE_PROCESSING[key].callback(e.clientX, e.clientY);
}
});
window.addEventListener("mouseup", function(): void {
for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) {
if (MOUSE_MOVE_PROCESSING[key].mouseUp) {
delete MOUSE_MOVE_PROCESSING[key];
};
}
});
function windowPreset(template: string): void {
let el: HTMLElement = document.querySelector(`#window-templates > [data-template-id="${template}"]`);
if (!el) { return; }
if (WINDOWS[template]) {
incrementZIndex(template, true);
return;
}
let config: _winInitConf = {
id: template,
title: "~ - tSh",
content: "<div><b class=\"green\">trinkey@website</b>:<b class=\"blue\">~</b>$&nbsp;<span data-type-area><i class=\"cursor\">&nbsp;</i></span></div>"
};
for (const field of el.querySelectorAll("[data-template-field]")) {
config[(field as HTMLElement).dataset.templateField] = (field as HTMLElement).dataset.isNumber === "" ? +(field as HTMLElement).innerText : (field as HTMLElement).innerHTML;
}
createWindow(config);
for (const command of el.querySelectorAll("li")) {
WINDOWS[template].element.querySelector("[data-type-area]").innerHTML = command.innerHTML;
commandManager(template, command.innerHTML);
}
}
function copyButton(): void {
navigator.clipboard.writeText("<a href=\"https://trinkey.com/\" target=\"_blank\"><img src=\"https://trinkey.com/img/88x31.png\" alt=\"trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button.\" title=\"trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button.\"></a>");
}