中原iLearning 2.0 頁面體驗增強

在中原iLearning 2.0 課程頁依種類分類課程段落,提供新分頁開啟PDF教材的功能

// ==UserScript==
// @name         中原iLearning 2.0 頁面體驗增強
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  在中原iLearning 2.0 課程頁依種類分類課程段落,提供新分頁開啟PDF教材的功能
// @icon         data:image/png;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKHzzACd78xkoffM3LIH0MD+N9Qcjf/QALof1ICuH9TguivUrOpL2A4nA+gE0k/YnLI/2ODOT9ywAAAAAAAAAACh88wAne/NuKH3z9iyB9NQ/jfUdI4D0AC6H9Y4rh/X5Lor1vjqS9g2JwPoFNZP2rCyP9vczk/fEZJb1AmOV9QYqfvMAJ3zzdCh98/8sgfTgP431HySA9AAuiPWWK4f1/y6K9ck6kvYNicD6BTWT9rYsj/b/M5T3zzt68yI0dfIxLXrzACd883QoffP/LIH04D+N9R8kgPQAL4j1liuH9f8uivXJOpL1DYnA+gU1k/a2LY/2/zOU9880dfJNMnPySSp38gAnfPN0KH7z/yyC9eBAjvYfJID1AC+I9ZYrh/X/Lov1yTuT9g2JwPoFNZP2ti2P9v8zlPfPO3rzPDt68y8uevIAKHzzdCl98P8ugO7iQovtIRt57wAviPWYK4f1/y6L9cs8k/YOf7v6BTST9rcsj/b/M5T3zzt69BU/fPQQRWyrAFV0oHpTa4//R1l1+EJFTKlET2FXMIj0xCuH9f8ui/XnPpX2QUCY9y8wkPbWLI/2/zOU975mjdgedoy3LpKNiB9hXl2ZT0xM/0VCQ/80MjT/Ky00+S1foP0th/P9LIn1/y6M9eoujvbmLI72/y+Q9vQ7mPdpkJ++G3mEnnxqb33QWlxl9UtLUP9BQEP/NTQ3/ygnKf8qMT7TOYHZczKN960vjPXcL4723jCQ9r84lfZhV6b4CHR8jwB9hpwGZmx8PFVaaZlFS1rfOzxF+y8uMf8rKS3/NDI1nVBCNhBMqv8GRZj2GUaa9xtLnvcLxOH8AH+6+QAAAAAAAAAAAG1mXQB+c2MDV1VWIUVER1g3NjmNLSwwtSkoLM00MzaVVFNUHzc2OQDHxcIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClpaYA4uLiAFZVWAY6Oj0TODY6JFVUVhUFBAkAw8LCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADhAAAA4QAAACEAAAAhAAAAIQAAACEAAAAgAAAAAAAAAAAAAACAAQAA4B8AAPwfAAD//wAA//8AAA==
// @match        *://ilearning.cycu.edu.tw/*
// @grant        none
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    const config = {
        "討論區": {
            "title": "討論區",
            "logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/forum/1744246650/monologo?filtericon=1"
        },
        "作業": {
            "title": "作業",
            "logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/assign/1744246650/monologo?filtericon=1"
        },
        "超級影片": {
            "title": "影片檔",
            "logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/supervideo/1744246650/monologo?filtericon=1"
        },
        "PDF Annotation": {
            "title": "PDF檔",
            "logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/pdfannotator/1744246650/monologo?filtericon=1"
        },
        "回饋單": {
            "title": "問卷",
            "logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/feedback/1744246650/monologo?filtericon=1"
        }
    };

    function extractFullUrl() {
        const scripts = document.scripts;
        for (let script of scripts) {
            const match = script.textContent.match(/"fullurl":\s*"([^"]+)"/);
            if (match) {
                return match[1].replace(/\\/g, ''); // 去除反斜線
            }
        }
        return null;
    }

    function createDownloadButton(fullUrl) {
        const button = document.createElement('button');
        button.id = 'pdf-download-btn';
        button.title = '下載 PDF';
        button.innerHTML = '➜'; // 下載符號
        button.onclick = function () {
            window.open(fullUrl, '_blank');
        };

        Object.assign(button.style, {
            position: 'fixed',
            bottom: '8rem',
            right: '2rem',
            width: '36px',
            height: '36px',
            backgroundColor: 'orange',
            color: 'white',
            border: 'none',
            borderRadius: '50%',
            fontSize: '26px',
            padding: '0 0 2px 0',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
            boxShadow: '0px 4px 6px rgba(0, 0, 0, 0.1)',
            transform: 'rotate(90deg)'
        });

        document.body.appendChild(button);
    }

    function getMoremenu() {
        const ul = document.querySelector('ul.nav.more-nav.nav-tabs');
        if (ul) {
            const li = document.createElement('li');
            li.className = 'nav-item';

            const a = document.createElement('a');
            a.className = 'nav-link';
            a.setAttribute('role', 'menuitem');
            a.setAttribute('style', 'color:#FF359A !important;');
            a.textContent = '★精簡化';
            a.onclick = showMenu;

            li.appendChild(a);
            ul.appendChild(li);
        }
    }

    function getItems() {
        const result = {};

        // 取得課程 ID(從網址中抓取 ?id= 後的數字)
        const courseIdMatch = window.location.href.match(/course\/view\.php\?id=(\d+)/);
        const courseId = courseIdMatch ? courseIdMatch[1] : null;

        if (!courseId) return result;

        for (let i = 0; i < sessionStorage.length; i++) {
            const key = sessionStorage.key(i);
            if (key.includes(`${courseId}/staticState`)) {
                try {
                    const json = JSON.parse(sessionStorage.getItem(key));
                    if (json && Array.isArray(json.cm)) {
                        for (const item of json.cm) {
                            const mod = item.modname;
                            if (!result[mod]) result[mod] = [];
                            result[mod].push(item);
                        }
                    }
                } catch (e) {
                    console.error('JSON 解析錯誤:', e);
                }
            }
        }

        for (const mod in result) {
            if (mod === "討論區") {
                result[mod].sort((a, b) => {
                    if (a.sectionnumber === b.sectionnumber) {
                        return b.id - a.id;
                    }
                    if (a.sectionnumber === Math.min(...result[mod].map(i => i.sectionnumber))) return -1;
                    if (b.sectionnumber === Math.min(...result[mod].map(i => i.sectionnumber))) return 1;
                    return b.sectionnumber - a.sectionnumber;
                });
            } else {
                result[mod].sort((a, b) => b.sectionnumber - a.sectionnumber);
            }
        }

        return result;
    }

    function showMenu() {
        const original = document.querySelector('ul.weeks');
        if (original) {
            original.style.display = 'none';

            const items = getItems();
            const container = document.createElement('ul');
            container.className = 'weeks';
            container.id = 'menuside';
            container.setAttribute('data-for', 'course_sectionlist');

            let sectionNum = 1;
            for (const modname in items) {
                const section = document.createElement('li');
                section.className = 'section course-section main clearfix';
                section.id = `side-section-${sectionNum}`;
                let secNum = 0;
                let isFisrtItem = true;
                let sectionHTML = `
                <div class="section-item">
                    <div class="course-section-header d-flex">
                        <div class="d-flex align-items-center position-relative">
                            <a role="button" class="btn btn-icon me-3 icons-collapse-expand justify-content-center collapsed" aria-expanded="false" href="#side-coursecontentcollapse${sectionNum}" data-for="sectiontoggler" data-toggle="collapse">
                                <span class="expanded-icon icon-no-margin p-2" title="展延">
                                    <i class="icon fa fa-chevron-down fa-fw" aria-hidden="true"></i>
                                    <span class="sr-only">展延</span>
                                </span>
                                <span class="collapsed-icon icon-no-margin p-2" title="展延">
                                    <span class="dir-rtl-hide"><i class="icon fa fa-chevron-right fa-fw" aria-hidden="true"></i></span>
                                    <span class="dir-ltr-hide"><i class="icon fa fa-chevron-left fa-fw" aria-hidden="true"></i></span>
                                    <span class="sr-only">展延</span>
                                </span>
                            </a>
                            <h3 class="h4 sectionname course-content-item d-flex align-self-stretch align-items-center mb-0" id="side-sectionid-${sectionNum}-title">
                                ${config[modname]?.title || modname}
                            </h3>
                        </div>
                    </div>
                    <div id="side-coursecontentcollapse${sectionNum}" class="content course-content-item-content collapse">
                        <div class="my-3" data-for="sectioninfo">
                            <div class="section_availability"></div>
                        </div>
                        <ul class="section m-0 p-0 img-text d-block" data-for="cmlist">`;

                for (const item of items[modname]) {
                    if (isFisrtItem) {
                        secNum = item.sectionnumber;
                        isFisrtItem = false;
                        let weeks = `<span>第${item.sectionnumber}週</span>`;
                        if (secNum == 0) {
                            weeks = "";
                        }
                        sectionHTML += `
                    <li class="activity activity-wrapper pdfannotator modtype_pdfannotator hasinfo" data-indexed="true">
                    ${weeks}`;
                    }
                    else if (secNum != item.sectionnumber) {
                        sectionHTML += `</li>
                    <li class="activity activity-wrapper pdfannotator modtype_pdfannotator hasinfo" data-indexed="true">
                    <span>第${item.sectionnumber}週</span>`;
                        secNum = item.sectionnumber;
                    }

                    sectionHTML += `<div class="activity-item focus-control">
                                    <div class="activity-grid">
                                        <div class="activity-icon activityiconcontainer smaller communication courseicon align-self-start me-2">
                                            ${config[modname]?.logo ? `<img src="${config[modname].logo}" class="activityicon">` : ''}
                                        </div>
                                        <div class="activity-name-area activity-instance d-flex flex-column me-2">
                                            <div class="activitytitle modtype_pdfannotator position-relative align-self-start">
                                                <div class="activityname">
                                                    <a href="${item.url}" class="aalink stretched-link">
                                                        <span class="instancename">${item.name}</span>
                                                    </a>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>`;
                }

                sectionHTML += `</li>
                        </ul>
                    </div>
                </div>`;

                section.innerHTML = sectionHTML;
                container.appendChild(section);
                sectionNum++;
            }

            original.parentNode.insertBefore(container, original.nextSibling);
        }
    }


    function init() {
        const url = window.location.href;

        if (/^https:\/\/ilearning\.cycu\.edu\.tw\/mod\/pdfannotator\//.test(url)) {
            const fullUrl = extractFullUrl();
            console.log(fullUrl);
            if (fullUrl) {
                createDownloadButton(fullUrl);
            }
        } else if (/^https:\/\/ilearning\.cycu\.edu\.tw\/course\//.test(url)) {
            getMoremenu();
            const items = getItems();
        }
    }

    window.addEventListener('load', init);
})();