// ==UserScript==
// @name 小雅爬爬爬
// @match https://*.ai-augmented.com/*
// @grant none
// @description 爬取小雅平台的课件
// @license MIT
// @author Yi
// @version 1.1.5
// @namespace https://greasyfork.org/users/1268039
// ==/UserScript==
var isProgressBarVisible = true;
// 添加样式规则来更改 placeholder 的颜色
var styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = `
#searchInput::placeholder {
color: #ffa500;
}
.custom-checkbox {
width: 20px;
height: 20px;
appearance: none;
background-color: #fff;
border: 2px solid #ffa500;
border-radius: 4px;
margin-right: 10px;
outline: none;
cursor: pointer;
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.custom-checkbox:checked {
background-color: #ffa500;
border-color: #ffa500;
}
.custom-checkbox:checked::before {
content: '✓';
display: block;
text-align: center;
line-height: 18px;
color: #fff;
font-size: 16px;
}
.custom-checkbox:hover {
border-color: #ff8c00;
}
@keyframes flowingGradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
`;
document.head.appendChild(styleSheet);
(function() {
'use strict';
// 动态导入dotlottie-player模块
const script = document.createElement('script');
script.src = 'https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs';
script.type = 'module'; // 指定导入类型为module
document.head.appendChild(script); // 将脚本添加到<head>中
// 监听脚本的加载事件以确认导入成功
script.onload = () => {
console.log('dotlottie-player模块已导入成功!');
};
// 监听错误事件以确认导入失败
script.onerror = () => {
console.error('无法导入dotlottie-player模块!');
};
})();
let course_resources;
let svgElementIds = [];
let originalOrder = null;
// 等待 iframe 加载完成
function waitForIframe(selector, callback) {
const iframe = document.querySelector(selector);
if (iframe && iframe.contentDocument.readyState === 'complete') {
callback(iframe); // iframe 已加载,执行回调
} else {
setTimeout(() => waitForIframe(selector, callback), 50); // 等待 50ms 后重试
}
}
// 获取 SVG 元素 ID (仅数字)
function getSvgElementIds(iframe) {
const iframeDocument = iframe.contentDocument;
const targetSvgElement = iframeDocument.querySelector("body > svg > g");
const gElementsWithId = targetSvgElement.querySelectorAll("g[id]");
// 过滤出纯数字 ID
const numericIds = Array.from(gElementsWithId)
.filter(element => /^\d+$/.test(element.id)) // 正则表达式匹配纯数字
.map(element => element.id);
return numericIds;
}
// 处理 iframe 的回调函数
function handleIframeLoad(iframe) {
console.log("目标 iframe 已加载完成!");
// 跨域处理
try {
svgElementIds = getSvgElementIds(iframe); // 更新外部作用域中的 svgElementIds
console.log(svgElementIds);
} catch (error) {
console.error("无法访问 iframe 内容,可能存在跨域限制:", error);
}
}
// 监视页面的变化
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (node.tagName === 'IFRAME') {
waitForIframe("#xy_app_content iframe", handleIframeLoad);
}
}
}
}
});
// 开始监视
observer.observe(document.body, { childList: true, subtree: true });
function parseContent() {
console.oldLog("::parseContent");
// 在解析课程内容前清除筛选条件
window.currentSearchKeyword = '';
window.currentFilterCategory = '';
const searchInput = document.getElementById("searchInput");
if (searchInput) {
searchInput.value = '';
}
const quickFilterSelect = document.getElementById("quickFilterSelect");
if (quickFilterSelect) {
quickFilterSelect.selectedIndex = 0; // 选择 "全部"
}
var hostname = window.location.hostname;
var download_url = 'https://' + hostname + '/api/jx-oresource/cloud/file_url/';
var download_list = document.getElementById("download_list");
download_list.innerHTML = '<h3 style="color:#fcbb34; font-weight:bold;">课程附件清单</h3>';
for (let i in course_resources) {
let resource = course_resources[i];
let file_name = resource.name;
let create_time = new Date(resource.created_at).toLocaleDateString();
if (resource.mimetype) {
const token = getCookie("HS-prd-access-token"); // 获取 token
fetch(download_url + resource.quote_id, { // 添加 headers
headers: {
Authorization: `Bearer ${token}`
}
})
.then(function(response) {
return response.json();
})
.then(function(data) {
if (data.success) {
let file_url = data.data.url;
var file_container = document.createElement('div');
file_container.className = 'file-item';
file_container.style.display = 'flex';
file_container.style.alignItems = 'center';
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'custom-checkbox';
checkbox.style.marginRight = '10px';
checkbox.setAttribute('data-visible', 'true');
var file_info = document.createElement('a');
file_info.innerHTML = file_name;
file_info.href = file_url;
file_info.target = "_blank";
file_info.className = 'download-link link-style'; // 添加'download-link'类
file_info.style.textDecoration = 'none';
file_info.title = `创建日期:${create_time}`;
file_info.setAttribute('data-created-at', resource.created_at);
file_info.setAttribute('data-origin-name', file_name); // 保存原始文件名称
file_info.setAttribute('data-resource-id', resource.id);
file_info.setAttribute('data-parent-id', resource.parent_id);
file_info.draggable = true; // 允许拖动
file_info.addEventListener('dragstart', (event) => {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', file_url); // 保存下载地址
event.dataTransfer.setData('text/filename', file_name); // 保存文件名
});
file_info.style.display = 'inline-block';
file_info.style.padding = '5px 10px';
file_info.style.borderRadius = '5px'; // 添加圆角
file_info.style.transition = 'all 0.3s ease'; // 过渡效果,用于动画
// 鼠标悬停样式
file_info.addEventListener('mouseover', () => {
file_info.style.backgroundColor = '#FFD180'; // 背景变色
file_info.style.color = 'white'; // 文字颜色
file_info.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)'; // 阴影效果
});
// 鼠标移出样式
file_info.addEventListener('mouseout', () => {
file_info.style.backgroundColor = 'transparent'; // 背景透明
file_info.style.color = '#007bff'; // 恢复文字颜色
file_info.style.boxShadow = 'none'; // 移除阴影
});
// 点击时的样式变化
file_info.addEventListener('mousedown', () => {
file_info.style.transform = 'scale(0.95)'; // 按下效果
});
file_info.addEventListener('mouseup', () => {
file_info.style.transform = 'scale(1)'; // 恢复原样
});
file_info.addEventListener('mouseover', () => {
file_info.style.textDecoration = 'underline';
file_info.style.color = '#000';
file_info.style.fontWeight = "bold";
});
file_info.addEventListener('mouseout', function() {
file_info.style.textDecoration = 'none';
file_info.style.color = '';
});
// 拦截点击事件
file_info.addEventListener('click', function(event) {
event.preventDefault(); // 阻止默认行为
courseDownload(file_url, file_name); // 调用下载函数
});
file_container.appendChild(checkbox);
file_container.appendChild(file_info);
file_container.style.borderBottom = '1px solid #eee';
file_container.style.fontWeight = "bold";
file_container.style.padding = '5px 10px';
file_container.style.justifyContent = 'flex-start';
file_container.style.alignItems = 'center';
console.oldLog('::parse', file_name, file_url);
download_list.append(file_container);
}
}).catch(function(e) {
console.oldLog('!!error', e);
});
}
}
}
let toggleButton;
// 在文档中创建一个容器来放置所有下载进度条
let downloadsContainer = document.getElementById('downloadsContainer');
if (!downloadsContainer) {
downloadsContainer = document.createElement('div');
downloadsContainer.id = 'downloadsContainer';
downloadsContainer.style.position = 'fixed';
downloadsContainer.style.bottom = '150px';
downloadsContainer.style.left = '10px';
downloadsContainer.style.right = 'auto';
downloadsContainer.style.zIndex = '9999';
downloadsContainer.style.backgroundColor = '#FFF3E0';
downloadsContainer.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.2)';
downloadsContainer.style.borderRadius = '8px';
downloadsContainer.style.padding = '30px';
downloadsContainer.style.width = 'auto';
downloadsContainer.style.minWidth = '300px';
downloadsContainer.style.boxSizing = 'border-box';
downloadsContainer.style.margin = '0 auto';
downloadsContainer.style.border = '1px solid #FFD180';
// 应用过渡效果到 transform 属性
downloadsContainer.style.transition = 'transform 0.3s ease-in-out';
// 设置最大高度限制和溢出滚动
downloadsContainer.style.maxHeight = '190px';
downloadsContainer.style.overflowY = 'auto';
downloadsContainer.style.transform = 'translateX(-90%)';
downloadsContainer.addEventListener('dragover', function(e) {
e.preventDefault(); // 阻止默认行为以允许拖放
e.dataTransfer.dropEffect = 'move';
});
downloadsContainer.addEventListener('drop', function(e) {
e.preventDefault(); // 阻止默认行为
var downloadUrl = e.dataTransfer.getData('text/plain');
var filename = e.dataTransfer.getData('text/filename');
if (downloadUrl && filename) {
courseDownload(downloadUrl, filename);
}
});
// 创建收起/展示按钮
toggleButton = document.createElement('button');
toggleButton.title = '点击展开/收折进度条'
toggleButton.textContent = '\u25BA'; // 初始为右箭头
toggleButton.style.position = 'absolute';
toggleButton.style.top = '50%';
toggleButton.style.right = '8px';
toggleButton.style.transform = 'translateY(-50%) rotate(0deg)';
toggleButton.style.zIndex = '10000';
toggleButton.style.border = 'none';
toggleButton.style.boxShadow = '0 2px 4px 0 rgba(0, 0, 0, 0.24), 0 4px 5px 0 rgba(0, 0, 0, 0.19)'; // 添加层次感阴影
toggleButton.style.background = 'linear-gradient(45deg, #FFC107, #FF9800)'; // 渐变背景
toggleButton.style.borderRadius = '8px'; // 圆角
toggleButton.style.padding = '4px 8px'; // 内边距
toggleButton.style.paddingRight = '2px';
toggleButton.style.paddingLeft = '2px';
toggleButton.style.color = 'white'; // 按钮颜色为白色
toggleButton.style.fontSize = '12px'; // 字体大小
toggleButton.style.cursor = 'pointer';
toggleButton.style.transition = 'background-color 0.3s, box-shadow 0.3s, transform 0.3s'; // 过渡效果
toggleButton.style.textShadow = '0 0 0px transparent';
toggleButton.onmouseover = function() {
toggleButton.style.background = 'linear-gradient(45deg, #FFEB3B, #FFC107)'; // 悬停时的渐变背景
toggleButton.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.24), 0 6px 10px 0 rgba(0, 0, 0, 0.19)'; // 悬停时增加阴影的层次和模糊度
toggleButton.style.transform = 'translateY(-60%) rotate(0deg)'; // 按钮向上移动,增加点击感
};
toggleButton.onmouseout = function() {
toggleButton.style.background = 'linear-gradient(45deg, #FFC107, #FF9800)'; // 正常时的渐变背景
toggleButton.style.boxShadow = '0 2px 4px 0 rgba(0, 0, 0, 0.24), 0 4px 5px 0 rgba(0, 0, 0, 0.19)'; // 恢复正常阴影
toggleButton.style.transform = 'translateY(-50%) rotate(0deg)'; // 恢复原位置
};
let isCollapsed = true; // 初始状态设为已收起
toggleButton.onclick = function() {
// 切换收起/展开状态
isCollapsed = !isCollapsed;
if (isCollapsed) {
downloadsContainer.style.transform = `translateX(-90%)`;
toggleButton.textContent = '\u25BA'; // 右箭头,表示容器已收起
} else {
downloadsContainer.style.transform = 'translateX(0)';
toggleButton.textContent = '\u25C4'; // 左箭头,表示容器已展开
}
toggleButton.style.transform = 'translateY(-50%) rotate(360deg)'; // 旋转箭头
};
// 将收起/展示按钮添加到 downloadsContainer
downloadsContainer.appendChild(toggleButton);
// 创建 dotlottie-player 容器
let lottieContainer = document.createElement('div');
lottieContainer.style.position = 'absolute';
lottieContainer.style.top = '0';
lottieContainer.style.left = '0';
lottieContainer.style.width = '100%';
lottieContainer.style.height = '100%';
lottieContainer.style.zIndex = '-1'; // 确保 lottie 在底层
lottieContainer.innerHTML = `
<dotlottie-player src="https://lottie.host/0500ecdb-7f3b-4f73-ab09-155a70f85ce3/ZCLltVc7A4.json"
background="transparent" speed="1"
style="width: 100%; height: 100%;" loop autoplay>
</dotlottie-player>`;
// 将 dotlottie-player 容器添加到 downloadsContainer 的顶部
downloadsContainer.prepend(lottieContainer);
}
// 确保 downloadsContainer 被添加到文档中
if (!document.body.contains(downloadsContainer)) {
document.body.appendChild(downloadsContainer);
}
function updateContainerPosition() {
// 获取窗口的高度
let windowHeight = window.innerHeight;
// 计算容器的高度(包括内边距和边框)
let downloadsContainerHeight = downloadsContainer.offsetHeight;
// 获取当前容器的顶部位置
let currentTop = downloadsContainer.getBoundingClientRect().top;
// 如果容器底部将要超出屏幕,则向上移动容器
if (currentTop + downloadsContainerHeight > windowHeight) {
let newTop = windowHeight - downloadsContainerHeight - 10; // 留出10px的边距
downloadsContainer.style.top = `${newTop}px`;
downloadsContainer.style.bottom = 'auto';
}
}
function updateIndicator() {
let indicator = document.getElementById('progressIndicator');
let progressBarCount = downloadsContainer.querySelectorAll('.progressBar').length;
if (!indicator) {
// 如果指示器不存在,创建指示器
indicator = document.createElement('div');
indicator.id = 'progressIndicator';
indicator.style.position = 'absolute';
indicator.style.top = '10px';
indicator.style.right = '5px';
indicator.style.backgroundColor = '#f00';
indicator.style.backgroundImage = 'linear-gradient(to bottom right, #ffcc80, #ff8c00)';
indicator.style.color = 'white';
indicator.style.textShadow = '0px 1px 2px rgba(0, 0, 0, 0.7)'; // 添加文本阴影
indicator.style.borderRadius = '50%';
indicator.style.width = '20px';
indicator.style.height = '20px';
indicator.style.display = 'flex';
indicator.style.alignItems = 'center';
indicator.style.justifyContent = 'center';
indicator.style.fontSize = '12px';
indicator.style.fontWeight = 'bold';
indicator.style.boxShadow = '0px 2px 4px rgba(0, 0, 0, 0.4)'; // 添加外阴影
indicator.style.transition = 'background-color 0.3s, box-shadow 0.3s'; // 平滑变化的过渡效果
downloadsContainer.appendChild(indicator);
}
// 更新指示器的文本,即使进度条数量为0也显示
indicator.textContent = progressBarCount;
}
function updateDownloadsContainerVisibility() {
// 检查是否存在除 lottieContainer、提示信息、收起按钮和指示器外的其他子元素
const nonEmptyNodes = Array.from(downloadsContainer.children).filter(child => {
return !child.classList.contains('slide-out') && child.tagName !== 'P' &&
child !== downloadsContainer.firstChild && child !== toggleButton &&
child.id !== 'progressIndicator';
});
if (nonEmptyNodes.length === 0) {
if (!downloadsContainer.querySelector('p')) {
let emptyText = document.createElement('p');
emptyText.innerHTML = `
暂无下载内容 ᓚᘏᗢ<br>
<span style="font-size: 10px;">(可拖动文件下载)</span>
`;
emptyText.style.color = '#FF9800';
emptyText.style.textAlign = 'center';
emptyText.style.fontFamily = 'Microsoft YaHei';
emptyText.style.fontSize = '16px';
emptyText.style.fontWeight = 'bold';
emptyText.style.padding = '20px';
emptyText.style.marginTop = '15px';
emptyText.style.borderRadius = '8px';
emptyText.style.opacity = '0';
emptyText.style.transition = 'opacity 1s ease-in-out, transform 1s';
emptyText.style.transform = 'translateY(-20px)';
emptyText.style.top = '50%';
// 添加到DOM后触发动画
setTimeout(() => {
emptyText.style.opacity = '1';
emptyText.style.transform = 'translateY(0)';
}, 100);
// 轻微上下浮动动画
emptyText.animate([
{ transform: 'translateY(0)' },
{ transform: 'translateY(-10px)' },
{ transform: 'translateY(0)' }
], {
duration: 3000,
iterations: Infinity,
easing: 'ease-in-out'
});
downloadsContainer.appendChild(emptyText);
}
// 显示或隐藏进度条容器基于 isProgressBarVisible 变量
downloadsContainer.style.display = isProgressBarVisible ? 'block' : 'none';
} else {
let emptyText = downloadsContainer.querySelector('p');
if (emptyText) {
downloadsContainer.removeChild(emptyText);
}
}
// 更新按钮可见性
toggleButton.style.display = isProgressBarVisible ? 'block' : 'none';
}
updateIndicator()
updateDownloadsContainerVisibility();
function courseDownload(file_url, file_name) {
const token = getCookie("HS-prd-access-token");
const downloadsContainer = document.getElementById('downloadsContainer');
// 创建进度条相关元素。
let progressText = document.createElement('span');
let progressBar = document.createElement('div');
let progressBarContainer = document.createElement('div');
let progressContainer = document.createElement('div');
// 设置文本。
progressText.innerText = `正在下载: ${file_name}`;
progressText.style.color = '#FF9800';
progressText.style.fontWeight = 'bold';
progressText.style.fontSize = '16px';
progressText.style.fontFamily = 'Microsoft YaHei';
progressText.style.textShadow = '1px 1px 2px rgba(0, 0, 0, 0.1)';
progressText.style.padding = '5px 0';
progressText.style.borderRadius = '4px';
// 设置progressBar。
progressBar.style.height = '18px';
progressBar.style.width = '0%';
progressBar.style.borderRadius = '8px';
progressBar.className = 'progressBar'
// 设置progressBarContainer。
progressBarContainer.style.background = '#E0E0E0';
progressBarContainer.style.borderRadius = '8px';
progressBarContainer.style.height = '18px';
progressBarContainer.style.width = '100%';
progressBarContainer.style.overflow = 'hidden';
progressBarContainer.appendChild(progressBar);
// 设置 progressContainer。
progressContainer.style.display = 'flex';
progressContainer.style.flexDirection = 'column'; // 子元素垂直排列
progressContainer.style.alignItems = 'center'; // 子元素在交叉轴上居中对齐
progressContainer.style.justifyContent = 'space-around'; // 子元素沿着主轴均匀分布
progressContainer.appendChild(progressText);
// 创建一个用来显示下载百分比的span元素
let progressPercentText = document.createElement('span');
progressPercentText.style.fontFamily = 'Arial Rounded MT Bold, Helvetica Rounded, Arial, sans-serif';
progressPercentText.style.fontSize = '16px';
progressPercentText.style.marginLeft = '10px'; // 与进度条保持一定距离
progressPercentText.style.position = 'absolute';
progressPercentText.style.left = '0';
progressPercentText.style.top = '0';
progressPercentText.style.width = '100%';
progressPercentText.style.textAlign = 'center';
progressPercentText.style.lineHeight = '18px';
progressPercentText.style.zIndex = '1';
// 调整字体粗细
progressPercentText.style.fontWeight = 'bold';
// 设置 progressBarContainer 的样式并添加到 progressContainer
progressContainer.appendChild(progressBarContainer);
// 插入用于显示百分比的 DOM 元素到 progressContainer
progressBarContainer.style.position = 'relative'; // 相对定位,以便内部的百分比文本能正确定位
progressBarContainer.appendChild(progressPercentText);
progressContainer.classList.add('slide-in');
updateIndicator()
updateDownloadsContainerVisibility();
// 添加 progressContainer 到 downloadsContainer
downloadsContainer.appendChild(progressContainer);
const controller = new AbortController();
const signal = controller.signal;
window.abortControllers = window.abortControllers || {};
window.abortControllers[file_name] = controller;
// 添加文件大小和停止按钮到一个新的横向排列的容器
let controlContainer = document.createElement('div');
controlContainer.style.display = 'flex';
controlContainer.style.justifyContent = 'space-between';
controlContainer.style.alignItems = 'center';
controlContainer.style.width = '100%';
// 创建文件大小显示元素
let fileSizeSpan = document.createElement('span');
// 设置样式为微软雅黑和圆润
fileSizeSpan.style.fontFamily = 'Microsoft YaHei';
fileSizeSpan.style.fontSize = '14px';
fileSizeSpan.style.marginRight = 'auto';
fileSizeSpan.style.fontWeight = 'bold';
let stopButton = document.createElement('button');
stopButton.textContent = '停止';
stopButton.style.padding = '5px 10px';
stopButton.style.border = 'none';
stopButton.style.borderRadius = '5px';
stopButton.style.backgroundColor = '#FF4136';
stopButton.style.color = 'white';
stopButton.style.cursor = 'pointer';
stopButton.style.fontSize = '14px';
stopButton.style.fontWeight = 'bold';
stopButton.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
stopButton.style.transition = 'transform 0.3s ease';
stopButton.style.marginTop = '10px'; // 增加顶部外边距以调整与进度条的距离
stopButton.onmouseover = function() {
stopButton.style.transform = 'scale(1.2)';
};
stopButton.onmouseout = function() {
stopButton.style.transform = 'scale(1)';
};
stopButton.onclick = function() {
console.log(file_name + ' 的下载已经停止了');
controller.abort();
progressContainer.classList.replace('slide-in', 'slide-out');
progressContainer.addEventListener('animationend', () => {
progressContainer.remove();
updateIndicator();
updateDownloadsContainerVisibility();
});
};
controlContainer.appendChild(fileSizeSpan);
controlContainer.appendChild(stopButton);
progressContainer.appendChild(controlContainer);
let styleElement = document.getElementById('progress-bar-styles');
if (!styleElement) {
let styles = document.createElement('style');
styles.id = 'progress-bar-styles';
styles.textContent = `
.progressBar {
background-color: #FF9800;
background-image: repeating-linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
background-size: 40px 40px;
animation: moveBackground 2s linear infinite;
}
@keyframes moveBackground {
from { background-position: 0 0; }
to { background-position: 40px 0; }
}
.slide-in {
animation: slideIn 0.5s ease-out forwards;
}
.slide-out {
animation: slideOut 0.5s ease-in forwards;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideOut {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0; }
}
`;
document.head.appendChild(styles);
}
function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) return '0 Byte';
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
}
updateIndicator()
updateDownloadsContainerVisibility();
updateContainerPosition();
// 使用fetch API开始下载文件
fetch(file_url, {
signal,
headers: {
Authorization: `Bearer ${token}`
}
})
.then(response => {
// 获取文件大小信息
const contentLength = response.headers.get('Content-Length');
if (contentLength) {
// 更新文件大小显示
const fileSize = bytesToSize(contentLength);
fileSizeSpan.innerText = `文件大小: ${fileSize}`;
} else {
// 如果无法获取文件大小,则显示消息
fileSizeSpan.innerText = `无法获取文件大小`;
updateIndicator()
updateDownloadsContainerVisibility();
}
const reader = response.body.getReader();
let receivedBytes = 0;
let chunks = [];
reader.read().then(function processResult(result) {
if (result.done) {
// 下载完成后的处理
const blob = new Blob(chunks, { type: 'application/octet-stream' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = file_name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl);
progressContainer.classList.add('slide-out');
progressContainer.addEventListener('animationend', () => {
progressContainer.remove();
updateIndicator(); // 更新指示器
updateDownloadsContainerVisibility(); // 刷新下载列表的可见性
}, { once: true });
return;
}
// 存储接收到的数据块
chunks.push(result.value);
receivedBytes += result.value.length;
// 计算下载的百分比并更新UI
let percentComplete = (receivedBytes / contentLength) * 100;
progressBar.style.width = `${percentComplete.toFixed(2)}%`; // 更新进度条的宽度
progressPercentText.innerText = `${percentComplete.toFixed(2)}%`;
// 读取下一部分数据
reader.read().then(processResult);
})
.catch(e => {
// 下载失败或被中止时的处理
if (e.name === 'AbortError') {
console.error(`${file_name} 的下载被用户取消了`);
} else {
console.error(e);
}
progressContainer.remove();
updateIndicator()
updateDownloadsContainerVisibility();
});
});
}
window.currentSearchKeyword = '';
window.currentFilterCategory = '';
window.updateUI = function() {
var download_list = document.getElementById("download_list");
if (!document.getElementById("lottie-animation-container")) {
var lottieContainer = document.createElement("div");
lottieContainer.id = "lottie-animation-container";
lottieContainer.innerHTML = `
<dotlottie-player src="https://lottie.host/f6cfdc36-5c9a-4dac-bb71-149cdf2e7d92/VRIhn9vXE5.json"
background="transparent" speed="1"
style="width: 300px; height: 300px; transform: scaleX(-1);" loop autoplay>
</dotlottie-player>`;
lottieContainer.style.position = "absolute";
lottieContainer.style.top = "50%";
lottieContainer.style.right = "0";
lottieContainer.style.transform = "translateY(-50%)";
lottieContainer.style.zIndex = "-1";
download_list.appendChild(lottieContainer);
}
// 为新创建的链接添加拖放事件监听器
const fileLinks = document.querySelectorAll("#download_list .file-item a");
fileLinks.forEach(fileLink => {
fileLink.draggable = true;
fileLink.addEventListener('dragstart', (event) => {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', fileLink.href);
event.dataTransfer.setData('text/filename', fileLink.getAttribute('data-origin-name'));
});
});
var container = document.getElementById("searchAndFilterContainer");
if (!container) {
container = document.createElement("div");
container.id = "searchAndFilterContainer";
container.style.position = "relative"
container.style.display = "flex";
container.style.marginBottom = '10px';
download_list.prepend(container);
}
var searchInput = document.getElementById("searchInput");
if (!searchInput) {
searchInput = document.createElement("input");
searchInput.type = "text";
searchInput.placeholder = "搜索文件名/后缀名";
searchInput.id = "searchInput";
searchInput.style.padding = '5px';
searchInput.style.marginRight = '10px';
searchInput.style.border = '2px solid #ffa500';
searchInput.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)';
searchInput.style.borderRadius = '4px';
searchInput.style.outline = 'none';
searchInput.style.color = '#ffa500';
searchInput.style.fontWeight = 'bold';
searchInput.style.backgroundColor = '#fffbe6';
searchInput.addEventListener("input", function () {
filterList(this.value);
});
container.appendChild(searchInput);
}
var quickFilterSelect = document.getElementById("quickFilterSelect");
if (!quickFilterSelect) {
quickFilterSelect = document.createElement("select");
quickFilterSelect.id = "quickFilterSelect";
quickFilterSelect.style.padding = '5px';
quickFilterSelect.style.marginRight = '10px';
quickFilterSelect.style.border = '2px solid #ffa500';
quickFilterSelect.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)';
quickFilterSelect.style.borderRadius = '4px';
quickFilterSelect.style.outline = 'none';
quickFilterSelect.style.cursor = 'pointer';
quickFilterSelect.style.color = '#ffa500';
quickFilterSelect.style.fontWeight = 'bold';
quickFilterSelect.style.backgroundColor = '#fffbe6';
window.quickFilters.forEach(function (filter) {
var option = document.createElement("option");
option.value = filter.value;
option.text = filter.label;
quickFilterSelect.appendChild(option);
});
quickFilterSelect.addEventListener("change", function () {
filterListByCategory(this.value);
});
container.appendChild(quickFilterSelect);
} else {
quickFilterSelect.innerHTML = ''; // 清空已有的选项
// 重新添加筛选选项
window.quickFilters.forEach(function (filter) {
var option = document.createElement("option");
option.value = filter.value;
option.text = filter.label;
option.style.fontWeight = 'bold';
quickFilterSelect.appendChild(option);
});
// 更新选中状态以匹配当前的筛选条件
for (var i = 0; i < quickFilterSelect.options.length; i++) {
if (quickFilterSelect.options[i].value === window.currentFilterCategory) {
quickFilterSelect.selectedIndex = i;
break;
}
}
}
// 添加排序下拉框
var sortSelect = document.getElementById("sortSelect");
if (!sortSelect) {
// 如果还没有排序下拉框,则创建
sortSelect = document.createElement("select");
sortSelect.id = "sortSelect";
// 修改排序下拉框样式
sortSelect.style.padding = '5px';
sortSelect.style.marginRight = '10px';
sortSelect.style.border = '2px solid #ffa500';
sortSelect.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)'; // 添加内部阴影效果
sortSelect.style.borderRadius = '4px';
sortSelect.style.outline = 'none';
sortSelect.style.cursor = 'pointer';
sortSelect.style.color = '#ffa500'; // 设置文本颜色
sortSelect.style.fontWeight = 'bold';
sortSelect.style.backgroundColor = '#fffbe6';
// 添加排序选项
var sortOptions = [
{ value: '', label: '排序方式' },
{ value: 'date_asc', label: '日期升序' },
{ value: 'date_desc', label: '日期降序' },
{ value: 'xiaoya_order', label: '小雅排序' }
];
sortOptions.forEach(function (option) {
var opt = document.createElement("option");
opt.value = option.value;
opt.text = option.label;
opt.style.fontWeight = 'bold';
sortSelect.appendChild(opt);
});
// 添加排序下拉框事件监听器
sortSelect.addEventListener("change", function () {
sortList(this.value); // 调用原有的日期排序函数
});
container.appendChild(sortSelect); // 将排序下拉框添加到容器中
}
var existingSelectAllCheckbox = document.getElementById("selectAllCheckbox");
if (!existingSelectAllCheckbox) {
var selectAllCheckbox = document.createElement('input');
selectAllCheckbox.type = 'checkbox';
selectAllCheckbox.className = 'custom-checkbox';
selectAllCheckbox.id = 'selectAllCheckbox';
selectAllCheckbox.style.marginRight = '10px';
var selectAllLabel = document.createElement('label');
selectAllLabel.htmlFor = 'selectAllCheckbox';
selectAllLabel.textContent = '全选';
selectAllLabel.style.fontWeight = 'bold';
selectAllLabel.style.color = '#FFA500';
selectAllLabel.style.userSelect = 'none';
var checkboxContainer = document.createElement('div');
checkboxContainer.appendChild(selectAllCheckbox);
checkboxContainer.appendChild(selectAllLabel);
download_list.prepend(checkboxContainer);
checkboxContainer.style.display = 'flex';
checkboxContainer.style.position = "relative"
checkboxContainer.style.alignItems = 'center';
checkboxContainer.style.padding = '5px 10px';
checkboxContainer.style.marginBottom = '10px';
selectAllCheckbox.addEventListener('change', function() {
var checkboxes = document.querySelectorAll("#download_list input[type='checkbox']:not(#selectAllCheckbox)");
checkboxes.forEach(function(checkbox) {
if (checkbox.getAttribute('data-visible') === 'true') {
checkbox.checked = selectAllCheckbox.checked;
var event = new Event('change', {
'bubbles': true,
'cancelable': true
});
checkbox.dispatchEvent(event);
}
});
});
}
var existingBulkDownloadButton = document.getElementById("bulkDownloadButton");
if (!existingBulkDownloadButton) {
var bulkDownloadButton = document.createElement('button');
bulkDownloadButton.innerHTML = '<span style="font-weight: bold;">批量下载</span>';
bulkDownloadButton.id = "bulkDownloadButton";
bulkDownloadButton.title = '点击下载所选文件';
bulkDownloadButton.style.cssText = `
position: fixed;
top: 15px;
right: 15px;
background: linear-gradient(90deg, #ffa500, #ff8c00, #ffa500);
background-size: 200% 100%;
animation: flowingGradient 3s ease infinite;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
transition: filter 0.3s, transform 0.3s; /* 修改 transition 属性 */
`;
bulkDownloadButton.addEventListener('mouseover', function() {
this.style.transform = 'scale(1.05)';
this.style.backgroundColor = '#ffd564';
});
bulkDownloadButton.addEventListener('mouseout', function() {
this.style.transform = 'scale(1)';
this.style.backgroundColor = '#fcbb34';
});
bulkDownloadButton.addEventListener('click', function() {
var checkboxes = document.querySelectorAll("#download_list input[type='checkbox']:checked");
checkboxes.forEach(function(checkbox) {
if (checkbox.checked && checkbox.getAttribute('data-visible') === 'true') {
var container = checkbox.closest('div');
var link = container.querySelector('a');
var file_name = link.getAttribute('data-origin-name'); // 使用原始文件名
var file_url = link.href;
courseDownload(file_url, file_name);
}
});
});
download_list.appendChild(bulkDownloadButton);
window.bulkDownloadButton = bulkDownloadButton;
}
}
function sortList(order) {
const downloadList = document.getElementById("download_list");
const items = Array.from(downloadList.querySelectorAll(".file-item"));
// 构建文件夹 ID 到索引的映射(用于小雅排序)
const folderIndexMap = {};
svgElementIds.forEach((id, index) => {
folderIndexMap[id] = index;
});
items.sort((a, b) => {
const aParentId = a.querySelector('a').getAttribute('data-parent-id');
const bParentId = b.querySelector('a').getAttribute('data-parent-id');
const aId = a.querySelector('a').getAttribute('data-resource-id');
const bId = b.querySelector('a').getAttribute('data-resource-id');
if (order === 'xiaoya_order') {
const aFolderIndex = folderIndexMap[aParentId] || Infinity;
const bFolderIndex = folderIndexMap[bParentId] || Infinity;
if (aFolderIndex !== bFolderIndex) {
return aFolderIndex - bFolderIndex; // 不同文件夹,按文件夹顺序排序
} else {
return aId.localeCompare(bId); // 同一文件夹,按文件 ID 排序
}
} else {
const aDate = new Date(a.querySelector('a').getAttribute('data-created-at'));
const bDate = new Date(b.querySelector('a').getAttribute('data-created-at'));
if (order === 'date_asc') {
return aDate - bDate;
} else if (order === 'date_desc') {
return bDate - aDate;
}
}
});
items.forEach(item => downloadList.appendChild(item));
// 重新应用筛选条件
applyFilters();
}
window.toggleListVisibility = function() {
var download_list = document.getElementById("download_list");
// 切换列表的显示和隐藏
if (download_list.style.maxHeight === '0px' || download_list.style.maxHeight === '') {
// 展开列表
download_list.style.maxHeight = "300px";
download_list.style.opacity = "1";
download_list.style.overflowY = "auto"; // 添加垂直滚动条
} else {
// 折叠列表
download_list.style.maxHeight = "0";
download_list.style.opacity = "0";
download_list.style.overflowY = "hidden"; // 隐藏垂直滚动条
}
}
function filterList(keyword) {
window.currentSearchKeyword = keyword.toLowerCase(); // 保存当前搜索关键词
// 更新搜索框的值
const searchInput = document.getElementById("searchInput");
if (searchInput) {
searchInput.value = keyword;
}
applyFilters();
}
function filterListByCategory(categoryValue) {
window.currentFilterCategory = categoryValue; // 保存当前分类
applyFilters(); // 应用筛选和搜索
}
function applyFilters() {
var searchKeyword = window.currentSearchKeyword;
var filterCategory = window.currentFilterCategory;
var extensions = filterCategory ? filterCategory.split(',').map(ext => ext.trim()) : [];
var containers = document.querySelectorAll("#download_list .file-item");
containers.forEach(function(container) {
var file = container.querySelector("a");
var checkbox = container.querySelector("input[type='checkbox']");
var fileName = file.getAttribute('data-origin-name').toLowerCase();
var fileExtension = fileName.slice(((fileName.lastIndexOf(".") - 1) >>> 0) + 2);
var isSearchMatch = searchKeyword === '' || fileName.includes(searchKeyword);
var isFilterMatch = filterCategory === "" || extensions.includes(fileExtension);
var isVisible = isSearchMatch && isFilterMatch;
container.style.display = isVisible ? "flex" : "none"; // 保持布局结构
checkbox.setAttribute('data-visible', isVisible.toString());
});
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
if (selectAllCheckbox) {
const visibleCheckboxes = Array.from(document.querySelectorAll("#download_list .file-item input[type='checkbox'][data-visible='true']"));
const allVisibleChecked = visibleCheckboxes.every(checkbox => checkbox.checked);
selectAllCheckbox.checked = visibleCheckboxes.length > 0 && allVisibleChecked;
}
// 检查筛选后是否还有可见的课件
var visibleItems = document.querySelectorAll("#download_list .file-item[style*='display: flex']");
// 获取下载列表容器
var download_list = document.getElementById("download_list");
// 获取已有的提示信息元素
var noResultsMessage = download_list.querySelector('p');
if (visibleItems.length === 0) {
// 如果没有可见的课件
if (!noResultsMessage) {
// 如果提示信息元素不存在,则创建
noResultsMessage = document.createElement('p');
noResultsMessage.style.color = '#FFA500'; // 保持橙色
noResultsMessage.style.textAlign = 'center';
noResultsMessage.style.fontWeight = 'bold'; // 加粗字体
noResultsMessage.innerHTML = '<span style="font-size: 1.2em;"><span style="color: red;">没有</span></span>课件( ´・・)ノ(._.`)';
download_list.appendChild(noResultsMessage);
}
} else {
// 如果有可见的课件,则移除之前的提示信息
if (noResultsMessage) {
download_list.removeChild(noResultsMessage);
}
}
}
function add_download_button() {
// 创建下载图标容器
var downloadIconContainer = document.createElement('div');
downloadIconContainer.id = "download_icon_container";
downloadIconContainer.innerHTML = `
<dotlottie-player class="download-icon"
src="https://lottie.host/604bb467-91d8-46f3-a7ce-786e25f8fded/alw6gwjRdU.json"
background="transparent"
speed="1"
style="width: 60px; height: 60px; margin: -15px;"
loop autoplay onclick="toggleListVisibility()"
title="点击展开或关闭列表"></dotlottie-player>
`;
// 设置下载图标容器的样式
downloadIconContainer.style.cssText = `
position: fixed;
right: 10px;
bottom: 10px;
z-index: 9000;
cursor: pointer;
`;
// 创建下载列表容器
var downloadListContainer = document.createElement('div');
downloadListContainer.id = "download_list";
downloadListContainer.style.cssText = `
z-index: 999;
backdrop-filter: blur(10px);
border: 2px solid #fcbb34;
border-radius: 5px;
max-height: 0;
overflow-y: hidden;
padding: 20px;
flex-direction: column;
align-items: flex-start;
transition: max-height 0.5s ease-in-out, opacity 0.5s ease-in-out;
opacity: 0;
position: fixed;
right: 30px;
bottom: 50px;
background-color: white;
`;
downloadListContainer.innerHTML = `
<h3 style="text-align: center;">
<span style="
background: linear-gradient(90deg, #ffa500, #ff8c00, #ffa500);
background-size: 200% 100%;
animation: flowingGradient 3s ease infinite;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight:bold;
">暂无课件♪(´▽`)</span>
</h3>
<br>
<p style="text-align: center; font-size: 12px; color: #888; font-weight:bold;">(在课程首页才能获取到资源)</p>
`;
// 添加下载图标的样式
var downloadIconStyle = document.createElement('style');
downloadIconStyle.innerHTML = `
.download-icon {
padding: 2px;
margin: -20px;
background-color: rgba(255, 255, 255, 0);
border-radius: 10px;
transition: transform 0.3s ease; // 确保动画效果的平滑过渡
cursor: pointer;
}
.download-icon:hover {
background-color: rgba(255, 255, 255, 0.3);
transform: scale(1.1); // 悬停时放大
}
`;
document.head.appendChild(downloadIconStyle);
document.body.appendChild(downloadIconContainer);
document.body.appendChild(downloadListContainer);
}
// 获取cookie
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
function initializeControlPanel() {
const controlPanel = document.createElement('div');
const checkboxesContainer = document.createElement('div');
const downloadInterfaceCheckbox = document.createElement('input');
const downloadButtonCheckbox = document.createElement('input');
const progressBarCheckbox = document.createElement('input');
const toggleButton = document.createElement('button');
const markReadButton = document.createElement('button');
let isControlPanelVisible = false;
// 创建Lottie动画播放器元素
const lottiePlayer = document.createElement('dotlottie-player');
lottiePlayer.setAttribute('src', "https://lottie.host/4f5910c1-63a3-4ffa-965c-7c0e46a29928/PCa2EgPj4N.json");
lottiePlayer.setAttribute('background', 'transparent');
lottiePlayer.setAttribute('speed', '1');
lottiePlayer.style.width = '100%';
lottiePlayer.style.height = '100%';
lottiePlayer.style.position = 'absolute';
lottiePlayer.style.zIndex = '-1';
lottiePlayer.style.top = '0';
lottiePlayer.style.left = '0';
lottiePlayer.setAttribute('loop', '');
lottiePlayer.setAttribute('autoplay', '');
// 设置控制面板样式
controlPanel.style.position = 'fixed';
controlPanel.style.top = '210px';
controlPanel.style.right = isControlPanelVisible ? '30px' : '-500px';
controlPanel.style.zIndex = '10000';
controlPanel.style.backgroundColor = '#fff';
controlPanel.style.padding = '10px';
controlPanel.style.borderRadius = '5px';
controlPanel.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
controlPanel.style.border = '1px solid #fcbb34';
controlPanel.style.transition = 'right 0.3s ease-in-out';
controlPanel.style.overflow = 'hidden';
controlPanel.appendChild(lottiePlayer);
// 创建流光线元素
const glowingLine = document.createElement('div');
glowingLine.style.position = 'relative';
glowingLine.style.height = '3px';
glowingLine.style.bottom = '22px';
glowingLine.style.right = '10px';
glowingLine.style.width = '110%';
glowingLine.style.zIndex = '9000';
glowingLine.style.background = 'linear-gradient(90deg, #ffa500, #ff8c00, #ffa500)';
glowingLine.style.backgroundSize = '200% 100%';
glowingLine.style.animation = 'flowingGradient 3s ease infinite';
// 一键更新按钮
const updateButton = document.createElement('button');
updateButton.textContent = '检查更新';
updateButton.style.marginTop = '10px';
updateButton.style.padding = '8px 15px';
updateButton.style.border = 'none';
updateButton.style.borderRadius = '25px';
updateButton.style.background = 'linear-gradient(90deg, #ffa500, #ff8c00, #ffa500)';
updateButton.style.backgroundSize = '200% 100%';
updateButton.style.animation = 'flowingGradient 3s ease infinite';
updateButton.style.color = '#fff';
updateButton.style.cursor = 'pointer';
updateButton.style.fontWeight = 'bold';
updateButton.style.fontSize = '16px';
updateButton.style.transition = 'all 0.3s ease';
updateButton.style.zIndex = '9999';
// 悬停、点击效果
updateButton.onmouseover = function() {
updateButton.style.filter = 'brightness(1.2)'; // 悬停时增加亮度
updateButton.style.boxShadow = '0px 4px 6px rgba(0, 0, 0, 0.2)';
updateButton.style.transform = 'scale(1.05) translateY(-2px)';
};
updateButton.onmouseout = function() {
updateButton.style.filter = 'brightness(1)'; // 恢复原始亮度
updateButton.style.boxShadow = 'none';
updateButton.style.transform = 'scale(1) translateY(0)';
};
updateButton.onmousedown = function() {
updateButton.style.transform = 'scale(0.95) translateY(2px)'; // 点击时缩小并下移
};
updateButton.onmouseup = function() {
updateButton.style.transform = 'scale(1.05) translateY(-2px)'; // 放开鼠标时恢复悬停效果
};
// 更新按钮容器,用于居中
const updateButtonContainer = document.createElement('div');
updateButtonContainer.style.display = 'flex';
updateButtonContainer.style.justifyContent = 'center';
updateButtonContainer.style.marginTop = '10px';
updateButtonContainer.appendChild(updateButton);
// 按钮悬停效果
updateButton.onmouseover = function() {
updateButton.style.transform = 'scale(1.15)';
};
updateButton.onmouseout = function() {
updateButton.style.transform = 'scale(1)';
};
function compareVersions(currentVersion, latestVersion) {
const currentParts = currentVersion.split('.').map(Number);
const latestParts = latestVersion.split('.').map(Number);
const maxLength = Math.max(currentParts.length, latestParts.length);
for (let i = 0; i < maxLength; i++) {
// 确保每个部分的位数相同
const currentPart = String(currentParts[i] || 0).padStart(3, '0'); // 补零到3位
const latestPart = String(latestParts[i] || 0).padStart(3, '0');
if (latestPart > currentPart) {
return -1; // 最新版本较大
} else if (currentPart > latestPart) {
return 1; // 当前版本较大
}
}
return 0; // 版本相同
}
// 按钮点击事件:检查更新
updateButton.onclick = function() {
const scriptInfo = GM_info;
const currentVersion = scriptInfo.script.version;
// 构建元数据文件的 URL
const metaURL = scriptInfo.script.downloadURL.replace(/\.user\.js$/, '.meta.js');
fetch(metaURL)
.then(response => response.text())
.then(text => {
console.log('Fetched meta data:', text); // 打印获取到的元数据内容
const match = text.match(/\/\/\s*@version\s+([\d.]+)/);
if (match && match[1]) {
const latestVersion = match[1];
// 使用修改后的版本比较函数
const comparisonResult = compareVersions(currentVersion, latestVersion);
if (comparisonResult < 0) {
if (confirm(`检测到新版本 ${latestVersion},是否立即更新?`)) {
window.location.href = scriptInfo.script.downloadURL;
}
} else {
alert('当前已是最新版本!');
}
} else {
throw new Error('无法从更新信息中提取版本号。');
}
})
.catch(error => {
console.error('更新检查失败:', error);
alert('更新检查失败,请稍后再试。');
});
};
// 定义按钮鼠标悬浮时的发光效果
const hoverGlowName = 'hover-glow';
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = `
@keyframes ${hoverGlowName} {
from {
box-shadow: 0 0 8px #fcbb34;
}
to {
box-shadow: 0 0 20px #fcbb34, 0 0 30px #fcbb34;
}
}
#toggleButton:hover {
animation: ${hoverGlowName} 1s ease-in-out infinite alternate;
}
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.switch:hover .slider {
transform: scale(1.1);
animation: ${hoverGlowName} 1s ease-in-out infinite alternate;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: background-color 0.4s, transform 0.4s ease;
transition: 0.4s;
border-radius: 20px;
}
.slider:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #fcbb34;
}
input:checked + .slider:before {
transform: translateX(20px);
}
`;
document.head.appendChild(styleSheet);
// 设置切换按钮样式
const gearIcon = document.createElement('span');
gearIcon.textContent = '⚙️';
gearIcon.style.fontSize = '16px'; // 保持与按钮相同的字体大小
// 齿轮图标的样式
gearIcon.style.transition = 'transform 0.3s ease';
gearIcon.style.display = 'inline-block';
gearIcon.style.verticalAlign = 'middle';
// 将齿轮图标添加到按钮中
toggleButton.appendChild(gearIcon);
toggleButton.id = 'toggleButton';
toggleButton.style.fontSize = '16px';
toggleButton.title = '控制面板';
toggleButton.style.position = 'fixed';
toggleButton.style.top = '210px';
toggleButton.style.right = '0';
toggleButton.style.zIndex = '10001';
toggleButton.style.backgroundColor = '#fcbb34';
toggleButton.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.2)';
toggleButton.style.color = '#fff';
toggleButton.style.border = 'none';
toggleButton.style.borderRadius = '5px 0 0 5px';
toggleButton.style.padding = '10px';
toggleButton.style.cursor = 'pointer';
toggleButton.style.transition = 'right 0.3s ease-in-out, transform 0.3s ease';
// 添加悬停效果
toggleButton.style.transform = 'scale(1)'; // 默认比例
toggleButton.addEventListener('mouseover', function() {
toggleButton.style.transform = 'scale(1.1)'; // 悬停时放大
});
toggleButton.addEventListener('mouseout', function() {
toggleButton.style.transform = 'scale(1)'; // 鼠标移出时恢复原大小
});
// 切换按钮点击事件
toggleButton.addEventListener('click', function() {
isControlPanelVisible = !isControlPanelVisible;
controlPanel.style.right = isControlPanelVisible ? '40px' : '-500px';
gearIcon.style.transform = 'rotate(360deg)';
gearIcon.addEventListener('transitionend', function() {
gearIcon.style.transform = 'none';
}, { once: true });
});
// 初始化复选框状态
function initializeCheckboxState(checkbox, index) {
const savedState = localStorage.getItem(`checkbox-${index}`);
checkbox.checked = savedState === null ? true : savedState === 'true';
progressBarCheckbox.addEventListener('change', () => {
isProgressBarVisible = progressBarCheckbox.checked;
updateVisibility();
});
}
// 保存复选框状态
function saveCheckboxState(checkbox, index) {
localStorage.setItem(`checkbox-${index}`, checkbox.checked);
}
// 初始化复选框
[downloadInterfaceCheckbox, downloadButtonCheckbox, progressBarCheckbox].forEach((checkbox, index) => {
checkbox.type = 'checkbox';
initializeCheckboxState(checkbox, index); // 调用函数以应用保存的状态
checkbox.id = `checkbox-${index}`;
checkbox.style.display = 'none'; // 隐藏原始复选框
const label = document.createElement('label');
label.className = 'switch';
label.htmlFor = `checkbox-${index}`;
const slider = document.createElement('span');
slider.className = 'slider';
label.appendChild(checkbox);
label.appendChild(slider);
const labelText = document.createElement('span');
labelText.textContent = ['右上角下载栏', '右下角下载栏', '左下角进度条'][index];
labelText.style.color = '#fcbb34'; // 橙色文字
labelText.style.marginRight = '10px';
labelText.style.fontWeight = 'bold'; // 文字加粗
labelText.style.verticalAlign = 'middle'; // 设置文字垂直居中
checkbox.addEventListener('change', () => {
if (index === 1) {
updateDownloadListVisibility(); // 仅当右下角下载列表复选框变化时,调用此函数
} else {
updateVisibility(); // 否则调用一般的更新可见性函数
}
saveCheckboxState(checkbox, index); // 保存复选框状态
});
const container = document.createElement('div');
container.style.display = 'flex';
container.style.justifyContent = 'space-between';
container.style.alignItems = 'center';
container.appendChild(labelText);
container.appendChild(label);
checkboxesContainer.appendChild(container);
});
// 将复选框容器添加到控制面板中
controlPanel.appendChild(checkboxesContainer);
// 按钮用于标记所有消息为已读
markReadButton.textContent = '已读所有消息';
markReadButton.style.marginTop = '10px';
markReadButton.style.padding = '5px 10px';
markReadButton.style.border = 'none';
markReadButton.style.borderRadius = '5px';
markReadButton.style.backgroundColor = '#fcbb34'; // 橙色背景
markReadButton.style.color = '#fff'; // 白色文字
markReadButton.style.cursor = 'pointer';
markReadButton.style.fontWeight = 'bold'; // 文字加粗
markReadButton.style.transition = 'transform 0.3s ease'; // 平滑过渡效果
// 按钮悬停效果
markReadButton.onmouseover = function() {
markReadButton.style.transform = 'scale(1.1)'; // 悬停时放大
};
markReadButton.onmouseout = function() {
markReadButton.style.transform = 'scale(1)'; // 鼠标移出时恢复原大小
};
// 绑定点击事件处理函数
markReadButton.onclick = function() {
markAllMessagesAsRead();
};
controlPanel.appendChild(markReadButton);
document.body.appendChild(controlPanel);
document.body.appendChild(toggleButton);
// 定义标记所有消息为已读的函数
async function markAllMessagesAsRead() {
const token = getCookie("HS-prd-access-token");
const hostname = window.location.hostname;
const pageSize = 20;
const messageTypes = ["todo", "group", "personal", "system"];
let hasUnreadMessages = false;
// 遍历每种消息类型
for (const messageType of messageTypes) {
try {
let pageIndex = 1;
let unreadMessageIds = [];
// 循环获取所有未读消息
while (true) {
const apiUrl = `https://${hostname}/api/jx-iresource/message/im/${messageType}?page_size=${pageSize}&page_index=${pageIndex}&msg_status=0`;
const response = await fetch(apiUrl, {
headers: {
"Authorization": `Bearer ${token}`
}
});
if (response.ok) {
const data = await response.json();
const messages = data.data.list;
if (messages.length === 0) {
break;
}
unreadMessageIds = unreadMessageIds.concat(messages.map(message => message.id));
pageIndex++;
} else {
console.error(`获取未读${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, response.status, response.statusText);
alert(`获取未读${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`);
return;
}
}
// 更新消息状态为已读
if (unreadMessageIds.length > 0) {
hasUnreadMessages = true;
const updateResponse = await fetch(`https://${hostname}/api/jx-iresource/message/im/updateStatus`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify({
message_ids: unreadMessageIds,
status: 1
})
});
if (updateResponse.ok) {
console.log(`所有${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息已标记为已读`);
} else {
console.error(`标记${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, updateResponse.status, updateResponse.statusText);
alert(`标记${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`);
return;
}
} else {
console.log(`没有未读${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息`);
}
} catch (error) {
console.error('Error:', error);
alert(`标记${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`);
return;
}
}
if (hasUnreadMessages) {
alert("所有消息已标记为已读");
} else {
console.log("没有未读消息");
alert("没有未读消息");
}
}
// 按钮用于清空所有消息
const clearMessagesButton = document.createElement('button');
clearMessagesButton.textContent = '清空所有消息';
clearMessagesButton.style.marginTop = '10px';
clearMessagesButton.style.marginLeft = '5px'; // 与标记已读按钮留出一些间隙
clearMessagesButton.style.padding = '5px 10px';
clearMessagesButton.style.border = 'none';
clearMessagesButton.style.borderRadius = '5px';
clearMessagesButton.style.backgroundColor = '#fcbb34'; // 橙色背景
clearMessagesButton.style.color = '#fff'; // 白色文字
clearMessagesButton.style.cursor = 'pointer';
clearMessagesButton.style.fontWeight = 'bold'; // 文字加粗
clearMessagesButton.style.transition = 'transform 0.3s ease'; // 平滑过渡效果
// 按钮悬停效果
clearMessagesButton.onmouseover = function() {
clearMessagesButton.style.transform = 'scale(1.1)'; // 悬停时放大
};
clearMessagesButton.onmouseout = function() {
clearMessagesButton.style.transform = 'scale(1)'; // 鼠标移出时恢复原大小
};
// 绑定点击事件处理函数
clearMessagesButton.onclick = function() {
// 弹出确认对话框
if (confirm("确定要清空所有消息吗?该操作无法撤销。")) {
clearAllMessages();
}
};
controlPanel.appendChild(clearMessagesButton);
addShowCourseNameText();
addTipsDisplay();
// 定义清空所有消息的函数
async function clearAllMessages() {
const token = getCookie("HS-prd-access-token");
const hostname = window.location.hostname;
const pageSize = 20;
const messageTypes = ["todo", "group", "personal", "system"];
let hasMessagesToClear = false;
for (const messageType of messageTypes) {
try {
let pageIndex = 1;
let messageIdsToClear = [];
while (true) {
const apiUrl = `https://${hostname}/api/jx-iresource/message/im/${messageType}?page_size=${pageSize}&page_index=${pageIndex}&msg_status=0`;
const response = await fetch(apiUrl, {
headers: {
"Authorization": `Bearer ${token}`
}
});
if (response.ok) {
const data = await response.json();
const messages = data.data.list;
if (messages.length === 0) {
break;
}
messageIdsToClear = messageIdsToClear.concat(messages.map(message => message.id));
pageIndex++;
} else {
console.error(`获取${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, response.status, response.statusText);
alert(`获取${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`);
return;
}
}
if (messageIdsToClear.length > 0) {
hasMessagesToClear = true;
const clearResponse = await fetch(`https://${hostname}/api/jx-iresource/message/im/selected/empty`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify({
message_ids: messageIdsToClear
})
});
if (clearResponse.ok) {
console.log(`所有${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息已清空`);
} else {
const errorText = await clearResponse.text();
console.error(`清空${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, errorText);
alert(`清空${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败: ${errorText}`);
return;
}
}
} catch (error) {
console.error('Error:', error);
alert(`清空${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息出错: ${error.message}`);
return;
}
}
if (hasMessagesToClear) {
alert("所有消息已清空");
} else {
console.log("没有消息可清空");
alert("没有消息可清空");
}
}
// 获取课程名称
function getCourseName() {
const footer = document.querySelector('.img_footer');
if (footer) {
const groupNameElement = footer.querySelector('.group_name');
if (groupNameElement) {
return groupNameElement.textContent; // 返回课程名称
}
}
return '未知'; // 如果没有找到课程名称,返回默认文本
}
function addShowCourseNameText() {
const showCourseNameText = document.createElement('div');
showCourseNameText.style.marginTop = '10px';
showCourseNameText.style.marginLeft = '5px';
showCourseNameText.style.backgroundColor = 'transparent';
showCourseNameText.style.color = '#fcbb34';
showCourseNameText.style.fontWeight = 'bold';
showCourseNameText.style.maxWidth = '200px';
const observer = new MutationObserver((mutations, obs) => {
// 始终尝试更新课程名称
const courseName = getCourseName();
showCourseNameText.textContent = `当前课程:${courseName}`;
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false
});
controlPanel.appendChild(showCourseNameText);
}
function addTipsDisplay() {
const tipsDisplay = document.createElement('div');
tipsDisplay.id = 'tipsDisplay';
// 设置样式
tipsDisplay.style.padding = '10px';
tipsDisplay.style.marginTop = '10px';
tipsDisplay.style.backgroundColor = 'rgba(252, 187, 52, 0.2)'; // 浅橙色背景
tipsDisplay.style.border = '1px solid #fcbb34'; // 橙色边框
tipsDisplay.style.color = '#fcbb34'; // 橙色文字
tipsDisplay.style.borderRadius = '5px';
tipsDisplay.style.fontSize = '14px';
tipsDisplay.style.fontWeight = 'bold';
tipsDisplay.style.textAlign = 'center';
tipsDisplay.style.position = 'relative'; // 用于动画定位
tipsDisplay.style.overflow = 'hidden'; // 隐藏溢出的文本
tipsDisplay.textContent = '右上角下载栏可下载单个文件。';
tipsDisplay.style.opacity = '1';
tipsDisplay.style.transform = 'translateY(0)';
tipsDisplay.style.transition = 'opacity 0.5s, transform 0.5s';
controlPanel.appendChild(tipsDisplay);
controlPanel.appendChild(updateButtonContainer);
controlPanel.appendChild(glowingLine);
// 提示信息列表
const tipsList = [
'右上角下载栏可下载单个文件。',
'右下角下载栏可下载多个文件。',
'可拖动链接至左下角进行下载。',
'进度条位于左下角折叠列表中。',
'搜索栏也可搜索后缀名等信息。',
'右上角支持文档和压缩包下载。',
'浏览器可以直接右键下载图片。',
'视频可使用其他现成插件下载。',
'如有建议请在油猴反馈处留言。',
];
// 当前显示的提示信息索引
let currentTipIndex = 0;
let intervalId; // 用于存储 setInterval 的 ID
// 更换提示信息的函数
function changeTip() {
// 更新当前提示索引
currentTipIndex = (currentTipIndex + 1) % tipsList.length;
// 执行移出动画
tipsDisplay.style.opacity = '0';
tipsDisplay.style.transform = 'translateX(-100%)';
}
// 动画结束后更新提示文本并执行移入动画
tipsDisplay.addEventListener('transitionend', () => {
if (tipsDisplay.style.opacity === '0') {
tipsDisplay.textContent = tipsList[currentTipIndex];
tipsDisplay.style.opacity = '1';
tipsDisplay.style.transform = 'translateX(100%)';
requestAnimationFrame(() => {
tipsDisplay.style.transform = 'translateX(0)';
});
}
});
// 页面可见性变化时处理提示信息切换
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
// 页面可见时,重新开始切换提示信息
intervalId = setInterval(changeTip, 5000);
} else {
// 页面不可见时,停止切换提示信息
clearInterval(intervalId);
}
});
// 开始定时切换提示信息
intervalId = setInterval(changeTip, 5000);
}
function updateVisibility() {
const downloadInterface = document.getElementById('downloadInterface');
const progressBarInterface = document.getElementById('downloadsContainer');
const downloadIconContainer = document.getElementById('download_icon_container');
const downloadListContainer = document.getElementById('download_list');
if (downloadInterface) {
downloadInterface.style.display = downloadInterfaceCheckbox.checked ? 'block' : 'none';
}
if (progressBarInterface) {
const isVisible = progressBarCheckbox.checked;
progressBarInterface.style.display = isVisible ? 'block' : 'none';
isProgressBarVisible = isVisible; // 更新 isProgressBarVisible 的值
}
}
let isFirstLoad = true;
// 专门更新右下角下载列表可见性的函数
function updateDownloadListVisibility() {
const downloadIconContainer = document.getElementById('download_icon_container');
const downloadListContainer = document.getElementById('download_list');
const isVisible = downloadButtonCheckbox.checked;
if (downloadIconContainer && downloadListContainer) {
downloadIconContainer.style.display = isVisible ? 'block' : 'none';
downloadListContainer.style.display = isVisible ? 'block' : 'none';
downloadListContainer.style.opacity = isVisible ? '1' : '0';
downloadListContainer.style.maxHeight = isVisible ? '300px' : '0';
}
}
// 一开始就应用一次设置以匹配初始复选框状态
updateVisibility();
updateDownloadListVisibility();
}
window.onload = ()=> {
add_download_button();
initializeControlPanel();
window.toggleListVisibility();
console.oldLog = console.log;
console.log = (...data) =>{
if (data[0] == 'nodesToData')
{
course_resources = data[1];
console.oldLog('::', course_resources);
parseContent();
return;
}
console.oldLog(...data);
};
// 定义快速筛选的类别选项
window.quickFilters = [
{ label: "全部", value: "" },
{ label: "文档", value: "doc,docx,pdf,txt,odt,rtf,html,htm,xls,xlsx,ppt,pptx,odp" },
{ label: "图片", value: "jpg,jpeg,png,gif,bmp,tiff,svg,webp,tif" },
{ label: "压缩包", value: "zip,rar,7z,gz,bz2,tar" },
// 可以继续添加其他类别
];
window.abortControllers = {};
// 创建一个 MutationObserver 实例
const observer = new MutationObserver(function(mutationsList, observer) {
// 在每次发生 DOM 变化时触发这个回调函数
for(let mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.target.id === 'download_list') {
// 重新添加搜索框和批量下载按钮
window.updateUI();
break; // 处理完毕后退出循环
}
}
});
// 配置需要观察的目标节点和观察的类型
observer.observe(document.body, { childList: true, subtree: true });
};
// 定义要抓取的后缀名
var extensions = [".doc",".pdf",".docx",".ppt",".pptx",".xls",".xlsx",".txt",".odt",".rtf",".zip",".rar",".7z",".tar",".gz",".bz2",".xz"];
// 创建一个元素,用于显示抓取到的 URL
var list = document.createElement("div");
initializeListStyles(list);
// 添加CSS动画样式
var style = document.createElement('style');
style.innerHTML = `
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
`;
document.head.appendChild(style);
// 监听 xhr 请求,检查响应的 URL 是否符合条件
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
var xhr = this;
// 强制将所有请求设置为异步
async = true;
xhr.addEventListener("load", function() {
// 如果 URL 包含指定的后缀名之一
for (var i = 0; i < extensions.length; i++) {
if (url.includes(extensions[i])) {
// 发送一个新的 xhr 请求,获取真正的下载地址
handleXhrResponse(url);
break;
}
}
});
open.call(xhr, method, url, async, user, pass);
};
// 初始化列表样式
function initializeListStyles(element) {
element.id = "downloadInterface";
element.style.position = "fixed";
element.style.top = "100px";
element.style.right = "0";
// 初始状态:缩成圆形,隐藏内容
element.style.width = "50px";
element.style.height = "50px";
element.style.borderRadius = "50%";
element.style.overflow = "hidden";
element.style.transition = "all 0.3s ease-in-out";
element.style.zIndex = "9997";
element.style.padding = "10px";
// 为元素添加渐变背景色
element.style.background = "linear-gradient(270deg, #ffc700, #ff8c00, #ff6500)";
element.style.backgroundSize = "400% 400%";
// 添加动态背景动画的 CSS 规则
var animStyle = document.createElement("style");
animStyle.textContent = `
@keyframes gradientBgAnimation {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.dynamic-gradient {
animation: gradientBgAnimation 15s ease infinite;
}
`;
document.head.appendChild(animStyle);
// 为元素添加动态渐变背景的类
element.classList.add("dynamic-gradient");
// 为元素添加阴影效果
element.style.boxShadow = "0 4px 8px 0 rgba(0, 0, 0, 0.2)";
// 为元素添加动画效果,悬停时放大
element.addEventListener("mouseover", function() {
element.style.transform = "scale(1.1)";
});
element.addEventListener("mouseout", function() {
element.style.transform = "scale(1)";
});
element.innerHTML = "<h3><span style=\"font-family: '微软雅黑', 'Microsoft YaHei', sans-serif; font-weight: bold; font-style: italic; font-size: 16px;\">抓取到的课件</span></h3>";
element.querySelector('h3').style.opacity = 0;
// 创建一个容器来存放Lottie动画
var lottieContainer = document.createElement('div');
lottieContainer.style.position = "absolute";
lottieContainer.style.transition = "all 0.3s ease-in-out"; // 添加 transition
// Lottie 动画容器的初始位置和大小
lottieContainer.style.width = "200%";
lottieContainer.style.height = "200%";
lottieContainer.style.left = "-60%";
lottieContainer.style.top = "-45%";
lottieContainer.style.overflow = "hidden";
lottieContainer.style.zIndex = "9998";
lottieContainer.style.pointerEvents = "none";
// 创建并配置Lottie播放器
var lottiePlayer = document.createElement('dotlottie-player');
lottiePlayer.setAttribute('src', "https://lottie.host/995b71c8-b7aa-45b0-bb77-94b850da5d5d/dyezqbvtia.json");
lottiePlayer.setAttribute('background', "transparent");
lottiePlayer.setAttribute('speed', "1");
lottiePlayer.setAttribute('style', "width: 125%; height: 100%; position: absolute; left: -12.5%;");
lottiePlayer.setAttribute('loop', "");
lottiePlayer.setAttribute('autoplay', "");
// 将Lottie播放器添加到动画容器中
lottieContainer.appendChild(lottiePlayer);
// 将动画容器添加为列表元素的子元素
element.appendChild(lottieContainer);
// 点击事件:展开/收缩列表
element.addEventListener("click", function() {
if (element.style.width === "50px") {
element.style.width = "350px";
element.style.height = "10%";
element.style.borderRadius = "10px";
element.style.overflow = "auto";
lottieContainer.style.width = "70%";
lottieContainer.style.height = "100%";
// 动态计算目标位置
const targetLeft = element.offsetWidth - lottieContainer.offsetWidth + 120;
const targetTop = -20;
// 使用 transition 过渡
lottieContainer.style.left = targetLeft + "px";
lottieContainer.style.top = targetTop + "px";
element.querySelector('h3').style.opacity = 1;
} else {
element.style.width = "50px";
element.style.height = "50px";
element.style.borderRadius = "50%";
element.style.overflow = "hidden";
// 缩小 Lottie 动画容器
lottieContainer.style.width = "200%";
lottieContainer.style.height = "200%";
lottieContainer.style.left = "-60%";
lottieContainer.style.top = "-45%";
element.querySelector('h3').style.opacity = 0;
}
});
document.body.appendChild(element);
}
// 全局变量,用于存储唯一的预览链接元素
var previewLink;
// 全局变量,用于标志是否有异步操作正在进行中
var isDownloading = false;
function handleXhrResponse(url) {
if (isDownloading) {
return; // 如果已经有下载在进行中,则跳过
}
isDownloading = true;
const token = getCookie("HS-prd-access-token");
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
xhr.onload = function () {
// 将响应的文本中的 "}}" 和引号替换为空字符串,去掉多余的符号
var text = xhr.responseText.replace("}}", "").replace(/"/g, "");
var match = text.match(/(http|https):\/\/\S+/);
var content = document.title.split('|')[0].trim();
if (match) {
// 如果预览链接存在,先移除它并带动画效果
if (previewLink) {
previewLink.style.animation = "fadeOut 0.5s forwards";
previewLink.addEventListener('animationend', function () {
if (previewLink && previewLink.parentNode) {
previewLink.parentNode.removeChild(previewLink);
previewLink = null;
createPreviewLink(match[0], content);
}
});
} else {
createPreviewLink(match[0], content);
}
}
isDownloading = false;
}
xhr.send();
}
// 检查是否已经存在预览链接,如果没有,则创建一个临时的提示预览链接
if (!previewLink || !document.contains(previewLink)) {
createPreviewLink("#", "等待课件...( _ _)ノ|");
previewLink.removeAttribute("href"); // 移除 href 属性使其不可点击
previewLink.style.pointerEvents = "none"; // 确保用户无法与之交互
previewLink.style.color = "#DDD"; // 将提示信息的颜色设置为浅灰色
}
function createPreviewLink(href, content) {
// 清空列表中子元素之外所有元素
while (list.childNodes.length > 2) {
list.removeChild(list.lastChild);
}
// 如果存在旧的 previewLink,则更新其内容
if (previewLink && document.contains(previewLink)) {
previewLink.href = href;
previewLink.textContent = content;
previewLink.style.pointerEvents = ""; // 恢复交互
previewLink.style.color = ""; // 恢复原有颜色
} else {
// 创建新预览链接
previewLink = document.createElement("a");
previewLink.style.background = "linear-gradient(to right, #ffffff, #ffecb3)";
previewLink.style.backgroundClip = "text";
previewLink.title = "点击以下载";
previewLink.style.webkitBackgroundClip = "text";
previewLink.style.color = "transparent";
previewLink.style.fontFamily = "'微软雅黑', 'Microsoft YaHei', sans-serif";
previewLink.style.fontWeight = "bold";
previewLink.style.transition = "transform 0.3s, text-shadow 0.3s";
previewLink.style.display = "inline-block";
previewLink.style.position = "relative"; // 为动画设置定位
// 为拖放功能添加拖动数据
previewLink.draggable = true;
previewLink.dataset.downloadUrl = href; // 设置下载网址
previewLink.dataset.filename = content; // 设置文件名
// 重新绑定拖动事件监听器
previewLink.addEventListener('dragstart', (event) => {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', previewLink.dataset.downloadUrl);
event.dataTransfer.setData('text/filename', previewLink.dataset.filename);
});
// 悬停时的样式变化
previewLink.addEventListener('mouseover', () => {
previewLink.style.transform = "scale(1.05)"; // 轻微放大
previewLink.style.textShadow = "0 0 8px rgba(255, 165, 0, 0.7)";
});
previewLink.addEventListener('mouseout', () => {
previewLink.style.transform = "scale(1)"; // 恢复原样
previewLink.style.textShadow = "none"; // 移除发光效果
});
// 点击时的样式变化
previewLink.addEventListener('mousedown', () => {
previewLink.style.transform = "scale(0.95)"; // 按下效果
});
previewLink.addEventListener('mouseup', () => {
previewLink.style.transform = "scale(1.05)"; // 放开后放大效果
});
// 将预览链接添加到列表中
list.appendChild(previewLink);
list.appendChild(document.createElement("br"));
// 更新预览链接的属性
previewLink.href = href;
previewLink.target = "_blank";
previewLink.textContent = content;
// 添加点击事件监听器,在点击时进行下载
previewLink.addEventListener("click", function (event) {
event.preventDefault(); // 阻止默认的点击行为
// 直接使用 courseDownload 函数进行下载
courseDownload(href, content);
});
// 添加动画效果
previewLink.style.animation = "slideIn 0.5s forwards";
}
}