Skip to content

Instantly share code, notes, and snippets.

@bagheera02
Created February 14, 2025 15:21

Revisions

  1. bagheera02 created this gist Feb 14, 2025.
    169 changes: 169 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,169 @@
    // ==UserScript==
    // @name Blueprint Exam Review – Blind Mode
    // @namespace http://tampermonkey.net/
    // @version 1.0
    // @description Hide answer feedback on Blueprint exam explanation pages for blind review; improved toggle behavior to reliably hide/reveal answers.
    // @match https://mcat.blueprintprep.com/exam_explanation/*
    // @grant none
    // @run-at document-idle
    // ==/UserScript==

    (function() {
    'use strict';

    const blindModeClass = 'bp-blind-mode';
    let observer = null;
    let enforcementIntervalId = null;

    // Utility for debugging
    function log(message) {
    console.log("[BP Blind Mode] " + message);
    }

    // Inject CSS rules that hide answer feedback elements when blind mode is active.
    function injectStyles() {
    const styleContent = `
    /* Hide answer feedback elements when blind mode is active */
    .${blindModeClass} [class*="radio__option--tooltip"],
    .${blindModeClass} [class*="radio__option--explanation"],
    .${blindModeClass} [class*="radio__option_icon"],
    .${blindModeClass} [class*="radio__option--change-icon"],
    .${blindModeClass} [class*="radio__option--changed-icon"],
    .${blindModeClass} [class*="radio__option--show-explanation"],
    .${blindModeClass} .answer__graph--container,
    .${blindModeClass} .question__review--information {
    display: none !important;
    }
    `;
    let styleEl = document.getElementById('bp-blind-style');
    if (!styleEl) {
    styleEl = document.createElement('style');
    styleEl.id = 'bp-blind-style';
    styleEl.type = 'text/css';
    styleEl.textContent = styleContent;
    document.head.appendChild(styleEl);
    log("Injected blind mode CSS.");
    }
    }

    // Create and add a floating toggle button.
    function addToggleButton() {
    if (document.getElementById('bp-blind-toggle-button')) return; // only add once
    const toggleButton = document.createElement('button');
    toggleButton.id = 'bp-blind-toggle-button';
    Object.assign(toggleButton.style, {
    position: 'fixed',
    top: '10px',
    right: '10px',
    zIndex: '10000',
    padding: '10px 15px',
    backgroundColor: '#007bff',
    color: '#fff',
    border: 'none',
    borderRadius: '4px',
    cursor: 'pointer',
    fontSize: '14px'
    });
    // Default state: blind mode enabled (answers hidden), so button reads "Reveal Answers"
    toggleButton.textContent = 'Reveal Answers';
    toggleButton.addEventListener('click', function() {
    if (document.body.classList.contains(blindModeClass)) {
    // User toggles to reveal answers: remove blind mode and stop auto‑enforcement.
    document.body.classList.remove(blindModeClass);
    toggleButton.textContent = 'Blind Answers';
    log("Blind mode disabled: answer feedback revealed by user.");
    stopAutoEnforcement();
    } else {
    // User toggles to hide answers: add blind mode and restart auto‑enforcement.
    document.body.classList.add(blindModeClass);
    toggleButton.textContent = 'Reveal Answers';
    log("Blind mode enabled: answer feedback hidden by user.");
    startAutoEnforcement();
    }
    });
    document.body.appendChild(toggleButton);
    log("Toggle button added.");
    }

    // Start the MutationObserver to monitor for new review content.
    function startMutationObserver() {
    if (observer) return; // already active
    observer = new MutationObserver((mutations) => {
    mutations.forEach(mutation => {
    mutation.addedNodes.forEach(node => {
    if (node.nodeType === Node.ELEMENT_NODE) {
    if (node.matches('.question__review--container') ||
    node.querySelector('.question__review--container')) {
    // If new review content is added while blind mode is on, ensure the class remains.
    if (!document.body.classList.contains(blindModeClass)) {
    document.body.classList.add(blindModeClass);
    log("MutationObserver: Reapplied blind mode due to new review content.");
    }
    }
    }
    });
    });
    });
    observer.observe(document.body, { childList: true, subtree: true });
    log("MutationObserver started.");
    }

    // Stop the MutationObserver.
    function stopMutationObserver() {
    if (observer) {
    observer.disconnect();
    observer = null;
    log("MutationObserver stopped.");
    }
    }

    // Start the interval check to enforce blind mode on dynamic content.
    function startIntervalCheck() {
    if (enforcementIntervalId) return; // already active
    enforcementIntervalId = setInterval(() => {
    const reviewContainer = document.querySelector('.question__review--container');
    if (reviewContainer && !document.body.classList.contains(blindModeClass)) {
    document.body.classList.add(blindModeClass);
    log("Interval check: added blind mode class to body.");
    }
    }, 1000);
    log("Interval check started.");
    }

    // Stop the interval check.
    function stopIntervalCheck() {
    if (enforcementIntervalId) {
    clearInterval(enforcementIntervalId);
    enforcementIntervalId = null;
    log("Interval check stopped.");
    }
    }

    // Start both auto‑enforcement mechanisms.
    function startAutoEnforcement() {
    startMutationObserver();
    startIntervalCheck();
    }

    // Stop both auto‑enforcement mechanisms.
    function stopAutoEnforcement() {
    stopMutationObserver();
    stopIntervalCheck();
    }

    // Initialization function.
    function init() {
    injectStyles();
    // By default, enable blind mode.
    document.body.classList.add(blindModeClass);
    addToggleButton();
    startAutoEnforcement();
    log("Blueprint Exam Review Blind Mode script initialized.");
    }

    if (document.readyState === "complete" || document.readyState === "interactive") {
    init();
    } else {
    document.addEventListener("DOMContentLoaded", init);
    }
    })();