// 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 = ` All tasks completed (${completedTasks}/${totalTasks})`; 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/tweaks/SVSApps) 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/right 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; }); 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}]` ); master.checked = Array.from(children).every((cb) => cb.checked); } document.addEventListener("DOMContentLoaded", () => { ["left", "right"].forEach((col) => { document .querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`) .forEach((cb) => cb.addEventListener("change", () => updateSelectAll(col)) ); }); }); // ======================================================================= // 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; if (!pwd) { alert("Please enter the password."); return; } const dropdown = document.getElementById("dattoDropdown"); dropdown.innerHTML = ''; try { const resp = await fetch("/getpw", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ password: pwd }), }); if (!resp.ok) throw "HTTP " + resp.status; const sites = await resp.json(); dropdown.innerHTML = ""; sites.forEach((site) => { const option = document.createElement("option"); option.value = site.UID; option.textContent = site.Name; dropdown.appendChild(option); }); document.getElementById("dattoRmmContainer").style.display = "block"; } catch (e) { console.error(e); dropdown.innerHTML = ''; alert("Failed to fetch sites. Check password and try again."); } } // ======================================================================= // Printer management (Devices tab) // ======================================================================= let allPrinters = []; // GET /getprinters with password from Devices tab (sent via SAMYPW header) async function fetchPrinters() { const pwdInput = document.getElementById("PrinterPassword"); const pwd = pwdInput?.value; if (!pwd) { alert("Please enter the printer password."); return; } const clientContainer = document.getElementById("printerClientContainer"); const listContainer = document.getElementById("printerListContainer"); const dropdown = document.getElementById("printerClientDropdown"); const checkboxContainer = document.getElementById("printerCheckboxContainer"); if (dropdown) { dropdown.innerHTML = ''; } if (checkboxContainer) { checkboxContainer.innerHTML = ""; } if (clientContainer) clientContainer.style.display = "none"; if (listContainer) listContainer.style.display = "none"; try { const resp = await fetch("/getprinters", { method: "GET", headers: { SAMYPW: 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 for this password."); return; } // Build unique sorted ClientCode list const codes = [...new Set(allPrinters.map((p) => p.ClientCode))].sort(); 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 = ''; } alert("Failed to fetch printers. Check password and try again."); } } 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"; //Install-Checkbox const cb = document.createElement("input"); cb.type = "checkbox"; cb.id = id; // stash all fields we might need later 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"; // Line 1: install checkbox + printer label label.appendChild(cb); label.appendChild(document.createTextNode(" ")); label.appendChild( document.createTextNode(`${nameText} (${locText})`) ); // Line 2: radio for "Make default" 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; // associate default choice with this checkbox/printer 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; } // See which radio is checked for "Make default" 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, // Only the printer whose checkbox id matches the selected radio gets SetAsDefault=true 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); alert( `Requested installation for ${selected.length} printer(s).` + (result && result.Message ? "\n" + result.Message : "") ); } 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 { // Figure out which standard tasks are checked const checkedTasks = tasks.filter((t) => { const cb = document.getElementById(t.id); return cb && cb.checked; }); // Rename checkbox / textbox const renameCB = document.getElementById("chkRenameComputer"); const newNameInput = document.getElementById("txtNewComputerName"); // Count how many "extra" tasks (rename) we're doing let extraTasks = 0; if (renameCB && renameCB.checked) { extraTasks = 1; // treat rename as one task in the progress counter } setTotalTaskCount(checkedTasks.length + extraTasks); // 1. DattoRMM first const dattoCB = document.getElementById("installDattoRMM"); 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 const svsCB = document.getElementById("installSVSMSPModule"); 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"].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(); // Same basic rules you'll enforce server-side 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." ); // still mark it as a failed task so progress reaches 100% 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; if (totalTasks > 0) { console.info( `[Info] All tasks completed (${completedTasks}/${totalTasks})` ); } // 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; container.style.display = checked ? "block" : "none"; container .querySelectorAll('input[type="checkbox"]') .forEach((cb) => (cb.checked = checked)); 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(); }); // NEW: Rename computer checkbox -> show/hide text box const renameCheckbox = document.getElementById("chkRenameComputer"); const renameBlock = document.getElementById("renameComputerBlock"); if (renameCheckbox && renameBlock) { function updateRenameVisibility() { renameBlock.style.display = renameCheckbox.checked ? "block" : "none"; } renameCheckbox.addEventListener("change", updateRenameVisibility); updateRenameVisibility(); } // 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 }); });