Greasy Fork is available in English.

Copy Amboss Questions to ChatGPT for Step 1

Add copy buttons to amboss.com to copy to ChatGPT

// ==UserScript==
// @name         Copy Amboss Questions to ChatGPT for Step 1
// @namespace    http://tampermonkey.net/
// @version      1.0.0.8
// @description  Add copy buttons to amboss.com to copy to ChatGPT
// @author       PinballDestiny
// @match        https://*.amboss.com/*
// @grant        none
// @require  https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
// ==/UserScript==

const loadCSS = (href) => {
    let link = document.createElement("link");
    link.href = href;
    link.rel = "stylesheet";
    document.head.appendChild(link);
};

loadCSS("https://fonts.googleapis.com/icon?family=Material+Icons");
loadCSS("https://fonts.googleapis.com/icon?family=Material+Icons+Outlined");
loadCSS("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");




(function () {
    "use strict";


    // Common helper functions
    const utils = {
        createStyle: function (cssText) {
            const styleElement = document.createElement("style");
            styleElement.type = "text/css";
            styleElement.appendChild(document.createTextNode(cssText.replace(/\n/g, "")));
            return styleElement;
        },
    };

    const tooltipStyle = utils.createStyle(`
        .fixed-tooltip { position: absolute; bottom: 20px; right: calc(100% + 2em); transform: translateX(-100%); background: #333; color: #fff; padding: 5px 10px; border-radius: 4px; opacity: 0; transition: opacity 0.3s; pointer-events: none; font-family: 'Open Sans', sans-serif; z-index: 9999; }
        .fixed-tooltip.visible {opacity: 1;}
        #explanationTooltipClass {margin-left:-20px!important;}
        #tooltipAltC { margin-left:-25px!important; top: 0px!important; z-index: 9999 !important; opacity: 1 !important; background-color: #2A2B32 !important; pointer-events: auto !important; position: relative; } #tooltipAltC::before, #tooltipAltC::after { z-index: 9998 !important; opacity: 1 !important; } #tooltipAltC * { opacity: 1 !important; }
        #tooltipAltG { margin-left:-25px!important; top: 0px!important; z-index: 9999 !important; opacity: 1 !important; background-color: #2A2B32 !important; pointer-events: auto !important; position: relative; } #tooltipAltC::before, #tooltipAltC::after { z-index: 9998 !important; opacity: 1 !important; } #tooltipAltC * { opacity: 1 !important; }
    `);
    document.head.appendChild(tooltipStyle);

    function addTooltip(button, tooltipText, left, top, id) {
        const tooltip = document.createElement("div");
        tooltip.innerText = tooltipText;
        tooltip.id = id;
        tooltip.style.cssText = "all: unset; position: absolute; top: " + top + "; left: " + left + "px; transform: translateX(-100%) translateY(-5px); background-color: #2A2B32; color: #ECECF1; border-radius: 5px; padding: 5px; font-size: 12px; text-align: center; white-space: nowrap; visibility: hidden; opacity: 0; transition: opacity 0.3s; font-family: 'Open Sans', sans-serif; z-index: 9999;";
        button.appendChild(tooltip);

        button.addEventListener("mouseenter", () => {
            tooltip.style.visibility = "visible";
            tooltip.style.opacity = "1";
        });
        button.addEventListener("mouseleave", () => {
            tooltip.style.visibility = "hidden";
            tooltip.style.opacity = "0";
        });
    }



    const removeToolTips = (element) => {
        let tooltips = element.querySelectorAll("div");
        tooltips.forEach((tooltip) => tooltip.remove());
    };

    const getPlainText = (element, removeLinks = false) => {
        let clone = element.cloneNode(true);
        let anchors = clone.querySelectorAll("a");
        anchors.forEach((a) => {
            const textNode = document.createTextNode(a.textContent);
            a.parentNode.replaceChild(textNode, a);
        });
        if (removeLinks) {
            let styles = clone.querySelectorAll("style");
            styles.forEach((style) => style.remove());
        }
        removeToolTips(clone);
        return clone.innerText;
    };

    const isVisible = (element) => {
        return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
    };

    const showNotification = (message) => {
        const notification = document.createElement("div");
        notification.textContent = message;
        notification.style.cssText =
            "position: fixed; bottom: 10px; right: 10px; background-color: rgba(0, 0, 0, 0.8); color: white; padding: 10px; border-radius: 5px;";
        document.body.appendChild(notification);
        setTimeout(() => notification.remove(), 1500);
    };

    // Function for Script 1
    const addButtonToElements = () => {
        const elements = document.querySelectorAll('[class^="explanationContent--"]');

        elements.forEach((element) => {
            if (!element.querySelector(".copy-button")) {
                const button = document.createElement("div");
                button.style.cssText =
                    "width: 100%; text-align: right; height: 32px; margin-left: 0px; margin-top: 8px; border-radius: 50%; display: block; opacity: 1; transition: opacity 0.3s; font-size: 16px; cursor: pointer;";
                button.innerHTML = "copy_all";
                button.className = "material-icons-outlined copy-button";

                button.addEventListener("click", async () => {
                    try {
                        await navigator.clipboard.writeText(
                            getPlainText(element, false, true).replace(/\s*copy_all\s*/g, "")
                        );
                        showNotification("Explanation copied!");
                    } catch (err) {
                        console.error("Failed to copy text: ", err);
                    }
                });

                element.appendChild(button);
                addTooltip(button, "Copy this item's explanation.", null, null, "explanationTooltipClass");
            }
        });
    };

    // Functions for Script 2
    // Functions for Script 2
    const extractContent = () => {
        // Select elements with class containing questionContent, answerContent, and explanationContent
        const questionContent = document.querySelector('[class*="questionContent"]');
        const answerContents = document.querySelectorAll('[class*="answerContent"]');
        const explanationContents = document.querySelectorAll('[class*="explanationContent"]');

        let combinedText = "";

        // Add question content
        if (questionContent) {
            combinedText += getPlainText(questionContent, true, true).trim() + "\n\n";
        }

        // Add answer content
        answerContents.forEach((answerContent) => {
            let answerContentText = getPlainText(answerContent, true, true).trim();
            if (answerContentText) {
                combinedText += answerContentText + "\n\n";
            }
        });

        // Add explanation content
        explanationContents.forEach((explanationContent) => {
            if (isVisible(explanationContent)) {
                combinedText += getPlainText(explanationContent, true, true).replace(/\s*copy_all\s*/g, "").trim() + "\n\n";
            }
        });

        combinedText = combinedText.replace(/\n{3,}/g, "\n\n");

        return combinedText.trim(); // Trim any trailing newlines
    };



    const addButton = () => {
        const questionContent = document.querySelector('[class^="questionContent--"]');

        if (questionContent && !questionContent.querySelector(".copy-button")) {
            const button = document.createElement("div");
            button.style.cssText =
                "width: 16px; height: 16px; border-radius: 50%; background-color: rgba(0, 0, 0, 0); position: absolute; bottom: 0; right: 0; cursor: pointer;";
            button.classList.add("copy-button");

            button.addEventListener("click", async () => {
                try {
                    await navigator.clipboard.writeText(
                        extractContent().replace(/\s*copy_all\s*/g, "")
                    );
                    showNotification("Single Explanation copied!");
                } catch (err) {
                    console.error("Failed to copy text: ", err);
                }
            });

            questionContent.style.position = "relative";
            questionContent.appendChild(button);
            addTooltip(button, "Copy Explanation");
        }
    };

    // Keyboard event for Alt + C
    document.addEventListener("keydown", async function (event) {
        if (event.altKey && event.key === "c") {
            try {
                await navigator.clipboard.writeText(
                    extractContent().replace(/\s*copy_all\s*/g, "")
                );
                showNotification("Question + Expanded Explanations Copied!");
            } catch (err) {
                console.error("Failed to copy text: ", err);
            }
        }
    });

    // Keyboard event for Alt + G
    document.addEventListener("keydown", async function (event) {
        if (event.altKey && event.key === "g") {
            try {
                const content = extractContent()
                .replace(/\s*copy_all\s*/g, "")
                .replace(/Copy Application/gi, "");
                const modifiedContent = content
                .replace(/copy explanation\./gi, "")
                .replace(/Copy Application/gi, "");
                await navigator.clipboard.writeText(preText + modifiedContent);
                showNotification("GPT Prompt and content copied to clipboard!");
            } catch (err) {
                console.error("Failed to copy text: ", err);
            }
        }
    });


    // Function to create a material design icon button
    function createMaterialIconButton(iconText, tooltipText, position, clickHandler, tooltipPosition, tooltipId) {
        const button = document.createElement("span");
        button.innerHTML = iconText;
        button.className = "material-icons-outlined";
        button.style.cssText = `font-size: 16px; cursor: pointer; opacity: 0.6; transition: opacity 0.3s;`;

        button.onmouseover = () => {
            button.style.opacity = "1";
        };
        button.onmouseout = () => {
            button.style.opacity = "0.25";
        };
        button.onclick = clickHandler;

        const buttonContainer = document.createElement("div");
        buttonContainer.style.cssText = `position: fixed; bottom: ${position.bottom}; right: ${position.right}; display: inline-block; opacity: 1; transition: opacity 0.3s; z-index: 9999;`;

        const buttonWrapper = document.createElement("div");
        buttonWrapper.style.cssText = "position: relative; display: inline-block;";

        buttonWrapper.appendChild(button);
        buttonContainer.appendChild(buttonWrapper);
        document.body.appendChild(buttonContainer);

        addTooltip(buttonWrapper, tooltipText, tooltipPosition.left, tooltipPosition.top, tooltipId);

        return button;
    }

    // Fullscreen Toggle
    /**
 * This function retrieves elements starting with a certain class.
 * @param {string} classStart - The class prefix to look for in the document
 * @return {Array} - An array of elements that match the class prefix
 */
    function getElementsByClassStart(classStart) {
        var all = document.querySelectorAll(`[class^="${classStart}"]`);
        return Array.from(all);
    }

    /**
 * This function toggles the visibility of a set of elements.
 * @param {Array} elements - The elements to hide/show
 */
    function toggleVisibility(elements) {
        for (var i = 0; i < elements.length; i++) {
            if (elements[i].style.display !== 'none') {
                elements[i].style.display = 'none';
            } else {
                elements[i].style.display = '';
            }
        }
    }

    /**
 * This function moves a set of elements to the top.
 * @param {Array} elements - The elements to move to top
 */
    function moveToTop(elements) {
        for (var i = 0; i < elements.length; i++) {
            elements[i].style.top = '0';
        }
    }


    document.addEventListener('keydown', function(event) {
        // If the 'F' key is pressed along with the 'Alt' key
        if (event.key === 'h' || event.key === 'H') {
            if (event.altKey) {
                // Call the handler function
                fullscreenButtonClickHandler();
            }
        }
    });
    /**
 * This function is the callback function for the fullscreen button click event.
 * It hides the 'topNav' elements and moves 'contentContainer' and 'topBarOffset' elements to top
 */
    function fullscreenButtonClickHandler() {
        var elementsToHide = getElementsByClassStart('topNav--');
        toggleVisibility(elementsToHide);

        var elementsToMove = getElementsByClassStart('contentContainer--');
        moveToTop(elementsToMove);

        elementsToMove = getElementsByClassStart('topBarOffset--');
        moveToTop(elementsToMove);
    }

    // Create Fullscreen button
    const fullscreenButton = createMaterialIconButton(
        "fullscreen",
        "Hide Header (Alt+H)",
        { bottom: "128px", right: "24px" },
        fullscreenButtonClickHandler,
        { left: "-50%", top: "-100%" },
        "tooltipFullscreen"
    );

    // Fade Fullscreen button after 3500ms
    setTimeout(() => {
        fullscreenButton.style.transition = "opacity 1s";
        fullscreenButton.style.opacity = "0.25";
    }, 3500);



    // Function to create an image button
    function createImageButton(src, tooltipText, position, clickHandler, tooltipPosition, tooltipId) {
        const button = document.createElement("img");
        button.src = src;
        button.style.cssText = `width: 16px; height: 16px; opacity: 0.6; transition: opacity 0.3s;`;

        button.onmouseover = () => {
            button.style.opacity = "1";
        };
        button.onmouseout = () => {
            button.style.opacity = "0.25";
        };
        button.onclick = clickHandler;

        const buttonContainer = document.createElement("div");
        buttonContainer.style.cssText = `position: fixed; bottom: ${position.bottom}; right: ${position.right}; display: inline-block; opacity: 1; transition: opacity 0.3s; z-index: 9999;`;

        const buttonWrapper = document.createElement("div");
        buttonWrapper.style.cssText = "position: relative; display: inline-block;";

        buttonWrapper.appendChild(button);
        buttonContainer.appendChild(buttonWrapper);
        document.body.appendChild(buttonContainer);

        addTooltip(buttonWrapper, tooltipText, tooltipPosition.left, tooltipPosition.top, tooltipId);

        return button;
    }
    const preText = "Respond to this prompt afresh, ignoring previous prompts and responses. Use the example provided between the \"###\" as a guide to format your response. Ensure the hierarchical outline has indents and line breaks and reduces the word count by about 75% while omitting as few details as possible. Follow the example closely to explain how to understand the vignette given after this example to reach the correct answer. Be sure there are no repetitive parts of your answer, and if so, simply omit that repeated part of the response leaving that section brief. Follow the example closely to explain how to understand “Your Vignette” in the text after the \"&&&\" so a medical student can understand how to reach the correct answer.\n###\nOriginal Question: 'A 2-month-old boy is brought to the clinic for irritability, poor feeding, and frequent vomiting. Weight is at the 15th percentile, and head circumference is at the 96th percentile. Lung sounds are clear throughout, and heart auscultation reveals no murmurs. The abdomen is soft and nondistended. There is hepatosplenomegaly. Diffusely reduced muscle tone is present in all extremities. Funduscopy reveals white-yellow chorioretinal lesions in both eyes. MRI of the brain shows enlarged ventricles and scattered intracranial calcifications. Which of the following is the most likely cause of this patient's condition?\nA. Chromosomal abnormality\nB. Intrapartum infection\nC. In utero infection\nD. Postpartum infection\nE. Single gene defect'\n\nCorrect Answer: 'C. In utero infection'\n\nIdeal Response (text enclosed in parenthesis is instruction for you and should not be included in response):\n\nI. Restated Question (all key clues from the vignette should be included in the restated question, and the question should ask the same question that makes sense with the original answer choice):\nWhat is the underlying condition in a 2-month-old boy presenting with irritability, poor feeding, vomiting, hepatosplenomegaly, reduced muscle tone, chorioretinal lesions, enlarged ventricles, and intracranial calcifications?\n\nII. Correct Answer (The following answer is provided exactly as it appears in the original text): C. In utero infection\n\nIII. To reach answer, you must understand (give details of key concept in 40 words or less, avoid repetition from prior sections): \n• Recognize increased head circumference and enlarged ventricles indicate increased intracranial pressure. \n• Chorioretinal lesions, intracranial calcifications, and hepatosplenomegaly are signs of systemic infection. \n• The presentation in a 2-month-old suggests an in utero, rather than postpartum, infection.\n\nIV. Vignette Key Clues (After each clue include 5-12 word explanation of why specifically the disease process causes the symptom or finding; omit obvious or self-evident explanations and do not include this if not needed. Only include key clues used to reach correct answer, use 40 words or less providing unique details of symptoms or findings using the descriptive language (more general) often favored by NBME and USMLE in vignettes, and be sure to avoid repetition from prior sections): \nChorioretinitis: \"Examination of the back of the eye reveals white-yellow lesions in both eyes.\" (toxoplasmosis has propensity for retina → inflammation → white-yellow lesions)\nHydrocephalus: \"Imaging of the brain shows enlarged ventricles.\" (inflammation of brain tissue   →   csf obstruction   →   ventricular enlargement i.e. hydrocephalus)\nIntracranial calcifications: \"Brain imaging reveals scattered dense, white spots within the brain tissue.\" (Chronic inflammation   →  scarring   →   brain calcifications as white spots on imaging)\n\nV. Explanation of Why Clues Point to Answer (Focus only on how the correct answer can be deduced by the reader based on the clues provided in vignette before the explanation section. Omit info that is true of other answer choices as well, and avoid repetition from prior sections.): \n* Congenital toxoplasmosis presents with a classic triad of hydrocephalus, intracranial calcifications, and chorioretinitis.\n* In utero transmission of TORCH infections--Toxoplasmosis, Other infections, Rubella, Cytomegalovirus, and Herpes simplex--leads to chronic CNS inflammation and the development of intracranial calcifications.\n* Obstructive hydrocephalus causes ventriculomegaly, while chronic chorioretinal inflammation produces white-yellow retinal scars.\n* The presence of growth restriction, hepatosplenomegaly, and rash further support the suspicion of in utero infection.\n###\n&&&\nYour Vignette:\n";
    // Create Alt+G button
    const altGButton = createImageButton(
        "https://user-images.githubusercontent.com/57292172/223011977-371c1677-a8f3-4c06-87fb-b774243b0545.svg",
        "Copy with Chat-GPT Prompt (Alt+G)",
        { bottom: "64px", right: "24px" },
        async () => {
            try {
                const content = extractContent().replace(/\s*copy_all\s*/g, "").replace(/Copy Application/gi, "");
                const modifiedContent = content.replace(/copy explanation\./gi, "").replace(/Copy Application/gi, "");
                await navigator.clipboard.writeText(preText + modifiedContent);
                showNotification("GPT Prompt and content copied to clipboard!");
            } catch (err) {
                console.error("Failed to copy text: ", err);
            }
        },
        { left: "-50%", top: "-100%" },
        "tooltipAltG"
    );

    // Fade Alt+G button after 3500ms
    setTimeout(() => {
        altGButton.style.transition = "opacity 1s";
        altGButton.style.opacity = "0.25";
    }, 3500);

    // Create Alt+C button
    // Create Alt+C button
    const altCButton = createMaterialIconButton(
        "copy_all",
        "Copy All Content (Alt+C)",
        { bottom: "96px", right: "24px" },
        async () => {
            try {
                await navigator.clipboard.writeText(extractContent().replace(/\s*copy_all\s*/g, ""));
                showNotification("Question + Visible Explanations Copied!");
            } catch (err) {
                console.error("Failed to copy text: ", err);
            }
        },
        { left: "50%", top: "-100%" },
        "tooltipAltC"
    );


    // Fade Alt+C button after 3500ms
    setTimeout(() => {
        altCButton.style.transition = "opacity 1s";
        altCButton.style.opacity = "0.25";
    }, 3500);

    // Call the functions
    addButtonToElements();
    addButton();

    // Observe the document body for changes
    new MutationObserver(() => {
        addButtonToElements();
        addButton();
    }).observe(document.body, {
        childList: true,
        subtree: true,
    });
})();