Greasy Fork is available in English.
在豆瓣电影标题下添加多个站外搜索按钮(含源隐藏/恢复、手动排序、yellowrabbit可读页、KDocs自动搜索)
// ==UserScript==
// @name 豆瓣站外搜索下载
// @namespace http://tampermonkey.net/
// @version 0.74
// @description 在豆瓣电影标题下添加多个站外搜索按钮(含源隐藏/恢复、手动排序、yellowrabbit可读页、KDocs自动搜索)
// @author JIEMO
// @match *://movie.douban.com/subject/*
// @match *://www.kdocs.cn/*
// @match *://appdocs.wpscdn.cn/*
// @match *://lemonun.top/*
// @match *://www.6v520.tv/*
// @icon https://www.google.com/s2/favicons?domain=douban.com
// @grant none
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// ==/UserScript==
(function () {
'use strict';
const CONTAINER_CLASS = 'douban-jump-btn-container';
const BTN_CLASS = 'douban-jump-btn';
const STYLE_CLASS = 'douban-btn-style';
const HIDDEN_SOURCES_KEY = 'douban_hidden_sources_v1';
const SOURCE_ORDER_KEY = 'douban_source_order_v1';
const SOURCE_LABEL_ALIASES_KEY = 'douban_source_label_aliases_v1';
const SOURCE_MANAGER_MODAL_ID = 'douban-source-manager-modal';
const KDOCS_OVERLAY_ID = 'douban-kdocs-overlay';
const KDOCS_RUNNING_FLAG = '__douban_kdocs_search_running__';
const KDOCS_MATCH_ATTR = 'data-douban-kdocs-match-id';
const KDOCS_MATCH_HIGHLIGHT_CLASS = 'douban-kdocs-match-highlight';
const KDOCS_SOURCES = [
{
id: 'orange-theater',
label: '橙子剧场',
url: 'https://www.kdocs.cn/l/cpCsvQoAunbY?R=L1MvMzU2',
type: 'sheet',
},
];
const SOURCES = [
{
label: 'gying',
buildUrl: (title) => `https://www.教父.com/s/1---1/${encodeURIComponent(title)}`,
},
{
label: '不太灵',
buildUrl: (title) => `https://web5.mukaku.com/search?sb=${encodeURIComponent(title)}`,
},
{
label: '看片咖',
buildUrl: (title) => `https://tv.kanpian.club/s/${encodeURIComponent(title)}.html`,
},
{
label: 'yppan',
buildUrl: (title) => `https://www.yppan.com/?s=${encodeURIComponent(title)}&cat=5`,
},
{
label: '豆荚盘',
buildUrl: (title) => `https://www.jpmom.com/?s=${encodeURIComponent(title)}`,
},
{
label: '秒搜',
buildUrl: (title) => `https://miaosou.fun/info?searchKey=${encodeURIComponent(title)}`,
},
{
label: '1LOU',
buildUrl: (title) => `https://www.1lou.me/search-${encodeAsUnderscoreUtf8Hex(title)}-1.htm`,
},
{
label: 'btbtla',
buildUrl: (title) => `https://www.btbtla.com/search/${title}`,
},
{
label: '夸克猫',
buildUrl: (title) => `https://www.kuakemao.com/?s=${encodeURIComponent(title)}`,
},
{
label: '磁力柠檬',
buildUrl: (title) => buildExternalAutoSearchUrl('https://lemonun.top/', 'lemonSearch', title),
onClick: ({ title }) => openLemonSearch(title),
},
{
label: 'BD影视',
buildUrl: (title) => `https://www.bdjuhe.com/q/index----?k=${encodeURIComponent(title)}`,
},
{
label: '爱恋动漫',
buildUrl: (title) => `https://www.kisssub.org/search.php?keyword=${encodeURIComponent(title)}`,
},
{
label: '七味',
buildUrl: (title) => `https://www.qmp4.com/vs/-------------.html?wd=${encodeURIComponent(title)}`,
},
{
label: 'SeedHub',
buildUrl: (title) => `https://www.seedhub.cc/s/${encodeURIComponent(title)}`,
},
{
label: '影巢',
buildUrl: (title) => `https://hdhive.com/search?query=${encodeURIComponent(title)}&type=multi&page=1`,
},
{
label: '盘尊社区',
buildUrl: (title) => `https://www.panzun.cc/?q=${encodeURIComponent(title)}`,
},
{
label: '海绵小站',
buildUrl: (title) => `https://www.hmxz.org/search.htm?keyword=${encodeURIComponent(title)}`,
},
{
label: '6v电影',
buildUrl: (title) => buildExternalAutoSearchUrl('https://www.6v520.tv/sousuo.html', 'sixvSearch', title),
onClick: ({ title }) => open6vMovieSearch(title),
},
...KDOCS_SOURCES.map(function (source) {
return {
label: source.label,
buildUrl: (title) => buildKdocsSearchUrl(source, title),
onClick: ({ title }) => openKdocsViewer(source, title),
};
}),
{
label: 'yellowrabbit',
buildUrl: (title) => `https://api.yellowrabbit.online/api/movie/search?key=${encodeURIComponent(title)}`,
onClick: ({ title, url }) => openYellowrabbitViewer(title, url),
},
];
let renderTimer = 0;
let sourceManagerLastFocused = null;
function normalizeTitle(raw) {
return String(raw || '')
.replace(/\s+\(\d{4}\)\s*$/, '')
.replace(/\s+/g, ' ')
.trim();
}
function getMovieTitle() {
const h1Element = document.querySelector('h1');
if (!h1Element) return '';
const itemReviewed = h1Element.querySelector('span[property="v:itemreviewed"]');
const yearNode = h1Element.querySelector('.year');
const itemReviewedText = normalizeTitle(itemReviewed ? itemReviewed.textContent : '');
if (itemReviewedText) return itemReviewedText;
const fullTitle = normalizeTitle(document.title.replace('(豆瓣)', ''));
const yearText = normalizeTitle(yearNode ? yearNode.textContent : '').replace(/[()]/g, '');
if (yearText) {
return normalizeTitle(fullTitle.replace(new RegExp(`\\b${yearText}\\b`), ''));
}
return fullTitle;
}
function getSearchKeyword(title) {
const normalized = normalizeTitle(title);
if (!normalized) return '';
return normalized.split(' ')[0].trim();
}
function getDefaultSourceLabels() {
return SOURCES.map(function (source) {
return source.label;
});
}
function loadSourceOrder() {
const defaultLabels = getDefaultSourceLabels();
const allLabels = new Set(defaultLabels);
try {
const raw = localStorage.getItem(SOURCE_ORDER_KEY);
if (!raw) return defaultLabels.slice();
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return defaultLabels.slice();
const seen = new Set();
const ordered = parsed.filter(function (label) {
if (typeof label !== 'string' || !allLabels.has(label) || seen.has(label)) {
return false;
}
seen.add(label);
return true;
});
const orderedSet = new Set(ordered);
for (const label of defaultLabels) {
if (!orderedSet.has(label)) {
ordered.push(label);
}
}
return ordered;
} catch (_error) {
return defaultLabels.slice();
}
}
function saveSourceOrder(labels) {
localStorage.setItem(SOURCE_ORDER_KEY, JSON.stringify(labels));
}
function clearSourceOrder() {
localStorage.removeItem(SOURCE_ORDER_KEY);
}
function normalizeSourceDisplayLabel(value, fallbackLabel) {
const normalized = String(value == null ? '' : value).trim();
return normalized || fallbackLabel;
}
function loadSourceLabelAliases() {
const allLabels = new Set(getDefaultSourceLabels());
try {
const raw = localStorage.getItem(SOURCE_LABEL_ALIASES_KEY);
if (!raw) return {};
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
return Object.keys(parsed).reduce(function (aliases, label) {
const value = parsed[label];
if (!allLabels.has(label) || typeof value !== 'string') {
return aliases;
}
const displayLabel = normalizeSourceDisplayLabel(value, label);
if (displayLabel !== label) {
aliases[label] = displayLabel;
}
return aliases;
}, {});
} catch (_error) {
return {};
}
}
function saveSourceLabelAliases(labelAliases) {
localStorage.setItem(SOURCE_LABEL_ALIASES_KEY, JSON.stringify(labelAliases));
}
function clearSourceLabelAliases() {
localStorage.removeItem(SOURCE_LABEL_ALIASES_KEY);
}
function getSourceDisplayLabel(label, labelAliases) {
if (!labelAliases || typeof labelAliases[label] !== 'string') {
return label;
}
return normalizeSourceDisplayLabel(labelAliases[label], label);
}
function getOrderedSources() {
const sourceMap = new Map(SOURCES.map(function (source) {
return [source.label, source];
}));
return loadSourceOrder().map(function (label) {
return sourceMap.get(label);
}).filter(Boolean);
}
function getAllSourceLabels() {
return getOrderedSources().map(function (source) {
return source.label;
});
}
function loadHiddenSources() {
const allLabels = new Set(getDefaultSourceLabels());
try {
const raw = localStorage.getItem(HIDDEN_SOURCES_KEY);
if (!raw) return new Set();
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return new Set();
return new Set(parsed.filter(function (label) {
return typeof label === 'string' && allLabels.has(label);
}));
} catch (_error) {
return new Set();
}
}
function saveHiddenSources(hiddenSet) {
localStorage.setItem(HIDDEN_SOURCES_KEY, JSON.stringify(Array.from(hiddenSet)));
}
function hideSource(label) {
const hidden = loadHiddenSources();
hidden.add(label);
saveHiddenSources(hidden);
}
function toggleSourceHidden(label) {
const hidden = loadHiddenSources();
if (hidden.has(label)) {
hidden.delete(label);
saveHiddenSources(hidden);
return false;
}
hidden.add(label);
saveHiddenSources(hidden);
return true;
}
function clearHiddenSources() {
localStorage.removeItem(HIDDEN_SOURCES_KEY);
}
function openSourceOrderManager() {
openSourceManager();
}
function openSourceManager() {
if (!document.body) return;
sourceManagerLastFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
closeSourceManager();
const defaultLabels = getDefaultSourceLabels();
const state = {
labels: getAllSourceLabels(),
hidden: loadHiddenSources(),
labelAliases: loadSourceLabelAliases(),
draggedLabel: '',
};
const mask = document.createElement('div');
mask.id = SOURCE_MANAGER_MODAL_ID;
mask.className = 'douban-source-manager-mask';
const panel = document.createElement('section');
panel.className = 'douban-source-manager-panel';
panel.tabIndex = -1;
panel.setAttribute('role', 'dialog');
panel.setAttribute('aria-modal', 'true');
panel.setAttribute('aria-label', '源设置');
const header = document.createElement('div');
header.className = 'douban-source-manager-header';
const titleWrap = document.createElement('div');
titleWrap.className = 'douban-source-manager-title-wrap';
const title = document.createElement('h2');
title.className = 'douban-source-manager-title';
title.textContent = '源设置';
const subtitle = document.createElement('div');
subtitle.className = 'douban-source-manager-subtitle';
subtitle.textContent = '点击卡片按钮切换显示,拖动卡片调整顺序,也可直接修改显示名称。';
titleWrap.appendChild(title);
titleWrap.appendChild(subtitle);
const closeBtn = document.createElement('button');
closeBtn.type = 'button';
closeBtn.className = 'douban-source-manager-close';
closeBtn.setAttribute('aria-label', '关闭源设置');
closeBtn.textContent = '×';
header.appendChild(titleWrap);
header.appendChild(closeBtn);
const toolbar = document.createElement('div');
toolbar.className = 'douban-source-manager-toolbar';
const resetOrderBtn = document.createElement('button');
resetOrderBtn.type = 'button';
resetOrderBtn.className = 'douban-source-manager-action';
resetOrderBtn.textContent = '恢复默认排序';
const resetVisibleBtn = document.createElement('button');
resetVisibleBtn.type = 'button';
resetVisibleBtn.className = 'douban-source-manager-action';
resetVisibleBtn.textContent = '恢复全部显示';
toolbar.appendChild(resetOrderBtn);
toolbar.appendChild(resetVisibleBtn);
const summary = document.createElement('div');
summary.className = 'douban-source-manager-summary';
const list = document.createElement('ul');
list.className = 'douban-source-manager-list';
const footer = document.createElement('div');
footer.className = 'douban-source-manager-footer';
const cancelBtn = document.createElement('button');
cancelBtn.type = 'button';
cancelBtn.className = 'douban-source-manager-btn-secondary';
cancelBtn.textContent = '取消';
const saveBtn = document.createElement('button');
saveBtn.type = 'button';
saveBtn.className = 'douban-source-manager-btn-primary';
saveBtn.textContent = '保存';
footer.appendChild(cancelBtn);
footer.appendChild(saveBtn);
panel.appendChild(header);
panel.appendChild(toolbar);
panel.appendChild(summary);
panel.appendChild(list);
panel.appendChild(footer);
mask.appendChild(panel);
function renderSummary() {
const hiddenCount = state.hidden.size;
const visibleCount = state.labels.length - hiddenCount;
summary.textContent = '共 ' + state.labels.length + ' 个源,当前显示 ' + visibleCount + ' 个,隐藏 ' + hiddenCount + ' 个。';
}
function moveLabelToIndex(draggedLabel, targetIndex) {
if (!draggedLabel) return;
const nextLabels = state.labels.slice();
const sourceIndex = nextLabels.indexOf(draggedLabel);
if (sourceIndex === -1) return;
nextLabels.splice(sourceIndex, 1);
const safeIndex = Math.max(0, Math.min(targetIndex, nextLabels.length));
nextLabels.splice(safeIndex, 0, draggedLabel);
state.labels = nextLabels;
}
function getReorderEntries() {
return Array.from(list.querySelectorAll('.douban-source-manager-item')).filter(function (item) {
return item.getAttribute('data-label') !== state.draggedLabel;
}).map(function (item, index) {
return {
item: item,
index: index,
rect: item.getBoundingClientRect(),
};
});
}
function clearDropIndicators() {
Array.from(list.querySelectorAll('.is-drop-target, .is-drop-after')).forEach(function (node) {
node.classList.remove('is-drop-target', 'is-drop-after');
});
}
function updateDropIndicators(insertIndex) {
clearDropIndicators();
const entries = getReorderEntries();
if (entries.length === 0) return;
if (insertIndex >= entries.length) {
entries[entries.length - 1].item.classList.add('is-drop-after');
return;
}
entries[insertIndex].item.classList.add('is-drop-target');
}
function getInsertIndexFromPointer(clientX, clientY) {
const entries = getReorderEntries();
if (entries.length === 0) return 0;
const rows = [];
entries.forEach(function (entry) {
const lastRow = rows[rows.length - 1];
const sameRow = lastRow && Math.abs(lastRow.top - entry.rect.top) < Math.max(12, entry.rect.height / 2);
if (sameRow) {
lastRow.entries.push(entry);
lastRow.bottom = Math.max(lastRow.bottom, entry.rect.bottom);
return;
}
rows.push({
top: entry.rect.top,
bottom: entry.rect.bottom,
entries: [entry],
});
});
for (let rowIndex = 0; rowIndex < rows.length; rowIndex += 1) {
const row = rows[rowIndex];
const rowMiddle = row.top + (row.bottom - row.top) / 2;
if (clientY <= rowMiddle) {
for (let entryIndex = 0; entryIndex < row.entries.length; entryIndex += 1) {
const entry = row.entries[entryIndex];
const entryMiddle = entry.rect.left + entry.rect.width / 2;
if (clientX <= entryMiddle) {
return entry.index;
}
}
return row.entries[row.entries.length - 1].index + 1;
}
}
return entries.length;
}
function handleListDragOver(event) {
event.preventDefault();
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move';
}
if (!state.draggedLabel) return;
updateDropIndicators(getInsertIndexFromPointer(event.clientX, event.clientY));
}
function handleListDrop(event) {
event.preventDefault();
const draggedLabel = state.draggedLabel || (event.dataTransfer ? event.dataTransfer.getData('text/plain') : '');
if (!draggedLabel) return;
moveLabelToIndex(draggedLabel, getInsertIndexFromPointer(event.clientX, event.clientY));
clearDropIndicators();
renderList();
}
function renderList() {
list.innerHTML = '';
renderSummary();
state.labels.forEach(function (label, index) {
const item = document.createElement('li');
const isHidden = state.hidden.has(label);
const displayLabel = getSourceDisplayLabel(label, state.labelAliases);
item.className = 'douban-source-manager-item' + (isHidden ? ' is-hidden' : '');
item.draggable = true;
item.setAttribute('data-label', label);
const indexNode = document.createElement('span');
indexNode.className = 'douban-source-manager-index';
indexNode.textContent = String(index + 1).padStart(2, '0');
const main = document.createElement('div');
main.className = 'douban-source-manager-main';
const nameGroup = document.createElement('div');
nameGroup.className = 'douban-source-manager-name-group';
const itemTop = document.createElement('div');
itemTop.className = 'douban-source-manager-item-top';
const labelNode = document.createElement('div');
labelNode.className = 'douban-source-manager-label';
labelNode.textContent = displayLabel;
const metaNode = document.createElement('div');
metaNode.className = 'douban-source-manager-meta';
metaNode.textContent = '原始名称:' + label;
const renameInput = document.createElement('input');
renameInput.type = 'text';
renameInput.className = 'douban-source-manager-rename';
renameInput.value = displayLabel;
renameInput.placeholder = label;
renameInput.draggable = false;
renameInput.setAttribute('aria-label', '修改源“' + label + '”的显示名称');
renameInput.addEventListener('mousedown', function (event) {
event.stopPropagation();
});
renameInput.addEventListener('dragstart', function (event) {
event.preventDefault();
event.stopPropagation();
});
renameInput.addEventListener('input', function () {
const nextDisplayLabel = normalizeSourceDisplayLabel(renameInput.value, label);
labelNode.textContent = nextDisplayLabel;
if (nextDisplayLabel === label) {
delete state.labelAliases[label];
} else {
state.labelAliases[label] = nextDisplayLabel;
}
});
renameInput.addEventListener('blur', function () {
renameInput.value = getSourceDisplayLabel(label, state.labelAliases);
});
const toggleBtn = document.createElement('button');
toggleBtn.type = 'button';
toggleBtn.className = 'douban-source-manager-toggle' + (isHidden ? ' is-hidden' : '');
toggleBtn.textContent = isHidden ? '显示' : '隐藏';
toggleBtn.addEventListener('click', function () {
if (state.hidden.has(label)) {
state.hidden.delete(label);
} else {
state.hidden.add(label);
}
renderList();
});
item.addEventListener('dragstart', function (event) {
state.draggedLabel = label;
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', label);
}
item.classList.add('is-dragging');
});
item.addEventListener('dragover', function (event) {
handleListDragOver(event);
});
item.addEventListener('dragleave', function () {
item.classList.remove('is-drop-target', 'is-drop-after');
});
item.addEventListener('drop', function (event) {
event.stopPropagation();
handleListDrop(event);
});
item.addEventListener('dragend', function () {
state.draggedLabel = '';
Array.from(list.querySelectorAll('.is-drop-target, .is-drop-after, .is-dragging')).forEach(function (node) {
node.classList.remove('is-drop-target', 'is-drop-after', 'is-dragging');
});
});
itemTop.appendChild(indexNode);
itemTop.appendChild(toggleBtn);
main.appendChild(itemTop);
nameGroup.appendChild(labelNode);
nameGroup.appendChild(metaNode);
nameGroup.appendChild(renameInput);
main.appendChild(nameGroup);
item.appendChild(main);
list.appendChild(item);
});
}
resetOrderBtn.addEventListener('click', function () {
state.labels = defaultLabels.slice();
renderList();
});
resetVisibleBtn.addEventListener('click', function () {
state.hidden.clear();
renderList();
});
list.addEventListener('dragover', function (event) {
handleListDragOver(event);
});
list.addEventListener('drop', function (event) {
handleListDrop(event);
});
closeBtn.addEventListener('click', closeSourceManager);
cancelBtn.addEventListener('click', closeSourceManager);
saveBtn.addEventListener('click', function () {
if (state.hidden.size > 0) {
saveHiddenSources(state.hidden);
} else {
clearHiddenSources();
}
if (JSON.stringify(state.labels) === JSON.stringify(defaultLabels)) {
clearSourceOrder();
} else {
saveSourceOrder(state.labels.slice());
}
if (Object.keys(state.labelAliases).length > 0) {
saveSourceLabelAliases(state.labelAliases);
} else {
clearSourceLabelAliases();
}
closeSourceManager();
renderButtons(true);
});
mask.addEventListener('click', function (event) {
if (event.target === mask) {
closeSourceManager();
}
});
document.body.appendChild(mask);
document.addEventListener('keydown', handleSourceManagerKeydown, true);
renderList();
closeBtn.focus();
}
function closeSourceManager() {
const existing = document.getElementById(SOURCE_MANAGER_MODAL_ID);
if (existing) {
existing.remove();
}
document.removeEventListener('keydown', handleSourceManagerKeydown, true);
if (sourceManagerLastFocused && document.contains(sourceManagerLastFocused)) {
sourceManagerLastFocused.focus();
}
sourceManagerLastFocused = null;
}
function handleSourceManagerKeydown(event) {
const modal = document.getElementById(SOURCE_MANAGER_MODAL_ID);
if (!modal) return;
if (event.key === 'Escape') {
event.preventDefault();
closeSourceManager();
return;
}
if (event.key !== 'Tab') return;
const focusables = Array.from(modal.querySelectorAll('button, input, [href], [tabindex]:not([tabindex="-1"])')).filter(function (node) {
return node instanceof HTMLElement && !node.hasAttribute('disabled') && node.tabIndex !== -1;
});
if (focusables.length === 0) return;
const first = focusables[0];
const last = focusables[focusables.length - 1];
const active = document.activeElement;
if (event.shiftKey && active === first) {
event.preventDefault();
last.focus();
return;
}
if (!event.shiftKey && active === last) {
event.preventDefault();
first.focus();
}
}
function encodeAsUnderscoreUtf8Hex(text) {
const bytes = new TextEncoder().encode(String(text || ''));
let output = '';
for (const value of bytes) {
output += `_${value.toString(16).toUpperCase().padStart(2, '0')}`;
}
return output;
}
function getKdocsSourceById(sourceId) {
return KDOCS_SOURCES.find(function (source) {
return source.id === sourceId;
}) || null;
}
function buildKdocsSearchUrl(source, keyword) {
const config = {
doubanMode: 'kdocsSearch',
sourceId: source.id,
sourceType: source.type,
keyword: keyword,
sheetLabel: source.label,
};
const url = new URL(source.url);
url.searchParams.set('doubanMode', config.doubanMode);
url.searchParams.set('doubanSourceId', config.sourceId);
url.searchParams.set('doubanSourceType', config.sourceType);
url.searchParams.set('doubanKeyword', config.keyword);
url.searchParams.set('doubanSheetLabel', config.sheetLabel);
url.hash = new URLSearchParams(config).toString();
return url.toString();
}
function openKdocsViewer(source, keyword) {
const url = buildKdocsSearchUrl(source, keyword);
window.open(url, '_blank');
}
function parseKdocsSearchConfig() {
const hash = String(window.location.hash || '');
const hashParams = hash.startsWith('#') ? new URLSearchParams(hash.slice(1)) : null;
const searchParams = new URLSearchParams(window.location.search || '');
const mode = (hashParams && hashParams.get('doubanMode')) || searchParams.get('doubanMode') || '';
if (mode !== 'kdocsSearch') return null;
const sourceId = (hashParams && hashParams.get('sourceId')) || searchParams.get('doubanSourceId') || '';
const sourceTypeFromQuery = (hashParams && hashParams.get('sourceType')) || searchParams.get('doubanSourceType') || '';
const matchedSource = getKdocsSourceById(sourceId);
const keyword = (hashParams && hashParams.get('keyword')) || searchParams.get('doubanKeyword') || '';
const sheetLabel = (hashParams && hashParams.get('sheetLabel'))
|| searchParams.get('doubanSheetLabel')
|| (matchedSource && matchedSource.label)
|| document.title
|| 'KDocs';
const sourceType = sourceTypeFromQuery || (matchedSource && matchedSource.type) || 'sheet';
if (!keyword) return null;
return { keyword, sheetLabel, sourceId, sourceType };
}
function ensureKdocsOverlayStyle() {
if (document.getElementById(KDOCS_OVERLAY_ID + '-style')) return;
const style = document.createElement('style');
style.id = KDOCS_OVERLAY_ID + '-style';
style.textContent = `
#${KDOCS_OVERLAY_ID} {
position: fixed;
top: 16px;
right: 16px;
width: min(440px, calc(100vw - 24px));
max-height: calc(100vh - 32px);
overflow: auto;
z-index: 2147483647;
background: rgba(255, 255, 255, 0.98);
border: 1px solid #cfe0f6;
border-radius: 14px;
box-shadow: 0 14px 38px rgba(15, 23, 42, 0.18);
color: #0f172a;
font: 14px/1.55 -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;
}
#${KDOCS_OVERLAY_ID} * {
box-sizing: border-box;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-head {
padding: 12px 14px 8px;
border-bottom: 1px solid #e3ecf8;
background: linear-gradient(180deg, #f7fbff 0%, #ffffff 100%);
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-title {
margin: 0;
font-size: 17px;
font-weight: 700;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-sub,
#${KDOCS_OVERLAY_ID} .douban-kdocs-count,
#${KDOCS_OVERLAY_ID} .douban-kdocs-empty {
margin-top: 4px;
color: #4b5563;
word-break: break-word;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-status {
margin-top: 6px;
font-weight: 600;
color: #1565c0;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-status.is-error {
color: #b91c1c;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-btn {
appearance: none;
border: 1px solid #c8d7ef;
background: #eef5ff;
color: #1565c0;
border-radius: 999px;
padding: 6px 12px;
font: inherit;
font-weight: 600;
cursor: pointer;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-body {
padding: 10px 14px 14px;
display: grid;
gap: 8px;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-row {
border: 1px solid #dbe6f4;
background: #f8fbff;
border-radius: 10px;
padding: 8px 10px;
word-break: break-word;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-row[data-anchor-id] {
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease;
}
#${KDOCS_OVERLAY_ID} .douban-kdocs-row[data-anchor-id]:hover {
background: #eef5ff;
border-color: #b7ccef;
}
[${KDOCS_MATCH_ATTR}].${KDOCS_MATCH_HIGHLIGHT_CLASS} {
outline: 3px solid rgba(21, 101, 192, 0.35);
background: rgba(255, 243, 205, 0.75) !important;
border-radius: 8px;
transition: outline 0.2s ease, background 0.2s ease;
}
@media (max-width: 720px) {
#${KDOCS_OVERLAY_ID} {
top: auto;
right: 8px;
bottom: 8px;
left: 8px;
width: auto;
max-height: 55vh;
}
}
`;
document.head.appendChild(style);
}
function escapeHtml(input) {
return String(input || '')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function kdocsNormalizeOverlayItems(result) {
if (Array.isArray(result.items) && result.items.length > 0) {
return result.items.map(function (item) {
if (item && typeof item === 'object') {
return {
text: String(item.text || ''),
anchorId: item.anchorId ? String(item.anchorId) : '',
};
}
return { text: String(item || ''), anchorId: '' };
}).filter(function (item) {
return Boolean(item.text);
});
}
return (Array.isArray(result.rows) ? result.rows : []).map(function (row) {
return { text: String(row || ''), anchorId: '' };
}).filter(function (item) {
return Boolean(item.text);
});
}
function kdocsFindMatchNode(anchorId) {
if (!anchorId) return null;
return Array.from(document.querySelectorAll('[' + KDOCS_MATCH_ATTR + ']')).find(function (node) {
return node.getAttribute(KDOCS_MATCH_ATTR) === anchorId;
}) || null;
}
function kdocsScrollToMatch(anchorId) {
const node = kdocsFindMatchNode(anchorId);
if (!node) return false;
node.scrollIntoView({ behavior: 'smooth', block: 'center' });
node.classList.add(KDOCS_MATCH_HIGHLIGHT_CLASS);
window.setTimeout(function () {
node.classList.remove(KDOCS_MATCH_HIGHLIGHT_CLASS);
}, 2200);
return true;
}
function renderKdocsSearchOverlay(config, result) {
ensureKdocsOverlayStyle();
let root = document.getElementById(KDOCS_OVERLAY_ID);
if (!root) {
root = document.createElement('section');
root.id = KDOCS_OVERLAY_ID;
document.body.appendChild(root);
}
const items = kdocsNormalizeOverlayItems(result);
const countText = result.countText || (items.length > 0 ? ('结果数: ' + items.length) : '');
const statusClass = result.errorText ? 'douban-kdocs-status is-error' : 'douban-kdocs-status';
const statusText = result.statusText || (result.errorText ? ('搜索失败: ' + result.errorText) : '搜索完成');
const rowsHtml = items.length > 0
? items.map(function (item, index) {
const anchorAttr = item.anchorId ? ' data-anchor-id="' + escapeHtml(item.anchorId) + '"' : '';
return '<div class="douban-kdocs-row" data-item-index="' + String(index) + '"' + anchorAttr + '>' + escapeHtml(item.text) + '</div>';
}).join('')
: '<div class="douban-kdocs-empty">未找到匹配行</div>';
root.innerHTML = ''
+ '<div class="douban-kdocs-head">'
+ '<h1 class="douban-kdocs-title">' + escapeHtml(config.sheetLabel || 'KDocs') + ' 自动搜索</h1>'
+ '<div class="douban-kdocs-sub">关键词: ' + escapeHtml(config.keyword) + '</div>'
+ '<div class="' + statusClass + '">' + escapeHtml(statusText) + '</div>'
+ (countText ? '<div class="douban-kdocs-count">' + escapeHtml(countText) + '</div>' : '')
+ '<div class="douban-kdocs-actions">'
+ '<button type="button" class="douban-kdocs-btn" data-action="retry">重新搜索</button>'
+ '<button type="button" class="douban-kdocs-btn" data-action="close">关闭结果</button>'
+ '</div>'
+ '</div>'
+ '<div class="douban-kdocs-body">' + rowsHtml + '</div>';
const retryBtn = root.querySelector('[data-action="retry"]');
const closeBtn = root.querySelector('[data-action="close"]');
if (retryBtn) {
retryBtn.onclick = function () {
runKdocsAutoSearch(config);
};
}
if (closeBtn) {
closeBtn.onclick = function () {
root.remove();
};
}
Array.from(root.querySelectorAll('.douban-kdocs-row[data-anchor-id]')).forEach(function (rowNode) {
rowNode.onclick = function () {
const anchorId = rowNode.getAttribute('data-anchor-id') || '';
kdocsScrollToMatch(anchorId);
};
});
const firstAnchorItem = items.find(function (item) {
return Boolean(item.anchorId);
});
if (firstAnchorItem && !result.errorText) {
window.setTimeout(function () {
kdocsScrollToMatch(firstAnchorItem.anchorId);
}, 120);
}
}
function kdocsSleep(ms) {
return new Promise(function (resolve) {
window.setTimeout(resolve, ms);
});
}
function kdocsTextOf(node) {
return String((node && (node.innerText || node.textContent)) || '').trim();
}
function kdocsFindByText(selector, text, exact) {
return Array.from(document.querySelectorAll(selector)).find(function (node) {
const value = kdocsTextOf(node);
return exact ? value === text : value.indexOf(text) !== -1;
}) || null;
}
function kdocsClick(node) {
if (!node) return false;
node.click();
return true;
}
function kdocsSetInputValue(input, value) {
if (!input) return false;
const prototype = input.tagName === 'TEXTAREA'
? window.HTMLTextAreaElement.prototype
: window.HTMLInputElement.prototype;
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'value');
if (descriptor && typeof descriptor.set === 'function') {
descriptor.set.call(input, value);
} else {
input.value = value;
}
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
return true;
}
function kdocsCloseLoginModal() {
const closeBtn = document.querySelector('.component-login-modal-pop .kdv-button');
if (closeBtn) {
closeBtn.click();
}
}
function kdocsNormalizeResultText(text) {
return String(text || '').replace(/\s+/g, ' ').trim();
}
function kdocsCollectTexts(selectors) {
const values = [];
for (const selector of selectors) {
const nodes = document.querySelectorAll(selector);
for (const node of nodes) {
const text = kdocsNormalizeResultText(kdocsTextOf(node));
if (!text) continue;
values.push(text);
}
if (values.length > 0) {
break;
}
}
return values;
}
function kdocsSplitTextLines(text) {
return String(text || '')
.split(/\r?\n+/)
.map(function (line) {
return kdocsNormalizeResultText(line);
})
.filter(Boolean);
}
function kdocsIncludesKeyword(text, keyword) {
return String(text || '').toLowerCase().indexOf(String(keyword || '').toLowerCase()) !== -1;
}
async function kdocsResolveValue(value) {
if (value && typeof value.then === 'function') {
return await value;
}
return value;
}
function kdocsIsNoiseLine(text, keyword) {
if (!text) return true;
if (text === keyword) return true;
if (text.indexOf('重新搜索') !== -1) return true;
if (text.indexOf('关闭结果') !== -1) return true;
if (text.indexOf('正在自动搜索') !== -1) return true;
if (text.indexOf('自动搜索超时') !== -1) return true;
if (text.indexOf('查找全部') !== -1) return true;
return false;
}
function kdocsFilterKeywordLines(lines, keyword) {
return lines.filter(function (line) {
if (!kdocsIncludesKeyword(line, keyword)) return false;
if (line.length > 500) return false;
return !kdocsIsNoiseLine(line, keyword);
});
}
function kdocsFilterKeywordItems(items, keyword) {
return items.filter(function (item) {
const text = item && typeof item === 'object' ? String(item.text || '') : String(item || '');
if (!kdocsIncludesKeyword(text, keyword)) return false;
if (text.length > 500) return false;
return !kdocsIsNoiseLine(text, keyword);
}).map(function (item) {
if (item && typeof item === 'object') return item;
return { text: String(item || ''), anchorId: '' };
});
}
function kdocsCreateAnchorId() {
return 'kdocs-match-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);
}
function kdocsEnsureMatchAnchor(node) {
if (!node || !node.setAttribute) return '';
let anchorId = node.getAttribute(KDOCS_MATCH_ATTR) || '';
if (!anchorId) {
anchorId = kdocsCreateAnchorId();
node.setAttribute(KDOCS_MATCH_ATTR, anchorId);
}
return anchorId;
}
function kdocsUniqueItems(items) {
const seen = new Set();
return items.filter(function (item) {
const key = (item.anchorId || '') + '::' + item.text;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
function kdocsIsVisibleElement(node) {
if (!node || node.nodeType !== 1) return false;
const style = window.getComputedStyle(node);
if (!style) return true;
if (style.display === 'none' || style.visibility === 'hidden') return false;
return true;
}
function kdocsIsClickableElement(node) {
if (!node || node.nodeType !== 1) return false;
const tagName = String(node.tagName || '').toLowerCase();
if (['button', 'a', 'summary'].indexOf(tagName) !== -1) return true;
if (node.hasAttribute('role')) {
const role = String(node.getAttribute('role') || '').toLowerCase();
if (['tab', 'button', 'link', 'menuitem', 'option', 'switch', 'radio'].indexOf(role) !== -1) return true;
}
if (node.hasAttribute('tabindex')) return true;
if (node.hasAttribute('aria-selected')) return true;
if (node.hasAttribute('aria-pressed')) return true;
if (node.hasAttribute('aria-expanded')) return true;
if (node.hasAttribute('aria-checked')) return true;
if (typeof node.onclick === 'function') return true;
const style = window.getComputedStyle(node);
return Boolean(style && style.cursor === 'pointer');
}
function kdocsNormalizeSectionLabel(text, node) {
const normalized = kdocsNormalizeResultText(text);
if (!normalized || normalized.length > 40) return '';
const hasTabSemantics = Boolean(
node
&& (
node.getAttribute('role') === 'tab'
|| node.hasAttribute('aria-selected')
|| node.hasAttribute('aria-controls')
|| node.closest('[role="tablist"]')
|| node.closest('[class*="tab"]')
|| node.closest('[class*="segmented"]')
|| node.closest('[class*="segment"]')
)
);
if (/^[0-9]+$/.test(normalized) && !hasTabSemantics) return '';
if (normalized.length < 2 && !hasTabSemantics) return '';
return normalized;
}
function kdocsGetDocSectionSwitches() {
const selector = [
'[role="tablist"] [role="tab"]',
'[role="tablist"] [aria-selected]',
'[role="tab"]',
'[aria-selected]',
'[aria-controls]',
'[aria-owns]',
'[class*="tab"]',
'[class*="segmented"] [role="button"]',
'[class*="segment"] [role="button"]',
'[class*="toggle"] [role="button"]',
'[class*="catalog"] [class*="item"]',
'[class*="toc"] [class*="item"]',
'[class*="nav"] [class*="item"]',
'[class*="menu"] [class*="item"]',
'[class*="outline"] [class*="item"]',
'[class*="sidebar"] [class*="item"]',
'[class*="switch"] [class*="item"]',
'[data-tab]',
'[data-panel]',
'[data-column]',
'[data-col]',
'[data-index]',
'nav [role="tab"]',
'nav button',
'nav a',
'[class*="tabs"] *',
].join(', ');
const seen = new Set();
const results = [];
Array.from(document.querySelectorAll(selector)).forEach(function (node) {
if (!kdocsIsVisibleElement(node)) return;
if (node.closest('#' + KDOCS_OVERLAY_ID)) return;
const clickable = kdocsIsClickableElement(node) ? node : node.closest('button, a, [role="tab"], [role="button"], [tabindex]');
if (!clickable || !kdocsIsVisibleElement(clickable)) return;
const label = kdocsNormalizeSectionLabel(kdocsTextOf(clickable), clickable);
if (!label) return;
const key = (clickable.getAttribute('aria-controls') || clickable.getAttribute('data-tab') || clickable.getAttribute('data-column') || label)
+ '::' + String(clickable.className || '');
if (seen.has(key)) return;
seen.add(key);
results.push({ node: clickable, label: label });
});
return results.slice(0, 20);
}
function kdocsFindGenericDocMatchItems(config) {
if (!document.body) return [];
const results = [];
const nodes = document.body.querySelectorAll('*');
for (const node of nodes) {
if (!kdocsIsVisibleElement(node)) continue;
if (node.closest('#' + KDOCS_OVERLAY_ID)) continue;
const tagName = String(node.tagName || '').toLowerCase();
if (['script', 'style', 'noscript', 'svg', 'path'].indexOf(tagName) !== -1) continue;
const text = kdocsNormalizeResultText(kdocsTextOf(node));
if (!text || text.length < config.keyword.length || text.length > 500) continue;
if (!kdocsIncludesKeyword(text, config.keyword)) continue;
if (kdocsIsNoiseLine(text, config.keyword)) continue;
const childContains = Array.from(node.children || []).some(function (child) {
if (!kdocsIsVisibleElement(child)) return false;
const childText = kdocsNormalizeResultText(kdocsTextOf(child));
return childText && childText.length <= 500 && kdocsIncludesKeyword(childText, config.keyword);
});
if (childContains) continue;
results.push({ text: text, anchorId: kdocsEnsureMatchAnchor(node) });
if (results.length >= 20) break;
}
return results;
}
async function kdocsScanDocSectionsForMatches(config) {
const sections = kdocsGetDocSectionSwitches();
for (const section of sections) {
try {
section.node.click();
} catch (_error) {
continue;
}
await kdocsSleep(260);
const directResult = kdocsExtractDocDirectResult(config, { allowPlainTextFallback: false });
if (Array.isArray(directResult.items) && directResult.items.some(function (item) { return Boolean(item.anchorId); })) {
directResult.countText = section.label
? ('栏目匹配: ' + directResult.items.length + '(' + section.label + ')')
: ('栏目匹配: ' + directResult.items.length);
return directResult;
}
}
return {
countText: '',
rows: [],
items: [],
errorText: '',
};
}
function kdocsBuildDocRowsResult(rows, label) {
const items = kdocsUniqueItems(rows.map(function (row) {
if (row && typeof row === 'object') {
return {
text: String(row.text || ''),
anchorId: row.anchorId ? String(row.anchorId) : '',
};
}
return { text: String(row || ''), anchorId: '' };
}).filter(function (item) {
return Boolean(item.text);
}));
return {
countText: items.length > 0 ? (label + ': ' + items.length) : '',
rows: items.map(function (item) {
return item.text;
}).slice(0, 20),
items: items.slice(0, 20),
errorText: '',
};
}
function kdocsGetDocApiCandidates() {
const candidates = [
window.wpsInstance && window.wpsInstance.Application,
window.instance && window.instance.Application,
window.jssdk && window.jssdk.Application,
window.webOfficeInstance && window.webOfficeInstance.Application,
window.Application,
window.WpsApplication,
window.wpsApplication,
].filter(Boolean);
return Array.from(new Set(candidates));
}
async function kdocsExtractDocApiResult(config) {
const apps = kdocsGetDocApiCandidates();
for (const app of apps) {
try {
const doc = await kdocsResolveValue(app.ActiveDocument);
if (!doc) continue;
const rows = [];
const paragraphs = await kdocsResolveValue(doc.Paragraphs);
const paragraphCount = paragraphs ? Number(await kdocsResolveValue(paragraphs.Count)) || 0 : 0;
if (paragraphCount > 0 && typeof paragraphs.Item === 'function') {
const limit = Math.min(paragraphCount, 400);
for (let index = 1; index <= limit; index += 1) {
const paragraph = await kdocsResolveValue(paragraphs.Item(index));
if (!paragraph) continue;
const range = await kdocsResolveValue(paragraph.Range);
const text = range ? kdocsNormalizeResultText(await kdocsResolveValue(range.Text)) : '';
if (!text) continue;
rows.push({ text: text, anchorId: '' });
}
}
let filteredRows = kdocsFilterKeywordItems(rows, config.keyword);
if (filteredRows.length === 0 && typeof doc.Range === 'function') {
const fullRange = await kdocsResolveValue(doc.Range(0, 200000));
const fullText = fullRange ? await kdocsResolveValue(fullRange.Text) : '';
filteredRows = kdocsFilterKeywordItems(kdocsSplitTextLines(fullText), config.keyword);
}
if (filteredRows.length > 0) {
return kdocsBuildDocRowsResult(filteredRows, 'API匹配');
}
} catch (_error) {
continue;
}
}
return {
countText: '',
rows: [],
errorText: '',
};
}
function kdocsExtractDocDirectResult(config, options) {
const settings = options || {};
const directSelectors = [
'p',
'li',
'h1',
'h2',
'h3',
'h4',
'blockquote',
'pre',
'td',
'th',
'[class*="paragraph"]',
'[class*="para"]',
'[class*="doc"] [class*="text"]',
'[class*="editor"] [class*="text"]',
'[class*="reader"] [class*="text"]',
'[class*="page"] [class*="text"]',
'[data-content]',
'[data-contents]',
];
const rows = [];
for (const selector of directSelectors) {
const nodes = document.querySelectorAll(selector);
for (const node of nodes) {
if (node.closest('#' + KDOCS_OVERLAY_ID)) continue;
const text = kdocsNormalizeResultText(kdocsTextOf(node));
if (!text || text.length < config.keyword.length) continue;
if (text.length > 500) continue;
if (!kdocsIncludesKeyword(text, config.keyword)) continue;
if (kdocsIsNoiseLine(text, config.keyword)) continue;
rows.push({ text: text, anchorId: kdocsEnsureMatchAnchor(node) });
}
if (rows.length >= 10) {
break;
}
}
if (rows.length === 0) {
const genericMatches = kdocsFindGenericDocMatchItems(config);
if (genericMatches.length > 0) {
rows.push.apply(rows, genericMatches);
}
}
if (rows.length === 0 && settings.allowPlainTextFallback !== false) {
const bodyText = document.body ? String(document.body.innerText || '') : '';
const lines = kdocsFilterKeywordLines(kdocsSplitTextLines(bodyText), config.keyword);
rows.push.apply(rows, lines.slice(0, 20).map(function (line) {
return { text: line, anchorId: '' };
}));
}
return kdocsBuildDocRowsResult(rows, '正文匹配');
}
function kdocsExtractSearchResult(config) {
const countText = kdocsTextOf(document.querySelector('.match-result-text'));
const selectors = config.sourceType === 'doc'
? [
'.db-global-find-result .db-global-find-select-list .select-item-value',
'.db-global-find-result .select-item-value',
'.db-global-find-result [class*="select-item-value"]',
'.db-global-find-result [class*="snippet"]',
'.db-global-find-result [class*="content"]',
'.db-global-find-result [class*="item"]',
]
: [
'.db-global-find-result .db-global-find-select-list:not(.db-global-find-small-select-list) .select-item-value',
'.db-global-find-result .db-global-find-select-list .select-item-value',
'.db-global-find-result .select-item-value',
'.db-global-find-result [class*="select-item-value"]',
];
const rows = kdocsCollectTexts(selectors).filter(function (text) {
return text && text !== countText && text !== config.keyword;
});
let uniqueRows = Array.from(new Set(rows));
const errorText = Array.from(document.querySelectorAll('.db-global-find-modal-panel *'))
.map(function (node) {
return kdocsTextOf(node);
})
.find(function (text) {
return text.indexOf('未找到') !== -1;
}) || '';
if (config.sourceType === 'doc' && uniqueRows.length === 0) {
const directResult = kdocsExtractDocDirectResult(config);
if (directResult.rows.length > 0) {
return directResult;
}
}
return {
countText: countText,
rows: uniqueRows.slice(0, 50),
errorText: errorText,
};
}
async function runKdocsAutoSearch(config) {
if (window[KDOCS_RUNNING_FLAG]) return;
window[KDOCS_RUNNING_FLAG] = true;
renderKdocsSearchOverlay(config, {
statusText: '正在自动搜索,请稍候...'
});
try {
for (let attempt = 0; attempt < 60; attempt += 1) {
kdocsCloseLoginModal();
if (config.sourceType === 'doc') {
const directAnchoredResult = kdocsExtractDocDirectResult(config, { allowPlainTextFallback: false });
if (directAnchoredResult.rows.length > 0) {
renderKdocsSearchOverlay(config, directAnchoredResult);
return;
}
const sectionResult = await kdocsScanDocSectionsForMatches(config);
if (sectionResult.rows.length > 0) {
renderKdocsSearchOverlay(config, sectionResult);
return;
}
const apiResult = await kdocsExtractDocApiResult(config);
if (apiResult.rows.length > 0) {
renderKdocsSearchOverlay(config, apiResult);
return;
}
const directResult = kdocsExtractDocDirectResult(config);
if (directResult.rows.length > 0) {
renderKdocsSearchOverlay(config, directResult);
return;
}
}
const findButton = kdocsFindByText('button, [role="button"], span', '查找', true);
if (findButton && !document.querySelector('.db-global-find-modal-panel')) {
kdocsClick(findButton);
await kdocsSleep(300);
}
const input = document.querySelector(
'.db-global-find-keyword-setting input, '
+ '.db-global-find-keyword-setting textarea, '
+ 'input[placeholder*="查找"], '
+ 'textarea[placeholder*="查找"], '
+ 'input[type="search"]'
);
if (!input) {
await kdocsSleep(300);
continue;
}
kdocsSetInputValue(input, config.keyword);
await kdocsSleep(150);
const allSheetsLabel = config.sourceType === 'sheet' ? kdocsFindByText('label, span', '全部数据表', true) : null;
if (allSheetsLabel) {
kdocsClick(allSheetsLabel);
await kdocsSleep(150);
}
const findAllButton = kdocsFindByText('button, [role="button"], span', '查找全部', true);
if (findAllButton) {
kdocsClick(findAllButton);
await kdocsSleep(800);
}
for (let waitRound = 0; waitRound < 20; waitRound += 1) {
const result = kdocsExtractSearchResult(config);
if (result.countText || result.rows.length > 0 || result.errorText) {
renderKdocsSearchOverlay(config, result);
return;
}
await kdocsSleep(300);
}
}
if (config.sourceType === 'doc') {
const bodyLines = kdocsSplitTextLines(document.body ? String(document.body.innerText || '') : '');
if (bodyLines.length > 20) {
renderKdocsSearchOverlay(config, {
statusText: '正文已加载,但未找到匹配内容。',
countText: '正文扫描: 0',
rows: [],
errorText: '',
});
return;
}
}
renderKdocsSearchOverlay(config, {
statusText: '自动搜索超时,请点击 KDocs 页面的查找按钮再试一次。',
countText: '',
rows: [],
errorText: '自动搜索超时',
});
} finally {
window[KDOCS_RUNNING_FLAG] = false;
}
}
async function openYellowrabbitViewer(title, apiUrl) {
const win = window.open('about:blank', '_blank');
if (!win) {
window.open(apiUrl, '_blank', 'noopener');
return;
}
writeViewerWindow(win, buildYellowrabbitViewerHtml({
title: title,
apiUrl: apiUrl,
statusText: '加载中...',
phase: 'loading',
items: [],
rawText: '',
errorText: '',
}));
try {
const response = await fetch(apiUrl, {
method: 'GET',
headers: { Accept: 'application/json' },
credentials: 'omit',
});
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
const text = await response.text();
let payload = null;
try {
payload = JSON.parse(text);
} catch (_error) {
throw new Error('返回不是合法 JSON');
}
const items = extractYellowrabbitItems(payload);
writeViewerWindow(win, buildYellowrabbitViewerHtml({
title: title,
apiUrl: apiUrl,
statusText: '共 ' + items.length + ' 条结果',
phase: 'done',
items: items,
rawText: text,
errorText: '',
}));
} catch (error) {
writeViewerWindow(win, buildYellowrabbitViewerHtml({
title: title,
apiUrl: apiUrl,
statusText: '加载失败',
phase: 'error',
items: [],
rawText: '',
errorText: error && error.message ? error.message : '未知错误',
}));
}
}
function writeViewerWindow(win, html) {
if (!win || win.closed) return;
win.document.open();
win.document.write(html);
win.document.close();
}
function buildExternalAutoSearchUrl(baseUrl, mode, keyword) {
const url = new URL(baseUrl);
url.hash = new URLSearchParams({
doubanMode: mode,
doubanKeyword: keyword,
}).toString();
return url.toString();
}
function openAutoSubmitWindow(config) {
const url = String((config && config.url) || '');
if (!url) return;
window.open(url, '_blank', 'noopener');
}
function openLemonSearch(keyword) {
openAutoSubmitWindow({
url: buildExternalAutoSearchUrl('https://lemonun.top/', 'lemonSearch', keyword),
});
}
function open6vMovieSearch(keyword) {
openAutoSubmitWindow({
url: buildExternalAutoSearchUrl('https://www.6v520.tv/sousuo.html', 'sixvSearch', keyword),
});
}
function parseExternalAutoSearchConfig() {
const hash = String(window.location.hash || '');
const hashParams = hash.startsWith('#') ? new URLSearchParams(hash.slice(1)) : null;
const searchParams = new URLSearchParams(window.location.search || '');
const mode = (hashParams && hashParams.get('doubanMode')) || searchParams.get('doubanMode') || '';
const keyword = normalizeTitle((hashParams && hashParams.get('doubanKeyword')) || searchParams.get('doubanKeyword') || '');
if (!mode || !keyword) return null;
return { mode: mode, keyword: keyword };
}
function ensureHiddenFormField(form, name, value) {
let input = form.querySelector('input[name="' + name + '"]');
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form.appendChild(input);
}
input.value = String(value);
}
function scheduleExternalAutoSubmit(submitter) {
let attempts = 0;
function trySubmit() {
if (submitter()) return;
attempts += 1;
if (attempts < 40) {
window.setTimeout(trySubmit, 150);
}
}
trySubmit();
}
function runLemonAutoSearch(config) {
scheduleExternalAutoSubmit(function () {
const form = document.querySelector('#search-form');
const input = form && form.querySelector('input[name="keyword"]');
if (!form || !input) return false;
kdocsSetInputValue(input, config.keyword);
form.submit();
return true;
});
}
function runSixvAutoSearch(config) {
scheduleExternalAutoSubmit(function () {
const form = document.querySelector('#searchform');
const input = form && form.querySelector('input[name="keyboard"]');
if (!form || !input) return false;
ensureHiddenFormField(form, 'show', 'title,smalltext');
ensureHiddenFormField(form, 'tempid', '1');
ensureHiddenFormField(form, 'x', '0');
ensureHiddenFormField(form, 'y', '0');
const tbnameField = form.querySelector('[name="tbname"]');
if (tbnameField) {
tbnameField.value = 'article';
} else {
ensureHiddenFormField(form, 'tbname', 'article');
}
kdocsSetInputValue(input, config.keyword);
form.submit();
return true;
});
}
function extractYellowrabbitItems(payload) {
if (Array.isArray(payload)) return payload;
if (payload && Array.isArray(payload.data)) return payload.data;
return [];
}
function getHttpLinks(item) {
const links = [];
for (const key in item) {
if (!Object.prototype.hasOwnProperty.call(item, key)) continue;
const value = item[key];
if (typeof value === 'string' && /^https?:\/\//i.test(value)) {
links.push({ key: key, url: value });
}
}
return links;
}
function renderYellowrabbitItems(items, phase) {
if (phase === 'loading') {
return '<article class="item"><div class="meta">正在请求数据,请稍候...</div></article>';
}
if (!Array.isArray(items) || items.length === 0) {
return '<article class="item"><div class="meta">暂无结果</div></article>';
}
return items.map(function (item, index) {
const idText = item && item.id != null ? String(item.id) : String(index + 1);
const titleText = item && item.title ? String(item.title) : '未命名资源';
const titleDisplay = titleText.length > 85 ? titleText.slice(0, 85) + '...' : titleText;
const links = getHttpLinks(item || {});
const linkHtml = links.length > 0
? links.map(function (entry) {
return '<a class="link" target="_blank" rel="noopener noreferrer" href="' + escapeHtml(entry.url) + '">' + escapeHtml(entry.key) + '</a>';
}).join('')
: '<span class="meta">无可用链接</span>';
return '<article class="item">'
+ '<div class="item-row">'
+ '<div class="item-main">'
+ '<div class="meta">#' + escapeHtml(idText) + '</div>'
+ '<h2 class="item-title">' + escapeHtml(titleDisplay) + '</h2>'
+ '</div>'
+ '<div class="links">' + linkHtml + '</div>'
+ '</div>'
+ '</article>';
}).join('');
}
function buildYellowrabbitViewerHtml(options) {
const title = options.title;
const apiUrl = options.apiUrl;
const statusText = options.statusText;
const phase = options.phase;
const items = options.items;
const rawText = options.rawText;
const errorText = options.errorText;
const listHtml = renderYellowrabbitItems(items, phase);
const errorHtml = errorText
? '<div class="error">错误: ' + escapeHtml(errorText) + '</div>'
: '';
const rawHtml = rawText ? escapeHtml(rawText) : '';
return `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>yellowrabbit 搜索结果</title>
<style>
:root {
color-scheme: light;
--bg: #f6f8fc;
--card: #ffffff;
--line: #dfe5ef;
--text: #111827;
--muted: #5f6b7a;
--primary: #1565c0;
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 8px;
font: 14px/1.6 -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;
background: var(--bg);
color: var(--text);
}
.header {
width: 55%;
margin: 0 auto 6px;
background: var(--card);
border: 1px solid var(--line);
border-radius: 9px;
padding: 8px 10px;
}
.title {
font-size: 17px;
font-weight: 700;
margin: 0 0 2px;
}
.sub {
color: var(--muted);
word-break: break-all;
}
.status {
margin-top: 4px;
color: var(--muted);
}
.error {
margin-top: 6px;
color: #b91c1c;
font-weight: 600;
word-break: break-word;
}
.list {
width: 55%;
margin: 0 auto;
display: grid;
gap: 6px;
}
.item {
background: var(--card);
border: 1px solid var(--line);
border-radius: 9px;
padding: 7px 9px;
}
.item-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.item-main {
min-width: 0;
flex: 1;
}
.item-title {
font-size: 14px;
font-weight: 600;
margin: 0;
word-break: break-word;
line-height: 1.35;
}
.meta {
color: var(--muted);
margin-bottom: 2px;
}
.links {
display: flex;
flex-wrap: wrap;
gap: 6px;
justify-content: flex-end;
align-items: center;
max-width: 42%;
}
.link {
border: 1px solid #c8d7ef;
color: var(--primary);
text-decoration: none;
background: #f1f6ff;
border-radius: 999px;
padding: 6px 16px;
font-size: 15px;
font-weight: 600;
line-height: 1.1;
white-space: nowrap;
}
.link:hover {
background: #e3efff;
}
details {
width: 55%;
margin: 8px auto 0;
background: var(--card);
border: 1px solid var(--line);
border-radius: 10px;
padding: 6px 10px;
}
pre {
margin: 6px 0 0;
white-space: pre-wrap;
word-break: break-word;
max-height: 320px;
overflow: auto;
color: #1f2937;
}
@media (max-width: 900px) {
.header,
.list,
details {
width: 96%;
}
.item-row {
align-items: flex-start;
flex-direction: column;
gap: 6px;
}
.links {
justify-content: flex-start;
max-width: 100%;
}
}
</style>
</head>
<body>
<section class="header">
<h1 class="title">yellowrabbit 搜索结果</h1>
<div class="sub">关键词: ${escapeHtml(title)}</div>
<div class="sub">API: <a class="link" target="_blank" rel="noopener noreferrer" href="${escapeHtml(apiUrl)}">打开原始 JSON</a></div>
<div class="status">${escapeHtml(statusText)}</div>
${errorHtml}
</section>
<section class="list">${listHtml}</section>
<details>
<summary>原始 JSON</summary>
<pre>${rawHtml}</pre>
</details>
</body>
</html>`;
}
function ensureStyle() {
if (document.querySelector(`.${STYLE_CLASS}`)) return;
const style = document.createElement('style');
style.className = STYLE_CLASS;
style.textContent = `
.${CONTAINER_CLASS} {
margin-top: 10px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.${BTN_CLASS} {
padding: 3px 10px;
border: 1px solid #1565C0;
border-radius: 3px;
text-decoration: none;
color: #ffffff;
font-size: 18px;
font-weight: normal;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
display: inline-block;
vertical-align: middle;
line-height: 1.5;
/* background: linear-gradient(to bottom, #2196F3, #1976D2); */
}
.${BTN_CLASS}:hover {
background: linear-gradient(to bottom, #64B5F6, #42A5F5);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
.${BTN_CLASS}:active {
transform: translateY(0);
box-shadow: none;
}
.douban-source-manager-btn {
color: #4d7c2f;
border-style: dashed;
border-color: #9fc37b;
background: #f2f9e8;
}
.douban-source-manager-btn:hover {
background: #e5f3d4;
border-color: #86af61;
}
#${SOURCE_MANAGER_MODAL_ID}.douban-source-manager-mask {
--douban-source-manager-accent: #5f9133;
--douban-source-manager-accent-strong: #467121;
--douban-source-manager-accent-soft: #edf6e3;
--douban-source-manager-accent-soft-strong: #dcedc7;
--douban-source-manager-border: #c8dbb6;
--douban-source-manager-border-strong: #a7c48b;
--douban-source-manager-text: #1c2c12;
--douban-source-manager-muted: #5f7050;
--douban-source-manager-panel-bg: linear-gradient(180deg, #f6faef 0%, #ffffff 18%, #f0f6e7 100%);
--douban-source-manager-card-bg: linear-gradient(180deg, #ffffff 0%, #f4f8ea 100%);
--douban-source-manager-card-hidden-bg: linear-gradient(180deg, #f4f6ef 0%, #eaf1df 100%);
--douban-source-manager-shadow: 0 24px 54px rgba(43, 71, 20, 0.24);
--douban-source-manager-ring: rgba(95, 145, 51, 0.22);
position: fixed;
inset: 0;
z-index: 2147483647;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
background: rgba(15, 23, 42, 0.45);
backdrop-filter: blur(2px);
}
#${SOURCE_MANAGER_MODAL_ID} * {
box-sizing: border-box;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-panel {
width: min(860px, 100%);
max-height: min(720px, calc(100vh - 40px));
display: flex;
flex-direction: column;
gap: 14px;
padding: 18px;
overflow: hidden;
border: 1px solid var(--douban-source-manager-border);
border-radius: 18px;
background: var(--douban-source-manager-panel-bg);
box-shadow: var(--douban-source-manager-shadow);
color: var(--douban-source-manager-text);
font: 14px/1.55 -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-header,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-toolbar,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
flex-wrap: wrap;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-title-wrap {
min-width: 0;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-title {
margin: 0;
font-size: 22px;
font-weight: 700;
color: var(--douban-source-manager-accent-strong);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-subtitle,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-summary {
color: var(--douban-source-manager-muted);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-subtitle {
margin-top: 4px;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-summary {
padding: 10px 12px;
border: 1px solid var(--douban-source-manager-border);
border-radius: 12px;
background: var(--douban-source-manager-accent-soft);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-close,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-action,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-toggle,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-primary,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-secondary {
appearance: none;
border: 1px solid var(--douban-source-manager-border);
border-radius: 999px;
background: var(--douban-source-manager-accent-soft);
color: var(--douban-source-manager-accent-strong);
font: inherit;
font-weight: 600;
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-close:hover,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-action:hover,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-toggle:hover,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-primary:hover,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-secondary:hover {
background: var(--douban-source-manager-accent-soft-strong);
border-color: var(--douban-source-manager-border-strong);
transform: translateY(-1px);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-close {
width: 38px;
height: 38px;
padding: 0;
font-size: 22px;
line-height: 1;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-action,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-toggle,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-primary,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-secondary {
padding: 8px 14px;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-list {
margin: 0;
padding: 0;
list-style: none;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
align-content: start;
gap: 12px;
overflow: auto;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item {
position: relative;
display: block;
min-height: 150px;
padding: 12px;
border: 1px solid var(--douban-source-manager-border);
border-radius: 14px;
background: var(--douban-source-manager-card-bg);
box-shadow: 0 10px 22px rgba(95, 145, 51, 0.1);
cursor: move;
transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item.is-hidden {
background: var(--douban-source-manager-card-hidden-bg);
border-style: dashed;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item.is-dragging {
opacity: 0.42;
transform: scale(0.98);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item.is-drop-target,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item.is-drop-after {
border-color: var(--douban-source-manager-accent);
box-shadow: 0 0 0 3px var(--douban-source-manager-ring), 0 14px 26px rgba(95, 145, 51, 0.18);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item.is-drop-target {
transform: translateY(-2px);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item.is-drop-after {
box-shadow: 0 0 0 3px var(--douban-source-manager-ring), inset 0 -4px 0 var(--douban-source-manager-accent), 0 14px 26px rgba(95, 145, 51, 0.18);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-index {
min-width: 36px;
padding: 4px 8px;
border-radius: 999px;
background: var(--douban-source-manager-accent);
color: #ffffff;
text-align: center;
font-weight: 700;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-main {
min-width: 0;
height: 100%;
display: grid;
grid-template-rows: auto 1fr;
gap: 8px;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-name-group {
display: grid;
gap: 6px;
min-width: 0;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-label {
font-size: 15px;
font-weight: 700;
color: var(--douban-source-manager-text);
word-break: break-word;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-meta {
font-size: 12px;
color: var(--douban-source-manager-muted);
word-break: break-word;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-rename {
width: 100%;
padding: 8px 10px;
border: 1px solid var(--douban-source-manager-border);
border-radius: 10px;
background: rgba(255, 255, 255, 0.92);
color: var(--douban-source-manager-text);
font: inherit;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-rename:focus {
outline: none;
border-color: var(--douban-source-manager-accent);
box-shadow: 0 0 0 3px var(--douban-source-manager-ring);
background: #ffffff;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-toggle.is-hidden,
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-secondary {
background: #ffffff;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-primary {
border-color: var(--douban-source-manager-accent-strong);
background: linear-gradient(180deg, #78b44d 0%, #5f9133 100%);
color: #ffffff;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-btn-primary:hover {
background: linear-gradient(180deg, #89be61 0%, #4f7f2b 100%);
border-color: var(--douban-source-manager-accent-strong);
}
@media (max-width: 720px) {
#${SOURCE_MANAGER_MODAL_ID}.douban-source-manager-mask {
padding: 12px;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-panel {
padding: 14px;
max-height: calc(100vh - 24px);
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-list {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-item {
min-height: 138px;
}
#${SOURCE_MANAGER_MODAL_ID} .douban-source-manager-toggle {
padding-inline: 12px;
}
}
`;
document.head.appendChild(style);
}
function createButton(source, title, labelAliases) {
const link = document.createElement('a');
const url = source.buildUrl(title);
const displayLabel = getSourceDisplayLabel(source.label, labelAliases);
link.href = url;
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.textContent = displayLabel;
link.className = BTN_CLASS;
link.title = displayLabel + '(右键隐藏该源)';
if (typeof source.onClick === 'function') {
link.addEventListener('click', function (event) {
event.preventDefault();
source.onClick({ title: title, url: url, event: event });
});
}
link.addEventListener('contextmenu', function (event) {
event.preventDefault();
const ok = window.confirm('隐藏源「' + displayLabel + '」?\n可通过“源设置”按钮恢复。');
if (!ok) return;
hideSource(source.label);
renderButtons(true);
});
return link;
}
function createSourceManagerButton() {
const manager = document.createElement('a');
manager.href = 'javascript:void(0)';
manager.target = '_self';
manager.textContent = '源设置';
manager.className = BTN_CLASS + ' douban-source-manager-btn';
manager.title = '管理隐藏源、排序和显示名称';
manager.addEventListener('click', function (event) {
event.preventDefault();
openSourceManager();
});
return manager;
}
function removeOldContainer() {
const old = document.querySelector(`.${CONTAINER_CLASS}`);
if (old) old.remove();
}
function renderButtons(force) {
const h1Element = document.querySelector('h1');
if (!h1Element) return;
const title = getMovieTitle();
if (!title) return;
const searchKeyword = getSearchKeyword(title);
if (!searchKeyword) return;
if (!force && document.querySelector(`.${CONTAINER_CLASS}`)) return;
ensureStyle();
removeOldContainer();
const container = document.createElement('div');
container.className = CONTAINER_CLASS;
const hiddenSources = loadHiddenSources();
const labelAliases = loadSourceLabelAliases();
for (const source of getOrderedSources()) {
if (hiddenSources.has(source.label)) continue;
container.appendChild(createButton(source, searchKeyword, labelAliases));
}
container.appendChild(createSourceManagerButton());
h1Element.insertAdjacentElement('afterend', container);
}
function scheduleRender() {
if (renderTimer) {
clearTimeout(renderTimer);
}
renderTimer = window.setTimeout(renderButtons, 120);
}
const host = window.location.hostname;
const isKdocsHost = host === 'www.kdocs.cn' || host === 'appdocs.wpscdn.cn';
const isLemonHost = host === 'lemonun.top';
const isSixvHost = host === 'www.6v520.tv';
const kdocsConfig = isKdocsHost ? parseKdocsSearchConfig() : null;
const externalAutoSearchConfig = (isLemonHost || isSixvHost) ? parseExternalAutoSearchConfig() : null;
if (isKdocsHost) {
if (kdocsConfig) {
runKdocsAutoSearch(kdocsConfig);
}
return;
}
if (isLemonHost) {
if (externalAutoSearchConfig && externalAutoSearchConfig.mode === 'lemonSearch') {
runLemonAutoSearch(externalAutoSearchConfig);
}
return;
}
if (isSixvHost) {
if (externalAutoSearchConfig && externalAutoSearchConfig.mode === 'sixvSearch') {
runSixvAutoSearch(externalAutoSearchConfig);
}
return;
}
const observer = new MutationObserver(scheduleRender);
observer.observe(document.body, { childList: true, subtree: true });
scheduleRender();
})();