Edio Hotkeys

Adds shortcuts to get across the site faster

目前为 2025-01-21 提交的版本。查看 最新版本

// ==UserScript==
// @name         Edio Hotkeys
// @namespace    http://tampermonkey.net/
// @version      Alpha-v3
// @description  Adds shortcuts to get across the site faster
// @author       Crowned Studios
// @license      CC BY-NC
// @match        https://www.myedio.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_cookieSet
// @grant        GM_cookieDelete
// ==/UserScript==

/*
  _    _       _   _                        _____
 | |  | |     | | | |                     _|  __ \
 | |__| | ___ | |_| | _____ _   _ ___    (_) |  | |
 |  __  |/ _ \| __| |/ / _ \ | | / __|     | |  | |
 | |  | | (_) | |_|   <  __/ |_| \__ \    _| |__| |
 |_|  |_|\___/ \__|_|\_\___|\__, |___/   (_)_____/
                             __/ |
                            |___/
*/


    // === User Configurable Settings ===


// Set this to true to enable console logging, or false to disable it.
const isLoggingEnabled = true;

    // === End Of Configurable Settings ===

(function() {
    'use strict';

    function logMessage(message) {
        if (isLoggingEnabled) {
            console.log(message);
        }
    }

    function saveLinkIfMatches() {
        const link = window.location.href;
        const matchPatterns = [
            "https://www.myedio.com/learning/courses/",
            "/lessons/",
            "/summary/"
        ];

        if (matchPatterns.every(pattern => link.includes(pattern))) {
            try {
                GM_setValue('siteLink', link);
                logMessage(`Link saved using GM_setValue: ${link}`);
            } catch (e) {
                logMessage("Error saving with GM_setValue, falling back to cookies...");
                showErrorPopup(101, e.message);
                document.cookie = `siteLink=${link}; path=/; expires=${new Date(Date.now() + 86400000).toUTCString()}`;
                logMessage("Link saved to cookie: " + link);
            }
        } else {
            logMessage("Link does not match the pattern, not saved.");
        }
    }

    function getSavedLink() {
        return new Promise((resolve, reject) => {
            const gmLink = GM_getValue('siteLink', null);
            if (gmLink) {
                resolve(gmLink);
            } else {
                const cookieMatch = document.cookie.match(/siteLink=([^;]*)/);
                if (cookieMatch) {
                    resolve(cookieMatch[1]);
                } else {
                    showErrorPopup(102, "No site link found, or It was never saved.");
                    reject("No site link found, or It was never saved.");
                }
            }
        });
    }

    async function handleHotkeys(event) {
        if (event.altKey) {
            const key = event.key.toLowerCase();
            switch (key) {
                case 'r':
                    event.preventDefault();
                    try {
                        const savedLink = await getSavedLink();
                        logMessage(`Redirecting to saved link: ${savedLink}`);
                        window.location.href = savedLink;
                    } catch (e) {
                        logMessage(e);
                        showErrorPopup(103, "Error redirecting to saved link.");
                    }
                    break;
                case 'c':
                    event.preventDefault();
                    window.location.href = "https://www.myedio.com/calendar/day/";
                    break;
                case 'q':
                    event.preventDefault();
                    window.location.href = "https://www.myedio.com/login/?sessionExpired=true";
                    break;
                case 'd':
                    event.preventDefault();
                    window.location.href = "https://www.myedio.com/dashboard/";
                    break;
                case 'l':
                    event.preventDefault();
                    alert("Test successful!");
                    break;
                default:
                    handleCustomHotkeys(key);
                    break;
            }
        }
    }

    function handleCustomHotkeys(key) {
        const customHotkeys = GM_getValue('customHotkeys', {});
        if (customHotkeys[key]) {
            window.location.href = customHotkeys[key];
        }
    }

    function showErrorPopup(errorNumber, errorMessage) {
        if (document.querySelector('.error-overlay')) return;

        const overlay = document.createElement('div');
        overlay.classList.add('error-overlay');
        const overlayContent = document.createElement('div');
        overlayContent.classList.add('error-overlay-content');

        overlayContent.innerHTML = `
            <h2>Error ${errorNumber}</h2>
            <p>${errorMessage}</p>
        `;

        const closeButton = document.createElement('button');
        closeButton.innerHTML = 'Close';
        closeButton.addEventListener('click', () => {
            document.body.removeChild(overlay);
            document.body.removeChild(overlayBackground);
            document.body.style.overflow = '';
            document.body.style.userSelect = '';
        });

        overlayContent.appendChild(closeButton);
        overlay.appendChild(overlayContent);
        document.body.appendChild(overlay);

        const overlayBackground = document.createElement('div');
        overlayBackground.classList.add('error-overlay-background');
        document.body.appendChild(overlayBackground);

        document.body.style.overflow = 'hidden';
        document.body.style.userSelect = 'none';
    }

    function addHotkeysElement() {
        if (document.querySelector('.c-navigation__item.hotkeys-item')) return;

        const newElement = document.createElement('li');
        newElement.classList.add('c-navigation__item', 'hotkeys-item');
        newElement.innerHTML = `
            <a class="" target="" title="Hotkeys">
                <span>Hotkeys</span>
            </a>
        `;

        const navigationElement = document.querySelector('.c-navigation');
        if (navigationElement) navigationElement.appendChild(newElement);

        newElement.addEventListener('click', showHotkeysOverlay);

        observeSidebarToggle(newElement);
    }

    function observeSidebarToggle(hotkeysElement) {
        const toggleButton = document.querySelector('.c-button.-icon.c-sidebar__toggle');
        if (toggleButton) {
            const observer = new MutationObserver(() => {
                const isExpanded = toggleButton.getAttribute('aria-expanded') === 'true';
                const span = hotkeysElement.querySelector('span');
                span.textContent = isExpanded ? 'Hotkeys' : 'HK';
            });

            observer.observe(toggleButton, { attributes: true, attributeFilter: ['aria-expanded'] });
        }
    }

    function showHotkeysOverlay() {
        const overlay = document.createElement('div');
        overlay.classList.add('hotkeys-overlay');
        const overlayContent = document.createElement('div');
        overlayContent.classList.add('hotkeys-overlay-content');

        const userNameElement = document.querySelector('.c-avatar__name');
        let userName = userNameElement ? userNameElement.textContent.trim() : "User";

        function toTitleCase(str) {
            return str.replace(/\b\w/g, char => char.toUpperCase());
        }

        userName = toTitleCase(userName);

        overlayContent.innerHTML = `
            <h2>List of Hotkeys</h2>
            <p><strong>Logout:</strong> Alt + Q</p>
            <p><strong>Calendar:</strong> Alt + C</p>
            <p><strong>Dashboard:</strong> Alt + D</p>
            <p><strong>Return to Course Link:</strong> Alt + R</p>
            <p><strong>Test Key:</strong> Alt + L</p>
            <p><strong>Custom Hotkeys:</strong></p>
            <ul id="custom-hotkeys-list"></ul>
            <p>Hello ${userName}! You are running the Alpha version of <strong>Edio Hotkeys</strong>.
            There is still lots of development needed for this script, including one known error,
            but please enjoy what you currently have!</p>
            <button id="add-custom-hotkey">Add Custom Hotkey (Experimental)</button>
        `;

        const closeButton = document.createElement('button');
        closeButton.innerHTML = 'Close';
        closeButton.addEventListener('click', () => {
            document.body.removeChild(overlay);
            document.body.removeChild(overlayBackground);
            document.body.style.overflow = '';
            document.body.style.userSelect = '';
        });

        overlayContent.appendChild(closeButton);
        overlay.appendChild(overlayContent);
        document.body.appendChild(overlay);

        const overlayBackground = document.createElement('div');
        overlayBackground.classList.add('hotkeys-overlay-background');
        document.body.appendChild(overlayBackground);

        document.body.style.overflow = 'hidden';
        document.body.style.userSelect = 'none';

        displayCustomHotkeys();
        document.getElementById('add-custom-hotkey').addEventListener('click', addCustomHotkey);
    }

    function displayCustomHotkeys() {
        const customHotkeys = GM_getValue('customHotkeys', {});
        const customHotkeysList = document.getElementById('custom-hotkeys-list');
        customHotkeysList.innerHTML = '';
        for (const [key, url] of Object.entries(customHotkeys)) {
            const li = document.createElement('li');
            li.textContent = `Alt + ${key.toUpperCase()}: ${url}`;
            customHotkeysList.appendChild(li);
        }
    }

    function addCustomHotkey() {
        const key = prompt("Enter the key for the custom hotkey (e.g., 'n' for Alt+N):");
        const url = prompt("Enter the URL to be associated with this hotkey:");
        if (key && url) {
            const customHotkeys = GM_getValue('customHotkeys', {});
            customHotkeys[key.toLowerCase()] = url;
            GM_setValue('customHotkeys', customHotkeys);
            displayCustomHotkeys();
        }
    }

    const style = document.createElement('style');
    style.innerHTML = `
        .c-navigation__item.hotkeys-item a {
            display: flex;
            align-items: center;
            color: #333;
            font-size: 16px;
            text-decoration: none;
            padding: 10px;
            border-radius: 4px;
            transition: background-color 0.2s ease, transform 0.2s ease;
            cursor: pointer;
        }

        .c-navigation__item.hotkeys-item a:hover {
            background-color: #f4f4f4;
            transform: translateX(5px);
            cursor: pointer;
        }

        .hotkeys-overlay-background {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.8);
            z-index: 1000;
            pointer-events: none;
        }

        .hotkeys-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            z-index: 1001;
            display: flex;
            justify-content: center;
            align-items: center;
            opacity: 0;
            animation: fadeIn 0.3s forwards;
        }

        .hotkeys-overlay-content {
            background: #ffffff;
            padding: 30px 25px;
            border-radius: 5px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
            max-width: 600px;
            text-align: center;
        }

        .hotkeys-overlay-content h2 {
            margin-bottom: 20px;
            font-size: 24px;
        }

        .hotkeys-overlay-content p {
            margin-bottom: 15px;
            font-size: 16px;
        }

        .hotkeys-overlay-content ul {
            list-style: none;
            padding: 0;
        }

        .hotkeys-overlay-content ul li {
            margin-bottom: 5px;
            font-size: 16px;
        }

        .hotkeys-overlay-content button {
            background-color: #007BFF;
            color: white;
            border: none;
            padding: 10px 20px;
            font-size: 14px;
            cursor: pointer;
            border-radius: 4px;
            margin-top: 20px;
        }

        .hotkeys-overlay-content button:hover {
            background-color: #0056b3;
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        .error-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            z-index: 1001;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: rgba(0, 0, 0, 0.8);
        }

        .error-overlay-content {
            background: #ffebee;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
            max-width: 500px;
            text-align: center;
        }

        .error-overlay-content h2 {
            margin-bottom: 15px;
            font-size: 20px;
            color: #d32f2f;
        }

        .error-overlay-content p {
            margin-bottom: 15px;
            font-size: 16px;
            color: #333;
        }

        .error-overlay-content button {
            background-color: #d32f2f;
            color: white;
            border: none;
            padding: 10px 20px;
            font-size: 14px;
            cursor: pointer;
            border-radius: 4px;
            margin-top: 10px;
        }

        .error-overlay-content button:hover {
            background-color: #b71c1c;
        }
    `;
    document.head.appendChild(style);

    saveLinkIfMatches();
    window.addEventListener('keydown', handleHotkeys);
    setInterval(() => {
        addHotkeysElement();
    }, 10);
})();