Better Daymap

Modern redesign, customization, transparency, hidden soccer game, and more for Daymap.

目前為 2025-05-22 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Better Daymap
// @namespace    Better Daymap
// @version      3
// @description  Modern redesign, customization, transparency, hidden soccer game, and more for Daymap.
// @author       LiamGo
// @match        https://*.daymap.net/*
// @icon         https://lh3.googleusercontent.com/_Jt3LvQt0VV4wQkW6brDIvKNCQMSWgzbE_ofiwnWCgWTw4pUv4HsLX0AH8PpNEde85jt8XPWyXQo91d4MEYqZZgm-k4=s60
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // --- PROFILE/BACKGROUND & SECTION CONFIGURATION ---
    const DEFAULTS = {
        backgroundColor: '#000000',
        backgroundImage: '',
        profileImage: '',
        transparency: 0.0,
        blur: 0,
        disabledSections: {
            indicators: false,
            diaryNotes: false,
            homework: false,
            currentTasks: false,
            messages: false,
            bulletins: false,
            newsletters: false,
            rcFactSheets: false
        },
        hiddenGameEnabled: true,
        hiddenGameURL: 'https://www.twoplayergames.org/gameframe/soccer-random'
    };

    const SECTIONS = [
        { id: 'indicators',    name: 'Indicators',      selector: '#pnlMid > div.card.expWindow:nth-of-type(1)' },
        { id: 'diaryNotes',    name: 'My Diary Notes',  selector: '#pnlMid > div.card.expWindow:nth-of-type(2)' },
        { id: 'homework',      name: 'Homework',        selector: '#pnlMid > div.card.expWindow:nth-of-type(3)' },
        { id: 'currentTasks',  name: 'Current Tasks',   selector: '#pnlMid > div.card.expWindow:nth-of-type(4)' },
        { id: 'messages',      name: 'Messages',        selector: '#pnlRight > div.card.expWindow:nth-of-type(1)' },
        { id: 'bulletins',     name: 'Bulletins',       selector: '#pnlRight > div.card.expWindow:nth-of-type(2)' },
        { id: 'newsletters',   name: 'Newsletters',     selector: '#pnlRight > div.card.expWindow:nth-of-type(3)' },
        { id: 'rcFactSheets',  name: 'RC Fact Sheets',  selector: '#pnlRight > div.card.expWindow:nth-of-type(4)' }
    ];

    // --- TRANSPARENCY/BLUR FUNCTIONALITY ---
    function applyTransBlur() {
        const transparency = parseFloat(GM_getValue('transparency', DEFAULTS.transparency));
        const blur = parseInt(GM_getValue('blur', DEFAULTS.blur), 10);

        let dark = 0;
        if(document.cookie && document.cookie.length > 0) {
            const lastChar = document.cookie.substr(document.cookie.length - 1, 1);
            dark = lastChar === "1" ? 1 : 0;
        }
        const lightBg = "237,235,233";
        const darkBg = "37,37,37";
        const bg = dark ? darkBg : lightBg;

        function setStyle(selector, bgColor, transparency, blur) {
            document.querySelectorAll(selector).forEach(el => {
                el.style.background = `rgba(${bgColor},${transparency})`;
                el.style.backdropFilter = `blur(${blur}px)`;
            });
        }

        setStyle(
            ".card, .msg, .ditm, .Toolbar, .ditm .t, .ditm .c, .hasDatepicker, #tblTt tbody tr td, .msg, #bCalendar, #btnDiary",
            bg,
            transparency,
            blur
        );

        // Header
        let header = document.querySelector("daymap-header");
        if(header) {
            header.style.backgroundColor = `rgba(255,255,255,${transparency * 1.2})`;
            header.style.backdropFilter = `blur(${blur * 3}px)`;
            let lis = header.querySelectorAll("div ul li");
            lis.forEach(li => {
                li.style.backgroundColor = `rgba(255,255,255,${transparency * 0.8})`;
            });
        }
        // Navigation containers
        setStyle(
            ".nav-container, .nav-user-container",
            bg,
            transparency * 0.7,
            blur
        );
    }
    window.setTimeout(applyTransBlur, 0);

    // --- PROFILE/BACKGROUND/SECTION FUNCTIONALITY ---
    function applySettings() {
        const bgColor = GM_getValue('backgroundColor', DEFAULTS.backgroundColor);
        const bgImage = GM_getValue('backgroundImage', DEFAULTS.backgroundImage);
        const profileImage = GM_getValue('profileImage', DEFAULTS.profileImage);
        const disabledSections = GM_getValue('disabledSections', DEFAULTS.disabledSections);

        document.body.style.backgroundColor = bgColor;

        if (bgImage) {
            document.body.style.backgroundImage = `url(${bgImage})`;
            document.body.style.backgroundSize = '100% 100%';
            document.body.style.backgroundRepeat = 'no-repeat';
            document.body.style.backgroundPosition = 'top left';
            document.body.style.backgroundAttachment = 'fixed';
        } else {
            document.body.style.backgroundImage = '';
        }

        if (profileImage) {
            setTimeout(() => {
                const avatarElements = [
                    document.querySelector('.nav-user-image'),
                    document.querySelector('.photoThumb')
                ];
                avatarElements.forEach(avatarElement => {
                    if (avatarElement) {
                        avatarElement.style.backgroundImage = `url(${profileImage})`;
                    }
                });
            }, 1750);
        }

        SECTIONS.forEach(section => {
            document.querySelectorAll(section.selector).forEach(el => {
                el.style.display = disabledSections[section.id] ? 'none' : '';
            });
        });

        // Re-apply transparency/blur when settings change
        applyTransBlur();
    }
    function expandContentHeight() {
        const currentTasks = document.querySelector('#pnlMid > div.card.expWindow:nth-of-type(4) .expContent');
        const messages = document.querySelector('#pnlRight > div.card.expWindow:nth-of-type(1) .expContent');
        if (currentTasks) currentTasks.style.minHeight = '575px';
        if (messages) messages.style.minHeight = '575px';
    }

    // --- SETTINGS PAGE ---
    function createSettingsPage() {
        const modal = document.createElement('div');
        modal.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 20px rgba(0,0,0,0.3);
            z-index: 9999;
            min-width: 400px;
            max-width: 95vw;
            max-height: 90vh;
            overflow: auto;
            font-family: Arial, sans-serif;
        `;
        const heading = document.createElement('h2');
        heading.textContent = 'Better Daymap Settings';
        heading.style.marginTop = '0';

        const form = document.createElement('div');
        form.style.display = 'grid';
        form.style.gap = '15px';

        const colorLabel = createLabel('Background Color:', 'color');
        const colorInput = createInput('color', 'backgroundColor', GM_getValue('backgroundColor', DEFAULTS.backgroundColor));

        const bgImageLabel = createLabel('Background Image URL:', 'bgImage');
        const bgImageInput = createInput('text', 'bgImage', GM_getValue('backgroundImage', DEFAULTS.backgroundImage));
        bgImageInput.placeholder = 'https://example.com/image.jpg';

        const profileLabel = createLabel('Profile Image URL:', 'profile');
        const profileInput = createInput('text', 'profile', GM_getValue('profileImage', DEFAULTS.profileImage));
        profileInput.placeholder = 'https://example.com/avatar.jpg';

        // --- TRANSPARENCY/BLUR CONTROLS ---
        const transparencyLabel = createLabel('Transparency:', 'transparency');
        const transparencyValue = document.createElement('span');
        transparencyValue.style.marginLeft = '10px';
        transparencyValue.style.fontWeight = 'bold';
        const transparencyInput = document.createElement('input');
        transparencyInput.type = 'range';
        transparencyInput.id = 'transparency';
        transparencyInput.min = 0;
        transparencyInput.max = 1;
        transparencyInput.step = 0.01;
        transparencyInput.value = GM_getValue('transparency', DEFAULTS.transparency);
        transparencyValue.textContent = transparencyInput.value;
        transparencyInput.addEventListener('input', () => {
            transparencyValue.textContent = transparencyInput.value;
        });

        const blurLabel = createLabel('Blur (px):', 'blur');
        const blurValue = document.createElement('span');
        blurValue.style.marginLeft = '10px';
        blurValue.style.fontWeight = 'bold';
        const blurInput = document.createElement('input');
        blurInput.type = 'range';
        blurInput.id = 'blur';
        blurInput.min = 0;
        blurInput.max = 20;
        blurInput.step = 1;
        blurInput.value = GM_getValue('blur', DEFAULTS.blur);
        blurValue.textContent = blurInput.value;
        blurInput.addEventListener('input', () => {
            blurValue.textContent = blurInput.value;
        });

        // Section toggles
        const sectionLabel = document.createElement('div');
        sectionLabel.textContent = 'Disable Sections:';
        sectionLabel.style.fontWeight = 'bold';
        sectionLabel.style.marginTop = '15px';

        const togglesContainer = document.createElement('div');
        togglesContainer.style.display = 'flex';
        togglesContainer.style.gap = '20px';

        const leftColumn = document.createElement('div');
        leftColumn.style.display = 'flex';
        leftColumn.style.flexDirection = 'column';
        leftColumn.style.gap = '8px';

        const rightColumn = document.createElement('div');
        rightColumn.style.display = 'flex';
        rightColumn.style.flexDirection = 'column';
        rightColumn.style.gap = '8px';

        const disabledSections = GM_getValue('disabledSections', DEFAULTS.disabledSections);
        SECTIONS.forEach((section, index) => {
            const wrapper = document.createElement('div');
            wrapper.style.display = 'flex';
            wrapper.style.alignItems = 'center';
            wrapper.style.gap = '8px';

            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.id = `section-${section.id}`;
            checkbox.checked = disabledSections[section.id];

            const label = document.createElement('label');
            label.htmlFor = `section-${section.id}`;
            label.textContent = section.name;
            label.style.cursor = 'pointer';

            wrapper.appendChild(checkbox);
            wrapper.appendChild(label);

            if (index < 4) leftColumn.appendChild(wrapper);
            else rightColumn.appendChild(wrapper);
        });

        togglesContainer.appendChild(leftColumn);
        togglesContainer.appendChild(rightColumn);

        // --- HIDDEN GAME TOGGLE & URL ---
        const hiddenGameEnabled = GM_getValue('hiddenGameEnabled', DEFAULTS.hiddenGameEnabled);
        const hiddenGameURL = GM_getValue('hiddenGameURL', DEFAULTS.hiddenGameURL);

        const gameToggleLabel = createLabel('Show Hidden Game Button:', 'hiddenGameEnabled');
        const gameToggleInput = document.createElement('input');
        gameToggleInput.type = 'checkbox';
        gameToggleInput.id = 'hiddenGameEnabled';
        gameToggleInput.checked = hiddenGameEnabled;

        const gameUrlLabel = createLabel('Hidden Game URL:', 'hiddenGameURL');
        const gameUrlInput = createInput('text', 'hiddenGameURL', hiddenGameURL);
        gameUrlInput.placeholder = 'https://example.com/game';

        // --- BUTTONS ---
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.gap = '10px';
        buttonContainer.style.marginTop = '15px';

        const saveButton = createButton('Save', () => {
            const newDisabled = {};
            SECTIONS.forEach(section => {
                newDisabled[section.id] = document.getElementById(`section-${section.id}`).checked;
            });
            GM_setValue('backgroundColor', colorInput.value);
            GM_setValue('backgroundImage', bgImageInput.value);
            GM_setValue('profileImage', profileInput.value);
            GM_setValue('transparency', transparencyInput.value);
            GM_setValue('blur', blurInput.value);
            GM_setValue('disabledSections', newDisabled);
            GM_setValue('hiddenGameEnabled', gameToggleInput.checked);
            GM_setValue('hiddenGameURL', gameUrlInput.value);
            applySettings();
            expandContentHeight();
            // Remove and re-add hidden game button if on timetable page
            if (location.pathname.match(/\/daymap\/timetable\/timetable\.aspx$/)) {
                removeHiddenGameElements();
                if (gameToggleInput.checked) addHiddenGameButton();
            }
            modal.remove();
        });

        const resetButton = createButton('Reset to Defaults', () => {
            GM_setValue('backgroundColor', DEFAULTS.backgroundColor);
            GM_setValue('backgroundImage', DEFAULTS.backgroundImage);
            GM_setValue('profileImage', DEFAULTS.profileImage);
            GM_setValue('transparency', DEFAULTS.transparency);
            GM_setValue('blur', DEFAULTS.blur);
            GM_setValue('disabledSections', DEFAULTS.disabledSections);
            GM_setValue('hiddenGameEnabled', DEFAULTS.hiddenGameEnabled);
            GM_setValue('hiddenGameURL', DEFAULTS.hiddenGameURL);
            applySettings();
            expandContentHeight();
            if (location.pathname.match(/\/daymap\/timetable\/timetable\.aspx$/)) {
                removeHiddenGameElements();
                if (DEFAULTS.hiddenGameEnabled) addHiddenGameButton();
            }
            modal.remove();
        });

        const closeButton = createButton('Close', () => modal.remove());

        // Append in desired order
        form.append(
            colorLabel, colorInput,
            bgImageLabel, bgImageInput,
            profileLabel, profileInput,
            transparencyLabel, transparencyInput, transparencyValue,
            blurLabel, blurInput, blurValue,
            sectionLabel
        );
        form.append(togglesContainer);

        // Insert hidden game controls
        form.append(gameToggleLabel, gameToggleInput, gameUrlLabel, gameUrlInput);

        buttonContainer.append(saveButton, resetButton, closeButton);
        modal.append(heading, form, buttonContainer);

        return modal;
    }
    function createLabel(text, forId) {
        const label = document.createElement('label');
        label.textContent = text;
        label.htmlFor = forId;
        label.style.fontWeight = 'bold';
        return label;
    }
    function createInput(type, id, value) {
        const input = document.createElement('input');
        input.type = type;
        input.id = id;
        input.value = value;
        input.style.padding = '5px';
        input.style.width = '100%';
        if (type === 'color') input.style.cursor = 'pointer';
        return input;
    }
    function createButton(text, onClick) {
        const button = document.createElement('button');
        button.textContent = text;
        button.onclick = onClick;
        button.style.cssText = `
            padding: 8px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            background: #007bff;
            color: white;
            transition: background 0.3s;
        `;
        button.addEventListener('mouseover', () => button.style.background = '#0056b3');
        button.addEventListener('mouseout', () => button.style.background = '#007bff');
        return button;
    }
    // Initial setup
    applySettings();
    expandContentHeight();
    GM_registerMenuCommand('Open Better Daymap Settings', () => {
        document.body.appendChild(createSettingsPage());
    });

    // --- HIDDEN GAME BUTTON/IFRAME LOGIC ---
    function removeHiddenGameElements() {
        const btn = document.getElementById('soccerToggleBtn');
        const cont = document.getElementById('soccerEmbedContainer');
        if (btn) btn.remove();
        if (cont) cont.remove();
    }
    function addHiddenGameButton() {
        // Remove if already present
        removeHiddenGameElements();

        const hiddenGameURL = GM_getValue('hiddenGameURL', DEFAULTS.hiddenGameURL);

        const soccerContainer = document.createElement('div');
        soccerContainer.id = 'soccerEmbedContainer';
        soccerContainer.style.cssText = 'width:100%; overflow:hidden; max-height:0; transition:max-height 0.4s ease;';
        const soccerFrame = document.createElement('iframe');
        soccerFrame.src = hiddenGameURL;
        soccerFrame.style.cssText = 'width:100%; height:100vh; border:none;';
        soccerContainer.appendChild(soccerFrame);
        document.body.appendChild(soccerContainer);

        const soccerBtn = document.createElement('button');
        soccerBtn.id = 'soccerToggleBtn';
        soccerBtn.textContent = '▶️';
        soccerBtn.style.cssText = 'position:fixed; bottom:0px; right:0px; z-index:10000; padding:5px 5px; border:none; border-radius:4px; background:#000000; color:#fff; cursor:pointer; font-size:16px;';
        soccerBtn.addEventListener('click', () => {
            if (soccerContainer.style.maxHeight === '0px' || !soccerContainer.style.maxHeight) {
                soccerContainer.style.maxHeight = '100vh';
                soccerBtn.textContent = '◀️';
            } else {
                soccerContainer.style.maxHeight = '0';
                soccerBtn.textContent = '▶️';
            }
        });
        document.body.appendChild(soccerBtn);
    }

    // --- TIMETABLE PAGE ENHANCEMENTS ---
    if (location.pathname.match(/\/daymap\/timetable\/timetable\.aspx$/)) {
        // Add custom CSS for timetable redesign
        GM_addStyle(`
            /* Main layout improvements */
            .main-layout {
                padding: 20px;
                max-width: 14000px;
                margin: 0 auto;
            }
            .grid { gap: 0px; }
            .card {
                border-radius: 10px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.1);
                border: none;
                overflow: hidden;
            }
            /* Timetable styling */
            .tt {
                border-collapse: separate;
                border-spacing: 0;
                width: 100%;
            }
            .tt th {
                background: #1888C9;
                color: white;
                padding: 10px;
                text-align: center;
                font-weight: 500;
            }
            .tt td {
                padding: 0;
                border: 1px solid #e9ecef;
            }
            .ttCell {
                padding: 8px;
                height: 80px;
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                transition: all 0.2s ease;
            }
            .ttCell:hover {
                filter: brightness(95%);
                transform: translateY(-2px);
            }
            .ttSubject {
                font-weight: 600;
                font-size: 0.9rem;
                margin-bottom: 5px;
            }
            .ttTeacher, .ttRoom {
                font-size: 0.8rem;
                color: white;
            }
            .Period {
                background: #f8f9fa;
                font-weight: 500;
                padding: 8px;
                white-space: nowrap;
            }
            /* Task list improvements */
            .feed {
                width: 100%;
                border-collapse: collapse;
            }
            .feed tr {
                border-bottom: 1px solid #e9ecef;
            }
            .feed td {
                padding: 12px;
            }
            .feed .cap {
                width: 120px;
                font-weight: 500;
                color: #2c3e50;
                vertical-align: top;
            }
            .feed .itm {
                cursor: pointer;
                transition: background 0.2s ease;
            }
            .feed .itm:hover {
                background: #f8f9fa;
            }
            .Caption {
                font-size: 0.8rem;
                color: #6c757d;
            }
            /* Message list improvements */
            .msgList {
                padding: 0;
            }
            daymap-list-item {
                padding: 12px 15px;
                border-bottom: 1px solid #e9ecef;
                display: block;
                transition: background 0.2s ease;
            }
            daymap-list-item:hover {
                background: #f8f9fa;
            }
            /* Button improvements */
            .btn {
                border-radius: 6px;
                padding: 8px 16px;
                transition: all 0.2s ease;
            }
            .btn:hover {
                transform: translateY(-1px);
            }
            /* Responsive adjustments */
            @media (max-width: 768px) {
                .grid > div {
                    width: 100% !important;
                }
                .ttCell {
                    height: auto;
                    min-height: 60px;
                }
            }
        `);

        // Wait for page to load
        window.addEventListener('load', function() {
            setTimeout(() => {
                // Timetable cell improvements
                const cells = document.querySelectorAll('.ttCell');
                cells.forEach(cell => {
                    cell.style.cursor = 'pointer';
                    const subject = cell.querySelector('.ttSubject');
                    if (subject) {
                        const text = subject.textContent.trim();
                        if (text.length > 25) {
                            subject.textContent = text.substring(0, 22) + '...';
                        }
                    }
                });
                // Task list color coding
                const tasks = document.querySelectorAll('.feed .itm');
                tasks.forEach(task => {
                    task.style.transition = 'all 0.2s ease';
                    if (task.innerHTML.includes('Overdue') || task.innerHTML.includes('Uh did you submit on Turnitin or something?') || task.innerHTML.includes('Work has been received')) {
                        task.style.borderLeft = '3px solid #e81123';
                    } else if (task.innerHTML.includes('Grade:') || task.innerHTML.includes('Mark:')) {
                        task.style.borderLeft = '3px solid #107c10';
                    } else {
                        task.style.borderLeft = '3px solid #ffb900';
                    }
                });
            });
        });

        // --- HIDDEN GAME BUTTON ---
        if (GM_getValue('hiddenGameEnabled', DEFAULTS.hiddenGameEnabled)) {
            addHiddenGameButton();
        }
    }
})();