Greasy Fork is available in English.

Education Perfect Hack (Language)

Gets answers automatically if in language list mode

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Education Perfect Hack (Language)
// @version      1.3
// @match        https://app.educationperfect.com/*
// @run-at       document-start
// @description  Gets answers automatically if in language list mode
// @author       @blastedmeteor44
// @grant        none
// @license MIT
// @namespace https://greasyfork.org/users/1232048
// ==/UserScript==

function copyToClipboard(textToCopy) {
  navigator.clipboard.writeText(textToCopy).then(() => {
    // Optional: Add user feedback (e.g., a "Copied!" message)
    console.log("Text successfully copied to clipboard");
  }).catch(err => {
    // Handle errors (e.g., permissions issues)
    console.error("Could not copy text: ", err);
  });
}


let consoleState = {
    html: "",
    scroll: 0,
    top: "100px",
    left: "100px"
};

function startAntiSnitch() {
    // Block the specified URLs
    const blockedUrls = [
        'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.AppServicesPortal.SubmitTaskMonitoringStatus',
        'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.UserProfileFactsPortal.GetValues',
        'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.AppServicesPortal.GetTaskMonitoringStatus',
        'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.UserProfileFactsPortal.SetValues'
    ];

    // Intercept and block the requests to the specified URLs
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        for (const url of blockedUrls) {
            if (arguments[1].includes(url)) {
                console.log(`Blocked request to: ${arguments[1]}`);
                return; // Cancel the request
            }
        }
        originalOpen.apply(this, arguments);
    };

    // Function to remove the focus tracking popup element
    function removeFocusTrackingPopup() {
        const popup = document.querySelector('ep-focus-tracking-popup');
        if (popup) {
            popup.remove();
            console.log('Focus tracking popup removed');
            document.title = "EP"; // stop the popup shiiiii
        }
    }

    // Create a MutationObserver to watch for changes in the DOM
    const observer = new MutationObserver(() => {
        removeFocusTrackingPopup();
    });

    // Start observing the document for changes
    observer.observe(document, { childList: true, subtree: true });

    // Initial removal of the popup if it's already present
    removeFocusTrackingPopup();
}

function pressKey(key) {
    const eventOptions = {
        key: key,
        code: key,
        keyCode: key.charCodeAt(0),
        which: key.charCodeAt(0),
        bubbles: true
    };

    document.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
    document.dispatchEvent(new KeyboardEvent('keypress', eventOptions));
    document.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
}

function createConsole() {
    if (document.getElementById('answer-console')) return;

    const box = document.createElement('div');
    box.id = 'answer-console';
    box.innerHTML = `
        <div id="console-header">Answer Console</div>
        <div id="console-body"></div>
    `;

    Object.assign(box.style, {
        position: 'fixed',
        top: '100px',
        left: '100px',
        width: '300px',
        maxHeight: '400px',
        background: '#111',
        color: '#0f0',
        fontFamily: 'monospace',
        fontSize: '14px',
        border: '2px solid #0f0',
        borderRadius: '8px',
        zIndex: '999999',
        overflow: 'hidden'
    });

    document.body.appendChild(box);

    const header = box.querySelector('#console-header');
    const body = box.querySelector('#console-body');

    Object.assign(header.style, {
        padding: '8px',
        background: '#222',
        cursor: 'move',
        fontWeight: 'bold'
    });

    Object.assign(body.style, {
        padding: '8px',
        overflowY: 'auto',
        maxHeight: '150px'
    });

    // Drag logic
    let offsetX = 0, offsetY = 0, isDragging = false;

    header.addEventListener('mousedown', (e) => {
        isDragging = true;
        offsetX = e.clientX - box.offsetLeft;
        offsetY = e.clientY - box.offsetTop;
    });

    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        box.style.left = (e.clientX - offsetX) + 'px';
        box.style.top = (e.clientY - offsetY) + 'px';
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
    });
    body.innerHTML = consoleState.html;
    body.scrollTop = consoleState.scroll;
    box.style.top = consoleState.top;
    box.style.left = consoleState.left;
}

function destroyConsole() {
    const box = document.getElementById('answer-console');
    const body = document.getElementById('console-body');

    if (!box || !body) return;

    consoleState.html = body.innerHTML;
    consoleState.scroll = body.scrollTop;
    consoleState.top = box.style.top;
    consoleState.left = box.style.left;

    box.remove();
}
function logToConsole(message) {
    const body = document.getElementById('console-body');
    if (!body) return;

    const line = document.createElement('div');
    line.textContent = message;
    body.appendChild(line);
    body.scrollTop = body.scrollHeight;
}

(function () {
    'use strict';

    let vocabularyMap = {};

    startAntiSnitch();

    function extractList() {
        const items = document.querySelectorAll('.preview-grid .stats-item');

        if (!items.length) {
            console.log("No items found.");
            return;
        }

        const results = [];

        items.forEach(item => {
            const target = item.querySelector('.targetLanguage');
            const base = item.querySelector('.baseLanguage');

            if (target && base) {
                const targetText = target.childNodes[0]?.textContent.trim();
                const baseText = base.textContent.trim();

                if (targetText && baseText) {
                    results.push({ indonesian: targetText, english: baseText });

                    // Store both directions for matching
                    vocabularyMap[targetText] = baseText;
                    vocabularyMap[baseText] = targetText;
                }
            }
        });

        results.sort((a, b) => a.indonesian.localeCompare(b.indonesian, 'id'));

        console.log("=== Extracted Vocabulary List (Alphabetical) ===");
        results.forEach((item, index) => {
            console.log(`${index + 1}. ${item.indonesian} — ${item.english}`);
        });
        console.log("=== End of List ===");
    }

    function startQuestionLoop() {
        createConsole(); // create our on-screen console

        console.log("Starting question watcher...");
        logToConsole("Starting question watcher...");

        let lastQuestion = "";
        let past = false // the check for lastQuestion

        setInterval(() => {
            const box = document.getElementById('answer-console'); //kill if there is no box
            if(!box) return;

            const el = document.querySelector('#question-text');
            if (!el) return;

            const questionText = el.textContent.trim();
            if (!questionText || questionText === lastQuestion) {
                past = true
            }

            lastQuestion = questionText;

            const answer = vocabularyMap[questionText];

            if(!past) {
                if (answer) {
                    console.log("Answer:", answer);
                    logToConsole(answer);
                } else {
                    console.log("No match found for:", questionText);
                    logToConsole("No match found for: " + questionText);
                }
            }
            past = false

            copyToClipboard(answer)

        }, 500);
    }

    function waitForGrid() {
        const observer = new MutationObserver(() => {
            const grid = document.querySelector('.preview-grid');
            const hasItems = grid && grid.querySelector('.stats-item');

            if (hasItems) {
                observer.disconnect();
                console.log("Grid loaded.");

                extractList();
                startQuestionLoop();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    waitForGrid();

    document.addEventListener('keydown', function(event) {
        if (event.ctrlKey && event.key == 'k') {
            const box = document.getElementById('answer-console'); //toggle console
            if (box) {
                destroyConsole();
            } else {
                createConsole();
            }
        }
        if(event.key == "Enter") {
            copyToClipboard("");
            console.log("copied nothin to keyboard");
        }

    });

})();