Edmentum Skip Through Tutorials

Automatically progresses through Edmentum tutorials, waiting for each section to finish loading

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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();