// ==UserScript==
// @name 18Comic 之路
// @namespace https://github.com/zyf722
// @version 1.0
// @author zyf722
// @description JM / 18Comic 车牌号划词查询工具
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=18comic.vip
// @match *://weibo.com/*
// @match *://*.weibo.com/*
// @match *://*.weibo.cn/*
// @match *://tieba.baidu.com/*
// @match *://*.bilibili.com/
// @match *://*.bilibili.com/*
// @require https://cdn.jsdelivr.net/npm/[email protected]/crypto-js.js
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// ==/UserScript==
(o=>{if(typeof GM_addStyle=="function"){GM_addStyle(o);return}const e=document.createElement("style");e.textContent=o,document.head.append(e)})(" .jm-select-none,.jm-select-none *{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jm-overflow{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#jm-popup{position:absolute;background-color:#fff;padding:10px;margin-top:10px;border:1px solid #ddd;box-shadow:0 4px 8px #0003;z-index:999999999999;display:none;max-width:25%;column-gap:10px;align-items:center}.jm-title{max-width:100%;font-size:14px;grid-column:1;grid-row:2}#jm-title-text{display:none}#jm-number-container{max-width:100%;grid-column:1;grid-row:1;display:flex;align-items:center}#jm-number{font-size:18px;font-weight:700}#jm-number-icon{width:16px;height:16px;margin-right:5px}#jm-copy{border:none;background-color:#fff;width:32px;height:32px;font-size:16px;cursor:pointer;grid-column:2;grid-row:1 / 3;transition:background-color .3s;display:flex;align-items:center;justify-content:center}#jm-copy:hover:not(:disabled){background-color:#f6f6f6}#jm-copy:active:not(:disabled){background-color:#e6e6e6}#jm-copy-icon{width:16px;height:16px;transition:opacity .25s}.jm-copy-icon-hide{opacity:0}#jm-details-container{grid-column:1 / span 2;grid-row:3;display:grid;gap:5px;font-size:12px;margin-top:10px;border-top:1px solid #eee;padding-top:10px}.jm-detail-row{display:contents}.jm-detail-row>span{padding:2px 0}.jm-detail-label{font-weight:700;color:#555;white-space:nowrap}.jm-detail-value{color:#333}.jm-tags-container{display:flex;flex-wrap:wrap;gap:4px}.jm-tag-item{vertical-align:middle;background:#00000012;color:#777;font-size:12px;line-height:16px;display:inline-block;padding:0 3px;margin:-2px 0 0 2px;border-radius:2px;letter-spacing:-.6px;bottom:0}#rt18-config-dialog-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:9999}#rt18-config-dialog{background-color:#fff;padding:20px;border-radius:8px;box-shadow:0 4px 12px #00000026;width:500px;max-width:90%;position:relative}.rt18-config-close-button{position:absolute;top:10px;right:10px;background:none;border:none;font-size:1.5em;cursor:pointer}.rt18-config-section{margin-top:10px;margin-bottom:10px;display:flex;flex-direction:column;gap:10px}#rt18-source-list{list-style:none;padding:0;max-height:200px;overflow-y:auto;border:1px solid #ddd;border-radius:4px}#rt18-source-list li{padding:8px 12px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}.rt18-source-text{flex-grow:1;margin-right:10px}.rt18-source-controls{display:flex;align-items:center}.rt18-source-button{margin-left:5px;padding:4px 8px;font-size:.9em;cursor:pointer;border:1px solid #ccc;border-radius:3px;background-color:#f0f0f0;display:inline-flex;align-items:center;justify-content:center;min-width:20px;text-align:center}.rt18-source-button:hover{background-color:#e0e0e0}.rt18-source-button-delete{background-color:#f8d7da;color:#721c24;border-color:#f5c6cb}.rt18-source-button-delete:hover{background-color:#f1b0b7}.rt18-button-disabled{opacity:.5;cursor:not-allowed;background-color:#e9ecef}.rt18-button-disabled:hover{background-color:#e9ecef}.rt18-add-source-container{display:flex;margin-top:10px}#rt18-add-source-input{flex-grow:1;padding:8px;border:1px solid #ccc;border-radius:4px 0 0 4px}#rt18-add-source-btn{padding:8px 15px;border:1px solid #ccc;border-left:none;background-color:#007bff;color:#fff;cursor:pointer;border-radius:0 4px 4px 0;display:inline-flex;align-items:center;justify-content:center}#rt18-add-source-btn:hover{background-color:#0056b3}.rt18-config-input{width:100%;padding:8px;border:1px solid #ccc;border-radius:4px;box-sizing:border-box}.rt18-config-description{font-size:.9em;color:#666;margin-top:0} ");
(function (CryptoJS) {
'use strict';
const loadingIcon = "data:image/svg+xml,%3csvg%20viewBox='0%200%201024%201024'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M512%20170.666667a341.333333%20341.333333%200%201%200%200%20682.666666%20341.333333%20341.333333%200%200%200%200-682.666666zM85.333333%20512C85.333333%20276.352%20276.352%2085.333333%20512%2085.333333s426.666667%20191.018667%20426.666667%20426.666667-191.018667%20426.666667-426.666667%20426.666667S85.333333%20747.648%2085.333333%20512z%20m426.666667-256a42.666667%2042.666667%200%200%201%2042.666667%2042.666667v195.669333l115.498666%20115.498667a42.666667%2042.666667%200%200%201-60.330666%2060.330666l-128-128A42.666667%2042.666667%200%200%201%20469.333333%20512V298.666667a42.666667%2042.666667%200%200%201%2042.666667-42.666667z'%20fill='currentColor'/%3e%3c/svg%3e";
const failIcon = "data:image/svg+xml,%3csvg%20viewBox='0%200%201024%201024'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M512%2097.52381c228.912762%200%20414.47619%20185.563429%20414.47619%20414.47619s-185.563429%20414.47619-414.47619%20414.47619S97.52381%20740.912762%2097.52381%20512%20283.087238%2097.52381%20512%2097.52381z%20m0%2073.142857C323.486476%20170.666667%20170.666667%20323.486476%20170.666667%20512s152.81981%20341.333333%20341.333333%20341.333333%20341.333333-152.81981%20341.333333-341.333333S700.513524%20170.666667%20512%20170.666667z%20m129.29219%20160.304762l51.736381%2051.736381L563.687619%20512l129.316571%20129.29219-51.73638%2051.736381L512%20563.687619l-129.29219%20129.316571-51.736381-51.73638L460.312381%20512l-129.316571-129.26781%2051.73638-51.73638L512%20460.263619l129.26781-129.29219z'%20fill='currentColor'/%3e%3c/svg%3e";
const successIcon = "data:image/svg+xml,%3csvg%20viewBox='0%200%201024%201024'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M512%2097.52381c228.912762%200%20414.47619%20185.563429%20414.47619%20414.47619s-185.563429%20414.47619-414.47619%20414.47619S97.52381%20740.912762%2097.52381%20512%20283.087238%2097.52381%20512%2097.52381z%20m0%2073.142857C323.486476%20170.666667%20170.666667%20323.486476%20170.666667%20512s152.81981%20341.333333%20341.333333%20341.333333%20341.333333-152.81981%20341.333333-341.333333S700.513524%20170.666667%20512%20170.666667z%20m193.194667%20145.188571l52.467809%2050.956191-310.662095%20319.683047-156.379429-162.230857%2052.662858-50.761143%20103.936%20107.812572%20257.974857-265.45981z'%20fill='currentColor'/%3e%3c/svg%3e";
const warningIcon = "data:image/svg+xml,%3csvg%20viewBox='0%200%201024%201024'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M545.718857%20130.608762c11.337143%206.265905%2020.699429%2015.555048%2026.989714%2026.819048l345.014858%20617.667047a68.87619%2068.87619%200%200%201-26.989715%2093.915429c-10.313143%205.705143-21.942857%208.704-33.718857%208.704H166.985143A69.266286%2069.266286%200%200%201%2097.52381%20808.643048c0-11.751619%202.998857-23.28381%208.752761-33.548191l344.990477-617.642667a69.656381%2069.656381%200%200%201%2094.451809-26.819047zM512%20191.000381L166.985143%20808.643048H856.990476L512%20191.000381zM546.718476%20670.47619v69.071239h-69.461333V670.47619h69.485714z%20m0-298.374095v252.318476h-69.461333V372.102095h69.485714z'%20fill='currentColor'/%3e%3c/svg%3e";
const doneIcon = "data:image/svg+xml,%3csvg%20viewBox='0%200%201024%201024'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M512%2016C238.066%2016%2016%20238.066%2016%20512s222.066%20496%20496%20496%20496-222.066%20496-496S785.934%2016%20512%2016z%20m0%2096c221.064%200%20400%20178.902%20400%20400%200%20221.064-178.902%20400-400%20400-221.064%200-400-178.902-400-400%200-221.064%20178.902-400%20400-400m280.408%20260.534l-45.072-45.436c-9.334-9.41-24.53-9.472-33.94-0.136L430.692%20607.394l-119.584-120.554c-9.334-9.41-24.53-9.472-33.94-0.138l-45.438%2045.072c-9.41%209.334-9.472%2024.53-0.136%2033.942l181.562%20183.032c9.334%209.41%2024.53%209.472%2033.94%200.136l345.178-342.408c9.408-9.336%209.468-24.532%200.134-33.942z'%20fill='currentColor'/%3e%3c/svg%3e";
const copyIcon = "data:image/svg+xml,%3csvg%20viewBox='0%200%201024%201024'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M931.882%20131.882l-103.764-103.764A96%2096%200%200%200%20760.236%200H416c-53.02%200-96%2042.98-96%2096v96H160c-53.02%200-96%2042.98-96%2096v640c0%2053.02%2042.98%2096%2096%2096h448c53.02%200%2096-42.98%2096-96v-96h160c53.02%200%2096-42.98%2096-96V199.764a96%2096%200%200%200-28.118-67.882zM596%20928H172a12%2012%200%200%201-12-12V300a12%2012%200%200%201%2012-12h148v448c0%2053.02%2042.98%2096%2096%2096h192v84a12%2012%200%200%201-12%2012z%20m256-192H428a12%2012%200%200%201-12-12V108a12%2012%200%200%201%2012-12h212v176c0%2026.51%2021.49%2048%2048%2048h176v404a12%2012%200%200%201-12%2012z%20m12-512h-128V96h19.264c3.182%200%206.234%201.264%208.486%203.514l96.736%2096.736a12%2012%200%200%201%203.514%208.486V224z'%20fill='currentColor'/%3e%3c/svg%3e";
const uiHtml = '<div id="jm-popup" class="jm-select-none" style="display: none;">\r\n <div id="jm-number-container">\r\n <div id="jm-number-icon"></div>\r\n <div id="jm-number" class="jm-overflow"></div>\r\n </div>\r\n <a id="jm-title-text" class="jm-overflow jm-title" target="_blank" rel="noopener noreferrer" style="display: none;"></a>\r\n <div id="jm-title-loading" class="jm-title">加载中...</div>\r\n <div id="jm-details-container" style="display: none;"></div>\r\n <button id="jm-copy" title="复制漫画名" disabled>\r\n <div id="jm-copy-icon"></div>\r\n </button>\r\n</div>\r\n';
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
const configHtml = '<div id="rt18-config-dialog-overlay">\r\n <div id="rt18-config-dialog">\r\n <h2>18Comic 之路配置</h2>\r\n <button id="rt18-config-close-btn" class="rt18-config-close-button">×</button>\r\n <div class="rt18-config-section">\r\n <h3>API 线路配置</h3>\r\n <ul id="rt18-source-list"></ul>\r\n <div class="rt18-add-source-container">\r\n <input type="text" id="rt18-add-source-input" placeholder="输入新线路域名">\r\n <div id="rt18-add-source-btn" class="rt18-config-button">添加线路</div>\r\n </div>\r\n <p class="rt18-config-description">脚本运行时将按照配置的 API 线路顺序获取车牌对应信息,直到成功获取为止。</p>\r\n </div>\r\n <div class="rt18-config-section">\r\n <h3>API 请求超时配置</h3>\r\n <input type="number" id="rt18-config-timeout-input" class="rt18-config-input" min="1000" step="1000">\r\n <p class="rt18-config-description">网络请求的超时时间,单位为毫秒。</p>\r\n </div>\r\n <div class="rt18-config-section">\r\n <h3>JM 网站线路</h3>\r\n <input type="url" id="rt18-config-jm-url-input" class="rt18-config-input" placeholder="例如: https://18comic.vip">\r\n <p class="rt18-config-description">用于拼接最终跳转的 18Comic / JM 网站地址。可访问 <a href="https://jmcomic-fb.vip" target="_blank" rel="noopener noreferrer">jmcomic-fb.vip</a> 获取最新线路。</p>\r\n </div>\r\n <div class="rt18-config-section">\r\n <h3>布局选择</h3>\r\n <select id="rt18-config-layout-select" class="rt18-config-input">\r\n <option value="details">详细布局(显示漫画详细信息)</option>\r\n <option value="compact">紧凑布局(旧版布局,仅显示漫画名称)</option>\r\n </select>\r\n <p class="rt18-config-description">漫画浮窗窗口的布局样式。</p>\r\n </div>\r\n </div>\r\n</div>\r\n';
const defaultConfig = {
sources: ["www.cdnmhwscc.vip", "www.cdnblackmyth.club", "www.cdnmhws.cc", "www.cdnuc.vip"],
timeout: 5e3,
jmWebsiteUrl: "https://18comic.vip",
layout: "details"
};
const loadConfig = () => {
const configString = localStorage.getItem("rt18_config");
if (configString) {
const parsedConfig = JSON.parse(configString);
return { ...defaultConfig, ...parsedConfig };
}
return { ...defaultConfig };
};
const config = loadConfig();
let isConfigDialogOpen = false;
const saveConfig = () => {
localStorage.setItem("rt18_config", JSON.stringify(config));
};
const resetConfig = () => {
if (confirm("确定要重置所有配置吗?")) {
localStorage.removeItem("rt18_config");
alert("配置已重置为默认值,请刷新页面以应用更改。");
}
};
const openConfigDialog$1 = () => {
const dialogContainerId = "rt18-config-dialog-container";
let dialogContainer = document.getElementById(dialogContainerId);
if (dialogContainer) {
dialogContainer.style.display = "flex";
return;
}
dialogContainer = document.createElement("div");
dialogContainer.id = dialogContainerId;
dialogContainer.innerHTML = configHtml;
document.body.appendChild(dialogContainer);
isConfigDialogOpen = true;
const closeButton = document.getElementById("rt18-config-close-btn");
const sourceList = document.getElementById("rt18-source-list");
const addSourceInput = document.getElementById("rt18-add-source-input");
const addSourceButton = document.getElementById("rt18-add-source-btn");
const timeoutInput = document.getElementById("rt18-config-timeout-input");
const jmWebsiteUrlInput = document.getElementById("rt18-config-jm-url-input");
const layoutSelect = document.getElementById("rt18-config-layout-select");
if (timeoutInput) {
timeoutInput.value = String(config.timeout);
timeoutInput.addEventListener("change", () => {
const newTimeout = parseInt(timeoutInput.value, 10);
if (!isNaN(newTimeout) && newTimeout > 0) {
config.timeout = newTimeout;
localStorage.setItem("rt18_config", JSON.stringify(config));
config.timeout = newTimeout;
} else {
timeoutInput.value = String(config.timeout);
alert("请输入有效的超时毫秒数。");
}
});
}
if (jmWebsiteUrlInput) {
jmWebsiteUrlInput.value = config.jmWebsiteUrl;
jmWebsiteUrlInput.addEventListener("change", () => {
const newUrl = jmWebsiteUrlInput.value.trim();
if (newUrl) {
try {
new URL(newUrl);
config.jmWebsiteUrl = newUrl;
localStorage.setItem("rt18_config", JSON.stringify(config));
} catch (e) {
jmWebsiteUrlInput.value = config.jmWebsiteUrl;
alert("请输入有效的 URL。");
}
} else {
jmWebsiteUrlInput.value = config.jmWebsiteUrl;
alert("URL 不能为空。");
}
});
}
if (layoutSelect) {
layoutSelect.value = config.layout;
layoutSelect.addEventListener("change", () => {
const newLayout = layoutSelect.value;
if (newLayout === "compact" || newLayout === "details") {
config.layout = newLayout;
localStorage.setItem("rt18_config", JSON.stringify(config));
} else {
layoutSelect.value = config.layout;
}
});
}
const renderSourceList = () => {
sourceList.innerHTML = "";
config.sources.forEach((source, index) => {
const listItem = document.createElement("li");
listItem.className = "rt18-source-list-item";
const sourceText = document.createElement("span");
sourceText.className = "rt18-source-text";
sourceText.textContent = source;
listItem.appendChild(sourceText);
const controlsContainer = document.createElement("div");
controlsContainer.className = "rt18-source-controls";
const upButton = document.createElement("div");
upButton.className = "rt18-source-button rt18-source-button-up";
upButton.innerHTML = "<span>↑</span>";
if (index === 0) {
upButton.classList.add("rt18-button-disabled");
} else {
upButton.addEventListener("click", () => {
const temp = config.sources[index];
config.sources[index] = config.sources[index - 1];
config.sources[index - 1] = temp;
saveConfig();
renderSourceList();
});
}
const downButton = document.createElement("div");
downButton.className = "rt18-source-button rt18-source-button-down";
downButton.innerHTML = "<span>↓</span>";
if (index === config.sources.length - 1) {
downButton.classList.add("rt18-button-disabled");
} else {
downButton.addEventListener("click", () => {
const temp = config.sources[index];
config.sources[index] = config.sources[index + 1];
config.sources[index + 1] = temp;
saveConfig();
renderSourceList();
});
}
const deleteButton = document.createElement("div");
deleteButton.className = "rt18-source-button rt18-source-button-delete";
deleteButton.textContent = "删除";
deleteButton.addEventListener("click", () => {
if (confirm(`确定删除源 "${source}" 吗?`)) {
config.sources.splice(index, 1);
saveConfig();
renderSourceList();
}
});
controlsContainer.appendChild(upButton);
controlsContainer.appendChild(downButton);
controlsContainer.appendChild(deleteButton);
listItem.appendChild(controlsContainer);
sourceList.appendChild(listItem);
});
};
addSourceButton.addEventListener("click", () => {
const newSource = addSourceInput.value.trim();
if (newSource && !config.sources.includes(newSource)) {
config.sources.push(newSource);
saveConfig();
config.sources = [...config.sources];
renderSourceList();
addSourceInput.value = "";
}
});
renderSourceList();
closeButton.addEventListener("click", () => {
if (dialogContainer) {
dialogContainer.style.display = "none";
dialogContainer.remove();
isConfigDialogOpen = false;
}
});
};
const APP_TOKEN_SECRET = "18comicAPP";
const APP_DATA_SECRET = "185Hcomic3PAPP7R";
const APP_VERSION = "1.7.9";
const getTokenWithTokenparam = (ts, ver = APP_VERSION, secret = APP_TOKEN_SECRET) => {
const tokenparam = `${ts},${ver}`;
const token = CryptoJS.MD5(`${ts}${secret}`).toString();
return {
token,
tokenparam
};
};
const decodeData = (data, ts, secret = APP_DATA_SECRET) => {
const dataWordArray = CryptoJS.enc.Base64.parse(data);
const token = CryptoJS.MD5(`${ts}${secret}`).toString();
const tokenWordArray = CryptoJS.enc.Utf8.parse(token);
const encrypted = CryptoJS.lib.CipherParams.create({
ciphertext: dataWordArray
});
const decrypted = CryptoJS.AES.decrypt(encrypted, tokenWordArray, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));
};
const JMFetchAlbumInfo = (jmSite, jmId, callback) => {
const timestamp = Math.floor(Date.now() / 1e3);
_GM_xmlhttpRequest({
method: "GET",
url: `https://${jmSite}/album?id=${jmId}`,
timeout: config.timeout,
headers: {
...getTokenWithTokenparam(timestamp),
"Accept-Encoding": "gzip, deflate",
"User-Agent": navigator.userAgent
},
onload: (gmResponse) => {
try {
const resp = JSON.parse(gmResponse.responseText);
const album = decodeData(resp.data, timestamp);
callback(album);
} catch (error) {
callback(null);
}
},
onerror: () => callback(null),
ontimeout: () => callback(null)
});
};
const openConfigDialog = () => {
var _a;
(_a = window.getSelection()) == null ? void 0 : _a.empty();
if (popupWindow.checkVisibility()) {
popupWindow.style.display = "none";
}
openConfigDialog$1();
};
_GM_registerMenuCommand("⚙ 打开配置菜单", openConfigDialog);
_GM_registerMenuCommand("⚠ 重置配置", resetConfig);
const setSVGWithColor = (wrapper, svgUrl, color) => {
wrapper.style.backgroundColor = color;
wrapper.style.mask = `url("${svgUrl}") no-repeat center`;
wrapper.style.webkitMask = `url("${svgUrl}") no-repeat center`;
};
const uiContainer = document.createElement("div");
uiContainer.innerHTML = uiHtml;
document.body.appendChild(uiContainer);
const popupWindow = document.getElementById("jm-popup");
const numberIcon = document.getElementById("jm-number-icon");
const numberText = document.getElementById("jm-number");
const titleText = document.getElementById("jm-title-text");
const titleLoadingText = document.getElementById("jm-title-loading");
const copyBtn = document.getElementById("jm-copy");
const copyBtnIcon = document.getElementById("jm-copy-icon");
const detailsContainer = document.getElementById("jm-details-container");
setSVGWithColor(numberIcon, loadingIcon, "black");
const populateDetails = (album) => {
detailsContainer.innerHTML = "";
const createDetailRow = (field, value, isHtml = false) => {
const isValueArray = Array.isArray(value);
if (!value || isValueArray && value.length === 0) return;
const row = document.createElement("div");
row.className = "jm-detail-row";
const labelSpan = document.createElement("span");
labelSpan.className = "jm-detail-label";
labelSpan.textContent = `${field}:`;
row.appendChild(labelSpan);
const valueSpan = document.createElement("span");
valueSpan.className = "jm-detail-value";
if (isHtml) {
valueSpan.innerHTML = isValueArray ? value.join(", ") : value;
} else {
valueSpan.textContent = isValueArray ? value.join(", ") : value;
}
if (isValueArray) {
valueSpan.classList.add("jm-tags-container");
valueSpan.innerHTML = "";
value.forEach((tag) => {
const tagSpan = document.createElement("span");
tagSpan.className = "jm-tag-item";
tagSpan.textContent = tag;
valueSpan.appendChild(tagSpan);
});
}
row.appendChild(valueSpan);
detailsContainer.appendChild(row);
};
if (config.layout === "details") {
createDetailRow("作者", album.author);
createDetailRow("标签", album.tags);
createDetailRow("系列", album.works);
createDetailRow("角色", album.actors);
createDetailRow(
"统计",
`浏览: ${album.total_views || 0} / 喜欢: ${album.likes || 0} / 评论: ${album.comment_total || 0}`
);
if (album.addtime) {
const date = new Date(parseInt(album.addtime) * 1e3);
createDetailRow("上传于", date.toLocaleString());
}
detailsContainer.style.display = "grid";
} else {
detailsContainer.style.display = "none";
}
};
const toggleLoading = async (status, albumOrMessage, link) => {
let numberIconUrl = loadingIcon;
let numberTextColor = "black";
let titleTextColor = "gray";
detailsContainer.style.display = "none";
if (status === "fail") {
numberIconUrl = failIcon;
numberTextColor = "red";
titleText.innerHTML = titleText.title = typeof albumOrMessage === "string" ? albumOrMessage : "获取信息失败";
} else if (status === "done" && albumOrMessage && typeof albumOrMessage !== "string") {
const album = albumOrMessage;
numberIconUrl = successIcon;
numberTextColor = "green";
titleTextColor = null;
titleText.innerHTML = titleText.title = album.name;
populateDetails(album);
} else if (status === "warning") {
numberIconUrl = warningIcon;
numberTextColor = "orange";
titleText.innerHTML = titleText.title = typeof albumOrMessage === "string" ? albumOrMessage : "发生错误";
} else if (status === "loading") {
titleText.innerHTML = titleText.title = "加载中...";
}
setSVGWithColor(numberIcon, numberIconUrl, numberTextColor);
numberText.style.color = numberTextColor;
const isLoading = status === "loading";
titleLoadingText.style.display = isLoading ? "inline" : "none";
titleText.style.display = !isLoading ? "inline" : "none";
titleText.style.color = titleTextColor || "";
if (link) {
titleText.href = link;
}
};
setSVGWithColor(copyBtnIcon, copyIcon, "dodgerblue");
const disableCopyBtn = (status) => {
copyBtn.disabled = status;
copyBtn.style.pointerEvents = status ? "none" : "auto";
copyBtnIcon.style.backgroundColor = status ? "gray" : "dodgerblue";
};
disableCopyBtn(true);
const copyToClipboard = () => {
navigator.clipboard.writeText(titleText.innerText);
copyBtn.style.pointerEvents = "none";
copyBtnIcon.classList.toggle("jm-copy-icon-hide");
setTimeout(() => {
copyBtnIcon.classList.toggle("jm-copy-icon-hide");
setSVGWithColor(copyBtnIcon, doneIcon, "dodgerblue");
}, 250);
setTimeout(() => {
copyBtnIcon.classList.toggle("jm-copy-icon-hide");
setTimeout(() => {
setSVGWithColor(copyBtnIcon, copyIcon, "dodgerblue");
copyBtnIcon.classList.toggle("jm-copy-icon-hide");
copyBtn.style.pointerEvents = "auto";
}, 250);
}, 1500);
};
copyBtn.addEventListener("click", copyToClipboard);
const showPopup = (event) => {
const selectedText = window.getSelection();
if (!event.target || !event.target.closest("#jm-popup")) {
popupWindow.style.display = "none";
disableCopyBtn(true);
}
if (!isConfigDialogOpen && selectedText && selectedText.toString().trim() !== "") {
const number = parseInt(selectedText.toString().replace(/\D/g, ""));
if (popupWindow.style.display !== "grid" && !Number.isNaN(number)) {
const range = selectedText.getRangeAt(0);
const activeEl = document.activeElement;
const rect = activeEl.tagName === "TEXTAREA" || activeEl.tagName === "INPUT" ? activeEl.getBoundingClientRect() : range.getBoundingClientRect();
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
let top = Math.floor(scrollTop + rect.top + rect.height);
const left = Math.floor(rect.left);
if (top === 0 && left === 0 && rect.width === 0 && rect.height === 0) return;
popupWindow.style.left = `${left}px`;
popupWindow.style.top = `${top}px`;
numberText.innerHTML = number.toString();
numberText.style.color = "";
popupWindow.style.display = "grid";
const nbnhhsh = document.getElementsByClassName(
"nbnhhsh-box nbnhhsh-box-pop"
)[0];
const originalNbnhhshTop = parseInt(nbnhhsh.style.top);
const nbnhhshAdjust = () => {
if (nbnhhsh) {
const popupHeight = popupWindow.offsetHeight;
const offset = popupHeight > 80 ? popupHeight + 10 : 80;
if (!isNaN(originalNbnhhshTop)) {
nbnhhsh.style.top = `${originalNbnhhshTop + offset}px`;
} else {
const rectNbnhhsh = nbnhhsh.getBoundingClientRect();
const scrollTopNbnhhsh = document.documentElement.scrollTop || document.body.scrollTop;
nbnhhsh.style.top = `${scrollTopNbnhhsh + rectNbnhhsh.top + offset}px`;
}
}
};
const configuredSources = config.sources;
if (!configuredSources || configuredSources.length === 0) {
toggleLoading("warning", "无可用线路,请先配置");
disableCopyBtn(true);
nbnhhshAdjust();
return;
}
let sourceIndex = 0;
toggleLoading("loading");
nbnhhshAdjust();
const tryNextSource = () => {
if (sourceIndex >= configuredSources.length) {
toggleLoading("fail", "获取信息失败或未找到车牌");
disableCopyBtn(true);
nbnhhshAdjust();
return;
}
const currentSite = configuredSources[sourceIndex];
JMFetchAlbumInfo(currentSite, number, (albumData) => {
if (!albumData || albumData.id === 0 || !albumData.name) {
sourceIndex++;
tryNextSource();
return;
}
toggleLoading("done", albumData, `${config.jmWebsiteUrl}/album/${albumData.id}`);
disableCopyBtn(false);
nbnhhshAdjust();
});
};
tryNextSource();
}
}
};
const _showPopup = (event) => {
setTimeout(() => {
showPopup(event);
}, 1);
};
document.addEventListener("mouseup", _showPopup);
document.addEventListener("keyup", _showPopup);
})(CryptoJS);