// ==UserScript==
// @name 【云】XMXMKB收藏功能增强
// @namespace http://tampermonkey.net/
// @version 2.19
// @description 为XMXMKB网站添加收藏功能,支持列表页和详情页收藏,可导出JSON,支持图片预加载,田字格布局,搜索排序,浏览次数统计,清空未加载浏览记录,黑名单功能,优化图片加载性能,支持分页功能,使用外部懒加载工具,新增iframe预加载功能绕过反爬策略,图片数据持久化存储
// @author You
// @match https://www.xmxmkb.com/search.php?mod=*
// @match https://www.xmxmkb.com/forum.php?mod=viewthread&tid=*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 添加样式
GM_addStyle(`
.favorite-btn {
background: #9e9e9e;
color: white;
border: none;
padding: 0px 10px 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-right: 5px;
transition: all 0.3s;
min-width: 50px;
text-align: center;
}
.favorite-btn:hover {
background: #757575;
transform: scale(1.05);
}
.favorite-btn.favorited {
background: #4caf50;
}
.favorite-btn.favorited:hover {
background: #45a049;
}
.super-like-btn {
background: #9e9e9e;
color: white;
border: none;
padding: 0px 10px 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-right: 5px;
transition: all 0.3s;
min-width: 50px;
text-align: center;
}
.super-like-btn:hover {
background: #757575;
transform: scale(1.05);
}
.super-like-btn.super-liked {
background: #ff9800;
}
.super-like-btn.super-liked:hover {
background: #f57c00;
}
.view-count {
background: #2196f3;
color: white;
border: none;
padding: 0px 10px 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-left: 5px;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 4px;
}
.view-count:hover {
background: #1976d2;
transform: scale(1.05);
}
.view-count-icon {
font-size: 10px;
}
.favorites-panel {
position: fixed;
top: 20px;
right: 20px;
width: 850px;
max-height: 700px;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
font-family: Arial, sans-serif;
overflow: hidden;
}
.favorites-header {
background: #f8f9fa;
padding: 15px 20px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
.favorites-title {
font-weight: bold;
color: #333;
font-size: 16px;
}
.favorites-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #666;
}
.favorites-close:hover {
color: #333;
}
.favorites-content {
max-height: 350px;
overflow-y: auto;
padding: 12px;
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 6px;
}
.favorites-search {
padding: 15px 20px;
border-bottom: 1px solid #ddd;
background: #f8f9fa;
}
.search-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 12px;
box-sizing: border-box;
}
.search-input:focus {
outline: none;
border-color: #007bff;
}
.favorites-sort {
padding: 10px 20px;
border-bottom: 1px solid #ddd;
background: #f8f9fa;
display: flex;
gap: 10px;
align-items: center;
}
.sort-select {
padding: 5px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 12px;
background: white;
}
.sort-select:focus {
outline: none;
border-color: #007bff;
}
.favorite-item {
padding: 4px;
border: 1px solid #eee;
cursor: pointer;
transition: all 0.2s;
border-radius: 4px;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
width: 100%;
max-width: 130px;
min-width: 110px;
}
.list-item-images {
margin-top: 8px;
height: 200px;
overflow: hidden;
border-radius: 4px;
position: relative;
background: #f5f5f5;
z-index: 1;
display: block !important;
}
.list-item-images-container {
display: flex;
height: 100%;
transition: transform 0.3s ease;
}
.list-item-image {
min-width: 33.333%;
height: 200px;
object-fit: cover;
border: none;
opacity: 0;
transition: opacity 0.3s ease;
display: block !important;
visibility: visible !important;
}
.list-item-image.loaded {
opacity: 1;
}
.list-item-image-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0,0,0,0.7);
color: white;
border: none;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: all 0.3s;
}
.list-item-image-nav:hover {
background: rgba(0,0,0,0.9);
transform: translateY(-50%) scale(1.1);
}
.list-item-image-nav.prev {
left: 4px;
}
.list-item-image-nav.next {
right: 4px;
}
.list-item-image-nav.hidden {
display: none;
}
.list-item-image-indicator {
position: absolute;
bottom: 4px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 4px;
z-index: 10;
}
.list-item-image-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(255,255,255,0.5);
cursor: pointer;
transition: background 0.3s;
}
.list-item-image-dot.active {
background: rgba(255,255,255,0.9);
}
.favorite-item:hover {
background: #f8f9fa;
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
transform: translateY(-1px);
}
.favorite-item.super-liked {
border: 2px solid #ff9800;
background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%);
}
.favorite-item.super-liked:hover {
background: linear-gradient(135deg, #ffe0b2 0%, #f8f9fa 100%);
}
.favorite-item.no-images {
border: 2px solid #f44336;
background: linear-gradient(135deg, #ffebee 0%, #ffffff 100%);
}
.favorite-item.no-images:hover {
background: linear-gradient(135deg, #ffcdd2 0%, #f8f9fa 100%);
}
.favorite-item.super-liked.no-images {
border: 2px solid #f44336;
background: linear-gradient(135deg, #ffebee 0%, #fff3e0 100%);
}
.favorite-item.super-liked.no-images:hover {
background: linear-gradient(135deg, #ffcdd2 0%, #ffe0b2 100%);
}
.super-like-badge {
position: absolute;
top: -2px;
right: -2px;
background: #ff9800;
color: white;
border-radius: 50%;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: bold;
z-index: 5;
}
/* 列表项背景色样式 */
.pbw.viewed-not-favorited {
background: linear-gradient(135deg, #ffebee 0%, #ffffff 100%);
border-left: 4px solid #f44336;
}
.pbw.viewed-not-favorited:hover {
background: linear-gradient(135deg, #ffcdd2 0%, #f8f9fa 100%);
}
.pbw.favorited {
background: linear-gradient(135deg, #e8f5e8 0%, #ffffff 100%);
border-left: 4px solid #4caf50;
}
.pbw.favorited:hover {
background: linear-gradient(135deg, #c8e6c9 0%, #f8f9fa 100%);
}
.pbw.viewed-and-favorited {
background: linear-gradient(135deg, #e8f5e8 0%, #ffffff 100%);
border-left: 4px solid #4caf50;
}
.pbw.viewed-and-favorited:hover {
background: linear-gradient(135deg, #c8e6c9 0%, #f8f9fa 100%);
}
/* 列表页左侧浮动按钮容器样式 */
.list-page .list-item-buttons {
// position: absolute;
// left: -350px;
// top: 50%;
// transform: translateY(-50%);
display: flex;
align-items: center;
gap: 5px;
min-width: 200px;
z-index: 10;
}
/* 列表页列表项相对定位,为绝对定位的按钮提供参考 */
.list-page .pbw {
position: relative;
}
/* 详情页按钮容器样式(保持原有样式) */
.detail-page .list-item-buttons {
display: flex;
align-items: center;
gap: 5px;
margin-left: 10px;
}
.favorite-item:last-child {
margin-bottom: 0;
}
.favorite-title {
font-size: 10px;
color: #333;
margin-top: 4px;
font-weight: 500;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
line-height: 1.2;
}
.favorite-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 4px;
font-size: 9px;
color: #666;
}
.favorite-views {
display: flex;
align-items: center;
gap: 2px;
}
.favorite-views-icon {
font-size: 8px;
}
.favorite-date {
font-size: 8px;
color: #999;
}
.favorite-images {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1px;
width: 100%;
aspect-ratio: 2;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
max-width: 100%;
}
.favorite-image {
width: 100%;
height: 100%;
object-fit: cover;
border: none;
transition: transform 0.2s;
background: #f5f5f5;
opacity: 0;
transition: opacity 0.3s ease;
}
.favorite-image.loaded {
opacity: 1;
}
.favorite-image:hover {
transform: scale(1.05);
}
.favorite-image.placeholder {
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 8px;
border: none;
cursor: pointer;
transition: all 0.3s;
}
.favorite-image.placeholder:hover {
background: #e0e0e0;
color: #666;
transform: scale(1.05);
}
.favorites-actions {
padding: 15px 20px;
border-top: 1px solid #ddd;
display: flex;
gap: 10px;
}
.favorites-action-btn {
background: #007bff;
color: white;
border: none;
padding: 0px 12px 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
flex: 1;
}
.favorites-action-btn:hover {
background: #0056b3;
}
.favorites-action-btn.danger {
background: #dc3545;
}
.favorites-action-btn.danger:hover {
background: #c82333;
}
.favorites-toggle {
display: none; /* 隐藏原始按钮,由下拉菜单替代 */
}
.favorites-toggle:hover {
background: #0056b3;
}
/* 右上角下拉菜单样式 */
.top-right-dropdown {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
}
.dropdown-arrow {
background: #007bff;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
display: flex;
align-items: center;
gap: 5px;
transition: all 0.3s;
}
.dropdown-arrow:hover {
background: #0056b3;
}
.dropdown-arrow .arrow-icon {
transition: transform 0.3s;
}
.dropdown-arrow.active .arrow-icon {
transform: rotate(180deg);
}
.dropdown-menu {
position: absolute;
top: 100%;
right: 0;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
min-width: 200px;
display: none;
z-index: 10001;
}
.dropdown-menu.show {
display: block;
}
.dropdown-item {
display: block;
width: 100%;
padding: 10px 15px;
border: none;
background: none;
text-align: left;
cursor: pointer;
font-size: 13px;
color: #333;
border-bottom: 1px solid #eee;
transition: background-color 0.2s;
}
.dropdown-item:last-child {
border-bottom: none;
}
.dropdown-item:hover {
background-color: #f8f9fa;
}
.dropdown-item.favorites {
color: #007bff;
}
.dropdown-item.preload {
color: #9c27b0;
}
.dropdown-item.blacklist {
color: #333;
}
.dropdown-item.clear {
color: #dc3545;
}
.preload-all-btn {
display: none; /* 隐藏原始按钮,由下拉菜单替代 */
}
.preload-all-btn:hover {
background: #7b1fa2;
transform: scale(1.05);
}
.preload-all-btn.loading {
background: #ff9800;
pointer-events: none;
}
.preload-all-btn.loading::after {
content: '';
display: inline-block;
width: 12px;
height: 12px;
border: 2px solid transparent;
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 5px;
}
.clear-unloaded-views-btn {
display: none; /* 隐藏原始按钮,由下拉菜单替代 */
}
.clear-unloaded-views-btn:hover {
background: #c2185b;
transform: scale(1.05);
}
.clear-unloaded-views-btn.loading {
background: #ff9800;
pointer-events: none;
}
.clear-unloaded-views-btn.loading::after {
content: '';
display: inline-block;
width: 12px;
height: 12px;
border: 2px solid transparent;
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 5px;
}
/* 黑名单功能样式 */
.blacklist-toggle {
display: none; /* 隐藏原始按钮,由下拉菜单替代 */
}
.blacklist-toggle:hover {
background: #555;
transform: scale(1.05);
}
.blacklist-panel {
position: fixed;
top: 20px;
right: 20px;
width: 400px;
max-height: 600px;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
font-family: Arial, sans-serif;
overflow: hidden;
display: none;
}
.blacklist-header {
background: #333;
color: white;
padding: 15px 20px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
.blacklist-title {
font-weight: bold;
font-size: 16px;
}
.blacklist-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: white;
}
.blacklist-close:hover {
color: #ccc;
}
.blacklist-content {
padding: 20px;
max-height: 400px;
overflow-y: auto;
}
.blacklist-search {
margin-bottom: 15px;
}
.blacklist-search-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
margin-bottom: 10px;
}
.blacklist-search-input:focus {
outline: none;
border-color: #333;
}
.blacklist-search-info {
font-size: 12px;
color: #666;
margin-bottom: 10px;
}
.blacklist-input-section {
margin-bottom: 20px;
}
.blacklist-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
margin-bottom: 10px;
}
.blacklist-input:focus {
outline: none;
border-color: #333;
}
.blacklist-add-btn {
background: #333;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.blacklist-add-btn:hover {
background: #555;
}
.blacklist-add-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.blacklist-items {
margin-top: 15px;
}
.blacklist-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 8px;
transition: all 0.3s;
}
.blacklist-item:hover {
background: #e9ecef;
}
.blacklist-item-text {
flex: 1;
font-size: 14px;
color: #333;
word-break: break-all;
}
.blacklist-item-remove {
background: #dc3545;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
margin-left: 10px;
transition: all 0.3s;
}
.blacklist-item-remove:hover {
background: #c82333;
}
.blacklist-actions {
padding: 15px 20px;
border-top: 1px solid #ddd;
display: flex;
gap: 10px;
}
.blacklist-action-btn {
background: #333;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
flex: 1;
transition: all 0.3s;
}
.blacklist-action-btn:hover {
background: #555;
}
.blacklist-action-btn.danger {
background: #dc3545;
}
.blacklist-action-btn.danger:hover {
background: #c82333;
}
.blacklist-count {
background: #dc3545;
color: white;
border-radius: 50%;
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
margin-left: 6px;
}
/* 黑名单列表项样式 */
.pbw.blacklisted {
background: #000 !important;
color: #333 !important;
border-left: 4px solid #333 !important;
opacity: 0.7;
position: relative;
}
.pbw.blacklisted:hover {
background: #000 !important;
opacity: 0.8;
}
.pbw.blacklisted a {
color: #333 !important;
text-decoration: none;
}
.pbw.blacklisted a:hover {
color: #333 !important;
text-decoration: none;
}
.pbw.blacklisted .list-item-buttons {
opacity: 0.3;
pointer-events: none;
}
.pbw.blacklisted .list-item-buttons button {
background: #666 !important;
color: #999 !important;
cursor: not-allowed !important;
}
.pbw.blacklisted .list-item-buttons button:hover {
background: #666 !important;
transform: none !important;
}
/* 黑名单提示样式 */
.blacklist-tooltip {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
white-space: nowrap;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
}
.pbw.blacklisted:hover .blacklist-tooltip {
opacity: 1;
}
.blacklist-tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 4px solid transparent;
border-top-color: #333;
}
.favorites-count {
background: #ff6b6b;
color: white;
border-radius: 50%;
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
margin-left: 6px;
}
.favorites-settings {
padding: 15px 20px;
border-top: 1px solid #ddd;
background: #f8f9fa;
}
.auto-update-toggle {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: #666;
cursor: pointer;
}
.auto-update-toggle input[type="checkbox"] {
margin: 0;
}
.auto-update-toggle span {
user-select: none;
}
.storage-info {
margin-top: 8px;
font-size: 11px;
color: #666;
display: flex;
justify-content: space-between;
align-items: center;
}
.storage-size {
font-weight: bold;
color: #007bff;
}
.storage-warning {
color: #ff9800;
font-weight: bold;
}
.storage-danger {
color: #f44336;
font-weight: bold;
}
.favorites-message {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 15px 20px;
border-radius: 8px;
color: white;
font-size: 14px;
font-weight: bold;
z-index: 10001;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
animation: messageSlideIn 0.3s ease-out;
}
.favorites-message-success {
background: #4caf50;
}
.favorites-message-error {
background: #f44336;
}
.favorites-message-info {
background: #2196f3;
}
@keyframes messageSlideIn {
from {
opacity: 0;
transform: translate(-50%, -50%) scale(0.8);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
.preload-btn {
background: #9c27b0;
color: white;
border: none;
padding: 0px 10px 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-left: 5px;
transition: all 0.3s;
}
.preload-btn:hover {
background: #7b1fa2;
transform: scale(1.05);
}
.preload-btn.preloaded {
background: #4caf50;
}
.preload-btn.preloaded:hover {
background: #45a049;
}
.preload-btn.loading {
background: #ff9800;
pointer-events: none;
}
.preload-btn.loading::after {
content: '';
display: inline-block;
width: 12px;
height: 12px;
border: 2px solid transparent;
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 5px;
}
.blacklist-btn {
background: #333;
color: white;
border: none;
padding: 0px 10px 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-left: 5px;
transition: all 0.3s;
}
.blacklist-btn:hover {
background: #555;
transform: scale(1.05);
}
.blacklist-btn.blacklisted {
background: #dc3545;
}
.blacklist-btn.blacklisted:hover {
background: #c82333;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 图片查看器样式 */
.image-viewer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.9);
z-index: 10003;
display: none;
align-items: center;
justify-content: center;
overflow: hidden;
}
.image-viewer-content {
position: relative;
max-width: 90%;
max-height: 90vh;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.image-viewer img {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
width: auto;
height: auto;
}
.image-viewer-close {
position: absolute;
top: -40px;
right: 0;
background: rgba(255,255,255,0.2);
color: white;
border: none;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.image-viewer-close:hover {
background: rgba(255,255,255,0.3);
transform: scale(1.1);
}
.image-viewer-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(255,255,255,0.2);
color: white;
border: none;
width: 48px;
height: 48px;
border-radius: 50%;
cursor: pointer;
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.image-viewer-nav:hover {
background: rgba(255,255,255,0.3);
transform: translateY(-50%) scale(1.1);
}
.image-viewer-nav.prev {
left: -60px;
}
.image-viewer-nav.next {
right: -60px;
}
.image-viewer-info {
position: absolute;
bottom: -40px;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 14px;
background: rgba(0,0,0,0.5);
padding: 8px 16px;
border-radius: 20px;
}
.image-viewer-counter {
position: absolute;
top: -40px;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 14px;
background: rgba(0,0,0,0.5);
padding: 8px 16px;
border-radius: 20px;
}
/* 图片点击效果 */
.list-item-image {
cursor: pointer;
transition: transform 0.2s;
}
.list-item-image:hover {
transform: scale(1.05);
}
.favorite-image {
cursor: pointer;
transition: transform 0.2s;
}
.favorite-image:hover {
transform: scale(1.05);
}
/* 分页控件样式 */
.pagination-container {
display: flex;
justify-content: center;
align-items: center;
margin: 15px 0;
padding: 5px 0;
border-top: 1px solid #eee;
}
.pagination-btn {
background: #f8f9fa;
border: 1px solid #ddd;
color: #333;
padding: 4px 8px;
margin: 0 3px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.pagination-btn:hover {
background: #e9ecef;
border-color: #adb5bd;
}
.pagination-btn.active {
background: #007bff;
color: white;
border-color: #007bff;
}
.pagination-btn.disabled {
background: #f8f9fa;
color: #adb5bd;
cursor: not-allowed;
border-color: #eee;
}
.page-size-container {
display: flex;
align-items: center;
margin-left: 15px;
font-size: 12px;
color: #666;
}
.page-size-select {
padding: 3px 5px;
border: 1px solid #ddd;
border-radius: 3px;
margin-left: 5px;
font-size: 12px;
}
.pagination-info {
margin-right: 15px;
font-size: 12px;
color: #666;
}
`);
// 图片查看器管理
class ImageViewer {
constructor() {
this.currentImages = [];
this.currentIndex = 0;
this.viewer = null;
this.init();
}
init() {
this.createViewer();
this.bindEvents();
}
createViewer() {
this.viewer = document.createElement('div');
this.viewer.className = 'image-viewer';
this.viewer.innerHTML = `
<div class="image-viewer-content">
<button class="image-viewer-close">×</button>
<button class="image-viewer-nav prev">‹</button>
<button class="image-viewer-nav next">›</button>
<div class="image-viewer-counter"></div>
<div class="image-viewer-info"></div>
<img src="" alt="查看图片" style="max-height: 90vh; max-width: 90%;">
</div>
`;
document.body.appendChild(this.viewer);
// 绑定关闭按钮
this.viewer.querySelector('.image-viewer-close').onclick = () => {
this.hide();
};
// 绑定导航按钮
this.viewer.querySelector('.image-viewer-nav.prev').onclick = () => {
this.prev();
};
this.viewer.querySelector('.image-viewer-nav.next').onclick = () => {
this.next();
};
// 点击背景关闭
this.viewer.onclick = (e) => {
if (e.target === this.viewer) {
this.hide();
}
};
}
bindEvents() {
// 键盘事件
document.addEventListener('keydown', (e) => {
if (this.viewer.style.display === 'flex') {
switch (e.key) {
case 'Escape':
this.hide();
break;
case 'ArrowLeft':
this.prev();
break;
case 'ArrowRight':
this.next();
break;
}
}
});
}
show(images, startIndex = 0) {
if (!images || images.length === 0) return;
this.currentImages = images;
this.currentIndex = startIndex;
this.updateDisplay();
this.viewer.style.display = 'flex';
}
hide() {
this.viewer.style.display = 'none';
this.currentImages = [];
this.currentIndex = 0;
}
prev() {
if (this.currentImages.length === 0) return;
this.currentIndex = (this.currentIndex - 1 + this.currentImages.length) % this.currentImages.length;
this.updateDisplay();
}
next() {
if (this.currentImages.length === 0) return;
this.currentIndex = (this.currentIndex + 1) % this.currentImages.length;
this.updateDisplay();
}
updateDisplay() {
const img = this.viewer.querySelector('img');
const counter = this.viewer.querySelector('.image-viewer-counter');
const info = this.viewer.querySelector('.image-viewer-info');
const prevBtn = this.viewer.querySelector('.image-viewer-nav.prev');
const nextBtn = this.viewer.querySelector('.image-viewer-nav.next');
if (this.currentImages.length > 0) {
// 先清除旧图片的src,避免切换时出现旧图片
img.src = '';
// 设置新图片加载事件
img.onload = () => {
// 图片加载完成后,根据图片比例调整显示
const imgRatio = img.naturalWidth / img.naturalHeight;
const viewerContent = this.viewer.querySelector('.image-viewer-content');
if (imgRatio > 1) {
// 横向图片,以宽度为主
img.style.width = 'auto';
img.style.height = 'auto';
img.style.maxWidth = '90%';
img.style.maxHeight = '90vh';
} else {
// 纵向图片,以高度为主
img.style.width = 'auto';
img.style.height = 'auto';
img.style.maxHeight = '90vh';
img.style.maxWidth = '90%';
}
};
// 设置新图片
img.src = this.currentImages[this.currentIndex];
counter.textContent = `${this.currentIndex + 1} / ${this.currentImages.length}`;
// 显示图片信息(如果有的话)
const imageUrl = this.currentImages[this.currentIndex];
const fileName = imageUrl.split('/').pop().split('?')[0];
info.textContent = fileName;
// 更新导航按钮状态
prevBtn.style.display = this.currentImages.length > 1 ? 'flex' : 'none';
nextBtn.style.display = this.currentImages.length > 1 ? 'flex' : 'none';
}
}
}
// iframe预加载管理
class IframeManager {
constructor() {
this.iframe = null;
this.urlMonitor = null;
this.pendingRequests = new Map(); // 存储待处理的请求
this.requestQueue = []; // 请求队列
this.isProcessing = false; // 是否正在处理请求
this.createIframe();
}
// 创建公共iframe窗口
createIframe() {
this.iframe = document.createElement('iframe');
this.iframe.style.cssText = `
position: fixed;
top: -9999px;
left: -9999px;
width: 1px;
height: 1px;
border: none;
opacity: 0;
pointer-events: none;
z-index: -1;
`;
this.iframe.src = 'about:blank';
document.body.appendChild(this.iframe);
// 监听iframe加载完成
this.iframe.onload = () => {
this.setupUrlMonitor();
};
}
// 设置URL变化监听
setupUrlMonitor() {
try {
const iframeDoc = this.iframe.contentDocument || this.iframe.contentWindow.document;
// 监听iframe内的URL变化
this.urlMonitor = setInterval(() => {
try {
const currentUrl = iframeDoc.URL || iframeDoc.location.href;
if (currentUrl && currentUrl.includes('_dsign')) {
console.log('检测到_dsign参数,URL:', currentUrl);
this.handleDsignUrl(currentUrl);
}
} catch (e) {
// 跨域访问限制,忽略错误
}
}, 500); // 每500毫秒检查一次,提高响应速度
} catch (e) {
console.log('设置URL监听失败:', e);
}
}
// 处理包含_dsign的URL
async handleDsignUrl(url) {
// 查找对应的待处理请求 - 优先匹配最旧的请求
let matchedRequest = null;
let matchedOriginalUrl = null;
// 按时间戳排序,优先处理最旧的请求
const sortedRequests = Array.from(this.pendingRequests.entries())
.sort((a, b) => a[1].timestamp - b[1].timestamp);
for (const [originalUrl, requestData] of sortedRequests) {
// 检查URL是否匹配(更精确的匹配逻辑)
if (this.isUrlMatch(originalUrl, url)) {
matchedRequest = requestData;
matchedOriginalUrl = originalUrl;
break;
}
}
if (matchedRequest && matchedOriginalUrl) {
console.log(`找到匹配的请求: ${matchedOriginalUrl} -> ${url}`);
try {
// 直接从iframe内获取HTML内容,避免重复请求
const iframeDoc = this.iframe.contentDocument || this.iframe.contentWindow.document;
const htmlText = iframeDoc.documentElement.outerHTML;
// 从iframe内的DOM直接提取图片
const images = this.extractImagesFromIframeDOM(iframeDoc);
const preloadData = {
title: matchedRequest.title,
loaded: true,
timestamp: Date.now(),
images: images,
html: htmlText
};
// 调用回调函数
if (matchedRequest.resolve) {
matchedRequest.resolve(preloadData);
}
// 从待处理列表中移除
this.pendingRequests.delete(matchedOriginalUrl);
console.log(`iframe预加载完成: ${matchedOriginalUrl}, 获取到 ${images.length} 张图片`);
} catch (error) {
console.error('从iframe获取内容失败:', error);
if (matchedRequest.reject) {
matchedRequest.reject(error);
}
this.pendingRequests.delete(matchedOriginalUrl);
}
} else {
console.log('未找到匹配的请求,当前待处理请求:', Array.from(this.pendingRequests.keys()));
}
}
// 检查URL是否匹配
isUrlMatch(originalUrl, currentUrl) {
// 提取原始URL的tid参数
const originalMatch = originalUrl.match(/tid=(\d+)/);
const currentMatch = currentUrl.match(/tid=(\d+)/);
if (originalMatch && currentMatch) {
return originalMatch[1] === currentMatch[1];
}
// 如果无法提取tid,则使用包含关系判断
return currentUrl.includes('_dsign');
}
// 从iframe的DOM中直接提取图片URL
extractImagesFromIframeDOM(iframeDoc) {
const images = [];
try {
// 查找ignore_js_op标签中的图片
const ignoreJsOpElements = iframeDoc.querySelectorAll('ignore_js_op');
if (ignoreJsOpElements.length > 0) {
ignoreJsOpElements.forEach(element => {
const imgElements = element.querySelectorAll('img');
imgElements.forEach(img => {
const src = img.zoomfile || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.getAttribute('data-original') || img.getAttribute('file');
if (src && src.trim()) {
const lowerSrc = src.toLowerCase();
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
!lowerSrc.includes('banner') &&
!lowerSrc.includes('ad') &&
!lowerSrc.includes('loading') &&
!lowerSrc.includes('placeholder') &&
(img.width > 50 || img.width === 0) &&
(img.height > 50 || img.height === 0)) {
images.push(src.trim());
}
}
});
});
}
// 如果没有找到ignore_js_op中的图片,查找所有可能的ignore_js_op标签
if (images.length === 0) {
const allIgnoreJsOp = iframeDoc.querySelectorAll('ignore_js_op, [ignore_js_op]');
allIgnoreJsOp.forEach(element => {
const imgElements = element.querySelectorAll('img');
imgElements.forEach(img => {
const src = img.src || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.getAttribute('data-original') || img.getAttribute('file');
if (src && src.trim()) {
const lowerSrc = src.toLowerCase();
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
!lowerSrc.includes('banner') &&
!lowerSrc.includes('ad') &&
!lowerSrc.includes('loading') &&
!lowerSrc.includes('placeholder') &&
(img.width > 50 || img.width === 0) &&
(img.height > 50 || img.height === 0)) {
images.push(src.trim());
}
}
});
});
}
// 去重并限制数量
const uniqueImages = [...new Set(images)];
const limitedImages = uniqueImages.slice(0, 2);
return limitedImages;
} catch (error) {
console.error('从iframe DOM提取图片失败:', error);
}
return images;
}
// 从HTML中提取图片URL(保留作为备用方法)
extractImagesFromHTML(htmlText) {
const images = [];
try {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
// 查找ignore_js_op标签中的图片
const ignoreJsOpElements = doc.querySelectorAll('ignore_js_op');
if (ignoreJsOpElements.length > 0) {
ignoreJsOpElements.forEach(element => {
const imgElements = element.querySelectorAll('img');
imgElements.forEach(img => {
const src = img.zoomfile || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.getAttribute('data-original') || img.getAttribute('file');
if (src && src.trim()) {
const lowerSrc = src.toLowerCase();
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
!lowerSrc.includes('banner') &&
!lowerSrc.includes('ad') &&
!lowerSrc.includes('loading') &&
!lowerSrc.includes('placeholder') &&
(img.width > 50 || img.width === 0) &&
(img.height > 50 || img.height === 0)) {
images.push(src.trim());
}
}
});
});
}
// 如果没有找到ignore_js_op中的图片,查找所有可能的ignore_js_op标签
if (images.length === 0) {
const allIgnoreJsOp = doc.querySelectorAll('ignore_js_op, [ignore_js_op]');
allIgnoreJsOp.forEach(element => {
const imgElements = element.querySelectorAll('img');
imgElements.forEach(img => {
const src = img.src || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.getAttribute('data-original') || img.getAttribute('file');
if (src && src.trim()) {
const lowerSrc = src.toLowerCase();
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
!lowerSrc.includes('banner') &&
!lowerSrc.includes('ad') &&
!lowerSrc.includes('loading') &&
!lowerSrc.includes('placeholder') &&
(img.width > 50 || img.width === 0) &&
(img.height > 50 || img.height === 0)) {
images.push(src.trim());
}
}
});
});
}
// 去重并限制数量
const uniqueImages = [...new Set(images)];
const limitedImages = uniqueImages.slice(0, 2);
return limitedImages;
} catch (error) {
console.error('解析HTML提取图片失败:', error);
}
return images;
}
// 使用iframe预加载URL
async preloadUrlWithIframe(url, title) {
return new Promise((resolve, reject) => {
// 将请求添加到队列
this.requestQueue.push({
url: url,
title: title,
resolve: resolve,
reject: reject,
timestamp: Date.now()
});
// 如果当前没有在处理请求,开始处理队列
if (!this.isProcessing) {
this.processQueue();
}
});
}
// 处理请求队列
async processQueue() {
if (this.isProcessing || this.requestQueue.length === 0) {
return;
}
this.isProcessing = true;
while (this.requestQueue.length > 0) {
const request = this.requestQueue.shift();
const { url, title, resolve, reject } = request;
try {
console.log(`开始处理iframe预加载请求: ${url}`);
// 将请求添加到待处理列表
this.pendingRequests.set(url, {
title: title,
targetUrl: url,
resolve: resolve,
reject: reject,
timestamp: Date.now()
});
// 在iframe中打开URL
this.iframe.src = url;
// 等待当前请求完成或超时
await this.waitForRequest(url, 30000); // 30秒超时
} catch (error) {
console.error(`处理iframe预加载请求失败: ${url}`, error);
if (reject) {
reject(error);
}
this.pendingRequests.delete(url);
}
}
this.isProcessing = false;
}
// 等待请求完成
waitForRequest(url, timeout) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const checkInterval = setInterval(() => {
// 检查请求是否已完成(从pendingRequests中移除)
if (!this.pendingRequests.has(url)) {
clearInterval(checkInterval);
resolve();
return;
}
// 检查是否超时
if (Date.now() - startTime > timeout) {
clearInterval(checkInterval);
console.log('iframe预加载超时:', url);
this.pendingRequests.delete(url);
reject(new Error('iframe预加载超时'));
}
}, 1000); // 每秒检查一次
});
}
// 清理资源
destroy() {
if (this.urlMonitor) {
clearInterval(this.urlMonitor);
this.urlMonitor = null;
}
if (this.iframe && this.iframe.parentNode) {
this.iframe.parentNode.removeChild(this.iframe);
this.iframe = null;
}
// 清理所有待处理的请求
this.pendingRequests.forEach((requestData, url) => {
if (requestData.reject) {
requestData.reject(new Error('iframe管理器已销毁'));
}
});
this.pendingRequests.clear();
// 清理请求队列
this.requestQueue.forEach(request => {
if (request.reject) {
request.reject(new Error('iframe管理器已销毁'));
}
});
this.requestQueue = [];
this.isProcessing = false;
}
}
// 预加载管理
class PreloadManager {
constructor() {
this.preloadedUrls = new Map(); // 存储预加载的URL和对应的数据
this.preloadQueue = []; // 预加载队列
this.maxPreloads = 100; // 增加最大预加载数量,避免频繁清理
this.iframeManager = new IframeManager(); // 创建iframe管理器
this.loadPersistedImages(); // 加载持久化的图片数据
}
// 加载持久化的图片数据
loadPersistedImages() {
try {
const saved = localStorage.getItem('xmxmkb_preloaded_images');
if (saved) {
const data = JSON.parse(saved);
// 检查数据是否过期(7天)
const maxAge = 7 * 24 * 60 * 60 * 1000; // 7天
Object.keys(data).forEach(url => {
const item = data[url];
if (item && item.timestamp && (Date.now() - item.timestamp < maxAge)) {
this.preloadedUrls.set(url, item);
}
});
console.log(`加载了 ${this.preloadedUrls.size} 个持久化的图片数据`);
}
} catch (error) {
console.error('加载持久化图片数据失败:', error);
}
}
// 保存图片数据到localStorage
saveImageData(url, preloadData) {
try {
const saved = localStorage.getItem('xmxmkb_preloaded_images') || '{}';
const data = JSON.parse(saved);
// 只保存有图片的数据
if (preloadData.images && preloadData.images.length > 0) {
data[url] = {
title: preloadData.title,
images: preloadData.images,
timestamp: preloadData.timestamp,
loaded: true
};
// 限制保存的数量,避免localStorage过大
const keys = Object.keys(data);
if (keys.length > this.maxPreloads) {
// 按时间戳排序,删除最旧的数据
const sortedKeys = keys.sort((a, b) => (data[a].timestamp || 0) - (data[b].timestamp || 0));
const keysToDelete = sortedKeys.slice(0, keys.length - this.maxPreloads);
keysToDelete.forEach(key => delete data[key]);
}
localStorage.setItem('xmxmkb_preloaded_images', JSON.stringify(data));
console.log(`保存图片数据: ${url}, 图片数量: ${preloadData.images.length}`);
}
} catch (error) {
console.error('保存图片数据失败:', error);
}
}
// 获取持久化的图片数据
getPersistedImageData(url) {
return this.preloadedUrls.get(url);
}
// 预加载URL - 优先使用iframe方式,失败则回退到fetch方式
async preloadUrl(url, title, retryCount = 0) {
if (this.preloadedUrls.has(url)) {
return Promise.resolve(this.preloadedUrls.get(url));
}
// 如果预加载数量超过限制,移除最旧的(但保留当前页面的预加载内容)
if (this.preloadedUrls.size >= this.maxPreloads) {
this.cleanupOldPreloads();
}
const maxRetries = 2; // 最大重试次数
try {
console.log(`开始预加载: ${url} (尝试 ${retryCount + 1}/${maxRetries + 1})`);
// 优先尝试iframe方式
try {
console.log(`尝试iframe预加载: ${url}`);
const preloadData = await this.iframeManager.preloadUrlWithIframe(url, title);
this.preloadedUrls.set(url, preloadData);
// 保存到localStorage
this.saveImageData(url, preloadData);
console.log(`iframe预加载成功: ${url}, 获取到 ${preloadData.images.length} 张图片`);
return preloadData;
} catch (iframeError) {
console.log('iframe预加载失败,回退到fetch方式:', iframeError);
}
// iframe方式失败,回退到原来的fetch方式
let htmlText = '';
// 使用fetch获取HTML内容
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'User-Agent': navigator.userAgent,
'Referer': window.location.href
},
mode: 'cors',
credentials: 'include' // 携带cookies保持登录状态
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
htmlText = await response.text();
} catch (fetchError) {
console.log('fetch请求失败,尝试使用GM_xmlhttpRequest:', fetchError);
// 如果fetch失败,尝试使用GM_xmlhttpRequest(如果可用)
if (typeof GM_xmlhttpRequest !== 'undefined') {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: url,
headers: {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'User-Agent': navigator.userAgent,
'Referer': window.location.href
},
cookie: document.cookie, // 携带当前页面的cookie
onload: function(response) {
if (response.status === 200) {
const images = this.extractImagesFromHTML(response.responseText);
const preloadData = {
title: title,
loaded: true,
timestamp: Date.now(),
images: images,
html: response.responseText
};
this.preloadedUrls.set(url, preloadData);
// 保存到localStorage
this.saveImageData(url, preloadData);
resolve(preloadData);
} else {
reject(new Error(`HTTP ${response.status}: ${response.statusText}`));
}
}.bind(this),
onerror: function(error) {
reject(new Error('GM_xmlhttpRequest请求失败'));
}
});
});
} else {
throw fetchError;
}
}
// 解析HTML获取图片
const images = this.extractImagesFromHTML(htmlText);
console.log(`fetch预加载完成: ${url}, 获取到 ${images.length} 张图片`);
const preloadData = {
title: title,
loaded: true,
timestamp: Date.now(),
images: images,
html: htmlText // 保存HTML以备后用
};
this.preloadedUrls.set(url, preloadData);
// 保存到localStorage
this.saveImageData(url, preloadData);
return preloadData;
} catch (error) {
console.error(`预加载失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`, url, error);
// 如果还有重试次数,则重试
if (retryCount < maxRetries) {
console.log(`等待1秒后重试: ${url}`);
await new Promise(resolve => setTimeout(resolve, 1000));
return this.preloadUrl(url, title, retryCount + 1);
}
throw error;
}
}
// 从HTML中提取图片URL
extractImagesFromHTML(htmlText) {
const images = [];
try {
// 创建临时DOM解析HTML
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
console.log(doc, 'doc111111111');
// 查找ignore_js_op标签中的图片
const ignoreJsOpElements = doc.querySelectorAll('ignore_js_op');
if (ignoreJsOpElements.length > 0) {
ignoreJsOpElements.forEach(element => {
const imgElements = element.querySelectorAll('img');
imgElements.forEach(img => {
const src = img.zoomfile || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.getAttribute('data-original') || img.getAttribute('file');
if (src && src.trim()) {
const lowerSrc = src.toLowerCase();
// 过滤掉不需要的图片
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
!lowerSrc.includes('banner') &&
!lowerSrc.includes('ad') &&
!lowerSrc.includes('loading') &&
!lowerSrc.includes('placeholder') &&
(img.width > 50 || img.width === 0) &&
(img.height > 50 || img.height === 0)) {
images.push(src.trim());
}
}
});
});
}
// 如果没有找到ignore_js_op中的图片,查找所有可能的ignore_js_op标签
if (images.length === 0) {
const allIgnoreJsOp = doc.querySelectorAll('ignore_js_op, [ignore_js_op]');
allIgnoreJsOp.forEach(element => {
const imgElements = element.querySelectorAll('img');
imgElements.forEach(img => {
const src = img.src || img.getAttribute('data-src') || img.getAttribute('data-lazy-src') || img.getAttribute('data-original') || img.getAttribute('file');
if (src && src.trim()) {
const lowerSrc = src.toLowerCase();
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
!lowerSrc.includes('banner') &&
!lowerSrc.includes('ad') &&
!lowerSrc.includes('loading') &&
!lowerSrc.includes('placeholder') &&
(img.width > 50 || img.width === 0) &&
(img.height > 50 || img.height === 0)) {
images.push(src.trim());
}
}
});
});
}
// 去重并限制数量
const uniqueImages = [...new Set(images)];
const limitedImages = uniqueImages.slice(0, 2); // 最多取2张图片
console.log(`从HTML中的ignore_js_op标签提取到 ${images.length} 张图片,去重后 ${uniqueImages.length} 张,限制后 ${limitedImages.length} 张`);
return limitedImages;
} catch (error) {
console.error('解析HTML提取图片失败:', error);
}
return images;
}
// 获取预加载的内容
getPreloaded(url) {
return this.preloadedUrls.get(url);
}
// 检查是否已预加载
isPreloaded(url) {
return this.preloadedUrls.has(url);
}
// 移除预加载
removePreload(url) {
const preload = this.preloadedUrls.get(url);
this.preloadedUrls.delete(url);
// 清理对应的图片显示
if (window.pageHandler && window.pageHandler.ui) {
const items = document.querySelectorAll('.pbw');
items.forEach(item => {
const link = item.querySelector('a');
if (link && link.href === url) {
const imagesContainer = item.querySelector('.list-item-images');
if (imagesContainer) {
imagesContainer.remove();
}
// 重置预加载按钮状态
const preloadBtn = item.querySelector('.preload-btn');
if (preloadBtn) {
preloadBtn.classList.remove('preloaded');
preloadBtn.textContent = '预加载';
}
}
});
}
}
// 智能清理旧预加载内容
cleanupOldPreloads() {
// 获取当前页面的所有URL
const currentPageUrls = new Set();
const items = document.querySelectorAll('.pbw');
items.forEach(item => {
const link = item.querySelector('a');
if (link) {
currentPageUrls.add(link.href);
}
});
// 按时间戳排序,保留最新的和当前页面的
const sortedUrls = Array.from(this.preloadedUrls.entries())
.sort((a, b) => a[1].timestamp - b[1].timestamp);
// 移除最旧的,但保留当前页面的内容
let removedCount = 0;
for (const [url, preload] of sortedUrls) {
// 如果是当前页面的URL,跳过
if (currentPageUrls.has(url)) {
continue;
}
// 移除最旧的预加载内容
this.removePreload(url);
removedCount++;
// 如果移除了一些内容,检查是否还需要继续移除
if (this.preloadedUrls.size < this.maxPreloads * 0.8) {
break;
}
}
console.log(`清理了 ${removedCount} 个旧预加载内容,当前剩余: ${this.preloadedUrls.size}`);
}
// 清空所有预加载
clearAll() {
this.preloadedUrls.clear();
// 清理iframe管理器
if (this.iframeManager) {
this.iframeManager.destroy();
this.iframeManager = new IframeManager(); // 重新创建iframe管理器
}
// 清理所有图片显示
if (window.pageHandler && window.pageHandler.ui) {
const imagesContainers = document.querySelectorAll('.list-item-images');
imagesContainers.forEach(container => {
container.remove();
});
// 重置所有预加载按钮状态
const preloadBtns = document.querySelectorAll('.preload-btn');
preloadBtns.forEach(btn => {
btn.classList.remove('preloaded', 'loading');
btn.textContent = '预加载';
});
}
}
// 清空非当前页面的预加载内容
clearNonCurrentPagePreloads() {
// 获取当前页面的所有URL
const currentPageUrls = new Set();
const items = document.querySelectorAll('.pbw');
items.forEach(item => {
const link = item.querySelector('a');
if (link) {
currentPageUrls.add(link.href);
}
});
// 只移除非当前页面的预加载内容
const urlsToRemove = [];
this.preloadedUrls.forEach((preload, url) => {
if (!currentPageUrls.has(url)) {
urlsToRemove.push(url);
}
});
urlsToRemove.forEach(url => {
this.removePreload(url);
});
console.log(`清空了 ${urlsToRemove.length} 个非当前页面的预加载内容`);
}
}
// 黑名单管理
class BlacklistManager {
constructor() {
this.blacklist = this.loadBlacklist();
this.initCrossTabSync();
}
initCrossTabSync() {
// 监听其他标签页的数据更新
window.addEventListener('storage', (e) => {
if (e.key === 'xmxmkb_blacklist' && e.newValue) {
try {
this.blacklist = JSON.parse(e.newValue);
console.log('收到其他标签页的黑名单数据更新');
// 触发UI更新
this.notifyDataChanged();
} catch (error) {
console.error('解析其他标签页黑名单数据失败:', error);
}
}
});
// 监听自定义事件(同一标签页内的更新)
window.addEventListener('blacklistUpdated', (e) => {
if (e.detail && e.detail.blacklist) {
this.blacklist = e.detail.blacklist;
console.log('收到同标签页的黑名单数据更新');
this.notifyDataChanged();
}
});
}
notifyDataChanged() {
// 通知UI更新
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateBlacklistToggleButton();
window.pageHandler.ui.updateBlacklistPanel();
window.pageHandler.updateAllBlacklistStates();
}
}
loadBlacklist() {
const saved = localStorage.getItem('xmxmkb_blacklist') || '[]';
try {
return JSON.parse(saved);
} catch (e) {
console.error('加载黑名单数据失败:', e);
return [];
}
}
saveBlacklist() {
try {
localStorage.setItem('xmxmkb_blacklist', JSON.stringify(this.blacklist));
// 触发自定义事件,通知其他标签页数据已更新
window.dispatchEvent(new CustomEvent('blacklistUpdated', {
detail: { blacklist: this.blacklist }
}));
} catch (e) {
console.error('保存黑名单数据失败:', e);
}
}
addBlacklistItem(text) {
const trimmedText = text.trim();
if (!trimmedText) return false;
if (!this.blacklist.includes(trimmedText)) {
this.blacklist.push(trimmedText);
this.saveBlacklist();
console.log(`添加黑名单项: ${trimmedText}`);
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
return true;
}
return false;
}
removeBlacklistItem(text) {
const index = this.blacklist.indexOf(text);
if (index !== -1) {
this.blacklist.splice(index, 1);
this.saveBlacklist();
console.log(`移除黑名单项: ${text}`);
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
return true;
}
return false;
}
isBlacklisted(title) {
if (!title) return false;
const lowerTitle = title.toLowerCase();
return this.blacklist.some(item =>
lowerTitle.includes(item.toLowerCase())
);
}
// 获取匹配的黑名单关键字
getMatchingBlacklistKeywords(title) {
if (!title) return [];
const lowerTitle = title.toLowerCase();
return this.blacklist.filter(item =>
lowerTitle.includes(item.toLowerCase())
);
}
getBlacklist() {
return this.blacklist;
}
clearAll() {
this.blacklist = [];
this.saveBlacklist();
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
this.notifyDataChanged();
}
exportJSON() {
const exportData = {
blacklist: this.blacklist,
exportTime: Date.now(),
version: '1.0'
};
return JSON.stringify(exportData, null, 2);
}
importJSON(jsonString) {
try {
const data = JSON.parse(jsonString);
let importBlacklist = [];
// 兼容旧版本数据(只有黑名单数组)
if (Array.isArray(data)) {
importBlacklist = data;
}
// 新版本数据(包含黑名单和其他信息)
else if (data.blacklist && Array.isArray(data.blacklist)) {
importBlacklist = data.blacklist;
} else {
return false;
}
// 获取当前黑名单
const currentBlacklist = [...this.blacklist];
// 合并黑名单,去重
const mergedBlacklist = [...new Set([...currentBlacklist, ...importBlacklist])];
// 计算新增的数量
const addedCount = mergedBlacklist.length - currentBlacklist.length;
// 更新黑名单
this.blacklist = mergedBlacklist;
this.saveBlacklist();
this.notifyDataChanged();
// 返回导入结果信息
return {
success: true,
totalCount: mergedBlacklist.length,
addedCount: addedCount,
existingCount: currentBlacklist.length,
importCount: importBlacklist.length
};
} catch (e) {
console.error('导入黑名单失败:', e);
return false;
}
}
}
// 浏览记录管理
class ViewHistoryManager {
constructor() {
this.viewHistory = this.loadViewHistory();
this.maxHistorySize = 1000; // 最大历史记录数量
this.cleanupThreshold = 800; // 清理阈值
}
loadViewHistory() {
const saved = localStorage.getItem('xmxmkb_view_history') || '{}';
try {
const compressedData = JSON.parse(saved);
// 检查是否为压缩格式数据
if (compressedData && typeof compressedData === 'object') {
const firstKey = Object.keys(compressedData)[0];
if (firstKey && compressedData[firstKey] && typeof compressedData[firstKey] === 'object') {
const firstRecord = compressedData[firstKey];
// 如果是压缩格式(包含 c, l, f 字段),则解压缩
if ('c' in firstRecord || 'l' in firstRecord || 'f' in firstRecord) {
const decompressedData = {};
Object.keys(compressedData).forEach(id => {
const record = compressedData[id];
decompressedData[id] = {
count: record.c || 0,
lastView: record.l || 0,
firstView: record.f || 0
};
});
return decompressedData;
}
}
}
// 如果不是压缩格式,直接返回
return compressedData;
} catch (e) {
console.error('加载浏览历史失败:', e);
return {};
}
}
saveViewHistory() {
try {
// 压缩数据,只保留关键信息
const compressedData = {};
Object.keys(this.viewHistory).forEach(id => {
const record = this.viewHistory[id];
compressedData[id] = {
c: record.count || 0, // count
l: record.lastView || 0, // lastView
f: record.firstView || 0 // firstView
};
});
localStorage.setItem('xmxmkb_view_history', JSON.stringify(compressedData));
// 触发自定义事件,通知其他标签页数据已更新
window.dispatchEvent(new CustomEvent('viewHistoryUpdated', {
detail: { viewHistory: this.viewHistory }
}));
} catch (e) {
console.error('保存浏览历史失败:', e);
}
}
// 增加浏览次数
incrementView(id) {
const now = Date.now();
if (!this.viewHistory[id]) {
this.viewHistory[id] = {
count: 0,
firstView: now,
lastView: now
};
console.log(`创建新的浏览记录 ID: ${id}`);
}
this.viewHistory[id].count++;
this.viewHistory[id].lastView = now;
console.log(`增加浏览次数 ID: ${id}, 新次数: ${this.viewHistory[id].count}`);
// 检查是否需要清理旧数据
if (Object.keys(this.viewHistory).length > this.cleanupThreshold) {
this.cleanupOldRecords();
}
this.saveViewHistory();
}
// 获取浏览次数
getViewCount(id) {
const record = this.viewHistory[id];
if (record) {
const count = record.count || 0;
console.log(`获取浏览次数 ID: ${id}, 次数: ${count}`);
return count;
} else {
console.log(`获取浏览次数 ID: ${id}, 无记录,返回 0`);
return 0;
}
}
// 获取最后浏览时间
getLastView(id) {
return this.viewHistory[id] ? this.viewHistory[id].lastView : 0;
}
// 清理旧记录
cleanupOldRecords() {
const records = Object.entries(this.viewHistory);
// 按最后浏览时间排序,保留最新的记录
records.sort((a, b) => b[1].lastView - a[1].lastView);
// 只保留最新的记录
const newHistory = {};
records.slice(0, this.maxHistorySize).forEach(([id, record]) => {
newHistory[id] = record;
});
this.viewHistory = newHistory;
console.log(`清理浏览历史,保留 ${Object.keys(this.viewHistory).length} 条记录`);
}
// 清空所有记录
clearAll() {
this.viewHistory = {};
this.saveViewHistory();
}
// 导出数据(包含完整信息)
exportData() {
const exportData = {};
Object.keys(this.viewHistory).forEach(id => {
const record = this.viewHistory[id];
exportData[id] = {
count: record.count,
firstView: record.firstView,
lastView: record.lastView
};
});
return exportData;
}
// 导入数据
importData(data) {
try {
if (typeof data === 'object' && data !== null) {
this.viewHistory = data;
this.saveViewHistory();
return true;
}
} catch (e) {
console.error('导入浏览历史失败:', e);
}
return false;
}
// 获取所有浏览记录
getAllRecords() {
return this.viewHistory;
}
}
// 收藏数据管理
class FavoritesManager {
constructor() {
this.favorites = this.loadFavorites();
this.viewHistoryManager = new ViewHistoryManager();
this.blacklistManager = new BlacklistManager();
this.hasChanges = false; // 标记是否有数据变化
// 创建预加载管理器并设为全局可用
if (!window.preloadManager) {
window.preloadManager = new PreloadManager();
}
this.initCrossTabSync();
}
initCrossTabSync() {
// 监听其他标签页的数据更新
window.addEventListener('storage', (e) => {
if (e.key === 'xmxmkb_favorites' && e.newValue) {
try {
this.favorites = JSON.parse(e.newValue);
console.log('收到其他标签页的收藏数据更新');
// 触发UI更新
this.notifyDataChanged();
} catch (error) {
console.error('解析其他标签页数据失败:', error);
}
}
if (e.key === 'xmxmkb_view_history' && e.newValue) {
try {
const compressedData = JSON.parse(e.newValue);
// 解压缩数据
this.viewHistoryManager.viewHistory = {};
Object.keys(compressedData).forEach(id => {
const record = compressedData[id];
this.viewHistoryManager.viewHistory[id] = {
count: record.c || 0,
lastView: record.l || 0,
firstView: record.f || 0
};
});
console.log('收到其他标签页的浏览历史更新');
this.notifyDataChanged();
} catch (error) {
console.error('解析其他标签页浏览历史失败:', error);
}
}
});
// 监听自定义事件(同一标签页内的更新)
window.addEventListener('favoritesUpdated', (e) => {
if (e.detail && e.detail.favorites) {
this.favorites = e.detail.favorites;
console.log('收到同标签页的收藏数据更新');
this.notifyDataChanged();
}
});
window.addEventListener('viewHistoryUpdated', (e) => {
if (e.detail && e.detail.viewHistory) {
this.viewHistoryManager.viewHistory = e.detail.viewHistory;
console.log('收到同标签页的浏览历史更新');
this.notifyDataChanged();
}
});
}
notifyDataChanged() {
// 通知UI更新
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updatePanel();
window.pageHandler.updateAllFavoriteStates();
window.pageHandler.updateAllViewCounts();
}
}
loadFavorites() {
const saved = localStorage.getItem('xmxmkb_favorites') || '[]';
try {
return JSON.parse(saved);
} catch (e) {
console.error('加载收藏数据失败:', e);
return [];
}
}
saveFavorites() {
try {
localStorage.setItem('xmxmkb_favorites', JSON.stringify(this.favorites));
// 触发自定义事件,通知其他标签页数据已更新
window.dispatchEvent(new CustomEvent('favoritesUpdated', {
detail: { favorites: this.favorites }
}));
} catch (e) {
console.error('保存收藏数据失败:', e);
}
}
addFavorite(id, title, url, images = []) {
if (!this.favorites.find(f => f.id === id)) {
// 尝试从PreloadManager获取预加载的图片数据
let finalImages = images;
if (window.preloadManager) {
const preloadedData = window.preloadManager.getPersistedImageData(url);
if (preloadedData && preloadedData.images && preloadedData.images.length > 0) {
// 合并预加载的图片数据,优先使用预加载的数据
finalImages = preloadedData.images;
console.log(`使用预加载的图片数据: ${url}, 图片数量: ${finalImages.length}`);
}
}
const hasImages = finalImages && finalImages.length > 0;
this.favorites.push({
id,
title,
url,
images: finalImages,
timestamp: Date.now(),
superLiked: false,
hasImages: hasImages
});
this.hasChanges = true; // 标记有变化
this.saveFavorites();
console.log(`添加收藏: ${id}, 标题: ${title}, 有图片: ${hasImages}, 图片数量: ${finalImages.length}`);
return true;
}
return false;
}
removeFavorite(id) {
const index = this.favorites.findIndex(f => f.id === id);
if (index !== -1) {
this.favorites.splice(index, 1);
this.hasChanges = true; // 标记有变化
this.saveFavorites();
return true;
}
return false;
}
isFavorited(id) {
return this.favorites.some(f => f.id === id);
}
toggleSuperLike(id) {
const favorite = this.favorites.find(f => f.id === id);
if (favorite) {
favorite.superLiked = !favorite.superLiked;
this.hasChanges = true; // 标记有变化
console.log('切换特别喜欢状态:', id, favorite.superLiked);
this.saveFavorites();
return true;
}
return false;
}
isSuperLiked(id) {
const favorite = this.favorites.find(f => f.id === id);
return favorite ? favorite.superLiked : false;
}
getFavorites() {
// 确保所有收藏项都有hasImages字段(兼容旧数据)
this.favorites.forEach(fav => {
if (fav.hasImages === undefined) {
fav.hasImages = fav.images && fav.images.length > 0;
}
});
return this.favorites;
}
clearAll() {
this.favorites = [];
this.hasChanges = true; // 标记有变化
this.saveFavorites();
// 通知UI更新
this.notifyDataChanged();
}
exportJSON() {
// 定义已经单独处理的localStorage键名,需要排除
const excludedKeys = [
'xmxmkb_favorites', // 收藏数据
'xmxmkb_view_history', // 浏览历史
'xmxmkb_blacklist', // 黑名单
'xmxmkb_preloaded_images' // 预加载图片
];
// 收集所有xmxmkb_开头但未单独处理的localStorage数据
const xmxmkbData = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith('xmxmkb_') && !excludedKeys.includes(key)) {
try {
const value = localStorage.getItem(key);
xmxmkbData[key] = value;
} catch (e) {
console.warn(`无法读取localStorage键: ${key}`, e);
}
}
}
// 导出包含收藏、浏览历史、黑名单、预加载图片数据和其他xmxmkb_数据的数据
const exportData = {
favorites: this.favorites,
viewHistory: this.viewHistoryManager.exportData(),
blacklist: this.blacklistManager.getBlacklist(),
preloadedImages: this.getPreloadedImagesForExport(),
xmxmkbLocalStorage: xmxmkbData,
exportTime: Date.now(),
version: '1.6'
};
return JSON.stringify(exportData, null, 2);
}
// 获取预加载的图片数据用于导出
getPreloadedImagesForExport() {
try {
const saved = localStorage.getItem('xmxmkb_preloaded_images');
if (saved) {
return JSON.parse(saved);
}
} catch (error) {
console.error('获取预加载图片数据失败:', error);
}
return {};
}
// 重置变化标记(在导出成功后调用)
resetChangesFlag() {
this.hasChanges = false;
console.log('重置数据变化标记');
}
importJSON(jsonString) {
try {
const data = JSON.parse(jsonString);
// 兼容旧版本数据(只有收藏数组)
if (Array.isArray(data)) {
// 为旧数据添加hasImages字段
this.favorites = data.map(fav => ({
...fav,
hasImages: fav.images && fav.images.length > 0
}));
this.hasChanges = true; // 标记有变化
this.saveFavorites();
this.notifyDataChanged();
return true;
}
// 新版本数据(包含收藏、浏览历史和黑名单)
if (data.favorites && Array.isArray(data.favorites)) {
// 确保所有收藏项都有hasImages字段
this.favorites = data.favorites.map(fav => ({
...fav,
hasImages: fav.hasImages !== undefined ? fav.hasImages : (fav.images && fav.images.length > 0)
}));
this.hasChanges = true; // 标记有变化
this.saveFavorites();
}
if (data.viewHistory && typeof data.viewHistory === 'object') {
this.viewHistoryManager.importData(data.viewHistory);
}
if (data.blacklist && Array.isArray(data.blacklist)) {
// 使用追加方式导入黑名单,而不是覆盖
const currentBlacklist = [...this.blacklistManager.blacklist];
const mergedBlacklist = [...new Set([...currentBlacklist, ...data.blacklist])];
this.blacklistManager.blacklist = mergedBlacklist;
this.blacklistManager.saveBlacklist();
// 标记有数据变化
this.hasChanges = true;
}
// 导入预加载的图片数据
if (data.preloadedImages && typeof data.preloadedImages === 'object') {
this.importPreloadedImages(data.preloadedImages);
}
// 导入所有xmxmkb_开头但未单独处理的localStorage数据
if (data.xmxmkbLocalStorage && typeof data.xmxmkbLocalStorage === 'object') {
// 定义已经单独处理的localStorage键名,需要排除
const excludedKeys = [
'xmxmkb_favorites', // 收藏数据
'xmxmkb_view_history', // 浏览历史
'xmxmkb_blacklist', // 黑名单
'xmxmkb_preloaded_images' // 预加载图片
];
let importedCount = 0;
Object.entries(data.xmxmkbLocalStorage).forEach(([key, value]) => {
if (key && key.startsWith('xmxmkb_') && !excludedKeys.includes(key)) {
try {
localStorage.setItem(key, value);
importedCount++;
} catch (e) {
console.warn(`无法设置localStorage键: ${key}`, e);
}
}
});
console.log(`成功导入 ${importedCount} 个其他xmxmkb_开头的localStorage数据`);
}
this.notifyDataChanged();
return true;
} catch (e) {
console.error('导入失败:', e);
}
return false;
}
// 导入预加载的图片数据
importPreloadedImages(preloadedImages) {
try {
const current = localStorage.getItem('xmxmkb_preloaded_images') || '{}';
const currentData = JSON.parse(current);
// 合并数据,保留较新的数据
Object.keys(preloadedImages).forEach(url => {
const importedItem = preloadedImages[url];
const currentItem = currentData[url];
// 如果当前没有该URL的数据,或者导入的数据更新,则使用导入的数据
if (!currentItem || (importedItem.timestamp && importedItem.timestamp > (currentItem.timestamp || 0))) {
currentData[url] = importedItem;
}
});
localStorage.setItem('xmxmkb_preloaded_images', JSON.stringify(currentData));
// 如果全局的preloadManager存在,更新它的内存数据
if (window.preloadManager) {
Object.keys(currentData).forEach(url => {
window.preloadManager.preloadedUrls.set(url, currentData[url]);
});
}
console.log(`导入了 ${Object.keys(preloadedImages).length} 个预加载图片数据`);
} catch (error) {
console.error('导入预加载图片数据失败:', error);
}
}
// 增加浏览次数
incrementView(id) {
this.viewHistoryManager.incrementView(id);
this.hasChanges = true; // 标记有变化
}
// 获取浏览次数
getViewCount(id) {
return this.viewHistoryManager.getViewCount(id);
}
// 获取最后浏览时间
getLastView(id) {
return this.viewHistoryManager.getLastView(id);
}
// 从页面中获取图片
getImagesFromPage() {
const ignoreJsOpElements = document.querySelectorAll('ignore_js_op');
if (ignoreJsOpElements.length === 0) {
console.log('未找到ignore_js_op标签元素');
return [];
}
const imageUrls = [];
ignoreJsOpElements.forEach(element => {
const images = element.querySelectorAll('img');
images.forEach(img => {
const src = img.src || img.getAttribute('data-src') || img.getAttribute('file');
if (src && src.trim()) {
// 过滤掉一些不需要的图片(如表情、图标等)
const lowerSrc = src.toLowerCase();
if (!lowerSrc.includes('emoji') &&
!lowerSrc.includes('icon') &&
!lowerSrc.includes('avatar') &&
!lowerSrc.includes('logo') &&
img.width > 50 && img.height > 50) { // 过滤掉太小的图片
imageUrls.push(src.trim());
}
}
});
});
// 最多取2张图片
const maxImages = Math.min(imageUrls.length, 2);
if (maxImages === 0) {
console.log('未找到合适的图片');
return [];
}
// 使用前2张图片
const selectedImages = imageUrls.slice(0, maxImages);
console.log(`从页面的ignore_js_op标签获取到 ${selectedImages.length} 张图片`);
return selectedImages;
}
}
// UI管理
class FavoritesUI {
constructor(manager) {
this.manager = manager;
this.preloadManager = window.preloadManager || new PreloadManager();
this.imageViewer = new ImageViewer();
this.panel = null;
this.toggleBtn = null;
this.currentPage = 1;
this.pageSize = 5;
this.blacklistCurrentPage = 1;
this.blacklistPageSize = 5;
this.init();
}
init() {
this.createToggleButton();
this.createPreloadAllButton();
this.createBlacklistToggleButton();
this.createTopRightDropdown(); // 创建右上角下拉菜单
this.createPanel();
this.createBlacklistPanel();
this.updateToggleButton();
this.updateBlacklistToggleButton();
this.updatePreloadCount();
this.updateStorageSize();
}
createToggleButton() {
this.toggleBtn = document.createElement('button');
this.toggleBtn.className = 'favorites-toggle';
this.toggleBtn.innerHTML = '收藏夹 <span class="favorites-count">0</span>';
this.toggleBtn.onclick = () => this.togglePanel();
document.body.appendChild(this.toggleBtn);
}
createPreloadAllButton() {
this.preloadAllBtn = document.createElement('button');
this.preloadAllBtn.className = 'preload-all-btn';
this.preloadAllBtn.textContent = '全部预加载';
this.preloadAllBtn.title = '点击预加载当前页面所有列表项(忽略黑名单项目)';
this.preloadAllBtn.onclick = () => this.preloadAllItems();
document.body.appendChild(this.preloadAllBtn);
// 创建清空未加载浏览记录按钮
this.clearUnloadedViewsBtn = document.createElement('button');
this.clearUnloadedViewsBtn.className = 'clear-unloaded-views-btn';
this.clearUnloadedViewsBtn.textContent = '清空未加载浏览记录';
this.clearUnloadedViewsBtn.title = '点击清空当前页面所有未预加载列表项的浏览记录';
this.clearUnloadedViewsBtn.onclick = () => this.clearUnloadedViews();
document.body.appendChild(this.clearUnloadedViewsBtn);
}
createBlacklistToggleButton() {
this.blacklistToggleBtn = document.createElement('button');
this.blacklistToggleBtn.className = 'blacklist-toggle';
this.blacklistToggleBtn.innerHTML = '黑名单 <span class="blacklist-count">0</span>';
this.blacklistToggleBtn.onclick = () => this.toggleBlacklistPanel();
document.body.appendChild(this.blacklistToggleBtn);
}
createTopRightDropdown() {
// 创建下拉菜单容器
this.dropdown = document.createElement('div');
this.dropdown.className = 'top-right-dropdown';
// 创建下拉箭头按钮
this.dropdownArrow = document.createElement('button');
this.dropdownArrow.className = 'dropdown-arrow';
this.dropdownArrow.innerHTML = '功能菜单 <span class="arrow-icon">▼</span>';
// 创建下拉菜单
this.dropdownMenu = document.createElement('div');
this.dropdownMenu.className = 'dropdown-menu';
// 创建菜单项
const menuItems = [
{
text: '收藏夹 <span class="favorites-count">0</span>',
className: 'favorites',
onclick: () => this.togglePanel()
},
{
text: '全部预加载',
className: 'preload',
onclick: () => this.preloadAllItems()
},
{
text: '清空未加载浏览记录',
className: 'clear',
onclick: () => this.clearUnloadedViews()
},
{
text: '黑名单 <span class="blacklist-count">0</span>',
className: 'blacklist',
onclick: () => this.toggleBlacklistPanel()
},
{
text: '关注作者',
className: 'follow-author',
onclick: () => {
// 触发自定义事件,让外部脚本处理
const event = new CustomEvent('followAuthorClick');
document.dispatchEvent(event);
}
}
];
menuItems.forEach(item => {
const menuItem = document.createElement('button');
menuItem.className = `dropdown-item ${item.className}`;
menuItem.innerHTML = item.text;
// 为关注作者按钮添加固定ID
if (item.className === 'follow-author') {
menuItem.id = 'follow-author';
}
menuItem.onclick = (e) => {
e.stopPropagation();
item.onclick();
this.hideDropdownMenu();
};
this.dropdownMenu.appendChild(menuItem);
});
// 组装下拉菜单
this.dropdown.appendChild(this.dropdownArrow);
this.dropdown.appendChild(this.dropdownMenu);
document.body.appendChild(this.dropdown);
// 添加点击事件
this.dropdownArrow.onclick = (e) => {
e.stopPropagation();
this.toggleDropdownMenu();
};
// 点击其他地方关闭菜单
document.addEventListener('click', (e) => {
if (!this.dropdown.contains(e.target)) {
this.hideDropdownMenu();
}
});
}
toggleDropdownMenu() {
const isVisible = this.dropdownMenu.classList.contains('show');
if (isVisible) {
this.hideDropdownMenu();
} else {
this.showDropdownMenu();
}
}
showDropdownMenu() {
this.dropdownMenu.classList.add('show');
this.dropdownArrow.classList.add('active');
// 更新菜单项的计数
this.updateDropdownCounts();
}
hideDropdownMenu() {
this.dropdownMenu.classList.remove('show');
this.dropdownArrow.classList.remove('active');
}
updateDropdownCounts() {
// 更新收藏夹计数
const favoritesItem = this.dropdownMenu.querySelector('.dropdown-item.favorites .favorites-count');
if (favoritesItem) {
const count = this.manager.getFavorites().length;
favoritesItem.textContent = count;
}
// 更新黑名单计数
const blacklistItem = this.dropdownMenu.querySelector('.dropdown-item.blacklist .blacklist-count');
if (blacklistItem) {
const count = this.manager.blacklistManager.getBlacklist().length;
blacklistItem.textContent = count;
}
}
createPanel() {
this.panel = document.createElement('div');
this.panel.className = 'favorites-panel';
this.panel.style.display = 'none';
this.panel.innerHTML = `
<div class="favorites-header">
<span class="favorites-title">我的收藏</span>
<button class="favorites-close">×</button>
</div>
<div class="favorites-search">
<input type="text" class="search-input" placeholder="搜索收藏标题或浏览次数..." id="search-input">
</div>
<div class="favorites-sort">
<span style="font-size: 12px; color: #666;">筛选:</span>
<select class="sort-select" id="filter-select" style="margin-right: 10px;">
<option value="all">全部收藏</option>
<option value="super-liked">特别喜欢</option>
<option value="no-images">无图片</option>
</select>
<span style="font-size: 12px; color: #666;">排序:</span>
<select class="sort-select" id="sort-select" style="margin-right: 10px;">
<option value="time-desc">收藏时间 (最新)</option>
<option value="time-asc">收藏时间 (最早)</option>
<option value="name-asc">标题 (A-Z)</option>
<option value="name-desc">标题 (Z-A)</option>
<option value="views-desc">浏览次数 (最多)</option>
<option value="views-asc">浏览次数 (最少)</option>
</select>
<button class="show-images-btn" id="show-images-btn" style="background: #28a745; color: white; border: none; padding: 4px 8px; border-radius: 4px; font-size: 12px; cursor: pointer; transition: all 0.3s;">显示图片</button>
</div>
<div class="favorites-content"></div>
<div class="favorites-actions">
<button class="favorites-action-btn" id="export-btn">导出JSON</button>
<button class="favorites-action-btn" id="import-btn">导入JSON</button>
<button class="favorites-action-btn danger" id="clear-btn">清空收藏</button>
</div>
<div class="favorites-settings">
<label class="auto-update-toggle">
<input type="checkbox" id="auto-update-checkbox" checked>
<span>自动更新状态 (5秒)</span>
</label>
<div style="margin-top: 8px; font-size: 11px; color: #666;">
预加载: <span id="preload-count">0</span> 个页面
<button id="clear-preloads" style="margin-left: 8px; background: #dc3545; color: white; border: none; padding: 0px 6px 2px 6px; border-radius: 3px; font-size: 10px; cursor: pointer;">清空</button>
</div>
<div class="storage-info">
<span>存储数据大小:</span>
<span id="storage-size" class="storage-size">计算中...</span>
</div>
</div>
`;
document.body.appendChild(this.panel);
// 绑定事件
this.panel.querySelector('.favorites-close').onclick = () => this.hidePanel();
this.panel.querySelector('#export-btn').onclick = () => this.exportFavorites();
this.panel.querySelector('#import-btn').onclick = () => this.importFavorites();
this.panel.querySelector('#clear-btn').onclick = () => this.clearFavorites();
// 绑定自动更新开关
const autoUpdateCheckbox = this.panel.querySelector('#auto-update-checkbox');
autoUpdateCheckbox.onchange = (e) => {
if (e.target.checked) {
window.pageHandler.startAutoUpdate();
} else {
window.pageHandler.stopAutoUpdate();
}
};
// 绑定清空预加载按钮
const clearPreloadsBtn = this.panel.querySelector('#clear-preloads');
clearPreloadsBtn.onclick = () => {
const choice = confirm('选择清空方式:\n\n确定 - 清空所有预加载内容\n取消 - 只清空非当前页面的预加载内容');
if (choice) {
// 清空所有预加载内容
this.preloadManager.clearAll();
this.updatePreloadCount();
// 更新所有预加载按钮状态
document.querySelectorAll('.preload-btn').forEach(btn => {
btn.classList.remove('preloaded', 'loading');
btn.textContent = '预加载';
});
} else {
// 只清空非当前页面的预加载内容
this.preloadManager.clearNonCurrentPagePreloads();
this.updatePreloadCount();
}
};
// 绑定搜索功能
const searchInput = this.panel.querySelector('#search-input');
if (searchInput) {
searchInput.addEventListener('input', () => {
this.updatePanel();
});
}
// 绑定筛选功能
const filterSelect = this.panel.querySelector('#filter-select');
if (filterSelect) {
filterSelect.addEventListener('change', () => {
this.updatePanel();
});
}
// 绑定排序功能
const sortSelect = this.panel.querySelector('#sort-select');
if (sortSelect) {
sortSelect.addEventListener('change', () => {
this.updatePanel();
});
}
// 绑定显示图片按钮
const showImagesBtn = this.panel.querySelector('#show-images-btn');
if (showImagesBtn) {
console.log('找到显示图片按钮,绑定点击事件');
showImagesBtn.addEventListener('click', (e) => {
console.log('显示图片按钮被点击');
e.preventDefault();
e.stopPropagation();
this.toggleImagesDisplay();
});
} else {
console.error('未找到显示图片按钮');
}
}
togglePanel() {
if (this.panel.style.display === 'none') {
this.showPanel();
} else {
this.hidePanel();
}
}
toggleBlacklistPanel() {
if (this.blacklistPanel.style.display === 'none') {
this.showBlacklistPanel();
} else {
this.hideBlacklistPanel();
}
}
showBlacklistPanel() {
this.blacklistPanel.style.display = 'block';
this.updateBlacklistPanel();
}
hideBlacklistPanel() {
this.blacklistPanel.style.display = 'none';
}
showPanel() {
console.log('显示收藏夹面板...');
this.panel.style.display = 'block';
// 显示收藏列表,但不加载图片
this.updatePanel();
this.updateToggleButton();
this.updateStorageSize();
console.log('收藏夹面板显示完成');
}
hidePanel() {
this.panel.style.display = 'none';
}
updatePanel() {
console.log('开始更新收藏夹面板...');
const content = this.panel.querySelector('.favorites-content');
const searchInput = this.panel.querySelector('#search-input');
const filterSelect = this.panel.querySelector('#filter-select');
const sortSelect = this.panel.querySelector('#sort-select');
let favorites = this.manager.getFavorites();
console.log('获取到收藏数量:', favorites.length);
// 更新存储大小
this.updateStorageSize();
// 筛选过滤
const filterValue = filterSelect ? filterSelect.value : 'all';
if (filterValue === 'super-liked') {
favorites = favorites.filter(fav => fav.superLiked);
} else if (filterValue === 'no-images') {
favorites = favorites.filter(fav => !fav.hasImages);
}
// 搜索过滤
const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
if (searchTerm) {
favorites = favorites.filter(fav => {
const titleMatch = fav.title.toLowerCase().includes(searchTerm);
const viewCountMatch = this.manager.viewHistoryManager.getViewCount(fav.id).toString().includes(searchTerm);
return titleMatch || viewCountMatch;
});
}
// 排序
const sortValue = sortSelect ? sortSelect.value : 'time-desc';
favorites.sort((a, b) => {
switch (sortValue) {
case 'time-desc':
return b.timestamp - a.timestamp;
case 'time-asc':
return a.timestamp - b.timestamp;
case 'name-asc':
return a.title.localeCompare(b.title);
case 'name-desc':
return b.title.localeCompare(a.title);
case 'views-desc':
return this.manager.viewHistoryManager.getViewCount(b.id) - this.manager.viewHistoryManager.getViewCount(a.id);
case 'views-asc':
return this.manager.viewHistoryManager.getViewCount(a.id) - this.manager.viewHistoryManager.getViewCount(b.id);
default:
return 0;
}
});
if (favorites.length === 0) {
const message = searchTerm ? '没有找到匹配的收藏' : '暂无收藏';
content.innerHTML = `<div style="text-align: center; color: #666; padding: 20px;">${message}</div>`;
// 清除分页控件
this.updateFavoritesPagination(favorites, 0);
console.log('无收藏数据,显示空状态');
return;
}
// 分页处理
const totalItems = favorites.length;
const totalPages = Math.ceil(totalItems / this.pageSize);
// 确保当前页在有效范围内
if (this.currentPage > totalPages) {
this.currentPage = totalPages;
}
if (this.currentPage < 1) {
this.currentPage = 1;
}
// 计算当前页的数据
const startIndex = (this.currentPage - 1) * this.pageSize;
const endIndex = Math.min(startIndex + this.pageSize, totalItems);
const currentPageItems = favorites.slice(startIndex, endIndex);
console.log(`分页信息: 总数=${totalItems}, 每页=${this.pageSize}, 总页数=${totalPages}, 当前页=${this.currentPage}, 显示=${startIndex}-${endIndex-1}`);
console.log('开始创建收藏列表HTML...');
// 创建2列布局,不加载图片
content.innerHTML = currentPageItems.map((fav, index) => {
const images = fav.images || [];
const imageElements = [];
// 创建2个图片位置(1行2列),但不加载图片
for (let i = 0; i < 2; i++) {
if (i < images.length) {
// 使用占位符,不加载实际图片
imageElements.push(`<div class="favorite-image placeholder" data-src="${images[i]}" data-image-index="${i}">点击显示图片</div>`);
} else {
imageElements.push(`<div class="favorite-image placeholder">无图</div>`);
}
}
// 确定CSS类
const cssClasses = ['favorite-item'];
if (fav.superLiked) cssClasses.push('super-liked');
if (!fav.hasImages) cssClasses.push('no-images');
return `
<div class="${cssClasses.join(' ')}" onclick="window.open('${fav.url}', '_blank')" title="${fav.title}" data-id="${fav.id}">
${fav.superLiked ? '<div class="super-like-badge">❤️</div>' : ''}
<div class="favorite-images">
${imageElements.join('')}
</div>
<div class="favorite-title">${fav.title}</div>
<div class="favorite-info">
<div class="favorite-views">
<span class="view-count-icon">👁️</span>
<span>${this.manager.viewHistoryManager.getViewCount(fav.id)}</span>
</div>
<span class="favorite-date">${new Date(fav.timestamp).toLocaleDateString()}</span>
</div>
</div>
`;
}).join('');
// 更新分页控件
this.updateFavoritesPagination(favorites, totalPages);
// 为收藏夹中的图片占位符添加点击事件
this.setupFavoriteImageClickEvents();
console.log('收藏夹面板更新完成');
}
// 更新收藏夹分页控件
updateFavoritesPagination(favorites, totalPages) {
// 先移除旧的分页控件
const oldPagination = this.panel.querySelector('.pagination-container');
if (oldPagination) {
oldPagination.remove();
}
// 如果没有数据或只有一页,不显示分页
if (favorites.length === 0 || totalPages <= 1) {
return;
}
// 创建分页容器
const paginationContainer = document.createElement('div');
paginationContainer.className = 'pagination-container';
// 添加分页信息
const paginationInfo = document.createElement('div');
paginationInfo.className = 'pagination-info';
const startIndex = (this.currentPage - 1) * this.pageSize + 1;
const endIndex = Math.min(startIndex + this.pageSize - 1, favorites.length);
paginationInfo.textContent = `显示 ${startIndex}-${endIndex},共 ${favorites.length} 项`;
paginationContainer.appendChild(paginationInfo);
// 添加分页按钮
// 首页按钮
const firstPageBtn = document.createElement('button');
firstPageBtn.className = `pagination-btn ${this.currentPage === 1 ? 'disabled' : ''}`;
firstPageBtn.textContent = '首页';
firstPageBtn.disabled = this.currentPage === 1;
firstPageBtn.onclick = () => {
if (this.currentPage !== 1) {
this.currentPage = 1;
this.updatePanel();
}
};
paginationContainer.appendChild(firstPageBtn);
// 上一页按钮
const prevPageBtn = document.createElement('button');
prevPageBtn.className = `pagination-btn ${this.currentPage === 1 ? 'disabled' : ''}`;
prevPageBtn.textContent = '上一页';
prevPageBtn.disabled = this.currentPage === 1;
prevPageBtn.onclick = () => {
if (this.currentPage > 1) {
this.currentPage--;
this.updatePanel();
}
};
paginationContainer.appendChild(prevPageBtn);
// 页码按钮
const maxPageButtons = 5; // 最多显示5个页码按钮
let startPage = Math.max(1, this.currentPage - Math.floor(maxPageButtons / 2));
let endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
// 调整startPage,确保显示maxPageButtons个按钮
if (endPage - startPage + 1 < maxPageButtons) {
startPage = Math.max(1, endPage - maxPageButtons + 1);
}
for (let i = startPage; i <= endPage; i++) {
const pageBtn = document.createElement('button');
pageBtn.className = `pagination-btn ${i === this.currentPage ? 'active' : ''}`;
pageBtn.textContent = i.toString();
pageBtn.onclick = () => {
if (this.currentPage !== i) {
this.currentPage = i;
this.updatePanel();
}
};
paginationContainer.appendChild(pageBtn);
}
// 下一页按钮
const nextPageBtn = document.createElement('button');
nextPageBtn.className = `pagination-btn ${this.currentPage === totalPages ? 'disabled' : ''}`;
nextPageBtn.textContent = '下一页';
nextPageBtn.disabled = this.currentPage === totalPages;
nextPageBtn.onclick = () => {
if (this.currentPage < totalPages) {
this.currentPage++;
this.updatePanel();
}
};
paginationContainer.appendChild(nextPageBtn);
// 末页按钮
const lastPageBtn = document.createElement('button');
lastPageBtn.className = `pagination-btn ${this.currentPage === totalPages ? 'disabled' : ''}`;
lastPageBtn.textContent = '末页';
lastPageBtn.disabled = this.currentPage === totalPages;
lastPageBtn.onclick = () => {
if (this.currentPage !== totalPages) {
this.currentPage = totalPages;
this.updatePanel();
}
};
paginationContainer.appendChild(lastPageBtn);
// 添加每页显示数量选择
const pageSizeContainer = document.createElement('div');
pageSizeContainer.className = 'page-size-container';
pageSizeContainer.innerHTML = '每页显示: ';
const pageSizeSelect = document.createElement('select');
pageSizeSelect.className = 'page-size-select';
[5, 10, 50, 100, 200].forEach(size => {
const option = document.createElement('option');
option.value = size;
option.textContent = size.toString();
option.selected = this.pageSize === size;
pageSizeSelect.appendChild(option);
});
pageSizeSelect.onchange = (e) => {
const newPageSize = parseInt(e.target.value);
// 计算新的当前页,尽量保持显示的是相同的内容
const firstItemIndex = (this.currentPage - 1) * this.pageSize;
this.pageSize = newPageSize;
this.currentPage = Math.floor(firstItemIndex / newPageSize) + 1;
this.updatePanel();
};
pageSizeContainer.appendChild(pageSizeSelect);
paginationContainer.appendChild(pageSizeContainer);
// 将分页控件添加到面板
const favoritesContent = this.panel.querySelector('.favorites-content');
favoritesContent.parentNode.insertBefore(paginationContainer, favoritesContent.nextSibling);
}
// 切换图片显示状态
toggleImagesDisplay() {
const showImagesBtn = this.panel.querySelector('#show-images-btn');
if (!showImagesBtn) {
console.error('显示图片按钮未找到');
return;
}
const isShowingImages = showImagesBtn.textContent === '隐藏图片';
console.log('切换图片显示状态,当前状态:', isShowingImages ? '显示图片' : '隐藏图片');
if (isShowingImages) {
// 隐藏图片,恢复占位符
this.hideImages();
showImagesBtn.textContent = '显示图片';
showImagesBtn.style.background = '#28a745';
console.log('已隐藏图片,恢复占位符');
} else {
// 显示图片,只加载当前可见区域的图片
this.showImages();
showImagesBtn.textContent = '隐藏图片';
showImagesBtn.style.background = '#dc3545';
console.log('已显示图片,加载当前可见区域图片');
}
}
// 显示图片(只加载当前可见区域的图片)
showImages() {
const content = this.panel.querySelector('.favorites-content');
if (!content) {
return;
}
// 获取当前页面的收藏项
const currentPageItems = content.querySelectorAll('.favorite-item');
let favoriteItemIndex = 9999; // 收藏夹图片从9999开始
currentPageItems.forEach(item => {
const imagesContainer = item.querySelector('.favorite-images');
if (!imagesContainer) {
return;
}
// 获取该收藏项的数据
const id = item.getAttribute('data-id');
const favorite = this.manager.getFavorites().find(f => f.id === id);
if (!favorite || !favorite.images || favorite.images.length === 0) {
return;
}
// 替换占位符为实际图片
const placeholders = imagesContainer.querySelectorAll('.favorite-image.placeholder[data-src]');
placeholders.forEach((placeholder, index) => {
if (index < favorite.images.length) {
const imgSrc = placeholder.getAttribute('data-src');
const img = document.createElement('img');
img.className = 'favorite-image';
img.alt = `图片 ${index + 1}`;
// 设置懒加载属性
img.setAttribute('lazy-src', imgSrc);
img.setAttribute('lazy-level', favoriteItemIndex++);
img.setAttribute('data-image-index', index);
// 添加点击事件
img.onclick = (e) => {
e.stopPropagation();
if (this.imageViewer) {
this.imageViewer.show(favorite.images, index);
}
};
// 设置图片加载事件
img.onload = () => {
img.classList.add('loaded');
};
img.onerror = () => {
img.classList.add('placeholder');
img.style.display = 'flex';
img.style.alignItems = 'center';
img.style.justifyContent = 'center';
img.innerHTML = '图片加载失败';
img.style.color = '#999';
img.style.fontSize = '8px';
};
// 替换占位符
placeholder.parentNode.replaceChild(img, placeholder);
}
});
});
// 设置显示图片按钮状态
const showImagesBtn = this.panel.querySelector('#show-images-btn');
if (showImagesBtn) {
showImagesBtn.textContent = '隐藏图片';
showImagesBtn.style.background = '#dc3545';
}
// 重新设置图片点击事件
this.setupFavoriteImageClickEvents();
}
// 隐藏图片,恢复占位符
hideImages() {
const content = this.panel.querySelector('.favorites-content');
if (!content) {
return;
}
// 将所有图片替换回占位符
const images = content.querySelectorAll('.favorite-image:not(.placeholder)');
images.forEach((img) => {
const dataSrc = img.getAttribute('lazy-src') || img.src;
const dataIndex = img.getAttribute('data-image-index');
const placeholder = document.createElement('div');
placeholder.className = 'favorite-image placeholder';
placeholder.setAttribute('data-src', dataSrc);
placeholder.setAttribute('data-image-index', dataIndex);
placeholder.textContent = '点击显示图片';
img.parentNode.replaceChild(placeholder, img);
});
// 设置显示图片按钮状态
const showImagesBtn = this.panel.querySelector('#show-images-btn');
if (showImagesBtn) {
showImagesBtn.textContent = '显示图片';
showImagesBtn.style.background = '#28a745';
}
// 重新设置占位符点击事件
this.setupFavoriteImageClickEvents();
}
updateBlacklistPanel() {
const itemsContainer = this.blacklistPanel.querySelector('#blacklist-items');
const searchInput = this.blacklistPanel.querySelector('#blacklist-search-input');
const searchInfo = this.blacklistPanel.querySelector('#blacklist-search-info');
let blacklist = this.manager.blacklistManager.getBlacklist();
const searchTerm = searchInput ? searchInput.value.toLowerCase().trim() : '';
// 搜索过滤
if (searchTerm) {
const filteredBlacklist = blacklist.filter(item =>
item.toLowerCase().includes(searchTerm)
);
// 更新搜索信息
if (searchInfo) {
searchInfo.textContent = `搜索 "${searchTerm}" 找到 ${filteredBlacklist.length}/${blacklist.length} 个黑名单项`;
}
blacklist = filteredBlacklist;
} else {
// 清除搜索信息
if (searchInfo) {
searchInfo.textContent = `共 ${blacklist.length} 个黑名单项`;
}
}
if (blacklist.length === 0) {
const message = searchTerm ? '没有找到匹配的黑名单项' : '暂无黑名单项';
itemsContainer.innerHTML = `<div style="text-align: center; color: #666; padding: 20px;">${message}</div>`;
// 清除分页控件
this.updateBlacklistPagination(blacklist, 0);
return;
}
// 分页处理
const totalItems = blacklist.length;
const totalPages = Math.ceil(totalItems / this.blacklistPageSize);
// 确保当前页在有效范围内
if (this.blacklistCurrentPage > totalPages) {
this.blacklistCurrentPage = totalPages;
}
if (this.blacklistCurrentPage < 1) {
this.blacklistCurrentPage = 1;
}
// 计算当前页的数据
const startIndex = (this.blacklistCurrentPage - 1) * this.blacklistPageSize;
const endIndex = Math.min(startIndex + this.blacklistPageSize, totalItems);
const currentPageItems = blacklist.slice(startIndex, endIndex);
console.log(`黑名单分页信息: 总数=${totalItems}, 每页=${this.blacklistPageSize}, 总页数=${totalPages}, 当前页=${this.blacklistCurrentPage}, 显示=${startIndex}-${endIndex-1}`);
itemsContainer.innerHTML = currentPageItems.map(item => `
<div class="blacklist-item" data-keyword="${item}">
<span class="blacklist-item-text">${item}</span>
<button class="blacklist-item-remove" data-keyword="${item}">删除</button>
</div>
`).join('');
// 更新分页控件
this.updateBlacklistPagination(blacklist, totalPages);
// 重新绑定删除按钮事件
this.bindBlacklistRemoveEvents();
}
// 更新黑名单分页控件
updateBlacklistPagination(blacklist, totalPages) {
// 先移除旧的分页控件
const oldPagination = this.blacklistPanel.querySelector('.pagination-container');
if (oldPagination) {
oldPagination.remove();
}
// 如果没有数据或只有一页,不显示分页
if (blacklist.length === 0 || totalPages <= 1) {
return;
}
// 创建分页容器
const paginationContainer = document.createElement('div');
paginationContainer.className = 'pagination-container';
// 添加分页信息
const paginationInfo = document.createElement('div');
paginationInfo.className = 'pagination-info';
const startIndex = (this.blacklistCurrentPage - 1) * this.blacklistPageSize + 1;
const endIndex = Math.min(startIndex + this.blacklistPageSize - 1, blacklist.length);
paginationInfo.textContent = `显示 ${startIndex}-${endIndex},共 ${blacklist.length} 项`;
paginationContainer.appendChild(paginationInfo);
// 添加分页按钮
// 首页按钮
const firstPageBtn = document.createElement('button');
firstPageBtn.className = `pagination-btn ${this.blacklistCurrentPage === 1 ? 'disabled' : ''}`;
firstPageBtn.textContent = '首页';
firstPageBtn.disabled = this.blacklistCurrentPage === 1;
firstPageBtn.onclick = () => {
if (this.blacklistCurrentPage !== 1) {
this.blacklistCurrentPage = 1;
this.updateBlacklistPanel();
}
};
paginationContainer.appendChild(firstPageBtn);
// 上一页按钮
const prevPageBtn = document.createElement('button');
prevPageBtn.className = `pagination-btn ${this.blacklistCurrentPage === 1 ? 'disabled' : ''}`;
prevPageBtn.textContent = '上一页';
prevPageBtn.disabled = this.blacklistCurrentPage === 1;
prevPageBtn.onclick = () => {
if (this.blacklistCurrentPage > 1) {
this.blacklistCurrentPage--;
this.updateBlacklistPanel();
}
};
paginationContainer.appendChild(prevPageBtn);
// 页码按钮
const maxPageButtons = 5; // 最多显示5个页码按钮
let startPage = Math.max(1, this.blacklistCurrentPage - Math.floor(maxPageButtons / 2));
let endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
// 调整startPage,确保显示maxPageButtons个按钮
if (endPage - startPage + 1 < maxPageButtons) {
startPage = Math.max(1, endPage - maxPageButtons + 1);
}
for (let i = startPage; i <= endPage; i++) {
const pageBtn = document.createElement('button');
pageBtn.className = `pagination-btn ${i === this.blacklistCurrentPage ? 'active' : ''}`;
pageBtn.textContent = i.toString();
pageBtn.onclick = () => {
if (this.blacklistCurrentPage !== i) {
this.blacklistCurrentPage = i;
this.updateBlacklistPanel();
}
};
paginationContainer.appendChild(pageBtn);
}
// 下一页按钮
const nextPageBtn = document.createElement('button');
nextPageBtn.className = `pagination-btn ${this.blacklistCurrentPage === totalPages ? 'disabled' : ''}`;
nextPageBtn.textContent = '下一页';
nextPageBtn.disabled = this.blacklistCurrentPage === totalPages;
nextPageBtn.onclick = () => {
if (this.blacklistCurrentPage < totalPages) {
this.blacklistCurrentPage++;
this.updateBlacklistPanel();
}
};
paginationContainer.appendChild(nextPageBtn);
// 末页按钮
const lastPageBtn = document.createElement('button');
lastPageBtn.className = `pagination-btn ${this.blacklistCurrentPage === totalPages ? 'disabled' : ''}`;
lastPageBtn.textContent = '末页';
lastPageBtn.disabled = this.blacklistCurrentPage === totalPages;
lastPageBtn.onclick = () => {
if (this.blacklistCurrentPage !== totalPages) {
this.blacklistCurrentPage = totalPages;
this.updateBlacklistPanel();
}
};
paginationContainer.appendChild(lastPageBtn);
// 添加每页显示数量选择
const pageSizeContainer = document.createElement('div');
pageSizeContainer.className = 'page-size-container';
pageSizeContainer.innerHTML = '每页显示: ';
const pageSizeSelect = document.createElement('select');
pageSizeSelect.className = 'page-size-select';
[5, 10, 50, 100, 200].forEach(size => {
const option = document.createElement('option');
option.value = size;
option.textContent = size.toString();
option.selected = this.blacklistPageSize === size;
pageSizeSelect.appendChild(option);
});
pageSizeSelect.onchange = (e) => {
const newPageSize = parseInt(e.target.value);
// 计算新的当前页,尽量保持显示的是相同的内容
const firstItemIndex = (this.blacklistCurrentPage - 1) * this.blacklistPageSize;
this.blacklistPageSize = newPageSize;
this.blacklistCurrentPage = Math.floor(firstItemIndex / newPageSize) + 1;
this.updateBlacklistPanel();
};
pageSizeContainer.appendChild(pageSizeSelect);
paginationContainer.appendChild(pageSizeContainer);
// 将分页控件添加到面板
const blacklistItems = this.blacklistPanel.querySelector('#blacklist-items');
blacklistItems.parentNode.insertBefore(paginationContainer, blacklistItems.nextSibling);
}
addBlacklistItem() {
const input = this.blacklistPanel.querySelector('#blacklist-input');
const text = input.value.trim();
if (!text) {
this.showMessage('请输入要屏蔽的关键词', 'error');
return;
}
const success = this.manager.blacklistManager.addBlacklistItem(text);
if (success) {
input.value = '';
this.updateBlacklistPanel();
this.updateBlacklistToggleButton();
this.showMessage(`已添加黑名单项: ${text}`, 'success');
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
// 更新所有列表项的黑名单状态
if (window.pageHandler) {
window.pageHandler.updateAllBlacklistStates();
}
// 重置变化标记(添加后)
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.resetChangesFlag();
}
} else {
this.showMessage('该关键词已存在', 'error');
}
}
removeBlacklistItem(text) {
const success = this.manager.blacklistManager.removeBlacklistItem(text);
if (success) {
this.updateBlacklistPanel();
this.updateBlacklistToggleButton();
this.showMessage(`已移除黑名单项: ${text}`, 'success');
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
// 更新所有列表项的黑名单状态
if (window.pageHandler) {
window.pageHandler.updateAllBlacklistStates();
}
// 重置变化标记(删除后)
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.resetChangesFlag();
}
}
}
// 绑定黑名单删除按钮事件
bindBlacklistRemoveEvents() {
const removeButtons = this.blacklistPanel.querySelectorAll('.blacklist-item-remove');
removeButtons.forEach(btn => {
btn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
const keyword = btn.getAttribute('data-keyword');
if (keyword) {
this.removeBlacklistItem(keyword);
}
};
});
}
exportBlacklist() {
const json = this.manager.blacklistManager.exportJSON();
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
const blacklistCount = this.manager.blacklistManager.getBlacklist().length;
const fileName = `blacklist_${blacklistCount}_${new Date().toISOString().split('T')[0]}.json`;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// 重置变化标记(导出成功后)
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.resetChangesFlag();
}
this.showMessage(`导出成功!共导出 ${blacklistCount} 个黑名单项`, 'success');
}
importBlacklist() {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
fileInput.style.display = 'none';
fileInput.onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
if (file.size > 1024 * 1024) {
this.showMessage('文件过大,请选择小于1MB的文件', 'error');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
try {
const jsonData = event.target.result;
const result = this.manager.blacklistManager.importJSON(jsonData);
if (result && result.success) {
this.updateBlacklistPanel();
this.updateBlacklistToggleButton();
// 显示详细的导入结果信息
let message = `导入成功!`;
if (result.addedCount > 0) {
message += `新增 ${result.addedCount} 个黑名单项`;
if (result.importCount > result.addedCount) {
message += `,跳过 ${result.importCount - result.addedCount} 个重复项`;
}
} else {
message += `所有 ${result.importCount} 个黑名单项都已存在,无新增`;
}
message += `,当前共 ${result.totalCount} 个黑名单项`;
this.showMessage(message, 'success');
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
// 更新所有列表项的黑名单状态
if (window.pageHandler) {
window.pageHandler.updateAllBlacklistStates();
}
// 重置变化标记(导入后)
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.resetChangesFlag();
}
} else {
this.showMessage('导入失败:数据格式错误', 'error');
}
} catch (error) {
console.error('导入黑名单错误:', error);
this.showMessage('导入失败:文件格式错误或读取失败', 'error');
}
};
reader.onerror = () => {
this.showMessage('导入失败:文件读取失败', 'error');
};
reader.readAsText(file);
};
document.body.appendChild(fileInput);
fileInput.click();
document.body.removeChild(fileInput);
}
clearBlacklist() {
const blacklistCount = this.manager.blacklistManager.getBlacklist().length;
if (blacklistCount === 0) {
this.showMessage('黑名单已为空', 'info');
return;
}
if (confirm(`确定要清空所有黑名单项吗?\n\n当前有 ${blacklistCount} 个黑名单项将被清空`)) {
this.manager.blacklistManager.clearAll();
this.updateBlacklistPanel();
this.updateBlacklistToggleButton();
this.showMessage('已清空所有黑名单项', 'success');
// 标记收藏管理器有数据变化
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.hasChanges = true;
}
// 更新所有列表项的黑名单状态
if (window.pageHandler) {
window.pageHandler.updateAllBlacklistStates();
}
// 重置变化标记(清空后)
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.resetChangesFlag();
}
}
}
createBlacklistPanel() {
this.blacklistPanel = document.createElement('div');
this.blacklistPanel.className = 'blacklist-panel';
this.blacklistPanel.style.display = 'none';
this.blacklistPanel.innerHTML = `
<div class="blacklist-header">
<span class="blacklist-title">黑名单管理</span>
<button class="blacklist-close">×</button>
</div>
<div class="blacklist-content">
<div class="blacklist-search">
<input type="text" class="blacklist-search-input" placeholder="搜索黑名单关键词..." id="blacklist-search-input">
<div class="blacklist-search-info" id="blacklist-search-info">共 0 个黑名单项</div>
</div>
<div class="blacklist-input-section">
<input type="text" class="blacklist-input" placeholder="输入要屏蔽的关键词..." id="blacklist-input">
<button class="blacklist-add-btn" id="blacklist-add-btn">添加</button>
</div>
<div class="blacklist-items" id="blacklist-items">
<!-- 黑名单项将在这里动态生成 -->
</div>
</div>
<div class="blacklist-actions">
<button class="blacklist-action-btn" id="blacklist-export-btn">导出JSON</button>
<button class="blacklist-action-btn" id="blacklist-import-btn">导入JSON</button>
<button class="blacklist-action-btn danger" id="blacklist-clear-btn">清空黑名单</button>
</div>
`;
document.body.appendChild(this.blacklistPanel);
// 绑定事件
this.blacklistPanel.querySelector('.blacklist-close').onclick = () => this.hideBlacklistPanel();
this.blacklistPanel.querySelector('#blacklist-add-btn').onclick = () => this.addBlacklistItem();
this.blacklistPanel.querySelector('#blacklist-export-btn').onclick = () => this.exportBlacklist();
this.blacklistPanel.querySelector('#blacklist-import-btn').onclick = () => this.importBlacklist();
this.blacklistPanel.querySelector('#blacklist-clear-btn').onclick = () => this.clearBlacklist();
// 绑定输入框回车事件
const input = this.blacklistPanel.querySelector('#blacklist-input');
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.addBlacklistItem();
}
});
// 绑定搜索框事件
const searchInput = this.blacklistPanel.querySelector('#blacklist-search-input');
searchInput.addEventListener('input', () => {
this.updateBlacklistPanel();
});
// 初始化黑名单项显示
this.updateBlacklistPanel();
}
updateToggleButton() {
const favoritesCount = this.manager.getFavorites().length;
const superLikedCount = this.manager.getFavorites().filter(f => f.superLiked).length;
const noImagesCount = this.manager.getFavorites().filter(f => !f.hasImages).length;
const viewHistoryCount = Object.keys(this.manager.viewHistoryManager.getAllRecords()).length;
const blacklistCount = this.manager.blacklistManager.getBlacklist().length;
this.toggleBtn.innerHTML = `收藏夹 <span class="favorites-count">${favoritesCount}</span>`;
// 同时更新下拉菜单的计数
this.updateDropdownCounts();
// 更新面板标题
const titleElement = this.panel.querySelector('.favorites-title');
if (titleElement) {
let title = `我的收藏 (${favoritesCount})`;
if (superLikedCount > 0) {
title += ` | 特别喜欢 (${superLikedCount})`;
}
if (noImagesCount > 0) {
title += ` | 无图片 (${noImagesCount})`;
}
if (viewHistoryCount > 0) {
title += ` | 浏览记录 (${viewHistoryCount})`;
}
if (blacklistCount > 0) {
title += ` | 黑名单 (${blacklistCount})`;
}
titleElement.textContent = title;
}
// 更新存储大小
this.updateStorageSize();
}
updateBlacklistToggleButton() {
const blacklistCount = this.manager.blacklistManager.getBlacklist().length;
this.blacklistToggleBtn.innerHTML = `黑名单 <span class="blacklist-count">${blacklistCount}</span>`;
// 同时更新下拉菜单的计数
this.updateDropdownCounts();
}
updatePreloadCount() {
const countElement = this.panel.querySelector('#preload-count');
if (countElement) {
countElement.textContent = this.preloadManager.preloadedUrls.size;
}
}
// 计算并更新存储数据大小
updateStorageSize() {
const sizeElement = this.panel.querySelector('#storage-size');
if (!sizeElement) return;
try {
// 计算收藏数据大小
const favoritesData = JSON.stringify(this.manager.getFavorites());
const favoritesSize = new Blob([favoritesData]).size;
// 计算浏览历史数据大小
const viewHistoryData = JSON.stringify(this.manager.viewHistoryManager.getAllRecords());
const viewHistorySize = new Blob([viewHistoryData]).size;
// 计算黑名单数据大小
const blacklistData = JSON.stringify(this.manager.blacklistManager.getBlacklist());
const blacklistSize = new Blob([blacklistData]).size;
// 计算预加载数据大小(估算)
let preloadSize = 0;
this.preloadManager.preloadedUrls.forEach((preload, url) => {
// 估算每个预加载项的大小(包含iframe、图片URL等)
const preloadData = JSON.stringify({
url: url,
title: preload.title || '',
images: preload.images || [],
timestamp: preload.timestamp || 0
});
preloadSize += new Blob([preloadData]).size;
});
// 总大小
const totalSize = favoritesSize + viewHistorySize + blacklistSize + preloadSize;
// 格式化显示
const formatSize = (bytes) => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const formattedSize = formatSize(totalSize);
// 根据大小设置颜色
let sizeClass = 'storage-size';
if (totalSize > 50 * 1024 * 1024) { // 超过50MB
sizeClass = 'storage-danger';
} else if (totalSize > 10 * 1024 * 1024) { // 超过10MB
sizeClass = 'storage-warning';
}
sizeElement.textContent = formattedSize;
sizeElement.className = sizeClass;
// 添加详细信息的tooltip
const details = `收藏数据: ${formatSize(favoritesSize)}\n浏览历史: ${formatSize(viewHistorySize)}\n黑名单数据: ${formatSize(blacklistSize)}\n预加载数据: ${formatSize(preloadSize)}`;
sizeElement.title = details;
} catch (error) {
console.error('计算存储大小失败:', error);
sizeElement.textContent = '计算失败';
sizeElement.className = 'storage-danger';
}
}
// 全部预加载功能
async preloadAllItems() {
if (this.preloadAllBtn.classList.contains('loading')) {
return;
}
// 获取当前页面的所有列表项
const items = document.querySelectorAll('.pbw');
if (items.length === 0) {
this.showMessage('当前页面没有找到列表项', 'info');
return;
}
this.preloadAllBtn.classList.add('loading');
this.preloadAllBtn.textContent = '预加载中...';
let successCount = 0;
let totalCount = 0;
let skippedCount = 0;
let blacklistedCount = 0;
for (const item of items) {
const link = item.querySelector('a');
if (!link) continue;
const url = link.href;
const title = link.textContent.trim();
const id = window.pageHandler.extractIdFromUrl(url);
// 跳过黑名单项目
if (this.manager.blacklistManager.isBlacklisted(title)) {
blacklistedCount++;
totalCount++;
continue;
}
// 跳过已经预加载的
if (this.preloadManager.isPreloaded(url)) {
successCount++;
totalCount++;
continue;
}
// 跳过已收藏的
if (id && this.manager.isFavorited(id)) {
skippedCount++;
totalCount++;
continue;
}
try {
await this.preloadManager.preloadUrl(url, title);
successCount++;
// 显示图片内容(在增加浏览次数之前)
this.showItemImages(item, url);
// 增加浏览次数(图片元素插入DOM后)
if (id) {
this.manager.viewHistoryManager.incrementView(id);
console.log(`预加载成功,增加浏览次数 ID: ${id}`);
// 更新对应项的浏览次数按钮
const viewCountBtn = item.querySelector('.view-count');
if (viewCountBtn) {
const newViewCount = this.manager.viewHistoryManager.getViewCount(id);
viewCountBtn.innerHTML = `<span class="view-count-icon">👁️</span><span>${newViewCount}</span>`;
viewCountBtn.title = `浏览次数: ${newViewCount}`;
}
// 更新列表项背景色
this.updateListItemBackground(item, id);
}
// 更新对应项的预加载按钮状态
const preloadBtn = item.querySelector('.preload-btn');
if (preloadBtn) {
preloadBtn.classList.remove('loading');
preloadBtn.classList.add('preloaded');
preloadBtn.textContent = '已预加载';
}
} catch (error) {
console.error('预加载失败:', url, error);
}
totalCount++;
// 更新按钮文本显示进度
this.preloadAllBtn.textContent = `预加载中... (${successCount}/${totalCount})`;
// 添加小延迟避免过于频繁的请求
await new Promise(resolve => setTimeout(resolve, 100));
}
this.preloadAllBtn.classList.remove('loading');
this.preloadAllBtn.textContent = '全部预加载';
this.updatePreloadCount();
let message = `预加载完成!成功 ${successCount}/${totalCount} 项`;
if (blacklistedCount > 0) {
message += `,跳过 ${blacklistedCount} 项(黑名单)`;
}
if (skippedCount > 0) {
message += `,跳过 ${skippedCount} 项(已收藏)`;
}
this.showMessage(message, 'success');
}
// 显示列表项的图片内容
showItemImages(item, url) {
// 检查是否已经显示过图片
const existingImagesContainer = item.querySelector('.list-item-images');
if (existingImagesContainer) {
// 如果已经存在图片容器,确保它是可见的
existingImagesContainer.style.display = 'block';
existingImagesContainer.style.visibility = 'visible';
return;
}
const preloaded = this.preloadManager.getPreloaded(url);
if (!preloaded || !preloaded.images || preloaded.images.length === 0) {
return;
}
const images = preloaded.images;
// 创建图片容器
const imagesContainer = document.createElement('div');
imagesContainer.className = 'list-item-images';
imagesContainer.style.display = 'block';
imagesContainer.style.visibility = 'visible';
// 创建图片滑动容器
const imagesWrapper = document.createElement('div');
imagesWrapper.className = 'list-item-images-container';
imagesWrapper.style.display = 'flex';
imagesWrapper.style.visibility = 'visible';
// 计算优先级 - 基于列表项在页面中的位置
const allItems = document.querySelectorAll('.pbw');
let itemIndex = Array.from(allItems).indexOf(item);
if (itemIndex === -1) itemIndex = 0;
// 添加图片 - 使用懒加载
images.forEach((imgSrc, index) => {
const img = document.createElement('img');
img.className = 'list-item-image';
img.alt = `图片 ${index + 1}`;
// 设置懒加载属性
img.setAttribute('lazy-src', imgSrc);
// 计算优先级:第一条第一页=1,第一条第二页=2,第一条第三页=3,第二条第一页=4...
const priority = itemIndex * 3 + index + 1;
img.setAttribute('lazy-level', priority);
// 添加点击事件,打开图片查看器
img.onclick = (e) => {
e.stopPropagation();
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.imageViewer) {
window.pageHandler.ui.imageViewer.show(images, index);
}
};
// 设置图片加载事件
img.onload = () => {
img.classList.add('loaded');
img.style.opacity = '1'; // 确保图片可见
img.style.display = 'block';
};
img.onerror = () => {
img.style.display = 'none';
img.style.opacity = '0'; // 确保图片不可见
img.classList.remove('loaded'); // 确保失败时移除loaded类
img.removeAttribute('lazy-src');
};
imagesWrapper.appendChild(img);
});
imagesContainer.appendChild(imagesWrapper);
// 由于最多只有2张图片,不需要导航功能
// 将图片容器添加到列表项
item.appendChild(imagesContainer);
// 由于最多只有2张图片,所有图片都直接显示
}
// 显示指定组的图片
showImageGroup(container, groupIndex) {
const wrapper = container.querySelector('.list-item-images-container');
const images = container.querySelectorAll('.list-item-image');
const dots = container.querySelectorAll('.list-item-image-dot');
const prevBtn = container.querySelector('.prev');
const nextBtn = container.querySelector('.next');
const totalGroups = Math.ceil(images.length / 3);
if (groupIndex < 0 || groupIndex >= totalGroups) return;
// 检查是否已经处理过这个组(防止重复调用)
const currentActiveDot = container.querySelector('.list-item-image-dot.active');
const currentActiveIndex = Array.from(dots).indexOf(currentActiveDot);
if (currentActiveIndex === groupIndex) {
return;
}
// 移动图片容器(每组3张图片,每张占33.333%宽度)
wrapper.style.transform = `translateX(-${groupIndex * 100}%)`;
// 确保当前组的图片可见
images.forEach((img, index) => {
const isInCurrentGroup = index >= groupIndex * 3 && index < (groupIndex + 1) * 3;
if (isInCurrentGroup) {
img.style.display = 'block';
// 更新优先级 - 当前显示的图片组优先级提高
const currentPriority = parseInt(img.getAttribute('lazy-level') || '999');
// 如果当前组的图片还没有加载,提高其优先级
if (!img.classList.contains('loaded')) {
// 设置更高的优先级,但仍保持列表项的顺序
img.setAttribute('lazy-level', Math.max(1, currentPriority - 100));
}
} else {
// 非当前组的图片可以隐藏以节省资源
img.style.display = 'none';
}
});
// 更新指示器
dots.forEach((dot, i) => {
dot.classList.toggle('active', i === groupIndex);
});
// 更新导航按钮状态
if (prevBtn) {
prevBtn.classList.toggle('hidden', totalGroups <= 1);
}
if (nextBtn) {
nextBtn.classList.toggle('hidden', totalGroups <= 1);
}
}
// 导航到上一组或下一组图片
navigateImages(container, direction) {
const dots = container.querySelectorAll('.list-item-image-dot');
const currentActiveDot = container.querySelector('.list-item-image-dot.active');
const currentIndex = Array.from(dots).indexOf(currentActiveDot);
const totalGroups = dots.length;
// 计算新的组索引
let newIndex = currentIndex + direction;
if (newIndex < 0) {
newIndex = totalGroups - 1;
} else if (newIndex >= totalGroups) {
newIndex = 0;
}
// 显示新的组
this.showImageGroup(container, newIndex);
}
exportFavorites() {
const json = this.manager.exportJSON();
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
// 获取收藏、浏览记录和黑名单数量
const favoritesCount = this.manager.getFavorites().length;
const viewHistoryCount = Object.keys(this.manager.viewHistoryManager.getAllRecords()).length;
const blacklistCount = this.manager.blacklistManager.getBlacklist().length;
const fileName = `favorites_${favoritesCount}_views_${viewHistoryCount}_blacklist_${blacklistCount}_${new Date().toISOString().split('T')[0]}.json`;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// 重置变化标记(导出成功后)
this.manager.resetChangesFlag();
// 显示导出成功消息
let message = `导出成功!共导出 ${favoritesCount} 个收藏`;
if (viewHistoryCount > 0) {
message += `,${viewHistoryCount} 条浏览记录`;
}
if (blacklistCount > 0) {
message += `,${blacklistCount} 个黑名单项`;
}
this.showMessage(message, 'success');
}
importFavorites() {
// 创建文件输入元素
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
fileInput.style.display = 'none';
fileInput.onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
// 检查文件大小(限制为2MB,因为包含浏览历史)
if (file.size > 2 * 1024 * 1024) {
this.showMessage('文件过大,请选择小于2MB的文件', 'error');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
try {
const jsonData = event.target.result;
const data = JSON.parse(jsonData);
// 验证数据格式(兼容旧版本和新版本)
let isValidData = false;
let importCount = 0;
let viewHistoryCount = 0;
let blacklistCount = 0;
if (Array.isArray(data)) {
// 旧版本数据(只有收藏数组)
isValidData = data.every(item =>
item && typeof item === 'object' &&
item.id && item.title && item.url
);
importCount = data.length;
} else if (data.favorites && Array.isArray(data.favorites)) {
// 新版本数据(包含收藏、浏览历史和黑名单)
isValidData = data.favorites.every(item =>
item && typeof item === 'object' &&
item.id && item.title && item.url
);
importCount = data.favorites.length;
viewHistoryCount = data.viewHistory ? Object.keys(data.viewHistory).length : 0;
blacklistCount = data.blacklist ? data.blacklist.length : 0;
}
if (!isValidData) {
this.showMessage('导入失败:数据格式不正确,缺少必要字段', 'error');
return;
}
// 询问用户是否导入数据
const currentCount = this.manager.getFavorites().length;
const currentViewCount = Object.keys(this.manager.viewHistoryManager.getAllRecords()).length;
const currentBlacklistCount = this.manager.blacklistManager.getBlacklist().length;
let confirmMessage = `将导入 ${importCount} 个收藏`;
if (viewHistoryCount > 0) {
confirmMessage += `,${viewHistoryCount} 条浏览记录`;
}
if (blacklistCount > 0) {
confirmMessage += `,${blacklistCount} 个黑名单项(将追加到现有的 ${currentBlacklistCount} 个中,重复项会自动去重)`;
}
if (currentCount > 0) {
confirmMessage += `,当前有 ${currentCount} 个收藏将被覆盖`;
}
if (currentViewCount > 0) {
confirmMessage += `,${currentViewCount} 条浏览记录将被覆盖`;
}
confirmMessage += ',确定继续吗?';
if (confirm(confirmMessage)) {
const success = this.manager.importJSON(jsonData);
if (success) {
// 更新UI
this.updateToggleButton();
this.updateBlacklistToggleButton();
this.updateBlacklistPanel();
this.updateStorageSize();
this.updateAllButtons();
// 更新所有列表项的黑名单状态
if (window.pageHandler) {
window.pageHandler.updateAllBlacklistStates();
}
// 显示成功消息
let successMessage = `导入成功!共导入 ${importCount} 个收藏`;
if (viewHistoryCount > 0) {
successMessage += `,${viewHistoryCount} 条浏览记录`;
}
if (blacklistCount > 0) {
const finalBlacklistCount = this.manager.blacklistManager.getBlacklist().length;
const addedBlacklistCount = finalBlacklistCount - currentBlacklistCount;
if (addedBlacklistCount > 0) {
successMessage += `,新增 ${addedBlacklistCount} 个黑名单项`;
if (blacklistCount > addedBlacklistCount) {
successMessage += `(跳过 ${blacklistCount - addedBlacklistCount} 个重复项)`;
}
} else {
successMessage += `,所有 ${blacklistCount} 个黑名单项都已存在`;
}
successMessage += `,当前共 ${finalBlacklistCount} 个黑名单项`;
}
this.showMessage(successMessage, 'success');
} else {
this.showMessage('导入失败:数据保存错误', 'error');
}
}
} catch (error) {
console.error('导入错误:', error);
this.showMessage('导入失败:文件格式错误或读取失败', 'error');
}
};
reader.onerror = () => {
this.showMessage('导入失败:文件读取失败', 'error');
};
reader.readAsText(file);
};
// 触发文件选择
document.body.appendChild(fileInput);
fileInput.click();
document.body.removeChild(fileInput);
}
showMessage(message, type = 'info') {
// 创建消息提示元素
const messageDiv = document.createElement('div');
messageDiv.className = `favorites-message favorites-message-${type}`;
messageDiv.textContent = message;
// 添加到页面
document.body.appendChild(messageDiv);
// 3秒后自动移除
setTimeout(() => {
if (messageDiv.parentNode) {
messageDiv.parentNode.removeChild(messageDiv);
}
}, 3000);
}
// 清空未加载浏览记录功能
clearUnloadedViews() {
if (this.clearUnloadedViewsBtn.classList.contains('loading')) {
return;
}
// 获取当前页面的所有列表项
const items = document.querySelectorAll('.pbw');
if (items.length === 0) {
this.showMessage('当前页面没有找到列表项', 'info');
return;
}
this.clearUnloadedViewsBtn.classList.add('loading');
this.clearUnloadedViewsBtn.textContent = '清空中...';
let clearedCount = 0;
let totalCount = 0;
let skippedCount = 0;
for (const item of items) {
const link = item.querySelector('a');
if (!link) continue;
const url = link.href;
const id = window.pageHandler.extractIdFromUrl(url);
if (!id) {
skippedCount++;
totalCount++;
continue;
}
// 检查是否已经预加载
if (this.preloadManager.isPreloaded(url)) {
skippedCount++;
totalCount++;
continue;
}
// 检查是否有浏览记录
const viewCount = this.manager.viewHistoryManager.getViewCount(id);
if (viewCount === 0) {
skippedCount++;
totalCount++;
continue;
}
// 清空浏览记录
try {
// 从浏览历史中删除该记录
delete this.manager.viewHistoryManager.viewHistory[id];
clearedCount++;
// 更新对应的浏览次数按钮
const viewCountBtn = item.querySelector('.view-count');
if (viewCountBtn) {
viewCountBtn.innerHTML = `<span class="view-count-icon">👁️</span><span>0</span>`;
viewCountBtn.title = '浏览次数: 0';
}
// 更新列表项背景色
if (window.pageHandler) {
window.pageHandler.updateListItemBackground(item, id);
}
} catch (error) {
console.error('清空浏览记录失败:', id, error);
}
totalCount++;
// 更新按钮文本显示进度
this.clearUnloadedViewsBtn.textContent = `清空中... (${clearedCount}/${totalCount})`;
}
// 保存更新后的浏览历史
this.manager.viewHistoryManager.saveViewHistory();
this.clearUnloadedViewsBtn.classList.remove('loading');
this.clearUnloadedViewsBtn.textContent = '清空未加载浏览记录';
// 更新收藏夹按钮
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updateStorageSize();
}
let message = `清空完成!成功清空 ${clearedCount} 个未预加载项的浏览记录`;
if (skippedCount > 0) {
message += `,跳过 ${skippedCount} 项(已预加载或无浏览记录)`;
}
this.showMessage(message, 'success');
}
clearFavorites() {
const favoritesCount = this.manager.getFavorites().length;
const viewHistoryCount = Object.keys(this.manager.viewHistoryManager.getAllRecords()).length;
const blacklistCount = this.manager.blacklistManager.getBlacklist().length;
let confirmMessage = '确定要清空所有收藏吗?';
if (viewHistoryCount > 0) {
confirmMessage += `\n\n同时将清空 ${viewHistoryCount} 条浏览记录`;
}
if (blacklistCount > 0) {
confirmMessage += `\n\n同时将清空 ${blacklistCount} 个黑名单项`;
}
if (confirm(confirmMessage)) {
this.manager.clearAll();
this.manager.viewHistoryManager.clearAll();
this.manager.blacklistManager.clearAll();
// 清空后重置变化标记
this.manager.resetChangesFlag();
this.updateToggleButton();
this.updateBlacklistToggleButton();
this.updateBlacklistPanel();
this.updateStorageSize();
this.updateAllButtons();
// 更新所有列表项的黑名单状态
if (window.pageHandler) {
window.pageHandler.updateAllBlacklistStates();
}
this.showMessage('已清空所有收藏、浏览记录和黑名单', 'success');
}
}
updateAllButtons() {
document.querySelectorAll('.favorite-btn').forEach(btn => {
const id = btn.getAttribute('data-id');
if (id) {
if (this.manager.isFavorited(id)) {
btn.classList.add('favorited');
btn.textContent = '已收藏';
} else {
btn.classList.remove('favorited');
btn.textContent = '收藏';
}
}
});
}
// 更新特别喜欢徽章(不重新渲染整个面板)
updateSuperLikeBadge(id) {
const content = this.panel.querySelector('.favorites-content');
if (!content) return;
// 找到对应的收藏项
const favoriteItem = content.querySelector(`.favorite-item[data-id="${id}"]`);
if (!favoriteItem) return;
const favorite = this.manager.getFavorites().find(f => f.id === id);
if (!favorite) return;
console.log('更新特别喜欢徽章:', id, favorite.superLiked);
// 更新特别喜欢状态
if (favorite.superLiked) {
favoriteItem.classList.add('super-liked');
// 如果还没有徽章,添加徽章
if (!favoriteItem.querySelector('.super-like-badge')) {
const badge = document.createElement('div');
badge.className = 'super-like-badge';
badge.innerHTML = '❤️';
favoriteItem.appendChild(badge);
console.log('添加特别喜欢徽章');
}
} else {
favoriteItem.classList.remove('super-liked');
// 移除徽章
const badge = favoriteItem.querySelector('.super-like-badge');
if (badge) {
badge.remove();
console.log('移除特别喜欢徽章');
}
}
// 确保no-images类保持正确
if (!favorite.hasImages) {
favoriteItem.classList.add('no-images');
} else {
favoriteItem.classList.remove('no-images');
}
}
// 为收藏夹中的图片添加点击事件
setupFavoriteImageClickEvents() {
const content = this.panel.querySelector('.favorites-content');
if (!content) {
return;
}
const favoriteItems = content.querySelectorAll('.favorite-item');
favoriteItems.forEach((item) => {
const id = item.getAttribute('data-id');
const favorite = this.manager.getFavorites().find(f => f.id === id);
if (favorite && favorite.images && favorite.images.length > 0) {
// 为占位符添加点击事件
const placeholders = item.querySelectorAll('.favorite-image.placeholder[data-src]');
placeholders.forEach((placeholder) => {
placeholder.onclick = (e) => {
e.stopPropagation();
// 检查是否已经显示图片
const showImagesBtn = this.panel.querySelector('#show-images-btn');
const isShowingImages = showImagesBtn && showImagesBtn.textContent === '隐藏图片';
if (!isShowingImages) {
// 如果还没有显示图片,先显示图片
this.showImages();
} else {
// 如果已经显示图片,只加载这个特定的图片
const dataSrc = placeholder.getAttribute('data-src');
const dataIndex = placeholder.getAttribute('data-image-index');
if (dataSrc) {
const img = document.createElement('img');
img.className = 'favorite-image';
img.alt = `图片 ${parseInt(dataIndex) + 1}`;
img.setAttribute('lazy-src', dataSrc);
img.setAttribute('lazy-level', '1'); // 最高优先级
img.setAttribute('data-image-index', dataIndex);
// 添加点击事件
img.onclick = (e) => {
e.stopPropagation();
if (this.imageViewer) {
this.imageViewer.show(favorite.images, parseInt(dataIndex));
}
};
// 替换占位符
placeholder.parentNode.replaceChild(img, placeholder);
}
}
};
});
}
});
}
}
// 页面处理
class PageHandler {
constructor(manager, ui) {
this.manager = manager;
this.ui = ui;
this.updateTimer = null;
this.init();
}
init() {
// 等待页面加载完成
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.processPage());
} else {
this.processPage();
}
// 监听动态内容变化
const observer = new MutationObserver(() => {
this.processPage();
});
observer.observe(document.body, { childList: true, subtree: true });
// 启动定时更新收藏状态
this.startAutoUpdate();
// 页面卸载时清理定时器
window.addEventListener('beforeunload', (e) => {
this.stopAutoUpdate();
// 不清理预加载的iframe,让它们保持到页面刷新
// 这样可以保持预览效果
// 如果是列表页且有收藏数据且有变化,提示导出
const currentUrl = window.location.href;
if (this.isListPage(currentUrl)) {
const favorites = this.manager.getFavorites();
const blacklist = this.manager.blacklistManager.getBlacklist();
if ((favorites.length > 0 || blacklist.length > 0) && this.manager.hasChanges) {
let message = '';
if (favorites.length > 0) {
message += `您有 ${favorites.length} 个收藏`;
}
if (blacklist.length > 0) {
if (message) message += ',';
message += `${blacklist.length} 个黑名单项`;
}
message += '且有数据变化,建议导出备份!\n\n确定要离开吗?';
e.preventDefault();
e.returnValue = message;
return message;
}
}
});
}
startAutoUpdate() {
// 清除可能存在的旧定时器
if (this.updateTimer) {
clearInterval(this.updateTimer);
}
// 每5秒更新一次所有收藏按钮状态和黑名单状态
this.updateTimer = setInterval(() => {
console.log('执行自动更新...');
this.updateAllFavoriteStates();
this.updateAllBlacklistStates();
}, 5000);
console.log('收藏状态自动更新已启动,间隔5秒,定时器ID:', this.updateTimer);
}
stopAutoUpdate() {
if (this.updateTimer) {
clearInterval(this.updateTimer);
this.updateTimer = null;
console.log('收藏状态自动更新已停止');
}
}
updateAllFavoriteStates() {
// 重新从localStorage加载最新数据
this.manager.favorites = this.manager.loadFavorites();
// 更新所有收藏按钮的状态
document.querySelectorAll('.favorite-btn').forEach(btn => {
const id = btn.getAttribute('data-id');
if (id) {
if (this.manager.isFavorited(id)) {
btn.classList.add('favorited');
btn.textContent = '已收藏';
} else {
btn.classList.remove('favorited');
btn.textContent = '收藏';
}
}
});
// 更新所有特别喜欢按钮的状态
document.querySelectorAll('.super-like-btn').forEach(btn => {
const id = btn.getAttribute('data-id');
if (id) {
if (this.manager.isSuperLiked(id)) {
btn.classList.add('super-liked');
btn.textContent = '特别喜欢';
} else {
btn.classList.remove('super-liked');
btn.textContent = '特别喜欢';
}
}
});
// 更新所有浏览次数按钮的状态
document.querySelectorAll('.view-count').forEach(btn => {
const id = btn.getAttribute('data-id');
if (id) {
const viewCount = this.manager.viewHistoryManager.getViewCount(id);
btn.innerHTML = `<span class="view-count-icon">👁️</span><span>${viewCount}</span>`;
btn.title = `浏览次数: ${viewCount}`;
}
});
// 更新所有黑名单按钮的状态
document.querySelectorAll('.blacklist-btn').forEach(btn => {
const item = btn.closest('.pbw');
if (item) {
const link = item.querySelector('a');
if (link) {
const title = link.textContent.trim();
const isBlacklisted = this.manager.blacklistManager.isBlacklisted(title);
if (isBlacklisted) {
btn.classList.add('blacklisted');
btn.textContent = '已屏蔽';
} else {
btn.classList.remove('blacklisted');
btn.textContent = '黑名单';
}
}
}
});
// 更新所有列表项的背景色
this.updateAllListItemBackgrounds();
// 更新收藏夹按钮计数
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updateStorageSize();
}
// 更新导出提醒按钮
this.updateExportReminder();
// 添加调试日志
console.log('自动更新收藏状态完成,当前收藏数量:', this.manager.getFavorites().length);
}
updateAllBlacklistStates() {
// 更新所有列表项的黑名单状态
document.querySelectorAll('.pbw').forEach(item => {
const link = item.querySelector('a');
if (link) {
const title = link.textContent.trim();
this.updateListItemBlacklistState(item, title);
}
});
// 更新黑名单按钮计数
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateBlacklistToggleButton();
}
console.log('自动更新黑名单状态完成,当前黑名单数量:', this.manager.blacklistManager.getBlacklist().length);
}
updateAllViewCounts() {
// 重新从localStorage加载最新数据
this.manager.viewHistoryManager.viewHistory = this.manager.viewHistoryManager.loadViewHistory();
// 更新所有收藏项的浏览次数显示
document.querySelectorAll('.favorite-item').forEach(item => {
const id = item.getAttribute('data-id');
if (id) {
const viewCountSpan = item.querySelector('.view-count span');
if (viewCountSpan) {
viewCountSpan.textContent = this.manager.viewHistoryManager.getViewCount(id);
}
}
});
// 更新所有列表项的背景色
this.updateAllListItemBackgrounds();
// 更新收藏夹按钮计数
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateStorageSize();
}
// 添加调试日志
console.log('自动更新浏览次数完成,当前浏览记录数量:', this.manager.viewHistoryManager.getAllRecords().length);
}
updateExportReminder() {
const reminderBtn = document.querySelector('.export-reminder-btn');
const favorites = this.manager.getFavorites();
const blacklist = this.manager.blacklistManager.getBlacklist();
if (reminderBtn) {
if (favorites.length > 0 || blacklist.length > 0) {
let text = '💾 导出数据';
if (favorites.length > 0) {
text += ` (收藏:${favorites.length}`;
if (blacklist.length > 0) {
text += `, 黑名单:${blacklist.length}`;
}
text += ')';
} else if (blacklist.length > 0) {
text += ` (黑名单:${blacklist.length})`;
}
reminderBtn.innerHTML = text;
reminderBtn.style.display = 'block';
} else {
reminderBtn.style.display = 'none';
}
} else if ((favorites.length > 0 || blacklist.length > 0) && this.isListPage(window.location.href)) {
// 如果按钮不存在但有数据,重新添加按钮
this.addExportReminder();
}
}
processPage() {
// 判断当前页面类型
const currentUrl = window.location.href;
console.log('当前页面URL:', currentUrl);
// 为页面添加类型标识
if (this.isListPage(currentUrl)) {
document.body.classList.add('list-page');
document.body.classList.remove('detail-page');
console.log('检测到列表页,处理列表页收藏按钮');
this.processListPage();
} else if (this.isDetailPage(currentUrl)) {
document.body.classList.add('detail-page');
document.body.classList.remove('list-page');
console.log('检测到详情页,处理详情页收藏按钮');
this.processDetailPage();
} else {
console.log('未检测到支持的页面类型');
}
}
isListPage(url) {
// 检查是否为列表页:包含 search.php?mod= 参数
const isList = url.includes('search.php?mod=');
console.log('列表页检查:', url, '结果:', isList);
return isList;
}
isDetailPage(url) {
// 检查是否为详情页:格式为 https://www.xmxmkb.com/forum.php?mod=viewthread&tid=数字
const isDetail = url.includes('forum.php?mod=viewthread&tid=');
console.log('详情页检查:', url, '结果:', isDetail);
return isDetail;
}
processListPage() {
const items = document.querySelectorAll('.pbw');
items.forEach(item => {
const link = item.querySelector('a');
if (!link) return;
const title = link.textContent.trim();
const url = link.href;
// 从列表项的id属性中提取ID
const id = item.id;
if (!id) {
console.log('无法从列表项的id属性中提取ID:', item);
return;
}
// 检查是否已经添加过按钮
if (item.querySelector('.favorite-btn')) return;
// 设置列表项的背景色样式
this.updateListItemBackground(item, id);
// 设置列表项的黑名单状态
this.updateListItemBlacklistState(item, title);
// 创建收藏按钮
const favoriteBtn = document.createElement('button');
favoriteBtn.className = 'favorite-btn';
favoriteBtn.setAttribute('data-id', id);
favoriteBtn.textContent = this.manager.isFavorited(id) ? '已收藏' : '收藏';
if (this.manager.isFavorited(id)) {
favoriteBtn.classList.add('favorited');
}
favoriteBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
if (window.pageHandler) {
window.pageHandler.toggleFavorite(id, title, url, favoriteBtn);
}
};
// 创建特别喜欢按钮
const superLikeBtn = document.createElement('button');
superLikeBtn.className = 'super-like-btn';
superLikeBtn.setAttribute('data-id', id);
superLikeBtn.textContent = this.manager.isSuperLiked(id) ? '特别喜欢' : '特别喜欢';
if (this.manager.isSuperLiked(id)) {
superLikeBtn.classList.add('super-liked');
}
superLikeBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
if (window.pageHandler) {
window.pageHandler.toggleSuperLikeInList(id, title, url, superLikeBtn);
}
};
// 创建浏览次数按钮
const viewCountBtn = document.createElement('button');
viewCountBtn.className = 'view-count';
viewCountBtn.setAttribute('data-id', id);
const viewCount = this.manager.viewHistoryManager.getViewCount(id);
viewCountBtn.innerHTML = `<span class="view-count-icon">👁️</span><span>${viewCount}</span>`;
viewCountBtn.title = `浏览次数: ${viewCount}`;
// 创建预加载按钮
const preloadBtn = document.createElement('button');
preloadBtn.className = 'preload-btn';
preloadBtn.textContent = '预加载';
preloadBtn.title = '点击预加载页面,获取图片内容';
// 预加载按钮的点击逻辑
preloadBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
// 检查内存中是否已经预加载
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.preloadManager.isPreloaded(url)) {
// 检查图片是否已经显示在DOM中
const hasImagesDisplayed = item.querySelector('.list-item-images');
if (!hasImagesDisplayed) {
// 虽然已预加载但图片未显示,需要显示图片
window.pageHandler.ui.showItemImages(item, url);
window.pageHandler.ui.showMessage('图片内容已显示', 'success');
} else {
window.pageHandler.ui.showMessage('已预加载,图片内容已显示', 'info');
}
return;
}
// 检查缓存中是否有预加载数据
const cachedData = window.pageHandler.ui.preloadManager.getPersistedImageData(url);
if (cachedData && cachedData.images && cachedData.images.length > 0) {
// 将缓存数据加载到内存中
window.pageHandler.ui.preloadManager.preloadedUrls.set(url, cachedData);
// 更新按钮状态
preloadBtn.classList.add('preloaded');
preloadBtn.textContent = '已预加载';
// 显示图片内容
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.showItemImages(item, url);
window.pageHandler.ui.showMessage('从缓存加载图片成功', 'success');
}
// 更新预加载计数
window.pageHandler.ui.updatePreloadCount();
return;
}
// 开始预加载
preloadBtn.classList.add('loading');
preloadBtn.textContent = '加载中';
window.pageHandler.ui.preloadManager.preloadUrl(url, title)
.then(() => {
preloadBtn.classList.remove('loading');
preloadBtn.classList.add('preloaded');
preloadBtn.textContent = '已预加载';
window.pageHandler.ui.updatePreloadCount();
// 显示图片内容(在增加浏览次数之前)
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.showItemImages(item, url);
}
// 增加浏览次数(图片元素插入DOM后)
if (id) {
window.pageHandler.manager.viewHistoryManager.incrementView(id);
console.log(`预加载成功,增加浏览次数 ID: ${id}`);
// 更新对应项的浏览次数按钮
const viewCountBtn = item.querySelector('.view-count');
if (viewCountBtn) {
const newViewCount = window.pageHandler.manager.viewHistoryManager.getViewCount(id);
viewCountBtn.innerHTML = `<span class="view-count-icon">👁️</span><span>${newViewCount}</span>`;
viewCountBtn.title = `浏览次数: ${newViewCount}`;
}
// 更新列表项背景色
window.pageHandler.updateListItemBackground(item, id);
}
})
.catch((error) => {
console.error('预加载失败:', error);
preloadBtn.classList.remove('loading');
preloadBtn.textContent = '预加载失败';
setTimeout(() => {
preloadBtn.textContent = '预加载';
}, 2000);
});
};
// 检查是否已经预加载
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.preloadManager.isPreloaded(url)) {
preloadBtn.classList.add('preloaded');
preloadBtn.textContent = '已预加载';
}
// 创建黑名单按钮
const blacklistBtn = document.createElement('button');
blacklistBtn.className = 'blacklist-btn';
blacklistBtn.textContent = '黑名单';
blacklistBtn.title = '点击将词条标题添加到黑名单';
// 检查是否已经在黑名单中
if (window.pageHandler && window.pageHandler.manager && window.pageHandler.manager.blacklistManager.isBlacklisted(title)) {
blacklistBtn.classList.add('blacklisted');
blacklistBtn.textContent = '已屏蔽';
}
// 黑名单按钮的点击逻辑
blacklistBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
if (window.pageHandler && window.pageHandler.manager && window.pageHandler.manager.blacklistManager) {
const isBlacklisted = window.pageHandler.manager.blacklistManager.isBlacklisted(title);
if (isBlacklisted) {
// 如果已经在黑名单中,询问是否移除
if (confirm(`"${title}" 已在黑名单中,是否要移除?`)) {
const success = window.pageHandler.manager.blacklistManager.removeBlacklistItem(title);
if (success) {
blacklistBtn.classList.remove('blacklisted');
blacklistBtn.textContent = '黑名单';
// 更新列表项的黑名单状态
window.pageHandler.updateListItemBlacklistState(item, title);
// 更新黑名单按钮计数
if (window.pageHandler.ui) {
window.pageHandler.ui.updateBlacklistToggleButton();
window.pageHandler.ui.updateBlacklistPanel();
}
window.pageHandler.ui.showMessage(`已从黑名单中移除: ${title}`, 'success');
}
}
} else {
// 直接添加到黑名单,不需要确认
const success = window.pageHandler.manager.blacklistManager.addBlacklistItem(title);
if (success) {
blacklistBtn.classList.add('blacklisted');
blacklistBtn.textContent = '已屏蔽';
// 更新列表项的黑名单状态
window.pageHandler.updateListItemBlacklistState(item, title);
// 更新黑名单按钮计数
if (window.pageHandler.ui) {
window.pageHandler.ui.updateBlacklistToggleButton();
window.pageHandler.ui.updateBlacklistPanel();
}
window.pageHandler.ui.showMessage(`已添加到黑名单: ${title}`, 'success');
} else {
window.pageHandler.ui.showMessage('该词条已在黑名单中', 'info');
}
}
}
};
// 创建左侧浮动按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.className = 'list-item-buttons';
// 将按钮添加到容器中
buttonContainer.appendChild(favoriteBtn);
buttonContainer.appendChild(superLikeBtn);
buttonContainer.appendChild(viewCountBtn);
buttonContainer.appendChild(preloadBtn);
buttonContainer.appendChild(blacklistBtn);
// 检查预加载状态并更新按钮
if (window.pageHandler && window.pageHandler.ui) {
// 检查内存中是否已预加载
if (window.pageHandler.ui.preloadManager.isPreloaded(url)) {
preloadBtn.classList.add('preloaded');
preloadBtn.textContent = '已预加载';
} else {
// 检查缓存中是否有数据
const cachedData = window.pageHandler.ui.preloadManager.getPersistedImageData(url);
if (cachedData && cachedData.images && cachedData.images.length > 0) {
// 将缓存数据加载到内存中
window.pageHandler.ui.preloadManager.preloadedUrls.set(url, cachedData);
preloadBtn.classList.add('preloaded');
preloadBtn.textContent = '已预加载';
}
}
}
// 将按钮容器添加到列表项中
item.appendChild(buttonContainer);
});
// 在列表页添加导出提醒按钮
this.addExportReminder();
}
// 更新列表项背景色
updateListItemBackground(item, id) {
if (!item || !id) return;
// 移除所有可能的状态类
item.classList.remove('viewed-not-favorited', 'favorited', 'viewed-and-favorited');
const isFavorited = this.manager.isFavorited(id);
const viewCount = this.manager.viewHistoryManager.getViewCount(id);
const isViewed = viewCount > 0;
if (isViewed && !isFavorited) {
// 已浏览但未收藏 - 浅红色
item.classList.add('viewed-not-favorited');
} else if (isFavorited) {
// 已收藏 - 浅绿色
item.classList.add('favorited');
}
// 如果既未浏览也未收藏,保持默认样式
}
addExportReminder() {
// 检查是否已经添加过导出提醒按钮
if (document.querySelector('.export-reminder-btn')) return;
const favorites = this.manager.getFavorites();
const blacklist = this.manager.blacklistManager.getBlacklist();
if (favorites.length === 0 && blacklist.length === 0) return; // 没有数据时不显示
const reminderBtn = document.createElement('button');
reminderBtn.className = 'export-reminder-btn';
let text = '💾 导出数据';
if (favorites.length > 0) {
text += ` (收藏:${favorites.length}`;
if (blacklist.length > 0) {
text += `, 黑名单:${blacklist.length}`;
}
text += ')';
} else if (blacklist.length > 0) {
text += ` (黑名单:${blacklist.length})`;
}
reminderBtn.innerHTML = text;
reminderBtn.title = '点击导出收藏和黑名单数据备份';
reminderBtn.onclick = () => {
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.exportFavorites();
}
};
// 添加到页面顶部
reminderBtn.style.position = 'fixed';
reminderBtn.style.top = '20px';
reminderBtn.style.left = '20px';
reminderBtn.style.zIndex = '9997';
reminderBtn.style.background = '#ff9800';
reminderBtn.style.color = 'white';
reminderBtn.style.border = 'none';
reminderBtn.style.padding = '0px 12px 8px 12px';
reminderBtn.style.borderRadius = '4px';
reminderBtn.style.cursor = 'pointer';
reminderBtn.style.fontSize = '12px';
reminderBtn.style.fontWeight = 'bold';
reminderBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
reminderBtn.style.transition = 'all 0.3s';
reminderBtn.onmouseover = () => {
reminderBtn.style.background = '#f57c00';
reminderBtn.style.transform = 'scale(1.05)';
};
reminderBtn.onmouseout = () => {
reminderBtn.style.background = '#ff9800';
reminderBtn.style.transform = 'scale(1)';
};
document.body.appendChild(reminderBtn);
}
processDetailPage() {
// 检查是否已经在右上角添加了收藏按钮
if (document.querySelector('.detail-favorite-btn')) return;
// 获取页面标题和URL
const title = document.title || '未知标题';
const url = window.location.href;
// 从URL中提取ID(XMXMKB详情页格式:forum.php?mod=viewthread&tid=数字)
const id = this.extractIdFromUrl(url);
if (!id) {
console.log('无法从URL中提取ID');
return;
}
// 增加浏览次数(详情页访问时)
this.manager.viewHistoryManager.incrementView(id);
const btn = document.createElement('button');
btn.className = 'favorite-btn detail-favorite-btn';
btn.setAttribute('data-id', id);
btn.textContent = this.manager.isFavorited(id) ? '已收藏' : '收藏';
if (this.manager.isFavorited(id)) {
btn.classList.add('favorited');
}
btn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
if (window.pageHandler) {
window.pageHandler.toggleFavorite(id, title, url, btn);
}
};
// 创建特别喜欢按钮
const superLikeBtn = document.createElement('button');
superLikeBtn.className = 'super-like-btn detail-super-like-btn';
superLikeBtn.setAttribute('data-id', id);
superLikeBtn.textContent = this.manager.isSuperLiked(id) ? '特别喜欢' : '特别喜欢';
if (this.manager.isSuperLiked(id)) {
superLikeBtn.classList.add('super-liked');
}
superLikeBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
if (window.pageHandler) {
window.pageHandler.toggleSuperLike(id, superLikeBtn);
}
};
// 创建浏览次数按钮
const viewCountBtn = document.createElement('button');
viewCountBtn.className = 'view-count detail-view-count';
viewCountBtn.setAttribute('data-id', id);
const viewCount = this.manager.viewHistoryManager.getViewCount(id);
viewCountBtn.innerHTML = `<span class="view-count-icon">👁️</span><span>${viewCount}</span>`;
viewCountBtn.title = `浏览次数: ${viewCount}`;
// 添加到右上角
btn.style.position = 'fixed';
btn.style.top = '20px';
btn.style.right = '340px'; // 在收藏夹面板左侧
btn.style.zIndex = '9998';
document.body.appendChild(btn);
superLikeBtn.style.position = 'fixed';
superLikeBtn.style.top = '20px';
superLikeBtn.style.right = '500px'; // 在收藏按钮左侧
superLikeBtn.style.zIndex = '9998';
document.body.appendChild(superLikeBtn);
viewCountBtn.style.position = 'fixed';
viewCountBtn.style.top = '20px';
viewCountBtn.style.right = '580px'; // 在特别喜欢按钮左侧
viewCountBtn.style.zIndex = '9998';
document.body.appendChild(viewCountBtn);
}
extractIdFromUrl(url) {
// 针对XMXMKB网站的URL格式提取ID
// 详情页格式:forum.php?mod=viewthread&tid=数字
const match = url.match(/[?&]tid=(\d+)/);
if (match) {
return match[1]; // 返回数字ID
}
return null;
}
toggleFavorite(id, title, url, btn) {
if (this.manager.isFavorited(id)) {
this.manager.removeFavorite(id);
btn.classList.remove('favorited');
btn.textContent = '收藏';
} else {
// 获取图片:优先从预加载内容获取,其次从详情页获取
let images = [];
// 首先尝试从预加载内容获取图片
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.preloadManager) {
const preloaded = window.pageHandler.ui.preloadManager.getPreloaded(url);
if (preloaded && preloaded.images && preloaded.images.length > 0) {
images = preloaded.images;
console.log('从预加载内容获取到图片:', images);
}
}
// 如果预加载没有图片,且当前在详情页,则从页面获取
if (images.length === 0 && this.isDetailPage(window.location.href)) {
images = this.manager.getImagesFromPage();
console.log('从详情页获取到图片:', images);
}
// 无论是否有图片,都允许收藏
const success = this.manager.addFavorite(id, title, url, images);
if (success) {
btn.classList.add('favorited');
btn.textContent = '已收藏';
// 如果没有图片,显示提示信息
if (images.length === 0) {
console.log('收藏成功,但未获取到图片');
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.showMessage('收藏成功,但未获取到图片', 'info');
}
}
}
}
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updateStorageSize();
}
// 更新所有相同ID的按钮状态,确保列表页和详情页同步
this.updateAllButtonsWithId(id);
// 更新导出提醒按钮
this.updateExportReminder();
}
updateAllButtonsWithId(id) {
// 更新所有具有相同ID的按钮状态
document.querySelectorAll(`.favorite-btn[data-id="${id}"]`).forEach(btn => {
if (this.manager.isFavorited(id)) {
btn.classList.add('favorited');
btn.textContent = '已收藏';
} else {
btn.classList.remove('favorited');
btn.textContent = '收藏';
}
});
// 更新所有具有相同ID的特别喜欢按钮状态
document.querySelectorAll(`.super-like-btn[data-id="${id}"]`).forEach(btn => {
if (this.manager.isSuperLiked(id)) {
btn.classList.add('super-liked');
btn.textContent = '特别喜欢';
} else {
btn.classList.remove('super-liked');
btn.textContent = '特别喜欢';
}
});
// 更新对应列表项的背景色
document.querySelectorAll('.pbw').forEach(item => {
if (item.id === id) {
this.updateListItemBackground(item, id);
}
});
}
updateViewCountButton(id) {
// 更新所有具有相同ID的浏览次数按钮
const viewCount = this.manager.viewHistoryManager.getViewCount(id);
document.querySelectorAll(`.view-count[data-id="${id}"]`).forEach(btn => {
btn.innerHTML = `<span class="view-count-icon">👁️</span><span>${viewCount}</span>`;
btn.title = `浏览次数: ${viewCount}`;
});
}
toggleSuperLike(id, btn) {
// 如果还没有收藏,先收藏
if (!this.manager.isFavorited(id)) {
const title = document.title || '未知标题';
const url = window.location.href;
let images = [];
// 获取图片:优先从预加载内容获取,其次从详情页获取
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.preloadManager) {
const preloaded = window.pageHandler.ui.preloadManager.getPreloaded(url);
if (preloaded && preloaded.images && preloaded.images.length > 0) {
images = preloaded.images;
console.log('从预加载内容获取到图片:', images);
}
}
// 如果预加载没有图片,且当前在详情页,则从页面获取
if (images.length === 0 && this.isDetailPage(window.location.href)) {
images = this.manager.getImagesFromPage();
console.log('从详情页获取到图片:', images);
}
// 无论是否有图片,都允许收藏
const success = this.manager.addFavorite(id, title, url, images);
if (success && images.length === 0) {
console.log('特别喜欢收藏成功,但未获取到图片');
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.showMessage('特别喜欢收藏成功,但未获取到图片', 'info');
}
}
}
// 切换特别喜欢状态
this.manager.toggleSuperLike(id);
// 更新按钮状态
if (this.manager.isSuperLiked(id)) {
btn.classList.add('super-liked');
btn.textContent = '特别喜欢';
} else {
btn.classList.remove('super-liked');
btn.textContent = '特别喜欢';
}
// 更新所有相同ID的按钮状态
this.updateAllSuperLikeButtonsWithId(id);
// 更新收藏夹面板(只更新标题,不重新渲染整个面板)
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updateSuperLikeBadge(id);
window.pageHandler.ui.updateStorageSize();
}
}
toggleSuperLikeInList(id, title, url, btn) {
// 如果还没有收藏,先收藏
if (!this.manager.isFavorited(id)) {
let images = [];
// 获取图片:优先从预加载内容获取
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.preloadManager) {
const preloaded = window.pageHandler.ui.preloadManager.getPreloaded(url);
if (preloaded && preloaded.images && preloaded.images.length > 0) {
images = preloaded.images;
console.log('从预加载内容获取到图片:', images);
}
}
// 无论是否有图片,都允许收藏
const success = this.manager.addFavorite(id, title, url, images);
if (success && images.length === 0) {
console.log('特别喜欢收藏成功,但未获取到图片');
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.showMessage('特别喜欢收藏成功,但未获取到图片', 'info');
}
}
}
// 切换特别喜欢状态
this.manager.toggleSuperLike(id);
// 更新按钮状态
if (this.manager.isSuperLiked(id)) {
btn.classList.add('super-liked');
btn.textContent = '特别喜欢';
} else {
btn.classList.remove('super-liked');
btn.textContent = '特别喜欢';
}
// 更新所有相同ID的按钮状态
this.updateAllSuperLikeButtonsWithId(id);
// 更新收藏夹面板(只更新标题,不重新渲染整个面板)
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updateSuperLikeBadge(id);
window.pageHandler.ui.updateStorageSize();
}
}
updateAllSuperLikeButtonsWithId(id) {
// 更新所有具有相同ID的特别喜欢按钮状态
document.querySelectorAll(`.super-like-btn[data-id="${id}"]`).forEach(btn => {
if (this.manager.isSuperLiked(id)) {
btn.classList.add('super-liked');
btn.textContent = '特别喜欢';
} else {
btn.classList.remove('super-liked');
btn.textContent = '特别喜欢';
}
});
}
// 更新所有列表项的背景色
updateAllListItemBackgrounds() {
document.querySelectorAll('.pbw').forEach(item => {
const id = item.id;
if (id) {
this.updateListItemBackground(item, id);
}
});
}
// 更新列表项黑名单状态
updateListItemBlacklistState(item, title) {
if (!item || !title) return;
const isBlacklisted = this.manager.blacklistManager.isBlacklisted(title);
if (isBlacklisted) {
item.classList.add('blacklisted');
// 获取匹配的黑名单关键字
const matchingKeywords = this.manager.blacklistManager.getMatchingBlacklistKeywords(title);
// 移除已存在的提示
const existingTooltip = item.querySelector('.blacklist-tooltip');
if (existingTooltip) {
existingTooltip.remove();
}
// 创建新的提示
if (matchingKeywords.length > 0) {
const tooltip = document.createElement('div');
tooltip.className = 'blacklist-tooltip';
tooltip.textContent = `被屏蔽: ${matchingKeywords.join(', ')}`;
tooltip.title = `被以下关键词屏蔽: ${matchingKeywords.join(', ')}`;
item.appendChild(tooltip);
}
} else {
item.classList.remove('blacklisted');
// 移除提示
const existingTooltip = item.querySelector('.blacklist-tooltip');
if (existingTooltip) {
existingTooltip.remove();
}
}
}
}
// 初始化
const manager = new FavoritesManager();
const ui = new FavoritesUI(manager);
const handler = new PageHandler(manager, ui);
// 将UI更新方法暴露到全局,供按钮更新使用
window.updateAllFavoriteButtons = () => {
ui.updateAllButtons();
};
// 将存储大小更新方法暴露到全局
window.updateStorageSize = () => {
if (window.pageHandler && window.pageHandler.ui) {
window.pageHandler.ui.updateStorageSize();
}
};
// 将重置变化标记方法暴露到全局
window.resetChangesFlag = () => {
if (window.pageHandler && window.pageHandler.manager) {
window.pageHandler.manager.resetChangesFlag();
console.log('手动重置数据变化标记');
}
};
// 将pageHandler暴露到全局,供收藏夹面板使用
window.pageHandler = handler;
// 将manager暴露到全局,供跨标签页同步使用
window.favoritesManager = manager;
// 添加测试图片查看器函数到全局
window.testImageViewer = (images) => {
if (window.pageHandler && window.pageHandler.ui && window.pageHandler.ui.imageViewer) {
console.log('手动测试图片查看器:', images);
window.pageHandler.ui.imageViewer.show(images, 0);
} else {
console.error('图片查看器未正确初始化');
}
};
// 添加测试无图片收藏函数到全局
window.testNoImagesFavorite = (id, title, url) => {
if (window.pageHandler && window.pageHandler.manager) {
console.log('手动测试无图片收藏:', id, title, url);
const success = window.pageHandler.manager.addFavorite(id, title, url, []);
if (success) {
console.log('无图片收藏测试成功');
if (window.pageHandler.ui) {
window.pageHandler.ui.updateToggleButton();
window.pageHandler.ui.updatePanel();
window.pageHandler.ui.updateStorageSize();
}
}
} else {
console.error('收藏管理器未正确初始化');
}
};
// 添加测试黑名单功能函数到全局
window.testBlacklist = (keyword) => {
if (window.pageHandler && window.pageHandler.manager && window.pageHandler.manager.blacklistManager) {
console.log('手动测试黑名单功能:', keyword);
const success = window.pageHandler.manager.blacklistManager.addBlacklistItem(keyword);
if (success) {
console.log('黑名单测试成功');
if (window.pageHandler.ui) {
window.pageHandler.ui.updateBlacklistToggleButton();
window.pageHandler.ui.updateBlacklistPanel();
window.pageHandler.updateAllBlacklistStates();
}
} else {
console.log('黑名单项已存在或为空');
}
} else {
console.error('黑名单管理器未正确初始化');
}
};
console.log('XMXMKB收藏功能脚本已加载 v2.13,使用localStorage存储,支持跨标签页同步,添加分页功能,使用外部懒加载工具,修复ignore_js_op标签选择器,优化iframe预加载避免重复请求,修复预加载缓存显示问题');
console.log('可以通过 window.testImageViewer(images) 手动测试图片查看器功能');
console.log('可以通过 window.testNoImagesFavorite(id, title, url) 手动测试无图片收藏功能');
console.log('可以通过 window.testBlacklist(keyword) 手动测试黑名单功能');
console.log('可以通过 window.updateStorageSize() 手动更新存储大小显示');
console.log('可以通过 window.resetChangesFlag() 手动重置数据变化标记');
console.log('浏览次数功能:详情页访问时自动增加次数,预加载成功后也会增加次数');
console.log('图片查看功能:点击列表页和收藏夹中的图片可以放大查看,支持左右切换和键盘操作');
console.log('无图片收藏功能:无法获取图片的收藏会标记为红色边框,但仍可成功收藏');
console.log('黑名单功能:输入关键词屏蔽匹配的列表项,被屏蔽的项显示为黑色且禁用所有功能,悬停显示被哪些关键词屏蔽');
console.log('黑名单按钮功能:在列表页每个词条旁添加黑名单按钮,点击可将词条标题添加到黑名单,已屏蔽的显示"已屏蔽"状态');
console.log('黑名单搜索功能:在黑名单管理面板中可以搜索关键词,实时过滤显示匹配的黑名单项');
console.log('黑名单导入优化:导入黑名单时采用追加方式,自动去重,不会覆盖现有黑名单项');
console.log('存储大小功能:实时显示收藏数据、浏览历史和预加载数据的总大小,超过10MB显示警告,超过50MB显示危险');
console.log('页面关闭提示功能:只有在收藏夹有数据变化时才会提示导出备份,导出或清空后自动重置变化标记');
console.log('清空未加载浏览记录功能:点击按钮清空当前页面所有未预加载列表项的浏览记录,已预加载的项会被跳过');
console.log('预加载功能:优先使用iframe方式绕过反爬策略,监听_dsign参数变化,失败时回退到fetch方式,支持重试机制');
console.log('iframe预加载功能:创建隐藏iframe窗口,在iframe中打开详情页,监听URL变化检测_dsign参数,直接从iframe DOM获取内容避免重复请求,支持请求队列避免并发冲突');
console.log('收藏夹图片优化:进入页面时不自动加载收藏夹内容,打开收藏夹面板时不加载图片,点击"显示图片"按钮后才开始加载');
console.log('图片加载优化:使用占位符显示,点击占位符可加载单个图片,点击"显示图片"按钮可加载当前可见区域的所有图片,使用外部懒加载工具');
console.log('适配XMXMKB网站:列表项类名为pbw,ID从元素id属性获取,详情页链接为forum.php?mod=viewthread&tid=数字,图片从<ignore_js_op>标签获取');
// 测试功能是否正常工作
setTimeout(() => {
console.log('测试图片查看器功能:', {
imageViewer: window.pageHandler?.ui?.imageViewer,
viewer: window.pageHandler?.ui?.imageViewer?.viewer,
viewerDisplay: window.pageHandler?.ui?.imageViewer?.viewer?.style?.display
});
// 测试图片查看器的显示状态
if (window.pageHandler?.ui?.imageViewer?.viewer) {
const viewer = window.pageHandler.ui.imageViewer.viewer;
console.log('图片查看器状态:', {
display: viewer.style.display,
visibility: viewer.style.visibility,
opacity: viewer.style.opacity,
zIndex: viewer.style.zIndex
});
}
console.log('预加载管理器状态:', {
preloadManager: window.pageHandler?.ui?.preloadManager,
preloadedCount: window.pageHandler?.ui?.preloadManager?.preloadedUrls?.size || 0
});
}, 2000);
})();
Wrap
Beautify