KhanHack

Khan Academy Problem Solver

Version au 17/09/2024. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Advertisement:

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

Advertisement:

// ==UserScript==
// @name         KhanHack
// @namespace    https://greasyfork.org/users/783447
// @version      5.0
// @description  Khan Academy Problem Solver
// @author       Logzilla6 - IlyTobias
// @match        https://*.khanacademy.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=khanacademy.org
// ==/UserScript==
 
let mainMenu = document.createElement('div');
mainMenu.id = 'mainMenu';
mainMenu.style.position = 'fixed';
mainMenu.style.bottom = '.5vw';
mainMenu.style.left = '19vw';
mainMenu.style.width = '300px';
mainMenu.style.height = '400px';
mainMenu.style.backgroundColor = '#123576';
mainMenu.style.border = '3px solid #07152e';
mainMenu.style.borderRadius = '20px';
mainMenu.style.padding = '10px';
mainMenu.style.color = "white";
mainMenu.style.fontFamily = "Noto sans";
mainMenu.style.fontWeight = "500";
mainMenu.style.transition = "all 0.3s ease";
 
let answerBlocks = [];
let currentCombinedAnswer = '';
 
const setMainMenuContent = () => {
    mainMenu.innerHTML =`
        <div id="menuContent" style="display: flex; flex-direction: column; align-items: center; position: relative; gap: 10px; opacity: 1; transition: opacity 0.5s ease;">
            <head>
                <img id="discordIcon" src="https://i.ibb.co/grF973h/discord.png" alt="Discord" style="position: absolute; left: 7px; top: 8px; width: 24px; height: 24px; opacity: 1; transition: opacity 0.5s ease; cursor: pointer;" />
                <img id="headerImage" src="https://i.ibb.co/pX592fL/khanhack.png" style="width: 170px; opacity: 1; transition: opacity 0.5s ease;" />
                <img id="gearIcon" src="https://i.ibb.co/q0QVKGG/gearicon.png" alt="Settings" style="position: absolute; right: 7px; top: 7px; width: 24px; height: 24px; opacity: 1; transition: opacity 0.5s ease; cursor: pointer;" />
            </head>
            <div id="answerList" style="width: 100%; display: flex; flex-direction: column; gap: 10px;"></div>
        </div>
 
        <img id="toggleButton" src="https://i.ibb.co/RpqPcR1/hamburger.png" style="width: 20px; height: 20px; left: 0; right: 0;" class="toggleButton">
        <img id="clearButton" src="https://i.ibb.co/VYs1BPQ/can.png" style="width: 20px; height: 20px; bottom: 10px; right: 8px; position: absolute; cursor: pointer;" class="clearButton">
 
 
 
        <style>
            @import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap');
 
            .toggleButton {
                position: absolute;
                bottom: 3px;
                left: 2px;
                background-color: #123576;
                color: white;
                border: none;
                padding: 5px 7px;
                border-radius: 15px;
                cursor: pointer;
                font-size: 15px;
                font-family: "Noto Sans";
                font-weight: 500;
            }
 
            .block {
                width: 93.5%;
                height: 40px;
                background-color: #f0f0f0;
                padding: 10px;
                border-radius: 10px;
                opacity: 1;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: opacity 0.5s ease;
            }
 
            .answer {
                margin: 0;
                text-align: center;
                color: black;
                font-family: "Noto Sans";
                font-weight: 500;
            }
 
            .imgBlock img {
                width: 250px;
                border-radius: 10px;
            }
        </style>
    `
    addToggle();
    addSettings();
    addDiscord();
    addClear();
 
    const answerList = document.getElementById('answerList');
    answerList.innerHTML = '';
 
    answerBlocks.forEach(block => {
        if (block.type === 'text') {
            addAnswerBlock(block.content, false);
        } else if (block.type === 'image') {
            addImgAnswerBlock(block.content, false);
        }
    });
};
 
const addAnswerBlock = (answer, save = true) => {
    const answerList = document.getElementById('answerList');
    const block = document.createElement('div');
    block.className = 'block';
 
    const p = document.createElement('p');
    p.className = 'answer';
    p.innerText = `Answer: ${answer}`;
 
    block.appendChild(p);
    answerList.appendChild(block);
 
    if (save) {
        answerBlocks.push({ type: 'text', content: answer });
    }
};
 
const addImgAnswerBlock = (imgSrc, save = true) => {
    const answerList = document.getElementById('answerList');
    const block = document.createElement('div');
    block.className = 'block imgBlock';
 
    const img = document.createElement('img');
    img.src = imgSrc;
 
    block.appendChild(img);
    answerList.appendChild(block);
 
    if (save) {
        answerBlocks.push({ type: 'image', content: imgSrc });
    }
};
 
let isMenuVisible = true;
const addToggle = () => {
    document.getElementById('toggleButton').addEventListener('click', function() {
        const clearButton = document.getElementById('clearButton');
        if (isMenuVisible) {
            mainMenu.style.height = '15px';
            mainMenu.style.width = '15px';
            document.getElementById('menuContent').style.opacity = '0';
            clearButton.style.opacity = '0';
            setTimeout(() => {
                document.getElementById('menuContent').style.display = 'none';
                clearButton.style.display = 'none';
            }, 50);
        } else {
            mainMenu.style.height = '400px';
            mainMenu.style.width = '300px';
            document.getElementById('menuContent').style.display = 'flex';
            clearButton.style.display = 'block';
            setTimeout(() => {
                document.getElementById('menuContent').style.opacity = '1';
                clearButton.style.opacity = '1';
            }, 100);
        }
        isMenuVisible = !isMenuVisible;
    });
};
 
 
const addSettings = () => {
    document.getElementById('gearIcon').addEventListener('click', function() {
        mainMenu.innerHTML = `
            <div id="settingsContent" style="display: flex; flex-direction: column; align-items: center; position: relative; opacity: 1; transition: opacity 0.5s ease;">
                <img id="backArrow" src="https://i.ibb.co/Jt4qrD7/pngwing-com-1.png" alt="Back" style="position: absolute; left: 7px; top: 3px; width: 24px; height: 24px; opacity: 1; transition: opacity 0.5s ease; cursor: pointer;" />
 
                <h3 style="margin: 0; text-align: center; color: white; font-family: Noto sans; font-weight: 500;">Settings Menu</h3>
                <p style="text-align: center; color: white; font-family: Noto sans; margin-top: 15px;">Ghost Mode: (Coming Soon)</p>
                <p style="text-align: center; color: white; font-family: Noto sans; margin-top: 15px;">Auto Answer: (Coming Soon)</p>
                <p style="text-align: center; color: white; font-family: Noto sans; margin-top: 15px;">Point Farmer: (Coming Soon)</p>
 
                <p style="text-align: center; color: white; font-family: Noto sans; margin-top: 180px;">KhanHack™</p>
 
                <style>
                    @import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap');
 
                </style>
            </div>
        `
        document.getElementById('backArrow').addEventListener('click', setMainMenuContent);
    });
};
 
const addDiscord = () => {
    document.getElementById('discordIcon').addEventListener('click', function() {
        window.open('https://discord.gg/khanhack', '_blank');
    });
};
 
const addClear = () => {
    document.getElementById('clearButton').addEventListener('click', function() {
        location.reload()
    });
};
 
document.body.appendChild(mainMenu);
setMainMenuContent();
 
let originalJson = JSON.parse;
JSON.parse = function (jsonString) {
    let parsedData = originalJson(jsonString);
    try {
        let itemData = JSON.parse(parsedData.data.assessmentItem.item.itemData);
 
        for (let widgetKey in itemData.question.widgets) {
            let widget = itemData.question.widgets[widgetKey];
            switch (widget.type) {
                case "numeric-input":
                    handleNumeric(widget);
                    break;
                case "radio":
                    handleRadio(widget);
                    break;
                case "expression":
                    handleExpression(widget);
                    break;
                case "dropdown":
                    handleDropdown(widget);
                    break;
                default:
                    console.log("Unknown widget: " + widget.type);
                    break;
            }
        }
        if (currentCombinedAnswer.trim() !== '') {
            addAnswerBlock(currentCombinedAnswer.trim());
            currentCombinedAnswer = '';
        }
 
    } catch (error) {
 
    }
    return parsedData;
};
 
 
function cleanLatexExpression(answer) {
    return answer
        .replace(/\\times/g, '×')
        .replace(/\\frac{([^}]*)}{([^}]*)}/g, '($1/$2)')
        .replace(/\\cdot/g, '⋅')
        .replace(/\\left|\\right/g, '')
        .replace(/[\$]/g, '')
        .replace(/\^/g, '^')
        .replace(/\\(?:[a-zA-Z]+)/g, '')
        .replace(/(?<!\\){|}/g, '');
}
 
function handleRadio(widget) {
    let content = widget.options.choices.filter(item => item.correct === true).map(item => item.content);
    let combinedAnswers = '';
    let hasImage = false;
    let imageUrl = null;
 
    content.forEach(answer => {
        const regex = answer.match(/:\/\/(.*?)\)/);
 
        if (regex) {
            const finalImg = "https" + regex[0].slice(0, -1) + ".svg";
            hasImage = true;
            imageUrl = finalImg;
        } else {
            const cleanedAnswer = cleanLatexExpression(answer);
            combinedAnswers += cleanedAnswer + ', ';
        }
    });
    combinedAnswers = combinedAnswers.trim().replace(/,$/, '');
 
    if (combinedAnswers !== '') {
        currentCombinedAnswer += `${combinedAnswers}` ;
    }
 
    if (hasImage && imageUrl) {
        addImgAnswerBlock(imageUrl);
    }
}
 
function handleNumeric(widget) {
    const numericAnswer = widget.options.answers[0].value;
    currentCombinedAnswer += `${numericAnswer}, `;
}
 
function handleExpression(widget) {
    let expressionAnswer = widget.options.answerForms[0].value;
    currentCombinedAnswer += `${expressionAnswer}` ;
}
 
function handleDropdown(widget) {
    let content = widget.options.choices.filter(item => item.correct === true).map(item => item.content);
    currentCombinedAnswer += `${content[0]},` ;
}