Sleazy Fork is available in English.

MTean 苹果壳 - 图片预览瀑布流(延迟加载)

MT 增加开关选项和图片预览瀑布流布局,延迟加载修复初始化问题(2025-01-29)

  1. // ==UserScript==
  2. // @name MTean 苹果壳 - 图片预览瀑布流(延迟加载)
  3. // @namespace http://tampermonkey.net/
  4. // @version 2025-01-29 3.0
  5. // @description MT 增加开关选项和图片预览瀑布流布局,延迟加载修复初始化问题(2025-01-29)
  6. // @author Yo
  7. // @match http*://xp.m-team.io/*/*
  8. // @match http*://xp.m-team.io/*
  9. // @match http*://kp.m-team.cc/*/*
  10. // @match http*://kp.m-team.cc/*
  11. // @match http*://zp.m-team.io/*/*
  12. // @match http*://zp.m-team.io/*
  13. // @icon https://www.google.com/s2/favicons?sz=64&domain=m-team.io
  14. // @grant GM_addStyle
  15. // @connect *
  16. // @require http://code.jquery.com/jquery-latest.js
  17. // @grant unsafeWindow
  18. // @run-at document-end
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. (function() {
  23. 'use strict';
  24.  
  25. // 定义 localStorage 的键名
  26. const STORAGE_KEY_HOVER = 'isHoverEnabled';
  27. const STORAGE_KEY_SEARCH_API = 'isSearchApiEnabled';
  28. const STORAGE_KEY_MASONRY = 'isMasonryEnabled';
  29.  
  30. // 从 localStorage 中读取用户的选择,如果没有则使用默认值
  31. let isHoverEnabled = localStorage.getItem(STORAGE_KEY_HOVER) !== 'false'; // 默认值为 true
  32. let isSearchApiEnabled = localStorage.getItem(STORAGE_KEY_SEARCH_API) !== 'false'; // 默认值为 true
  33. let isMasonryEnabled = localStorage.getItem(STORAGE_KEY_MASONRY) !== 'false'; // 默认值为 true
  34.  
  35. // 处理接口数据的方法
  36. const _XMLHttpRequest = unsafeWindow.XMLHttpRequest;
  37. function newXHR() {
  38. const xhr = new _XMLHttpRequest();
  39.  
  40. // 保存原始的 send 方法
  41. const originalSend = xhr.send;
  42.  
  43. // 保存原始的 onreadystatechange
  44. const originalOnReadyStateChange = xhr.onreadystatechange;
  45.  
  46. xhr.onreadystatechange = function () {
  47. if (this.readyState === 4) {
  48. if (this.status === 200) {
  49. let response = this.response;
  50. let url = this.responseURL; // 使用 responseURL 替代 url
  51. if (url.indexOf('/api/torrent/search') !== -1 && response !== '') {
  52. try {
  53. const dataParse = JSON.parse(response);
  54. if (isSearchApiEnabled && Array.isArray(dataParse.data.data) && dataParse.data.data.length > 0) {
  55.  
  56. let now = new Date(); // 当前时间
  57. let previous24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000); // 当前时间往前推24小时
  58.  
  59. // 分离过去24小时内和非过去24小时的数据,并为每一项添加 sum 值
  60. let { within24HoursData, otherData } = dataParse.data.data.reduce((acc, item) => {
  61. let sum = 0;
  62. if (item.status) {
  63. sum = parseInt(item.status.leechers, 10) + parseInt(item.status.seeders, 10);
  64. }
  65.  
  66. // 判断是否在当前时间和过去24小时之间
  67. let createdDate = new Date(item.createdDate);
  68. if (createdDate >= previous24Hours && createdDate <= now) {
  69. acc.within24HoursData.push({ ...item, sum });
  70. } else {
  71. acc.otherData.push({ ...item, sum });
  72. }
  73. return acc;
  74. }, { within24HoursData: [], otherData: [] });
  75.  
  76. // 过去24小时内的数据按 createdDate 时间升序排序
  77. within24HoursData.sort((a, b) => new Date(b.createdDate) - new Date(a.createdDate));
  78.  
  79. // 非过去24小时的数据按 sum 值降序排序
  80. otherData.sort((a, b) => b.sum - a.sum);
  81.  
  82. // 合并 24小时内的数据和其他数据
  83. let tempData = [...within24HoursData, ...otherData];
  84.  
  85. const modifiedResponse = JSON.stringify({
  86. ...dataParse,
  87. data: {
  88. ...dataParse.data,
  89. data: tempData,
  90. }
  91. });
  92. Object.defineProperty(this, 'response', {
  93. get: function () {
  94. return modifiedResponse;
  95. }
  96. });
  97. Object.defineProperty(this, 'responseText', {
  98. get: function () {
  99. return modifiedResponse;
  100. }
  101. });
  102. }
  103. } catch (error) {
  104. console.error('Error parsing or modifying response:', error);
  105. }
  106. }
  107. }
  108. if (originalOnReadyStateChange) {
  109. originalOnReadyStateChange.apply(this, arguments);
  110. }
  111. }
  112. };
  113.  
  114. // 重写 send 方法
  115. xhr.send = function (data) {
  116. return originalSend.call(this, data);
  117. };
  118.  
  119. // 添加辅助方法来获取请求头
  120. xhr.getRequestHeader = function (name) {
  121. return this.requestHeaders ? this.requestHeaders[name] : null;
  122. };
  123.  
  124. // 重写 setRequestHeader 方法
  125. const originalSetRequestHeader = xhr.setRequestHeader;
  126. xhr.requestHeaders = {};
  127. xhr.setRequestHeader = function (name, value) {
  128. this.requestHeaders[name] = value;
  129. return originalSetRequestHeader.apply(this, arguments);
  130. };
  131.  
  132. return xhr;
  133. }
  134.  
  135. // 替换为新的 XMLHttpRequest
  136. unsafeWindow.XMLHttpRequest = newXHR;
  137.  
  138. // 创建控制面板
  139. function createControlPanel() {
  140. const panel = document.createElement('div');
  141. panel.style.position = 'fixed';
  142. panel.style.top = '10px';
  143. panel.style.right = '10px';
  144. panel.style.zIndex = '10000';
  145. panel.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
  146. panel.style.padding = '10px';
  147. panel.style.borderRadius = '5px';
  148. panel.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
  149. panel.style.display = 'flex';
  150. panel.style.flexDirection = 'column';
  151. panel.style.gap = '10px';
  152.  
  153. // 悬停显示图片开关
  154. const hoverLabel = document.createElement('label');
  155. hoverLabel.style.display = 'flex';
  156. hoverLabel.style.alignItems = 'center';
  157. hoverLabel.style.gap = '5px';
  158.  
  159. const hoverCheckbox = document.createElement('input');
  160. hoverCheckbox.type = 'checkbox';
  161. hoverCheckbox.checked = isHoverEnabled;
  162. hoverCheckbox.onchange = (e) => {
  163. isHoverEnabled = e.target.checked;
  164. localStorage.setItem(STORAGE_KEY_HOVER, isHoverEnabled); // 存储用户选择
  165. if (!isHoverEnabled) {
  166. clearDom(); // 如果关闭功能,清除所有显示的图片
  167. }
  168. };
  169.  
  170. hoverLabel.appendChild(hoverCheckbox);
  171. hoverLabel.appendChild(document.createTextNode('开启悬停显示图片'));
  172.  
  173. // 处理 /api/torrent/search 开关
  174. const searchApiLabel = document.createElement('label');
  175. searchApiLabel.style.display = 'flex';
  176. searchApiLabel.style.alignItems = 'center';
  177. searchApiLabel.style.gap = '5px';
  178.  
  179. const searchApiCheckbox = document.createElement('input');
  180. searchApiCheckbox.type = 'checkbox';
  181. searchApiCheckbox.checked = isSearchApiEnabled;
  182. searchApiCheckbox.onchange = (e) => {
  183. isSearchApiEnabled = e.target.checked;
  184. localStorage.setItem(STORAGE_KEY_SEARCH_API, isSearchApiEnabled); // 存储用户选择
  185. };
  186.  
  187. searchApiLabel.appendChild(searchApiCheckbox);
  188. searchApiLabel.appendChild(document.createTextNode('开启搜索数据处理'));
  189.  
  190. // 瀑布流布局开关
  191. const masonryLabel = document.createElement('label');
  192. masonryLabel.style.display = 'flex';
  193. masonryLabel.style.alignItems = 'center';
  194. masonryLabel.style.gap = '5px';
  195.  
  196. const masonryCheckbox = document.createElement('input');
  197. masonryCheckbox.type = 'checkbox';
  198. masonryCheckbox.checked = isMasonryEnabled;
  199. masonryCheckbox.onchange = (e) => {
  200. isMasonryEnabled = e.target.checked;
  201. localStorage.setItem(STORAGE_KEY_MASONRY, isMasonryEnabled); // 存储用户选择
  202. applyMasonryLayout(); // 重新应用布局
  203. };
  204.  
  205. masonryLabel.appendChild(masonryCheckbox);
  206. masonryLabel.appendChild(document.createTextNode('开启瀑布流布局'));
  207.  
  208. // 将三个开关添加到面板
  209. panel.appendChild(hoverLabel);
  210. panel.appendChild(searchApiLabel);
  211. panel.appendChild(masonryLabel);
  212. document.body.appendChild(panel);
  213. }
  214.  
  215. // 清除显示的图片
  216. function clearDom() {
  217. var elements = document.getElementsByClassName('imgdom');
  218. var elementsArray = Array.from(elements);
  219. elementsArray.forEach(function (element) {
  220. element.parentNode.removeChild(element);
  221. });
  222. }
  223.  
  224. // 加载悬停显示图片功能
  225. function loadScript() {
  226. const hoverableImages = document.querySelectorAll('.ant-image');
  227. hoverableImages.forEach((image) => {
  228. image.addEventListener('mouseover', (event) => {
  229. if (isHoverEnabled && event && event.target && event.target.previousElementSibling && event.target.previousElementSibling.src && event.target.previousElementSibling.src !== '') {
  230. clearDom();
  231. const div = document.createElement('div');
  232. div.classList.add('imgdom');
  233. let img = document.createElement('img');
  234. img.src = event.target.previousElementSibling.src;
  235. img.alt = '18x';
  236. img.style.cssText = 'width: 70%;object-position: center;object-fit: contain';
  237. img.id = 'imgProId';
  238. div.style.cssText = 'position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);max-width: 100%;';
  239. div.appendChild(img);
  240. document.body.appendChild(div);
  241. }
  242. });
  243. });
  244. document.getElementById('app-content').onscroll = () => clearDom();
  245. }
  246.  
  247. // 应用瀑布流布局
  248. GM_addStyle(`
  249. /* 瀑布流容器 */
  250. .masonry-container {
  251. column-count: 6; /* 默认6列 */
  252. column-gap: 20px;
  253. padding: 10px;
  254. width: calc(100% - 10px); /* 考虑左右padding */
  255. box-sizing: border-box;
  256. margin: 10px auto; /* 上下和左右各20px外边距 */
  257. }
  258.  
  259. /* 响应式布局 */
  260. @media (max-width: 2000px) {
  261. .masonry-container {
  262. column-count: 6;
  263. }
  264. }
  265. @media (max-width: 1600px) {
  266. .masonry-container {
  267. column-count: 5;
  268. }
  269. }
  270. @media (max-width: 1400px) {
  271. .masonry-container {
  272. column-count: 4;
  273. }
  274. }
  275. @media (max-width: 1200px) {
  276. .masonry-container {
  277. column-count: 4;
  278. }
  279. }
  280. @media (max-width: 900px) {
  281. .masonry-container {
  282. column-count: 2;
  283. }
  284. }
  285. @media (max-width: 600px) {
  286. .masonry-container {
  287. column-count: 2;
  288. }
  289. }
  290.  
  291. /* 卡片样式 */
  292. .masonry-item {
  293. break-inside: avoid;
  294. background: #fff;
  295. border-radius: 12px;
  296. box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  297. overflow: hidden;
  298. transition: all 0.3s ease;
  299. cursor: pointer;
  300. margin-bottom: 20px; /* 添加底部间距 */
  301. display: inline-block;
  302. width: 100%;
  303. }
  304.  
  305. /* 图片容器 */
  306. .masonry-item .img-container {
  307. width: 100%;
  308. position: relative;
  309. overflow: hidden;
  310. }
  311.  
  312. .masonry-item .img-container img {
  313. width: 100%;
  314. height: auto;
  315. display: block;
  316. transition: transform 0.3s ease;
  317. }
  318. .masonry-item:hover {
  319. transform: translateY(-5px);
  320. box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
  321. }
  322.  
  323. .masonry-item:hover .img-container img {
  324. transform: scale(1.05);
  325. }
  326.  
  327. /* 信息容器样式 */
  328. .masonry-item .info {
  329. padding: 16px;
  330. cursor: pointer;
  331. }
  332.  
  333. /* 标题样式 */
  334. .masonry-item .title {
  335. font-size: 1rem;
  336. font-weight: 600;
  337. color: #1a1a1a;
  338. margin-bottom: 12px;
  339. line-height: 1.4;
  340. display: -webkit-box;
  341. -webkit-line-clamp: 2;
  342. -webkit-box-orient: vertical;
  343. overflow: hidden;
  344. text-overflow: ellipsis;
  345. }
  346.  
  347. /* 详情样式 */
  348. .masonry-item .details {
  349. display: grid;
  350. grid-template-columns: repeat(2, 1fr);
  351. gap: 8px;
  352. font-size: 0.875rem;
  353. color: #666;
  354. }
  355.  
  356. .masonry-item .details div {
  357. display: flex;
  358. align-items: center;
  359. gap: 4px;
  360. }
  361.  
  362. /* 标签样式 */
  363. .masonry-item .tag {
  364. display: inline-block;
  365. padding: 4px 8px;
  366. background: #f5f5f5;
  367. border-radius: 4px;
  368. font-size: 0.75rem;
  369. color: #666;
  370. margin-right: 6px;
  371. margin-bottom: 6px;
  372. }
  373.  
  374. /* 弹出层样式 */
  375. .modal-overlay {
  376. position: fixed;
  377. top: 0;
  378. left: 0;
  379. right: 0;
  380. bottom: 0;
  381. background: rgba(0, 0, 0, 0.7);
  382. display: flex;
  383. align-items: center;
  384. justify-content: center;
  385. z-index: 1000;
  386. opacity: 0;
  387. visibility: hidden;
  388. transition: all 0.3s ease;
  389. }
  390.  
  391. .modal-overlay.active {
  392. opacity: 1;
  393. visibility: visible;
  394. }
  395.  
  396. .modal-content {
  397. background: #fff;
  398. border-radius: 12px;
  399. max-width: 800px;
  400. width: 90%;
  401. max-height: 90vh;
  402. overflow-y: auto;
  403. transform: translateY(20px);
  404. transition: all 0.3s ease;
  405. }
  406.  
  407. .modal-overlay.active .modal-content {
  408. transform: translateY(0);
  409. }
  410.  
  411. /* 关闭按钮 */
  412. .modal-close {
  413. position: absolute;
  414. top: 20px;
  415. right: 20px;
  416. width: 30px;
  417. height: 30px;
  418. background: #fff;
  419. border-radius: 50%;
  420. display: flex;
  421. align-items: center;
  422. justify-content: center;
  423. cursor: pointer;
  424. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  425. }
  426.  
  427. /* 预览按钮容器 */
  428. .masonry-item .preview-container {
  429. position: absolute;
  430. top: 0;
  431. left: 0;
  432. right: 0;
  433. bottom: 0;
  434. display: flex;
  435. align-items: center;
  436. justify-content: center;
  437. background: rgba(0, 0, 0, 0.5);
  438. opacity: 0;
  439. transition: opacity 0.3s ease;
  440. }
  441.  
  442. .masonry-item:hover .preview-container {
  443. opacity: 1;
  444. }
  445.  
  446. /* 预览按钮样式 */
  447. .preview-btn {
  448. padding: 8px 16px;
  449. background: #fff;
  450. border: none;
  451. border-radius: 20px;
  452. color: #333;
  453. font-size: 14px;
  454. font-weight: 500;
  455. cursor: pointer;
  456. transition: all 0.3s ease;
  457. display: flex;
  458. align-items: center;
  459. gap: 6px;
  460. }
  461.  
  462. .preview-btn:hover {
  463. background: #f0f0f0;
  464. transform: scale(1.05);
  465. }
  466.  
  467. /* 预览按钮图标 */
  468. .preview-btn svg {
  469. width: 16px;
  470. height: 16px;
  471. }
  472.  
  473. /* 修改卡片整体的cursor */
  474. .masonry-item {
  475. cursor: default;
  476. }
  477. `);
  478.  
  479. function applyMasonryLayout() {
  480. const table = document.querySelector('table');
  481. if (!table) return;
  482.  
  483. // 移除现有的瀑布流容器
  484. const existingMasonry = document.querySelector('.masonry-container');
  485. if (existingMasonry) {
  486. existingMasonry.remove();
  487. }
  488.  
  489. if (isMasonryEnabled) {
  490. const masonryContainer = document.createElement('div');
  491. masonryContainer.classList.add('masonry-container');
  492. table.parentNode.insertBefore(masonryContainer, table);
  493.  
  494. const rows = Array.from(table.querySelectorAll('tr')).slice(1); // 跳过表头
  495.  
  496. rows.forEach(row => {
  497. const imgCell = row.querySelector('td:nth-child(2) img');
  498. const titleCell = row.querySelector('td:nth-child(3)');
  499. const dateCell = row.querySelector('td:nth-child(5)');
  500. const sizeCell = row.querySelector('td:nth-child(6)');
  501. const uploadsCell = row.querySelector('td:nth-child(7)');
  502. const downloadsCell = row.querySelector('td:nth-child(8)');
  503.  
  504. if (imgCell && titleCell && dateCell && sizeCell && downloadsCell && uploadsCell) {
  505. const item = createMasonryItem({
  506. img: imgCell,
  507. title: titleCell.textContent.trim(),
  508. date: dateCell.textContent.trim(),
  509. size: sizeCell.textContent.trim(),
  510. downloads: downloadsCell.textContent.trim(),
  511. uploads: uploadsCell.textContent.trim(),
  512. originalRow: row
  513. });
  514.  
  515. masonryContainer.appendChild(item);
  516. }
  517. });
  518.  
  519. // 隐藏原始表格
  520. table.style.display = 'none';
  521. } else {
  522. // 恢复原始表格布局
  523. table.style.display = '';
  524. }
  525. }
  526.  
  527.  
  528. // 获取详情页连接
  529. function getDetailUrl(tdElement) {
  530. // 首先尝试找到 a 标签
  531. const linkElement = tdElement.querySelector('a[href^="/detail/"]');
  532.  
  533. if (linkElement && linkElement.href) {
  534. // 使用 URL 对象解析链接
  535. try {
  536. const url = new URL(linkElement.href);
  537. // 确保路径以 /detail/ 开头
  538. if (url.pathname.startsWith('/detail/')) {
  539. return url.pathname;
  540. }
  541. } catch (e) {
  542. console.error('URL解析错误:', e);
  543. }
  544. }
  545.  
  546. return null;
  547. }
  548.  
  549.  
  550. // 创建瀑布流项
  551. function createMasonryItem(data) {
  552. const item = document.createElement('div');
  553. item.classList.add('masonry-item');
  554.  
  555. // 图片容器
  556. const imgContainer = document.createElement('div');
  557. imgContainer.classList.add('img-container');
  558.  
  559. // 创建新的 img 元素,而不是直接使用 data.img
  560. const img = document.createElement('img');
  561. img.src = data.img.src;
  562. img.alt = data.img.alt || '';
  563. imgContainer.appendChild(img);
  564.  
  565. // 添加预览按钮容器
  566. const previewContainer = document.createElement('div');
  567. previewContainer.classList.add('preview-container');
  568.  
  569. // 创建预览按钮
  570. const previewBtn = document.createElement('button');
  571. previewBtn.classList.add('preview-btn');
  572. previewBtn.innerHTML = `
  573. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  574. <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
  575. <circle cx="12" cy="12" r="3"></circle>
  576. </svg>
  577. 预览
  578. `;
  579.  
  580. // 绑定预览按钮点击事件
  581. previewBtn.addEventListener('click', (e) => {
  582. e.stopPropagation(); // 阻止事件冒泡
  583. showModal(data);
  584. });
  585.  
  586. previewContainer.appendChild(previewBtn);
  587. imgContainer.appendChild(previewContainer);
  588.  
  589. // 信息容器
  590. const info = document.createElement('div');
  591. info.classList.add('info');
  592.  
  593. // 标题
  594. const title = document.createElement('div');
  595. title.classList.add('title');
  596. title.textContent = data.title;
  597.  
  598. // 详情
  599. const details = document.createElement('div');
  600. details.classList.add('details');
  601. details.innerHTML = `
  602. <div>📅 ${data.date}</div>
  603. <div>💾 ${data.size}</div>
  604. <div>⬇️ ${data.downloads}</div>
  605. <div>⬆️ ${data.uploads}</div>
  606. `;
  607.  
  608. // 组装
  609. info.appendChild(title);
  610. info.appendChild(details);
  611. item.appendChild(imgContainer);
  612. item.appendChild(info);
  613.  
  614. // 让整个卡片可以点击链接到原始页面(如果需要的话)
  615. if (data.originalRow) {
  616. const href = getDetailUrl(data.originalRow.querySelector('td:nth-child(3)'));
  617. if (href) {
  618. item.addEventListener('click', () => {
  619. window.open(href, '_blank');
  620. });
  621. }
  622. }
  623.  
  624. return item;
  625. }
  626.  
  627. // 创建并显示模态框
  628. async function showModal(data) {
  629. // 创建遮罩层和基础结构
  630. const overlay = document.createElement('div');
  631. overlay.classList.add('modal-overlay');
  632.  
  633. const content = document.createElement('div');
  634. content.classList.add('modal-content');
  635.  
  636. // 先显示基础内容
  637. content.innerHTML = `
  638. <div style="padding: 24px;">
  639. <div style="text-align: center; margin-bottom: 20px;">
  640. <img src="${data.img.src}" style="max-width: 100%; max-height: 400px; object-fit: contain;">
  641. </div>
  642. <h2 style="margin-bottom: 16px; color: #1a1a1a;">${data.title}</h2>
  643. <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 20px;">
  644. <div>📅 发布日期:${data.date}</div>
  645. <div>💾 文件大小:${data.size}</div>
  646. <div>⬇️ 下载量:${data.downloads}</div>
  647. <div>⬆️ 做种量:${data.uploads}</div>
  648. </div>
  649. </div>
  650. `;
  651.  
  652. // 添加关闭按钮
  653. const closeButton = document.createElement('div');
  654. closeButton.classList.add('modal-close');
  655. closeButton.innerHTML = '✕';
  656. closeButton.onclick = () => {
  657. overlay.classList.remove('active');
  658. setTimeout(() => overlay.remove(), 300);
  659. };
  660.  
  661. overlay.appendChild(content);
  662. overlay.appendChild(closeButton);
  663. document.body.appendChild(overlay);
  664.  
  665. // 显示模态框
  666. requestAnimationFrame(() => {
  667. overlay.classList.add('active');
  668. });
  669.  
  670. // 添加关闭事件
  671. overlay.addEventListener('click', (e) => {
  672. if (e.target === overlay) {
  673. overlay.classList.remove('active');
  674. setTimeout(() => overlay.remove(), 300);
  675. }
  676. });
  677.  
  678. }
  679.  
  680.  
  681. // 监听分页按钮点击事件
  682. function setupPaginationListener() {
  683. const pagination = document.querySelector('.pagination');
  684. if (pagination) {
  685. pagination.addEventListener('click', (e) => {
  686. if (e.target.tagName === 'A' || e.target.parentElement.tagName === 'A') {
  687. // 分页按钮点击后,等待页面内容加载完成,然后重新应用瀑布流布局
  688. setTimeout(() => {
  689. applyMasonryLayout();
  690. }, 1000); // 延迟 1 秒以确保内容加载完成
  691. }
  692. });
  693. }
  694. }
  695.  
  696.  
  697. // 初始化
  698. function init() {
  699. if (location.pathname.indexOf('/browse') !== -1) {
  700. // 延迟 2 秒执行,确保页面内容加载完成
  701. setTimeout(() => {
  702. const table = document.querySelector('table');
  703. if (table) {
  704. loadScript();
  705. applyMasonryLayout();
  706. setupPaginationListener();
  707. }
  708. }, 3000); // 延迟 3 秒
  709. }
  710. }
  711.  
  712. // 监听页面变化
  713. let oldPushState = history.pushState;
  714. history.pushState = function () {
  715. init();
  716. return oldPushState.apply(history, arguments);
  717. };
  718.  
  719. window.addEventListener('popstate', init);
  720.  
  721. // 初始化控制面板和功能
  722. createControlPanel();
  723. init();
  724. })();