Automatically progresses through Edmentum tutorials, waiting for each section to finish loading
// ==UserScript==
// @name Edmentum Skip Through Tutorials
// @version 1.0.2
// @description Automatically progresses through Edmentum tutorials, waiting for each section to finish loading
// @author j01t3d
// @match https://*.edmentum.com/courseware-delivery/*
// @namespace https://github.com/j01t3d/edmentum-tutorial
// @license MIT
// ==/UserScript==
const MAX_ATTEMPTS = 10;
const RETRY_DELAY = 500;
const CLICK_DELAY = 500;
const POLL_INTERVAL = 300;
const JUMP_TIMEOUT = 3000; // fall back to sequential time
function enableButtons(sections) {
for (let child of sections.children) {
let button = child.querySelector("button");
if (!button || button.className.includes("toc-current")) continue;
button.className = "toc-section toc-visited";
button.removeAttribute("disabled");
console.log("[Edmentum Skip Through Tutorials]: Unlocked sect.", button.textContent.trim());
}
}
function waitForSectionComplete(button, callback) {
const checkCompletion = setInterval(() => {
if (button.className.includes("toc-visited")) {
clearInterval(checkCompletion);
console.log("[Edmentum Skip Through Tutorials]: Section completed", button.textContent.trim());
callback();
}
}, POLL_INTERVAL);
}
function clickSectionsSequentially(sections) {
let i = 0;
function clickNext() {
if (i >= sections.children.length) return;
let button = sections.children[i].querySelector("button");
i++;
if (button && !button.className.includes("toc-current")) {
button.click();
console.log("[Edmentum Skip Through Tutorials]: Clicked sect.", button.textContent.trim());
waitForSectionComplete(button, () => {
setTimeout(clickNext, CLICK_DELAY);
});
} else {
setTimeout(clickNext, CLICK_DELAY);
}
}
clickNext();
}
function tryDirectJump(sections) {
const lastSection = sections.children[sections.children.length - 1];
if (!lastSection) {
console.log("[Edmentum Skip Through Tutorials]: No sections found, falling back to sequential");
clickSectionsSequentially(sections);
return;
}
const lastButton = lastSection.querySelector("button");
if (!lastButton) {
console.log("[Edmentum Skip Through Tutorials]: No button in last section, falling back to sequential");
clickSectionsSequentially(sections);
return;
}
console.log("[Edmentum Skip Through Tutorials]: Attempting direct jump to:", lastButton.textContent.trim());
lastButton.click();
let jumpSucceeded = false;
const checkJump = setInterval(() => {
if (lastButton.className.includes("toc-current") || lastButton.className.includes("toc-visited")) {
jumpSucceeded = true;
clearInterval(checkJump);
console.log("[Edmentum Skip Through Tutorials]: Direct jump succeeded!");
let allVisited = true;
for (let child of sections.children) {
let button = child.querySelector("button");
if (button && !button.className.includes("toc-visited")) {
allVisited = false;
break;
}
}
if (!allVisited) {
console.log("[Edmentum Skip Through Tutorials]: Some sections not visited, falling back to sequential");
clickSectionsSequentially(sections);
}
}
}, POLL_INTERVAL);
setTimeout(() => {
clearInterval(checkJump);
if (!jumpSucceeded) {
console.log("[Edmentum Skip Through Tutorials]: Direct jump failed, falling back to sequential");
clickSectionsSequentially(sections);
}
}, JUMP_TIMEOUT);
}
function findSections(attempt = 1) {
const sections = document.querySelector(".tutorial-toc-sections");
if (!sections) {
if (attempt >= MAX_ATTEMPTS) {
console.log("[Edmentum Skip Through Tutorials]: Failed to locate sect. after " + attempt + " attempts.");
return;
}
setTimeout(() => findSections(attempt + 1), RETRY_DELAY);
} else {
enableButtons(sections);
tryDirectJump(sections);
}
}
findSections();