在中原iLearning 2.0 課程頁依種類分類課程段落,提供新分頁開啟PDF教材的功能,提供下載影片的功能
// ==UserScript==
// @name 中原iLearning 2.0 頁面體驗增強
// @namespace http://tampermonkey.net/
// @version 2.1
// @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/resource/1757814017/monologo"
},
"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/supervideo/1744246650/monologo?filtericon=1"
},
"網址": {
"title": "網址",
"logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/url/1757814017/monologo?filtericon=1"
},
"回饋單": {
"title": "問卷",
"logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/feedback/1744246650/monologo?filtericon=1"
},
"頁面": {
"title": "文章",
"logo": "https://ilearning.cycu.edu.tw/theme/image.php/boost_union/page/1757814017/monologo?filtericon=1"
}
};
const order = ["頁面", "討論區", "作業", "PDF Annotation", "檔案", "超級影片", "網址", "回饋單"];
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 getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
function setCookie(name, value, days = 30) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/`;
}
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;
let sections = [];
let main_section = {};
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) {
if (Array.isArray(json.section)) {
sections = json.section;
for (const sec of json.section) {
main_section[sec.id] = sec.parentsectionid || sec.id;
}
}
console.log(main_section);
if (Array.isArray(json.cm)) {
for (const item of json.cm) {
// 跳過子單元
if (item.modname === "子單元") continue;
// 處理子單元歸位
item.sectionid = main_section[item.sectionid];
item.sectionnumber = item.sectionnumber = sections.findIndex(section => section.id === item.sectionid);
const mod = item.modname;
if (!result[mod]) result[mod] = [];
result[mod].push(item);
}
}
console.log(result);
}
} catch (e) {
console.error('JSON 解析錯誤:', e);
}
}
}
// 按照指定順序排序
const sortedResult = {};
for (const mod of order) {
if (result[mod]) {
sortedResult[mod] = result[mod];
}
}
// 添加未在order中定義的類型
for (const mod in result) {
if (!sortedResult[mod]) {
sortedResult[mod] = result[mod];
}
}
// 按照周次排序
for (const mod in sortedResult) {
if (mod === "討論區") {
sortedResult[mod].sort((a, b) => {
if (a.sectionnumber === b.sectionnumber) {
return b.id - a.id;
}
if (a.sectionnumber === Math.min(...sortedResult[mod].map(i => i.sectionnumber))) return -1;
if (b.sectionnumber === Math.min(...sortedResult[mod].map(i => i.sectionnumber))) return 1;
return b.sectionnumber - a.sectionnumber;
});
} else {
sortedResult[mod].sort((a, b) => b.sectionnumber - a.sectionnumber);
}
}
return { items: sortedResult, sections };
}
function showMenu() {
const original = document.querySelector('ul.weeks');
const side = document.querySelector('#menuside');
if (original) {
original.style.display = 'none';
const data = getItems();
const items = data.items;
const sections = data.sections;
let container = null;
if (side) {
container = side;
container.innerHTML = "";
}
else {
container = document.createElement('ul');
container.className = 'weeks';
container.id = 'menuside';
container.setAttribute('data-for', 'course_sectionlist');
}
// 添加排序選擇器
const sortContainer = document.createElement('div');
sortContainer.className = 'mb-3';
sortContainer.innerHTML = `
<select class="form-select" id="week-sort-order">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
`;
// 設置默認值
const savedOrder = getCookie('weekSortOrder') || 'desc';
sortContainer.querySelector('#week-sort-order').value = savedOrder;
// 監聽變化
sortContainer.querySelector('#week-sort-order').addEventListener('change', function () {
setCookie('weekSortOrder', this.value);
sortContainer.innerHTML = "";
showMenu(); // 重新渲染
});
let sectionNum = 1;
// 獲取當前周次
const currentWeekSection = sections.find(s => s.current);
for (const modname in items) {
const section = document.createElement('li');
section.className = 'section course-section main clearfix';
section.id = `side-section-${sectionNum}`;
// 按照選擇的排序方式處理周次
const weekItems = {};
for (const item of items[modname]) {
if (!weekItems[item.sectionnumber]) {
weekItems[item.sectionnumber] = [];
}
weekItems[item.sectionnumber].push(item);
}
console.log(items)
console.log(weekItems)
// 獲取所有周次並排序
let weekNumbers = Object.keys(weekItems).map(Number);
// 第0周始終置頂
const week0 = weekNumbers.includes(0) ? [0] : [];
const otherWeeks = weekNumbers.filter(w => w !== 0);
// 按照選擇的排序方式排序
const sortOrder = getCookie('weekSortOrder') || 'asc';
if (sortOrder === 'asc') {
otherWeeks.sort((a, b) => a - b);
} else {
otherWeeks.sort((a, b) => b - a);
}
// 將當前周次提前
let sortedWeeks = week0;
if (currentWeekSection && otherWeeks.includes(currentWeekSection.sectionnumber)) {
sortedWeeks.push(currentWeekSection.sectionnumber);
sortedWeeks = sortedWeeks.concat(otherWeeks.filter(w => w !== currentWeekSection.sectionnumber));
} else {
sortedWeeks = sortedWeeks.concat(otherWeeks);
}
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 week of sortedWeeks) {
const weekItemsList = weekItems[week];
if (!weekItemsList) continue;
let weekTitle = `第${week}週`;
if (week === 0) {
weekTitle = "公告";
}
// 添加當前周標記
let currentBadge = "";
let currentCSS = "";
if (currentWeekSection.id == weekItemsList[0].sectionid) {
currentBadge = '<span class="badge rounded-pill bg-primary text-white order-1 ms-2">本週</span>';
currentCSS = "course-content current";
}
sectionHTML += `
<li class="activity activity-wrapper pdfannotator modtype_pdfannotator hasinfo" data-indexed="true">
<div class="week-title fw-bold fs-5 mb-2">${weekTitle}${currentBadge}</div><div class="${currentCSS}">`;
for (const item of weekItemsList) {
// 確定圖標
let logoUrl = config[modname]?.logo;
if (modname === "PDF Annotation") {
logoUrl = config["PDF Annotation"].logo;
}
sectionHTML += `<div class="activity-item focus-control mb-2">
<div class="activity-grid">
<div class="activity-icon activityiconcontainer smaller communication courseicon align-self-start me-2">
${logoUrl ? `<img src="${logoUrl}" 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 += `</div></li>`;
}
sectionHTML += `</ul>
</div>
</div>`;
section.innerHTML = sectionHTML;
container.appendChild(section);
sectionNum++;
}
original.parentNode.insertBefore(container, original.nextSibling);
original.parentNode.insertBefore(sortContainer, original.nextSibling);
}
}
function handleVideoPage() {
// 查找MP4連結
const mp4Regex = /https:\/\/[^"]+\.mp4/g;
let mp4Url = null;
let doc = document.documentElement.innerHTML;
const match = doc.match(mp4Regex);
if (match && match.length > 0) {
mp4Url = match[0];
}
if (mp4Url) {
// 創建下載按鈕
const button = document.createElement('button');
button.id = 'video-download-btn';
button.title = '下載影片';
button.innerHTML = '➜';
button.onclick = function () {
const link = document.createElement('a');
link.href = mp4Url;
link.download = '';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
if (confirm('停留此頁面將降低下載速率,是否離開此頁面?')) {
window.location.href = 'https://ilearning.cycu.edu.tw/my/courses.php';
} else {
}
};
Object.assign(button.style, {
position: 'fixed',
bottom: '8rem',
right: '2rem',
width: '36px',
height: '36px',
backgroundColor: 'blue',
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 init() {
const url = window.location.href;
if (/^https:\/\/ilearning\.cycu\.edu\.tw\/mod\/pdfannotator\//.test(url)) {
const fullUrl = extractFullUrl();
if (fullUrl) {
createDownloadButton(fullUrl);
}
} else if (/^https:\/\/ilearning\.cycu\.edu\.tw\/(mod\/supervideo\/|mod\/resource\/)/.test(url)) {
console.log("找尋影片網址中...");
handleVideoPage();
} else if (/^https:\/\/ilearning\.cycu\.edu\.tw\/course\//.test(url)) {
console.log("找尋影片網址中...");
getMoremenu();
}
}
window.addEventListener('load', init);
})();