Canvas Quiz Answer Identifier (Subtle Shadow + Edge Hover)

Stealthily highlights correct Canvas quiz answers with a faint right-edge shadow on hover only. Won’t steal focus or trigger LMS cursor alerts. API-based & unobtrusive UX by design. Fully self-contained & auto-loaded on quiz pages only.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Canvas Quiz Answer Identifier (Subtle Shadow + Edge Hover)
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Stealthily highlights correct Canvas quiz answers with a faint right-edge shadow on hover only. Won’t steal focus or trigger LMS cursor alerts. API-based & unobtrusive UX by design. Fully self-contained & auto-loaded on quiz pages only.
// @author       You
// @match        *://*.instructure.com/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    window.addEventListener('load', function () {
        const texts = document.getElementsByClassName("text");

        if (!texts.length) return;

        let questions = [];
        let optionsList = [];

        // Step 1: Extract questions and options
        for (let i = 0; i < texts.length; i++) {
            const questionDiv = texts[i].querySelector("div:nth-of-type(2)");
            const questionText = questionDiv?.innerText?.trim() || "";

            const optionLabels = texts[i].getElementsByClassName("answer_label");
            let optionsText = [];
            for (let j = 0; j < optionLabels.length; j++) {
                optionsText.push(optionLabels[j].innerText.trim());
            }

            questions.push(questionText);
            optionsList.push(optionsText);
        }

        // Step 2: Send questions to the API and get answers
        for (let i = 0; i < questions.length; i++) {
            const payload = {
                id: i,
                question: questions[i],
                options: optionsList[i].join(", "),
            };

            fetch("https://canvasquiz-new.uc.r.appspot.com/generate", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(payload),
            })
                .then((res) => {
                    if (!res.ok) throw new Error("Network response error");
                    return res.json();
                })
                .then((data) => {
                    const correctAnswer = data.answer?.trim();
                    if (!correctAnswer) return;

                    const answerLabels = texts[i].getElementsByClassName("answer_label");
                    for (let j = 0; j < answerLabels.length; j++) {
                        const label = answerLabels[j];
                        const labelText = label.innerText.trim();

                        if (labelText === correctAnswer) {
                            label.style.position = "relative";

                            label.addEventListener("mouseenter", () => {
                                let shadowMarker = document.createElement("div");
                                shadowMarker.className = "hover-shadow-marker";
                                shadowMarker.style.position = "absolute";
                                shadowMarker.style.top = "50%";
                                shadowMarker.style.left = "100%"; // outside the label
                                shadowMarker.style.transform = "translateY(-50%) translateX(2px)";
                                shadowMarker.style.width = "8px";
                                shadowMarker.style.height = "20px";
                                shadowMarker.style.boxShadow = "0 0 6px rgba(0, 0, 0, 0.1)";
                                shadowMarker.style.borderRadius = "3px";
                                shadowMarker.style.pointerEvents = "none"; // don't interfere
                                label.appendChild(shadowMarker);
                            });

                            label.addEventListener("mouseleave", () => {
                                const marker = label.querySelector(".hover-shadow-marker");
                                if (marker) marker.remove();
                            });
                        }
                    }
                })
                .catch((err) => {
                    console.warn("Answer API error:", err);
                });
        }
    });
})();