787 lines
25 KiB
JavaScript
787 lines
25 KiB
JavaScript
// Use globals provided by the PowerShell-generated HTML bridge
|
||
const tasks = (window.SAMY_TASKS || []);
|
||
const defaultPage = (window.SAMY_DEFAULT_PAGE || "onboard");
|
||
|
||
let completedTasks = 0;
|
||
let totalTasks = 0;
|
||
|
||
// Progress / title handling
|
||
function setTotalTaskCount(count) {
|
||
totalTasks = count;
|
||
completedTasks = 0;
|
||
updateTitle();
|
||
}
|
||
|
||
function logProgress(label, isSuccess) {
|
||
const statusBox = document.getElementById("status-box");
|
||
completedTasks++;
|
||
updateTitle();
|
||
|
||
const msg = isSuccess
|
||
? ` ${completedTasks}/${totalTasks} done: ${label}`
|
||
: ` ${completedTasks}/${totalTasks} failed: ${label}`;
|
||
|
||
const div = document.createElement("div");
|
||
div.style.color = isSuccess ? "lime" : "red";
|
||
div.textContent = msg;
|
||
statusBox?.appendChild(div);
|
||
|
||
if (completedTasks === totalTasks) {
|
||
const finalMsg = document.createElement("div");
|
||
finalMsg.style.marginTop = "10px";
|
||
finalMsg.innerHTML = `<strong> All tasks completed (${completedTasks}/${totalTasks})</strong>`;
|
||
statusBox?.appendChild(finalMsg);
|
||
|
||
document.title = ` ScriptMonkey - Complete (${completedTasks}/${totalTasks})`;
|
||
|
||
const sound = new Audio(
|
||
"data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQAAAAA="
|
||
);
|
||
sound.play().catch(() => {});
|
||
flashTitle(document.title);
|
||
}
|
||
}
|
||
|
||
function updateTitle() {
|
||
document.title = `ScriptMonkey - ${completedTasks}/${totalTasks} Done`;
|
||
}
|
||
|
||
function flashTitle(finalTitle) {
|
||
let flashes = 0;
|
||
const interval = setInterval(() => {
|
||
document.title = document.title === "" ? finalTitle : "";
|
||
flashes++;
|
||
if (flashes >= 10) {
|
||
clearInterval(interval);
|
||
document.title = finalTitle;
|
||
}
|
||
}, 800);
|
||
}
|
||
|
||
// =======================================================================
|
||
// Tab Navigation
|
||
// =======================================================================
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const tabButtons = document.querySelectorAll(".tab-button");
|
||
const tabContents = document.querySelectorAll(".tab-content");
|
||
|
||
if (!tabButtons?.length || !tabContents?.length) {
|
||
console.error("ScriptMonkey: no tab buttons or tab contents found.");
|
||
return;
|
||
}
|
||
|
||
tabButtons.forEach((btn) => {
|
||
btn.addEventListener("click", () => {
|
||
tabButtons.forEach((b) => b.classList.remove("active"));
|
||
tabContents.forEach((c) => c.classList.remove("active"));
|
||
|
||
btn.classList.add("active");
|
||
const targetId = btn.dataset.tab;
|
||
const target = document.getElementById(targetId);
|
||
if (target) target.classList.add("active");
|
||
});
|
||
});
|
||
|
||
// Default tab from PS (onboard/offboard/devices)
|
||
const defaultTabId = `${defaultPage}Tab`;
|
||
const defaultBtn = document.querySelector(
|
||
`.tab-button[data-tab='${defaultTabId}']`
|
||
);
|
||
const defaultTab = document.getElementById(defaultTabId);
|
||
|
||
if (defaultBtn) defaultBtn.classList.add("active");
|
||
if (defaultTab) defaultTab.classList.add("active");
|
||
});
|
||
|
||
// =======================================================================
|
||
// Onboarding: Select-all left columns
|
||
// =======================================================================
|
||
function toggleColumn(col) {
|
||
const master = document.getElementById(
|
||
`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`
|
||
);
|
||
const children = document.querySelectorAll(
|
||
`#onboardTab input[type=checkbox][data-column="${col}"]`
|
||
);
|
||
|
||
children.forEach((cb) => {
|
||
cb.checked = master.checked;
|
||
});
|
||
|
||
// fire change handlers
|
||
setTimeout(() => {
|
||
children.forEach((cb) => {
|
||
cb.dispatchEvent(new Event("change"));
|
||
});
|
||
}, 0);
|
||
}
|
||
|
||
function updateSelectAll(col) {
|
||
const master = document.getElementById(
|
||
`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`
|
||
);
|
||
const children = document.querySelectorAll(
|
||
`#onboardTab input[type=checkbox][data-column="${col}"]`
|
||
);
|
||
|
||
if (!master) return;
|
||
master.checked = Array.from(children).every((cb) => cb.checked);
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
["left"].forEach((col) => {
|
||
document
|
||
.querySelectorAll(`#onboardTab input[type=checkbox][data-column="${col}"]`)
|
||
.forEach((cb) =>
|
||
cb.addEventListener("change", () => updateSelectAll(col))
|
||
);
|
||
updateSelectAll(col);
|
||
});
|
||
});
|
||
|
||
// =======================================================================
|
||
// Onboarding: Right side split Select All (apps + tweaks)
|
||
// =======================================================================
|
||
|
||
// apps = only checkboxes marked data-group="apps"
|
||
// tweaks = everything in right column NOT marked as apps
|
||
const ONBOARD_RIGHT_GROUPS = {
|
||
apps: {
|
||
masterId: "selectAllAppsCheckbox",
|
||
selector:
|
||
'#onboardTab input[type=checkbox][data-column="right"][data-group="apps"]',
|
||
},
|
||
tweaks: {
|
||
masterId: "selectAllTweaksCheckbox",
|
||
selector:
|
||
'#onboardTab input[type=checkbox][data-column="right"]:not([data-group="apps"])',
|
||
},
|
||
};
|
||
|
||
function toggleOnboardGroup(groupKey) {
|
||
const cfg = ONBOARD_RIGHT_GROUPS[groupKey];
|
||
if (!cfg) return;
|
||
|
||
const master = document.getElementById(cfg.masterId);
|
||
if (!master) return;
|
||
|
||
const children = document.querySelectorAll(cfg.selector);
|
||
|
||
children.forEach((cb) => {
|
||
cb.checked = master.checked;
|
||
});
|
||
|
||
// fire change handlers so renameComputer / suboptions update properly
|
||
setTimeout(() => {
|
||
children.forEach((cb) => cb.dispatchEvent(new Event("change")));
|
||
}, 0);
|
||
}
|
||
|
||
function updateOnboardGroupSelectAll(groupKey) {
|
||
const cfg = ONBOARD_RIGHT_GROUPS[groupKey];
|
||
if (!cfg) return;
|
||
|
||
const master = document.getElementById(cfg.masterId);
|
||
if (!master) return;
|
||
|
||
const children = document.querySelectorAll(cfg.selector);
|
||
|
||
if (!children.length) {
|
||
master.checked = false;
|
||
return;
|
||
}
|
||
|
||
master.checked = Array.from(children).every((cb) => cb.checked);
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
Object.keys(ONBOARD_RIGHT_GROUPS).forEach((groupKey) => {
|
||
const cfg = ONBOARD_RIGHT_GROUPS[groupKey];
|
||
|
||
// When any child checkbox changes, refresh that group's Select All
|
||
document.querySelectorAll(cfg.selector).forEach((cb) => {
|
||
cb.addEventListener("change", () =>
|
||
updateOnboardGroupSelectAll(groupKey)
|
||
);
|
||
});
|
||
|
||
// Initial set
|
||
updateOnboardGroupSelectAll(groupKey);
|
||
});
|
||
});
|
||
|
||
|
||
// =======================================================================
|
||
// Off-boarding Select All
|
||
// =======================================================================
|
||
function toggleOffboardAll() {
|
||
const master = document.getElementById("offboardSelectAll");
|
||
const children = document.querySelectorAll(
|
||
"#offboardTab input[type=checkbox]:not(#offboardSelectAll)"
|
||
);
|
||
|
||
children.forEach((cb) => {
|
||
cb.checked = master.checked;
|
||
});
|
||
}
|
||
|
||
function updateOffboardSelectAll() {
|
||
const master = document.getElementById("offboardSelectAll");
|
||
if (!master) return;
|
||
|
||
const children = document.querySelectorAll(
|
||
"#offboardTab input[type=checkbox]:not(#offboardSelectAll)"
|
||
);
|
||
if (!children.length) {
|
||
master.checked = false;
|
||
return;
|
||
}
|
||
|
||
master.checked = Array.from(children).every((cb) => cb.checked);
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const offChildren = document.querySelectorAll(
|
||
"#offboardTab input[type=checkbox]:not(#offboardSelectAll)"
|
||
);
|
||
|
||
if (!offChildren?.length) return;
|
||
|
||
offChildren.forEach((cb) =>
|
||
cb.addEventListener("change", updateOffboardSelectAll)
|
||
);
|
||
updateOffboardSelectAll();
|
||
});
|
||
|
||
// =======================================================================
|
||
// DattoRMM options + Enter key handling
|
||
// =======================================================================
|
||
function toggleDattoRMMOptions() {
|
||
const master = document.getElementById("installDattoRMM");
|
||
const container = document.getElementById("installDattoRMMOptionsContainer");
|
||
if (!container) return;
|
||
|
||
const checked = master?.checked;
|
||
container.style.display = checked ? "block" : "none";
|
||
container
|
||
.querySelectorAll('input[type="checkbox"]')
|
||
.forEach((cb) => (cb.checked = checked));
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const master = document.getElementById("installDattoRMM");
|
||
if (master) {
|
||
master.addEventListener("change", toggleDattoRMMOptions);
|
||
}
|
||
|
||
const passwordField = document.getElementById("Password");
|
||
const goButton = document.querySelector("button[onclick='fetchSites()']");
|
||
|
||
if (passwordField && goButton) {
|
||
passwordField.addEventListener("keydown", (e) => {
|
||
if (e.key === "Enter") goButton.click();
|
||
});
|
||
}
|
||
|
||
const siteDropdown = document.getElementById("dattoDropdown");
|
||
const runButton = document.querySelector(".run-button");
|
||
|
||
if (siteDropdown && runButton) {
|
||
siteDropdown.addEventListener("keydown", (e) => {
|
||
if (e.key === "Enter" && siteDropdown.value) runButton.click();
|
||
});
|
||
}
|
||
});
|
||
|
||
// =======================================================================
|
||
// Fetch Sites handler (calls /getpw)
|
||
// =======================================================================
|
||
async function fetchSites() {
|
||
const pwdInput = document.getElementById("Password");
|
||
const pwd = (pwdInput?.value ?? "").trim(); // allow blank
|
||
|
||
const dropdown = document.getElementById("dattoDropdown");
|
||
if (!dropdown) return;
|
||
|
||
dropdown.innerHTML = '<option disabled selected>Loading sites...</option>';
|
||
|
||
try {
|
||
const resp = await fetch("/getpw", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ password: pwd }),
|
||
});
|
||
|
||
if (!resp.ok) throw new Error("HTTP " + resp.status);
|
||
|
||
const sites = await resp.json();
|
||
|
||
if (!Array.isArray(sites) || sites.length === 0) {
|
||
dropdown.innerHTML = '<option disabled selected>No sites returned</option>';
|
||
alert(
|
||
"No Datto sites returned. Verify credentials/allowlist, or try again in a moment."
|
||
);
|
||
return;
|
||
}
|
||
|
||
dropdown.innerHTML = "";
|
||
sites.forEach((site) => {
|
||
const option = document.createElement("option");
|
||
option.value = site.UID;
|
||
option.textContent = site.Name;
|
||
dropdown.appendChild(option);
|
||
});
|
||
|
||
const rmmContainer = document.getElementById("dattoRmmContainer");
|
||
if (rmmContainer) rmmContainer.style.display = "block";
|
||
} catch (e) {
|
||
console.error(e);
|
||
dropdown.innerHTML = '<option disabled selected>Error loading sites</option>';
|
||
alert(
|
||
"Failed to fetch sites. Check password or confirm your public IP is allowlisted."
|
||
);
|
||
}
|
||
}
|
||
|
||
// =======================================================================
|
||
// Printer management (Devices tab)
|
||
// =======================================================================
|
||
let allPrinters = [];
|
||
|
||
// POST /getprinters with password from Devices tab
|
||
async function fetchPrinters() {
|
||
const pwdInput = document.getElementById("PrinterPassword");
|
||
const pwd = (pwdInput?.value ?? ""); // allow blank
|
||
|
||
const clientContainer = document.getElementById("printerClientContainer");
|
||
const listContainer = document.getElementById("printerListContainer");
|
||
const dropdown = document.getElementById("printerClientDropdown");
|
||
const checkboxContainer = document.getElementById("printerCheckboxContainer");
|
||
|
||
if (dropdown) dropdown.innerHTML = '<option disabled selected>Loading clients...</option>';
|
||
if (checkboxContainer) checkboxContainer.innerHTML = "";
|
||
if (clientContainer) clientContainer.style.display = "none";
|
||
if (listContainer) listContainer.style.display = "none";
|
||
|
||
try {
|
||
const resp = await fetch("/getprinters", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ password: pwd }),
|
||
});
|
||
|
||
if (!resp.ok) throw new Error("HTTP " + resp.status);
|
||
|
||
const data = await resp.json();
|
||
allPrinters = Array.isArray(data) ? data : [];
|
||
|
||
if (!allPrinters.length) {
|
||
alert("No printers returned. Verify credentials/allowlist, or try again in a moment.");
|
||
return;
|
||
}
|
||
|
||
const codes = [...new Set(allPrinters.map((p) => p.ClientCode))].sort();
|
||
|
||
if (dropdown) {
|
||
dropdown.innerHTML = "";
|
||
const defaultOpt = new Option("Select a client...", "", true, true);
|
||
defaultOpt.disabled = true;
|
||
dropdown.appendChild(defaultOpt);
|
||
|
||
codes.forEach((code) => dropdown.appendChild(new Option(code, code)));
|
||
}
|
||
|
||
if (clientContainer) clientContainer.style.display = "block";
|
||
} catch (e) {
|
||
console.error("fetchPrinters error:", e);
|
||
if (dropdown) dropdown.innerHTML = '<option disabled selected>Error loading clients</option>';
|
||
alert("Failed to fetch printers. Check password or confirm your public IP is allowlisted.");
|
||
}
|
||
}
|
||
|
||
function renderPrintersForClient(clientCode) {
|
||
const container = document.getElementById("printerCheckboxContainer");
|
||
const listContainer = document.getElementById("printerListContainer");
|
||
if (!container) return;
|
||
|
||
container.innerHTML = "";
|
||
|
||
const printers = allPrinters.filter((p) => p.ClientCode === clientCode);
|
||
|
||
if (!printers.length) {
|
||
container.textContent = "No printers found for this client.";
|
||
if (listContainer) listContainer.style.display = "block";
|
||
return;
|
||
}
|
||
|
||
printers.forEach((p, idx) => {
|
||
const id = `printer_${clientCode}_${idx}`;
|
||
const label = document.createElement("label");
|
||
label.style.display = "block";
|
||
label.style.marginBottom = "4px";
|
||
|
||
const cb = document.createElement("input");
|
||
cb.type = "checkbox";
|
||
cb.id = id;
|
||
|
||
cb.dataset.clientCode = p.ClientCode;
|
||
cb.dataset.profileName = p.ProfileName;
|
||
cb.dataset.displayName = p.DisplayName;
|
||
cb.dataset.location = p.Location;
|
||
cb.dataset.address = p.Address;
|
||
cb.dataset.printServer = p.PrintServer;
|
||
cb.dataset.shareName = p.ShareName;
|
||
cb.dataset.driverName = p.DriverName;
|
||
cb.dataset.driverInfPath = p.DriverInfPath;
|
||
|
||
const nameText = p.DisplayName || p.ProfileName || "Unnamed printer";
|
||
const locText = p.Location || "Unknown location";
|
||
|
||
label.appendChild(cb);
|
||
label.appendChild(document.createTextNode(" "));
|
||
label.appendChild(document.createTextNode(`${nameText} (${locText})`));
|
||
|
||
const defaultWrapper = document.createElement("div");
|
||
defaultWrapper.style.marginLeft = "24px";
|
||
defaultWrapper.style.fontSize = "0.85em";
|
||
defaultWrapper.style.opacity = "0.9";
|
||
|
||
const radio = document.createElement("input");
|
||
radio.type = "radio";
|
||
radio.name = "defaultPrinter";
|
||
radio.value = id;
|
||
|
||
const radioLabel = document.createElement("span");
|
||
radioLabel.textContent = " Make default";
|
||
|
||
defaultWrapper.appendChild(radio);
|
||
defaultWrapper.appendChild(radioLabel);
|
||
|
||
label.appendChild(document.createElement("br"));
|
||
label.appendChild(defaultWrapper);
|
||
|
||
container.appendChild(label);
|
||
});
|
||
|
||
if (listContainer) listContainer.style.display = "block";
|
||
}
|
||
|
||
async function installSelectedPrinters() {
|
||
const container = document.getElementById("printerCheckboxContainer");
|
||
if (!container) return;
|
||
|
||
const checked = container.querySelectorAll("input[type=checkbox]:checked");
|
||
if (!checked.length) {
|
||
alert("Please select at least one printer.");
|
||
return;
|
||
}
|
||
|
||
const defaultRadio = container.querySelector(
|
||
'input[type=radio][name="defaultPrinter"]:checked'
|
||
);
|
||
const defaultId = defaultRadio ? defaultRadio.value : null;
|
||
|
||
const selected = Array.from(checked).map((cb) => ({
|
||
ClientCode: cb.dataset.clientCode,
|
||
ProfileName: cb.dataset.profileName,
|
||
DisplayName: cb.dataset.displayName,
|
||
Location: cb.dataset.location,
|
||
Address: cb.dataset.address,
|
||
PrintServer: cb.dataset.printServer,
|
||
ShareName: cb.dataset.shareName,
|
||
DriverName: cb.dataset.driverName,
|
||
DriverInfPath: cb.dataset.driverInfPath,
|
||
SetAsDefault: defaultId !== null && cb.id === defaultId,
|
||
}));
|
||
|
||
try {
|
||
const resp = await fetch("/installprinters", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ printers: selected }),
|
||
});
|
||
|
||
if (!resp.ok) throw new Error("HTTP " + resp.status);
|
||
|
||
const result = await resp.json().catch(() => null);
|
||
console.log("Printer install result:", result);
|
||
} catch (e) {
|
||
console.error("installSelectedPrinters error:", e);
|
||
alert("Failed to trigger printer install.");
|
||
}
|
||
}
|
||
|
||
// =======================================================================
|
||
// Run Selected (main trigger)
|
||
// =======================================================================
|
||
async function triggerInstall() {
|
||
const runBtn = document.querySelector(".run-button");
|
||
if (!runBtn) return;
|
||
|
||
runBtn.disabled = true;
|
||
|
||
const statusBox = document.getElementById("status-box");
|
||
if (statusBox) statusBox.innerHTML = "";
|
||
|
||
try {
|
||
// Special-case elements ONCE
|
||
const dattoCB = document.getElementById("installDattoRMM");
|
||
const svsCB = document.getElementById("installSVSMSPModule");
|
||
const renameCB = document.getElementById("renameComputer");
|
||
const newNameInput = document.getElementById("txtNewComputerName");
|
||
|
||
// Standard tasks = all tasks except special-case ones
|
||
const checkedTasks = tasks.filter((t) => {
|
||
if (["installDattoRMM", "installSVSMSPModule", "renameComputer"].includes(t.id)) return false;
|
||
const cb = document.getElementById(t.id);
|
||
return cb && cb.checked;
|
||
});
|
||
|
||
// Count special-case tasks
|
||
let specialTasks = 0;
|
||
if (dattoCB && dattoCB.checked) specialTasks++;
|
||
if (svsCB && svsCB.checked) specialTasks++;
|
||
|
||
const extraTasks = (renameCB && renameCB.checked) ? 1 : 0;
|
||
const total = checkedTasks.length + specialTasks + extraTasks;
|
||
|
||
if (total === 0) {
|
||
alert("Please select at least one task.");
|
||
return;
|
||
}
|
||
|
||
setTotalTaskCount(total);
|
||
|
||
// 1) DattoRMM first
|
||
if (dattoCB && dattoCB.checked) {
|
||
const sub = Array.from(
|
||
document.querySelectorAll(".sub-option-installDattoRMM:checked")
|
||
).map((x) => x.value);
|
||
|
||
const dropdown = document.getElementById("dattoDropdown");
|
||
const uid = dropdown?.value;
|
||
const name = dropdown?.selectedOptions?.[0]?.text || "Datto";
|
||
|
||
if (!uid) {
|
||
alert("Please select a Datto RMM site before running.");
|
||
logProgress("Install DattoRMM (no site selected)", false);
|
||
} else {
|
||
try {
|
||
await fetch("/installDattoRMM", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ checkedValues: sub, UID: uid, Name: name }),
|
||
});
|
||
logProgress("Install DattoRMM", true);
|
||
} catch (e) {
|
||
logProgress("Install DattoRMM", false);
|
||
console.error(e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2) SVSMSP module second
|
||
if (svsCB && svsCB.checked) {
|
||
try {
|
||
await fetch("/installSVSMSPModule", { method: "GET" });
|
||
logProgress("Install SVSMSP Module", true);
|
||
} catch (e) {
|
||
logProgress("Install SVSMSP Module", false);
|
||
console.error(e);
|
||
}
|
||
}
|
||
|
||
// 3) Remaining tasks
|
||
for (const t of tasks) {
|
||
if (["installDattoRMM", "installSVSMSPModule", "renameComputer"].includes(t.id)) continue;
|
||
|
||
const cb = document.getElementById(t.id);
|
||
if (!cb || !cb.checked) continue;
|
||
|
||
try {
|
||
await fetch(t.handler, { method: "GET" });
|
||
logProgress(t.label || t.id, true);
|
||
} catch (e) {
|
||
logProgress(t.label || t.id, false);
|
||
console.error(`Error running ${t.id}:`, e);
|
||
}
|
||
}
|
||
|
||
// 4) Rename computer LAST
|
||
if (renameCB && renameCB.checked && newNameInput) {
|
||
const newName = newNameInput.value.trim();
|
||
|
||
const nameIsValid =
|
||
newName.length > 0 &&
|
||
newName.length <= 15 &&
|
||
/^[A-Za-z0-9-]+$/.test(newName);
|
||
|
||
if (!nameIsValid) {
|
||
alert("Invalid computer name. Must be 1-15 characters and only letters, numbers, and hyphens.");
|
||
logProgress("Rename computer", false);
|
||
} else {
|
||
try {
|
||
await fetch("/renameComputer", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ newName }),
|
||
});
|
||
logProgress("Rename computer", true);
|
||
} catch (e) {
|
||
console.error("Error calling /renameComputer:", e);
|
||
logProgress("Rename computer", false);
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error("triggerInstall fatal error:", e);
|
||
} finally {
|
||
runBtn.disabled = false;
|
||
|
||
// Best-effort notification to the server
|
||
try {
|
||
await fetch("/tasksCompleted", { method: "POST" });
|
||
} catch (err) {
|
||
console.warn("Could not notify server about completion:", err);
|
||
}
|
||
}
|
||
}
|
||
|
||
// =======================================================================
|
||
// Shutdown handler (Exit button & window close)
|
||
// =======================================================================
|
||
function endSession() {
|
||
fetch("/quit", { method: "GET" }).finally(() => window.close());
|
||
}
|
||
|
||
// Sub-options auto-toggle, tagline rotation, and beforeunload hook
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
// Sub-option containers
|
||
const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]');
|
||
|
||
tasksWithSubOptions.forEach((container) => {
|
||
const taskId = container.id.replace("OptionsContainer", "");
|
||
const masterCheckbox = document.getElementById(taskId);
|
||
if (!masterCheckbox) return;
|
||
|
||
function updateVisibility() {
|
||
const checked = masterCheckbox.checked;
|
||
|
||
// Show/hide the sub-options panel
|
||
container.style.display = checked ? "block" : "none";
|
||
|
||
// Only sub-options checkboxes live here (text inputs won’t be touched)
|
||
const subCbs = container.querySelectorAll('input[type="checkbox"]');
|
||
|
||
// ==========================================================
|
||
// Special-case: 1Password sub-options should be user-chosen
|
||
// ==========================================================
|
||
if (taskId === "install1Password") {
|
||
if (!checked) {
|
||
// turning OFF -> clear sub-options
|
||
subCbs.forEach((cb) => (cb.checked = false));
|
||
} else {
|
||
// turning ON -> if nothing selected yet, default to desktop
|
||
const anySelected = Array.from(subCbs).some((cb) => cb.checked);
|
||
if (!anySelected) {
|
||
const desktop = container.querySelector(
|
||
'input[type="checkbox"][value="desktop"]'
|
||
);
|
||
if (desktop) desktop.checked = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ==========================================================
|
||
// Special-case: Disable Animations sub-options should be user-chosen
|
||
// ==========================================================
|
||
else if (taskId === "disableAnimations") {
|
||
if (!checked) {
|
||
// turning OFF -> clear sub-options
|
||
subCbs.forEach((cb) => (cb.checked = false));
|
||
} else {
|
||
// turning ON -> if nothing selected yet, default to ALL sub-options
|
||
const anySelected = Array.from(subCbs).some((cb) => cb.checked);
|
||
if (!anySelected) {
|
||
subCbs.forEach((cb) => (cb.checked = true));
|
||
}
|
||
}
|
||
}
|
||
|
||
// ==========================================================
|
||
// Default behavior: sub-options mirror the master checkbox
|
||
// (Datto uses this: check master -> check all sub-options)
|
||
// ==========================================================
|
||
else {
|
||
subCbs.forEach((cb) => (cb.checked = checked));
|
||
}
|
||
|
||
// ==========================================================
|
||
// Datto extra UI blocks (password + dropdown) tied to master checkbox
|
||
// ==========================================================
|
||
if (taskId === "installDattoRMM") {
|
||
const pwdBox = document.getElementById("PasswordContainer");
|
||
const rmmBox = document.getElementById("dattoRmmContainer");
|
||
if (pwdBox) pwdBox.style.display = checked ? "block" : "none";
|
||
if (rmmBox) rmmBox.style.display = checked ? "block" : "none";
|
||
}
|
||
}
|
||
|
||
|
||
// Keep your Datto UI show/hide behavior
|
||
if (taskId === "installDattoRMM") {
|
||
const pwdBox = document.getElementById("PasswordContainer");
|
||
const rmmBox = document.getElementById("dattoRmmContainer");
|
||
if (pwdBox) pwdBox.style.display = checked ? "block" : "none";
|
||
if (rmmBox) rmmBox.style.display = checked ? "block" : "none";
|
||
}
|
||
}
|
||
|
||
masterCheckbox.addEventListener("change", updateVisibility);
|
||
updateVisibility();
|
||
});
|
||
|
||
// Tagline rotation
|
||
const taglines = [
|
||
"Fast deployments, no monkey business.",
|
||
"Bananas for better builds.",
|
||
"Deploy without flinging code.",
|
||
"Tame your stack. Unleash the monkey.",
|
||
"Monkey see, monkey deploy.",
|
||
"Deploy smarter -- with a monkey on your team.",
|
||
"Don't pass the monkey -- let it deploy.",
|
||
"No more monkeying around. Stack handled.",
|
||
"Own your stack. But let the monkey do the work.",
|
||
"Why throw code when the monkey's got it?",
|
||
"Deployments so easy, a monkey could do it. Ours does.",
|
||
"Monkey in the stack, not on your back.",
|
||
];
|
||
|
||
const el = document.getElementById("tagline");
|
||
if (el) {
|
||
let idx = Math.floor(Math.random() * taglines.length);
|
||
el.textContent = taglines[idx];
|
||
|
||
setInterval(() => {
|
||
idx = (idx + 1) % taglines.length;
|
||
el.textContent = taglines[idx];
|
||
}, 10_000);
|
||
}
|
||
});
|
||
|
||
// printer dropdown
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const clientDropdown = document.getElementById("printerClientDropdown");
|
||
if (clientDropdown) {
|
||
clientDropdown.addEventListener("change", (e) => {
|
||
const code = e.target.value;
|
||
if (code) renderPrintersForClient(code);
|
||
});
|
||
}
|
||
});
|
||
|
||
// notify server on window close
|
||
window.addEventListener("beforeunload", () => {
|
||
fetch("/quit", { method: "GET", keepalive: true });
|
||
});
|