2025-01-14 15:58:30 +01:00
|
|
|
import { initializeAddInstanceFlow } from "./add_instance_flow.mjs";
|
2025-01-14 18:25:02 +01:00
|
|
|
import { findButtonOrFail, findDialogOrFail, findFormOrFail, findInputOrFail } from "./dom.mjs";
|
2025-01-14 17:41:14 +01:00
|
|
|
import knownSoftware from "./known_software.mjs";
|
|
|
|
import storageManager from "./storage_manager.mjs";
|
2025-01-13 06:59:58 +01:00
|
|
|
|
2025-01-14 18:25:02 +01:00
|
|
|
const radioButtonName = "instanceSelect";
|
|
|
|
|
2025-01-14 15:58:30 +01:00
|
|
|
const detailsDialog = findDialogOrFail(document.body, "#instanceDetails");
|
|
|
|
const addDialog = findDialogOrFail(document.body, "#addInstance");
|
2025-01-14 17:41:14 +01:00
|
|
|
const instanceSelectForm = findFormOrFail(document.body, "#instanceSelectForm");
|
2025-01-14 18:25:02 +01:00
|
|
|
const redirectButton = findButtonOrFail(document.body, "#redirect");
|
2025-01-15 06:44:47 +01:00
|
|
|
const redirectAlwaysButton = findButtonOrFail(document.body, "#redirectAlways");
|
2025-01-14 10:49:44 +01:00
|
|
|
|
2025-01-15 07:05:47 +01:00
|
|
|
redirectButton.addEventListener("click", e => {
|
|
|
|
// Can be assumed to not fail because the button is disabled if there are no options and the first one is selected by default
|
|
|
|
redirect(getSelectedOption()!);
|
|
|
|
});
|
|
|
|
|
|
|
|
redirectAlwaysButton.addEventListener("click", e => {
|
|
|
|
// Can be assumed to not fail because the button is disabled if there are no options and the first one is selected by default
|
|
|
|
const option = getSelectedOption()!;
|
|
|
|
setAutoRedirect(option);
|
|
|
|
redirect(option);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const getMainDialog = () => findDialogOrFail(document.body, "#mainDialog");
|
|
|
|
|
2025-01-14 10:49:44 +01:00
|
|
|
export const {
|
|
|
|
showAddInstanceDialog,
|
|
|
|
hideAddInstanceDialog
|
2025-01-15 07:05:47 +01:00
|
|
|
} = ((): {
|
|
|
|
showAddInstanceDialog: () => void,
|
|
|
|
hideAddInstanceDialog: () => void
|
|
|
|
} => {
|
|
|
|
// Don't bother initializing if we're performing autoredirect
|
|
|
|
if (autoRedirect()) return {
|
|
|
|
showAddInstanceDialog: () => { },
|
|
|
|
hideAddInstanceDialog: () => { }
|
|
|
|
}
|
|
|
|
createInstanceSelectOptions();
|
|
|
|
storageManager.addSaveCallback(createInstanceSelectOptions);
|
|
|
|
return initializeAddInstanceFlow(detailsDialog, addDialog)
|
|
|
|
})();
|
2025-01-14 17:41:14 +01:00
|
|
|
|
|
|
|
function createInstanceSelectOptions() {
|
2025-01-20 10:55:45 -05:00
|
|
|
if (storageManager.storage.instances.length > 0) {
|
|
|
|
document.querySelector("#no-instance")?.classList.add("hidden")
|
|
|
|
}
|
2025-01-14 17:41:14 +01:00
|
|
|
instanceSelectForm.replaceChildren(); // Erase all child nodes
|
|
|
|
for (const instance of storageManager.storage.instances) {
|
|
|
|
const div = document.createElement("div");
|
|
|
|
div.setAttribute("x-option", instance.origin);
|
|
|
|
const radio = document.createElement("input");
|
|
|
|
radio.id = instance.origin;
|
2025-01-14 18:25:02 +01:00
|
|
|
radio.value = instance.origin;
|
2025-01-14 17:41:14 +01:00
|
|
|
radio.type = "radio";
|
2025-01-14 18:25:02 +01:00
|
|
|
radio.name = radioButtonName;
|
2025-01-14 17:41:14 +01:00
|
|
|
const label = document.createElement("label");
|
|
|
|
label.htmlFor = instance.origin;
|
|
|
|
label.innerText = instance.name + " ";
|
|
|
|
if (instance.iconURL) {
|
|
|
|
const img = new Image();
|
|
|
|
img.src = instance.iconURL;
|
|
|
|
img.alt = `${instance.name} icon`;
|
|
|
|
img.className = "inlineIcon";
|
|
|
|
label.append(img, " ");
|
|
|
|
}
|
|
|
|
const small = document.createElement("small");
|
|
|
|
const softwareName = knownSoftware.software[instance.software].name;
|
|
|
|
small.innerText = `(${softwareName})`;
|
|
|
|
label.appendChild(small);
|
|
|
|
div.appendChild(radio);
|
|
|
|
div.appendChild(label);
|
|
|
|
instanceSelectForm.appendChild(div);
|
|
|
|
}
|
|
|
|
const firstInput = instanceSelectForm.querySelector("input");
|
2025-01-15 06:44:47 +01:00
|
|
|
if (firstInput) firstInput.checked = true;
|
|
|
|
setRedirectButtonState(firstInput !== null);
|
2025-01-14 17:41:14 +01:00
|
|
|
}
|
|
|
|
|
2025-01-15 06:44:47 +01:00
|
|
|
function setRedirectButtonState(enabled: boolean) {
|
|
|
|
redirectButton.disabled = !enabled;
|
|
|
|
redirectAlwaysButton.disabled = !enabled;
|
|
|
|
}
|
|
|
|
|
2025-01-15 07:05:47 +01:00
|
|
|
function getTargetSoftwareOrGroup(): string {
|
2025-01-15 06:44:47 +01:00
|
|
|
const currentURL = URL.parse(location.href)!;
|
|
|
|
const target = currentURL.pathname.match(/\/+([^\/]*)\/?/)?.[1];
|
|
|
|
if (target == null) throw new Error("Crossroad was served on an invalid path (likely a backend routing mistake)");
|
|
|
|
const softwareName = Object.entries(knownSoftware.software).find(([name, software]) => software.aliases.includes(target))?.[0];
|
|
|
|
if (softwareName) return softwareName;
|
|
|
|
const groupName = Object.entries(knownSoftware.groups).find(([name, group]) => group.aliases.includes(target))?.[0];
|
|
|
|
if (groupName) return groupName;
|
|
|
|
throw new Error("Could not identify target software or group");
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTargetPath(): string {
|
2025-01-14 18:25:02 +01:00
|
|
|
const currentURL = URL.parse(location.href)!;
|
2025-01-15 06:44:47 +01:00
|
|
|
return currentURL.pathname.replace(/\/+[^\/]*\/?/, "/");
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSelectedOption(): string | null {
|
|
|
|
try {
|
|
|
|
return findInputOrFail(instanceSelectForm, `input[name="${radioButtonName}"]:checked`).value;
|
|
|
|
} catch {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-15 07:05:47 +01:00
|
|
|
function autoRedirect(): boolean {
|
2025-01-15 06:44:47 +01:00
|
|
|
const targetSoftware = getTargetSoftwareOrGroup();
|
|
|
|
const preferredFor = storageManager.storage.instances.find(instance => instance.preferredFor?.includes(targetSoftware));
|
2025-01-15 07:05:47 +01:00
|
|
|
if (preferredFor) {
|
|
|
|
redirect(preferredFor.origin);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2025-01-15 06:44:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function setAutoRedirect(option: string) {
|
|
|
|
const instance = storageManager.storage.instances.find(e => e.origin === option);
|
|
|
|
if (!instance) throw new Error("Invalid argument");
|
|
|
|
instance.preferredFor ??= [];
|
|
|
|
instance.preferredFor.push(getTargetSoftwareOrGroup());
|
|
|
|
storageManager.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
function redirect(to: string) {
|
|
|
|
const url = URL.parse(to);
|
|
|
|
if (url === null) throw new Error("Couldn't parse destination");
|
|
|
|
url.pathname = getTargetPath();
|
2025-01-14 18:25:02 +01:00
|
|
|
location.href = url.toString();
|
|
|
|
}
|
2025-01-20 10:55:45 -05:00
|
|
|
|
|
|
|
document.querySelector("#path")!.innerHTML = getTargetPath()
|
|
|
|
if (storageManager.storage.instances.length === 0) {
|
|
|
|
document.querySelector("#no-instance")?.classList.remove("hidden")
|
|
|
|
}
|