SimpleNovelReader

简单的笔趣阁类网站小说阅读器

  1. // ==UserScript==
  2. // @name SimpleNovelReader
  3. // @namespace net.myitian.js.SimpleNovelReader
  4. // @version 0.7.3
  5. // @description 简单的笔趣阁类网站小说阅读器
  6. // @source https://github.com/Myitian/SimpleNovelReader
  7. // @author Myitian
  8. // @license MIT
  9. // @match *://*.xiaoshuohu.com/*/*/*.html*
  10. // @match *://*.ibiquge.cc/*/*.html*
  11. // @match *://*.beqege.com/*/*.html*
  12. // @match *://*.beqege.cc/*/*.html*
  13. // @match *://*.biqiuge.net/*_*/*.html*
  14. // @match *://*.biquge11.cc/read/*/*.html*
  15. // @match *://*.bqgpp.com/read/*/*.html*
  16. // @match *://*.qe19.cc/read/*/*.html
  17. // @match *://*.biquge66.net/book/*/*.html*
  18. // @match *://*.52bqg.org/book_*/*.html*
  19. // @match *://*.bqg78.cc/book/*/*.html*
  20. // @match *://*.bige3.cc/book/*/*.html*
  21. // @match *://*.biqg.cc/book/*/*.html*
  22. // @match *://*.wxsc8.com/book/*/*.html*
  23. // @match *://*.5scw.com/book_*/*.html*
  24. // @match *://*.zhenhunxiaoshuo.com/*.html*
  25. // @match *://*.xyyuedu.com/writer/*/*/*.html*
  26. // @match *://*.wxzpyd.com/novel/chapter/*.html*
  27. // @match *://*.e365xs.com/*/read_*.html*
  28. // @match *://*.xbanxia.com/books/*/*.html
  29. // @match *://*.faloo.com/*.html
  30. // @match *://funs.me/text/*/*.html
  31. // @match *://*.trxs.cc/tongren/*/*.html
  32. // @match *://trxs.cc/tongren/*/*.html
  33. // @match *://*.hjwzw.com/Book/Read/*,*
  34. // @match *://www.69shuba.pro/txt/*/*
  35. // @match *://bh3.mihoyo.com/news/*
  36. // @match *://ys.mihoyo.com/main/news/detail/*
  37. // @match *://sr.mihoyo.com/news/*
  38. // @match *://zzz.mihoyo.com/news/*
  39. // @match file:///*.txt
  40. // @match file:///*.htm
  41. // @match file:///*.html
  42. // @grant GM_getValue
  43. // @grant GM_setValue
  44. // @grant GM_deleteValue
  45. // @grant GM_listValues
  46. // @grant GM_registerMenuCommand
  47. // ==/UserScript==
  48.  
  49. const PageRegex = [
  50. /.*:\/\/.*\.xiaoshuohu\.com\/.*\/.*\/.*\.html.*/,
  51. /.*:\/\/.*\.ibiquge\.cc\/.*\/.*\.html.*/,
  52. /.*:\/\/.*\.beqege\.com\/.*\/.*\.html.*/,
  53. /.*:\/\/.*\.beqege\.cc\/.*\/.*\.html.*/,
  54. /.*:\/\/.*\.biqiuge\.net\/.*_.*\/.*\.html.*/,
  55. /.*:\/\/.*\.biquge11\.cc\/read\/.*\/.*\.html.*/,
  56. /.*:\/\/.*\.bqgpp\.com\/read\/.*\/.*\.html.*/,
  57. /.*:\/\/.*\.qe19\.cc\/read\/.*\/.*\.html/,
  58. /.*:\/\/.*\.biquge66\.net\/book\/.*\/.*\.html.*/,
  59. /.*:\/\/.*\.52bqg\.org\/book_.*\/.*\.html.*/,
  60. /.*:\/\/.*\.bqg78\.cc\/book\/.*\/.*\.html.*/,
  61. /.*:\/\/.*\.bige3\.cc\/book\/.*\/.*\.html.*/,
  62. /.*:\/\/.*\.biqg\.cc\/book\/.*\/.*\.html.*/,
  63. /.*:\/\/.*\.wxsc8\.com\/book\/.*\/.*\.html.*/,
  64. /.*:\/\/.*\.5scw\.com\/book_.*\/.*\.html.*/,
  65. /.*:\/\/.*\.zhenhunxiaoshuo\.com\/.*\.html.*/,
  66. /.*:\/\/.*\.xyyuedu\.com\/writer\/.*\/.*\/.*\.html.*/,
  67. /.*:\/\/.*\.wxzpyd\.com\/novel\/chapter\/.*\.html.*/,
  68. /.*:\/\/.*\.e365xs\.com\/.*\/read_.*\.html.*/,
  69. /.*:\/\/.*\.xbanxia\.com\/books\/.*\/.*\.html/,
  70. /.*:\/\/.*\.faloo\.com\/.*\.html/,
  71. /.*:\/\/funs\.me\/text\/.*\/.*\.html/,
  72. /.*:\/\/.*\.trxs\.cc\/tongren\/.*\/.*\.html/,
  73. /.*:\/\/trxs\.cc\/tongren\/.*\/.*\.html/,
  74. /.*:\/\/.*\.hjwzw\.com\/Book\/Read\/.*,.*/,
  75. /.*:\/\/www\.69shuba\.pro\/txt\/.*\/.*/,
  76. /.*:\/\/bh3\.mihoyo\.com\/news\/.*/,
  77. /.*:\/\/ys\.mihoyo\.com\/main\/news\/detail\/.*/,
  78. /.*:\/\/sr\.mihoyo\.com\/news\/.*/,
  79. /.*:\/\/zzz\.mihoyo\.com\/news\/.*/,
  80. /file:\/\/\/.*\.txt/,
  81. /file:\/\/\/.*\.htm/,
  82. /file:\/\/\/.*\.html/
  83. ];
  84. const StandalonePageRegex = [
  85. /.*:\/\/(?:sr|zzz)\.mihoyo\.com\/.*/,
  86. /file:\/\/\/.*\.txt/,
  87. /file:\/\/\/.*\.htm/,
  88. /file:\/\/\/.*\.html/
  89. ];
  90. const DynamicPageRegex = [
  91. /.*:\/\/.*\.mihoyo\.com\/.*/,
  92. /file:\/\/\/.*\.txt/,
  93. /file:\/\/\/.*\.htm/,
  94. /file:\/\/\/.*\.html/
  95. ];
  96. const ProcessLFRegex = [
  97. /file:\/\/\/.*\.txt/
  98. ];
  99. const FontSizes = [
  100. ["xx-small", "极小"],
  101. ["x-small", "小"],
  102. ["small", "较小"],
  103. ["medium", "中"],
  104. ["large", "较大"],
  105. ["x-large", "大"],
  106. ["xx-large", "极大"]
  107. ];
  108. const SimpleNovelReader = document.createElement("div");
  109. const OriginalUrl = window.location.origin + window.location.pathname + window.location.search;
  110. const ActivePageRegex = PageRegex.find(x => x.test(window.location.href));
  111. const IsDynamicPage = DynamicPageRegex.findIndex(x => x.test(window.location.href)) != -1;
  112. const IsStandalonePage = StandalonePageRegex.findIndex(x => x.test(window.location.href)) != -1;
  113. const IsProcessingLF = ProcessLFRegex.findIndex(x => x.test(window.location.href)) != -1;
  114.  
  115. /**
  116. * @param {Document} doc
  117. */
  118. function extractPageData(doc) {
  119. const pageTitle = doc.title.trim();
  120. /** @type {?HTMLElement} */
  121. const title = (
  122. doc.querySelector("#arcxs_title>h1,.bookname>h1,.pt-read-cont>.pt-read-title>h1,.pt-read>div") ??
  123. doc.querySelector(".article-title,.bookname,#nr_title,.title,.zhong,.cont-title,.article__title,.news-detail__title,tr>td[background='/image/bgheader.gif']") ??
  124. doc.querySelector("h1")
  125. );
  126. /** @type {string} */
  127. const content = (
  128. doc.querySelector("#onearcxsbd,#cont-body,.pt-read-text,.article__bd,#nr1") ??
  129. doc.querySelector(".article-content,#content,#chaptercontent,#nr,.article,.pt-read-cont,.main-wrap,.article__content,.news-detail__content,.read_chapterDetail,.txtnav,.nodeContent,#AllySite+div,#ChSize") ??
  130. doc.querySelector("article:not(#myt-snr-content)") ??
  131. doc.querySelector("html>body>pre") // for txt in Firefox
  132. )?.innerHTML.replaceAll(" ", "");
  133. /** @type {?HTMLAnchorElement} */
  134. const prev = (
  135. doc.querySelector("[rel=prev],#prev_url,#pb_prev,#link-preview,.pt-prechapter>a") ??
  136. doc.querySelector(".bottem1>a:nth-child(1),.col-md-6.text-center>a[href]:nth-child(1),b>a.prevPage:nth-child(1),td.prev>a,article>ul.pages>li:nth-child(2)>a,.page_chapter>ul>li:nth-child(1)>a,.pt-prechapter,.buttombar__prev:not(.buttombar__prev--disabled),.article__ft>a[href]:nth-child(1),.pageNav>a:nth-child(2),.page1>a:nth-child(1),.bl_pre,center>a.pages:nth-of-type(1)") ??
  137. doc.querySelector("body>table:has(#AllySite)+div>a:nth-child(1)")
  138. );
  139. /** @type {?HTMLAnchorElement} */
  140. const info = (
  141. doc.querySelector("[rel='category tag'],#info_url,#pb_mulu,#link-indexz,.pt-catalogue>a") ??
  142. doc.querySelector(".bottem1>a:nth-child(2),.col-md-6.text-center>a[href]:nth-child(2),a.returnIndex,td.mulu>a,article>ul.pages>li:nth-child(4)>a,.page_chapter>ul>li:nth-child(2)>a,.pt-prechapter+a,.news-detail .btn-back,.topbar__back,.nuxt-link-active,.pageNav>a:nth-child(5),.page1>a:nth-child(3),#page_muLu,tr>td>li>a:nth-of-type(3)") ??
  143. doc.querySelector("body>table:has(#AllySite)+div>a:nth-child(2)")
  144. );
  145. /** @type {?HTMLAnchorElement} */
  146. const next = (
  147. doc.querySelector("[rel=next],#next_url,#pb_next,#link-next,.pt-nextchapter>a") ??
  148. doc.querySelector(".bottem1>a:nth-child(3),.col-md-6.text-center>a[href]:nth-child(3),b>a.prevPage:nth-child(2),td.next>a,article>ul.pages>li:nth-child(3)>a,.page_chapter>ul>li:nth-child(3)>a,.pt-nextchapter,.buttombar__next:not(.buttombar__next--disabled),.article__ft>a[href]:nth-child(2),.pageNav>a:nth-child(3),.page1>a:nth-child(4),.bl_next,center>a.pages:nth-of-type(2)") ??
  149. doc.querySelector("body>table:has(#AllySite)+div>a:nth-child(3)")
  150. );
  151. const prevText = prev?.href ?? "";
  152. const nextText = next?.href ?? "";
  153. return {
  154. pageTitle: pageTitle,
  155. title: title?.innerText?.trim() ?? pageTitle,
  156. content: content?.trim() ?? "",
  157. prev: ActivePageRegex.test(prevText) ? prevText?.trim() : "",
  158. info: info?.href.trim() ?? "",
  159. next: ActivePageRegex.test(nextText) ? nextText?.trim() : ""
  160. };
  161. }
  162.  
  163. /**
  164. * @param {?{pageTitle:string,title:string,content:string,prev:string,info:string,next:string}} data
  165. */
  166. function loadPageData(data) {
  167. if (data) {
  168. document.title = data.pageTitle;
  169. /** @type {HTMLHeadingElement} */
  170. const title = SimpleNovelReader.querySelector("#myt-snr-title");
  171. title.innerText = data.title;
  172. if (IsProcessingLF) {
  173. const lines = data.content.split("\n");
  174. SimpleNovelReader.querySelector("#myt-snr-content").innerHTML = "<p>" + lines.join("</p><p>") + "</p>";
  175. } else {
  176. SimpleNovelReader.querySelector("#myt-snr-content").innerHTML = data.content;
  177. }
  178. /** @type {HTMLButtonElement} */
  179. const prev = SimpleNovelReader.querySelector("#myt-snr-prev");
  180. prev.dataset.href = data.prev;
  181. prev.disabled = !data.prev;
  182. /** @type {HTMLButtonElement} */
  183. const info = SimpleNovelReader.querySelector("#myt-snr-info");
  184. info.dataset.href = data.info;
  185. /** @type {HTMLButtonElement} */
  186. const next = SimpleNovelReader.querySelector("#myt-snr-next");
  187. next.dataset.href = data.next;
  188. next.disabled = !data.next;
  189. } else {
  190. SimpleNovelReader.querySelector("#myt-snr-title").innerHTML = "";
  191. SimpleNovelReader.querySelector("#myt-snr-content").innerHTML = "";
  192. /** @type {HTMLButtonElement} */
  193. const prev = SimpleNovelReader.querySelector("#myt-snr-prev");
  194. prev.dataset.href = "";
  195. prev.disabled = true;
  196. /** @type {HTMLButtonElement} */
  197. const info = SimpleNovelReader.querySelector("#myt-snr-info");
  198. info.dataset.href = "";
  199. /** @type {HTMLButtonElement} */
  200. const next = SimpleNovelReader.querySelector("#myt-snr-next");
  201. next.dataset.href = "";
  202. next.disabled = true;
  203. }
  204. }
  205.  
  206. /**
  207. * @param {HTMLMediaElement} media
  208. */
  209. function pause(media) {
  210. media.autoplay = false;
  211. media.pause();
  212. }
  213.  
  214. function loadPreload() {
  215. /** @type {HTMLIFrameElement} */
  216. const preloadFrame = SimpleNovelReader.querySelector("#myt-snr-preload");
  217. const doc = preloadFrame.contentWindow.document;
  218. doc.querySelectorAll("audio").forEach(pause);
  219. doc.querySelectorAll("video").forEach(pause);
  220. loadPageData(extractPageData(doc));
  221. SimpleNovelReader.querySelector("#myt-snr-content").scrollTop = 0;
  222. }
  223.  
  224. /**
  225. * @param {URL} url
  226. */
  227. function loadUrl(url) {
  228. if (IsStandalonePage) {
  229. loadPageData(extractPageData(document));
  230. } else if (IsDynamicPage) {
  231. /** @type {HTMLIFrameElement} */
  232. const preloadFrame = SimpleNovelReader.querySelector("#myt-snr-preload");
  233. url.hash = "disable-simple-novel-reader";
  234. if (preloadFrame.contentWindow && preloadFrame.src == url.href) {
  235. loadPageData(extractPageData(preloadFrame.contentWindow.document));
  236. SimpleNovelReader.querySelector("#myt-snr-content").scrollTop = 0;
  237. } else {
  238. loadPageData(null);
  239. preloadFrame.src = url.href;
  240. }
  241. } else {
  242. get(url).then(
  243. xhr => {
  244. loadPageData(extractPageData(xhr.response));
  245. SimpleNovelReader.querySelector("#myt-snr-content").scrollTop = 0;
  246. }
  247. );
  248. }
  249. }
  250.  
  251. /**
  252. * GET 请求
  253. * @param {string | URL} url 请求地址
  254. * @param {XMLHttpRequestResponseType} responseType 响应类型
  255. * @param {number} timeout 超时
  256. * @returns {Promise<XMLHttpRequest>} Promise 对象,其 resolve 和 reject 均传入请求所用的 XMLHttpRequest 对象
  257. */
  258. function get(url, responseType = "document", timeout = 0) {
  259. return new Promise(function (resolve, reject) {
  260. const xhr = new XMLHttpRequest();
  261. xhr.open("GET", url, true);
  262. xhr.timeout = timeout;
  263. xhr.withCredentials = true;
  264. xhr.responseType = responseType;
  265. xhr.send();
  266. xhr.ontimeout = () => reject(timeout);
  267. xhr.onload = () => {
  268. if (xhr.status < 300) {
  269. resolve(xhr);
  270. } else {
  271. reject(xhr);
  272. }
  273. };
  274. });
  275. }
  276.  
  277. function detectHashChange() {
  278. if (window.location.hash == "#simple-novel-reader") {
  279. SimpleNovelReader.style.top = "0";
  280. } else {
  281. SimpleNovelReader.style.top = "200%";
  282. }
  283. }
  284.  
  285. function toggle() {
  286. if (window.location.hash == "#simple-novel-reader") {
  287. hide();
  288. } else {
  289. show();
  290. }
  291. }
  292.  
  293. let prevUrl = "";
  294.  
  295. /**
  296. *
  297. * @param {?string} url
  298. */
  299. function show(url = undefined) {
  300. window.location.hash = "simple-novel-reader";
  301. document.documentElement.style.overflow = "hidden";
  302. document.body.style.overflow = "hidden";
  303. if (url || prevUrl != window.location.href) {
  304. const newUrl = new URL(url ?? window.location.href);
  305. newUrl.hash = "simple-novel-reader";
  306. history.pushState(null, "", newUrl.toString());
  307. SimpleNovelReader.scrollTop = 0;
  308. loadUrl(newUrl);
  309. }
  310. }
  311.  
  312. function hide() {
  313. const newUrl = window.location.origin + window.location.pathname + window.location.search;
  314. if (newUrl != OriginalUrl) {
  315. window.location.href = newUrl;
  316. } else {
  317. window.location.hash = "";
  318. document.documentElement.style.overflow = "";
  319. document.body.style.overflow = "";
  320. }
  321. prevUrl = newUrl;
  322. }
  323.  
  324. function toggleSettingDisplay() {
  325. /** @type {HTMLDivElement} */
  326. const settings = SimpleNovelReader.querySelector("#myt-snr-setting-items");
  327. /** @type {HTMLButtonElement} */
  328. const settingBtn = SimpleNovelReader.querySelector("#myt-snr-setting");
  329. if (settings.toggleAttribute("hidden")) {
  330. settingBtn.innerText = "展开样式设置";
  331. } else {
  332. settingBtn.innerText = "收起样式设置";
  333. }
  334. }
  335.  
  336. /**
  337. * @param {Event} event
  338. */
  339. function switchChapter(event) {
  340. /** @type {HTMLButtonElement} */
  341. // @ts-ignore
  342. const btn = event.target;
  343. if (btn.dataset.href) {
  344. show(btn.dataset.href);
  345. }
  346. }
  347.  
  348. function viewInfo() {
  349. /** @type {HTMLButtonElement} */
  350. const e = SimpleNovelReader.querySelector("#myt-snr-info");
  351. if (e?.dataset.href) {
  352. window.location.href = e.dataset.href;
  353. }
  354. }
  355.  
  356. function updateCustomFontButtonStyle() {
  357. /** @type {HTMLLabelElement} */
  358. const label = SimpleNovelReader.querySelector("[for=myt-snr-setting-font-family-custom]");
  359. label.style.fontFamily = GM_getValue("config.font-family.custom", "sans-serif");
  360. /** @type {HTMLInputElement} */
  361. const input = SimpleNovelReader.querySelector("#myt-snr-setting-font-family-custom-name");
  362. input.style.fontFamily = GM_getValue("config.font-family.custom", "sans-serif");
  363. }
  364.  
  365. function updateContentStyle() {
  366. const fontSizeStr = FontSizes[GM_getValue("config.font-size", 3)];
  367. const lineHeightStr = GM_getValue("config.line-height", 1.5).toFixed(1);
  368. const maxWidthStr = GM_getValue("config.max-width", 40) + "em";
  369. /** @type {HTMLSpanElement} */
  370. const fontSizeE = SimpleNovelReader.querySelector("#myt-snr-setting-font-size-value");
  371. fontSizeE.innerText = fontSizeStr[1];
  372. /** @type {HTMLSpanElement} */
  373. const lineHeightE = SimpleNovelReader.querySelector("#myt-snr-setting-line-height-value");
  374. lineHeightE.innerText = lineHeightStr;
  375. /** @type {HTMLSpanElement} */
  376. const maxWidthE = SimpleNovelReader.querySelector("#myt-snr-setting-max-width-value");
  377. maxWidthE.innerText = maxWidthStr;
  378. SimpleNovelReader.querySelector("#myt-snr-content-style").innerHTML = `
  379. #myt-snr-root * {
  380. font-family: ${GM_getValue("config.font-family.name", "sans-serif")};
  381. font-size: ${fontSizeStr[0]};
  382. line-height: ${lineHeightStr};
  383. }
  384.  
  385. #myt-snr-root {
  386. --x-max-width: ${maxWidthStr};
  387. }
  388. `;
  389. }
  390.  
  391. function updateCustomStyle() {
  392. if (GM_getValue("config.custom-style.enabled", false)) {
  393. SimpleNovelReader.querySelector("#myt-snr-custom-style").innerHTML = GM_getValue("config.custom-style", "");
  394. } else {
  395. SimpleNovelReader.querySelector("#myt-snr-custom-style").innerHTML = "";
  396. }
  397. }
  398.  
  399. /**
  400. * @param {string} name
  401. * @param {string} value
  402. */
  403. function updateRadioButtonGroup(name, value) {
  404. /** @type {HTMLInputElement} */
  405. const radio = SimpleNovelReader.querySelector(`input[name=${name}][data-value=${CSS.escape(value)}]`);
  406. radio.checked = true;
  407. radio.dispatchEvent(new Event('change'));
  408. }
  409.  
  410. /**
  411. * @param {Event} event
  412. */
  413. function updateRadioButton(event) {
  414. /** @type {HTMLInputElement} */
  415. // @ts-ignore
  416. const radio = event.target;
  417. SimpleNovelReader.querySelector(`label[for=${radio.id}]`).toggleAttribute("checked", true);
  418. for (const r of SimpleNovelReader.querySelectorAll(`input[name=${radio.name}]:not([id=${radio.id}])`)) {
  419. SimpleNovelReader.querySelector(`label[for=${r.id}]`).toggleAttribute("checked", false);
  420. }
  421. }
  422.  
  423. /**
  424. * @param {Event} event
  425. */
  426. function updateFontFamilyByRadio(event) {
  427. /** @type {HTMLInputElement} */
  428. // @ts-ignore
  429. const radio = event.target;
  430. /** @type {HTMLInputElement} */
  431. const customE = SimpleNovelReader.querySelector("#myt-snr-setting-font-family-custom-name");
  432. const custom = customE.value;
  433. GM_setValue("config.font-family.custom", custom);
  434. GM_setValue("config.font-family", radio.dataset.value);
  435. if (radio.dataset.value == "custom") {
  436. GM_setValue("config.font-family.name", custom);
  437. } else {
  438. GM_setValue("config.font-family.name", radio.dataset.value);
  439. }
  440. updateCustomFontButtonStyle();
  441. updateContentStyle();
  442. }
  443.  
  444. /**
  445. * @param {Event} event
  446. */
  447. function updateFontFamilyByInput(event) {
  448. /** @type {HTMLInputElement} */
  449. // @ts-ignore
  450. const input = event.target;
  451. // @ts-ignore
  452. if (GM_getValue("config.font-family", "sans-serif") == "custom") {
  453. GM_setValue("config.font-family.name", input.value);
  454. updateContentStyle();
  455. }
  456. GM_setValue("config.font-family.custom", input.value);
  457. updateCustomFontButtonStyle();
  458. }
  459.  
  460. /**
  461. * @param {number} diff
  462. */
  463. function updateFontSize(diff) {
  464. const min = 0;
  465. const max = FontSizes.length - 1;
  466. const oldVal = GM_getValue("config.font-size", 3) + diff;
  467. let newVal = oldVal;
  468. if (oldVal <= min) {
  469. newVal = min;
  470. SimpleNovelReader.querySelector("#myt-snr-setting-font-size-minus").toggleAttribute("disabled", true);
  471. } else {
  472. SimpleNovelReader.querySelector("#myt-snr-setting-font-size-minus").toggleAttribute("disabled", false);
  473. }
  474. if (oldVal >= max) {
  475. newVal = max;
  476. SimpleNovelReader.querySelector("#myt-snr-setting-font-size-plus").toggleAttribute("disabled", true);
  477. } else {
  478. SimpleNovelReader.querySelector("#myt-snr-setting-font-size-plus").toggleAttribute("disabled", false);
  479. }
  480. GM_setValue("config.font-size", newVal);
  481. updateContentStyle();
  482. }
  483.  
  484. /**
  485. * @param {number} diff
  486. */
  487. function updateLineSpace(diff) {
  488. const min = 0.5;
  489. const max = 5;
  490. const oldVal = GM_getValue("config.line-height", 1.5) + diff;
  491. let newVal = oldVal;
  492. if (oldVal <= min) {
  493. newVal = min;
  494. SimpleNovelReader.querySelector("#myt-snr-setting-line-height-minus").toggleAttribute("disabled", true);
  495. } else {
  496. SimpleNovelReader.querySelector("#myt-snr-setting-line-height-minus").toggleAttribute("disabled", false);
  497. }
  498. if (oldVal >= max) {
  499. newVal = max;
  500. SimpleNovelReader.querySelector("#myt-snr-setting-line-height-plus").toggleAttribute("disabled", true);
  501. } else {
  502. SimpleNovelReader.querySelector("#myt-snr-setting-line-height-plus").toggleAttribute("disabled", false);
  503. }
  504. GM_setValue("config.line-height", parseFloat(newVal.toFixed(1)));
  505. updateContentStyle();
  506. }
  507.  
  508. /**
  509. * @param {number} diff
  510. */
  511. function updateMaxWidth(diff) {
  512. const min = 5;
  513. const max = 10000;
  514. const oldVal = GM_getValue("config.max-width", 40) + diff;
  515. let newVal = oldVal;
  516. if (oldVal <= min) {
  517. newVal = min;
  518. SimpleNovelReader.querySelector("#myt-snr-setting-max-width-minus").toggleAttribute("disabled", true);
  519. } else {
  520. SimpleNovelReader.querySelector("#myt-snr-setting-max-width-minus").toggleAttribute("disabled", false);
  521. }
  522. if (oldVal >= max) {
  523. newVal = max;
  524. SimpleNovelReader.querySelector("#myt-snr-setting-max-width-plus").toggleAttribute("disabled", true);
  525. } else {
  526. SimpleNovelReader.querySelector("#myt-snr-setting-max-width-plus").toggleAttribute("disabled", false);
  527. }
  528. GM_setValue("config.max-width", newVal);
  529. updateContentStyle();
  530. }
  531.  
  532. /**
  533. * @param {Event} event
  534. */
  535. function updateColorScheme(event) {
  536. /** @type {HTMLInputElement} */
  537. // @ts-ignore
  538. const radio = event.target;
  539. GM_setValue("config.color-scheme", radio.dataset.value);
  540. SimpleNovelReader.dataset.colorScheme = radio.dataset.value;
  541. }
  542.  
  543. /**
  544. * @param {Event} event
  545. */
  546. function importCustomStyle(event) {
  547. /** @type {HTMLInputElement} */
  548. // @ts-ignore
  549. const input = event.target;
  550. input.files[0].text().then(
  551. s => {
  552. /** @type {HTMLInputElement} */
  553. const input = SimpleNovelReader.querySelector("#myt-snr-setting-custom-style");
  554. input.value = s;
  555. GM_setValue("config.custom-style", s);
  556. }
  557. );
  558. }
  559.  
  560. function applyCustomStyle() {
  561. /** @type {HTMLInputElement} */
  562. const input = SimpleNovelReader.querySelector("#myt-snr-setting-custom-style");
  563. GM_setValue("config.custom-style", input.value);
  564. GM_setValue("config.custom-style.enabled", true);
  565. updateCustomStyle();
  566. }
  567.  
  568. function disableCustomStyle() {
  569. GM_setValue("config.custom-style.enabled", false);
  570. updateCustomStyle();
  571. }
  572.  
  573. function deleteData() {
  574. if (confirm("确认删除储存的样式数据?")) {
  575. GM_deleteValue("config.font-family");
  576. GM_deleteValue("config.font-family.name");
  577. GM_deleteValue("config.font-family.custom");
  578. GM_deleteValue("config.font-size");
  579. GM_deleteValue("config.line-height");
  580. GM_deleteValue("config.max-width");
  581. GM_deleteValue("config.color-scheme");
  582. GM_deleteValue("config.custom-style");
  583. GM_deleteValue("config.custom-style.enabled");
  584. }
  585. }
  586.  
  587. function main() {
  588. SimpleNovelReader.id = "myt-snr-root";
  589. SimpleNovelReader.className = "x-scroll-container";
  590. SimpleNovelReader.innerHTML = `
  591. <div id="myt-snr-main">
  592. <header id="myt-snr-header" class="x-myt-content-style">
  593. <h1 id="myt-snr-title"></h1>
  594. <div id="myt-snr-tools"><!--
  595. --><button id="myt-snr-exit" class="x-myt-button">退出阅读模式</button><!--
  596. --><button id="myt-snr-settings" class="x-myt-button">展开样式设置</button><!--
  597. --></div>
  598. <div id="myt-snr-setting-items" class="x-scroll-container" hidden>
  599. <div id="myt-snr-setting-font-family" class="x-myt-list-item">
  600. <div id="myt-snr-close-settings">
  601. <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 24 24">
  602. <path d="M6 6l12 12m0-12L6 18" />
  603. </svg>
  604. </div>
  605. <h6 class="x-myt-content-style">字体</h6>
  606. <div><!--
  607. --><span class="x-nobr"><!--
  608. --><input id="myt-snr-setting-font-family-sans-serif"
  609. class="x-myt-hidden-radio x-myt-hidden-input x-myt-snr-setting-font-family" type="radio"
  610. data-value="sans-serif" name="font-family"><!--
  611. --><label for="myt-snr-setting-font-family-sans-serif"
  612. class="x-myt-hidden-radio-button x-myt-button" style="font-family: sans-serif;">无衬线体</label><!--
  613. --><input id="myt-snr-setting-font-family-serif"
  614. class="x-myt-hidden-radio x-myt-hidden-input x-myt-snr-setting-font-family" type="radio"
  615. data-value="serif" name="font-family"><!--
  616. --><label for="myt-snr-setting-font-family-serif" class="x-myt-hidden-radio-button x-myt-button"
  617. style="font-family: serif;">衬线体</label><!--
  618. --></span><!--
  619. --><wbr><!--
  620. --><input id="myt-snr-setting-font-family-custom"
  621. class="x-myt-hidden-radio x-myt-hidden-input x-myt-snr-setting-font-family" type="radio"
  622. data-value="custom" name="font-family"><!--
  623. --><wbr><!--
  624. --><label for="myt-snr-setting-font-family-custom"
  625. class="x-myt-hidden-radio-button x-myt-button">自定义</label><!--
  626. --><input id="myt-snr-setting-font-family-custom-name" type="text"><!--
  627. --></div>
  628. </div>
  629. <div class="x-myt-list-item">
  630. <div class="x-setting-short-item">
  631. <h6 class="x-myt-content-style">字号</h6>
  632. <div><!--
  633. --><button id="myt-snr-setting-font-size-minus" class="x-myt-button x-minus" title="减少"><!--
  634. --><svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"
  635. viewBox="0 0 24 24">
  636. <path d="M6 12h12" />
  637. </svg><!--
  638. --></button><!--
  639. --><span id="myt-snr-setting-font-size-value" class="x-middle x-myt-content-style">中</span><!--
  640. --><button id="myt-snr-setting-font-size-plus" class="x-myt-button x-plus" title="增加"><!--
  641. --><svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"
  642. viewBox="0 0 24 24">
  643. <path d="M6 12h12M12 6v12" />
  644. </svg><!--
  645. --></button><!--
  646. --></div>
  647. </div>
  648. <div class="x-setting-short-item">
  649. <h6 class="x-myt-default-color x-myt-content-style">行间距</h6>
  650. <div><!--
  651. --><button id="myt-snr-setting-line-height-minus" class="x-myt-button x-minus" title="减少"><!--
  652. --><svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"
  653. viewBox="0 0 24 24">
  654. <path d="M6 12h12" />
  655. </svg><!--
  656. --></button><!--
  657. --><span id="myt-snr-setting-line-height-value" class="x-middle x-myt-content-style">1.5</span><!--
  658. --><button id="myt-snr-setting-line-height-plus" class="x-myt-button x-plus" title="增加"><!--
  659. --><svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"
  660. viewBox="0 0 24 24">
  661. <path d="M6 12h12M12 6v12" />
  662. </svg><!--
  663. --></button><!--
  664. --></div>
  665. </div>
  666. <div class="x-setting-short-item">
  667. <h6 class="x-myt-content-style">最大内容宽度</h6>
  668. <div><!--
  669. --><button id="myt-snr-setting-max-width-minus" class="x-myt-button x-minus" title="减少"><!--
  670. --><svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"
  671. viewBox="0 0 24 24">
  672. <path d="M6 12h12" />
  673. </svg><!--
  674. --></button><!--
  675. --><span id="myt-snr-setting-max-width-value" class="x-middle x-myt-content-style">40em</span><!--
  676. --><button id="myt-snr-setting-max-width-plus" class="x-myt-button x-plus" title="增加"><!--
  677. --><svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"
  678. viewBox="0 0 24 24">
  679. <path d="M6 12h12M12 6v12" />
  680. </svg><!--
  681. --></button><!--
  682. --></div>
  683. </div>
  684. </div>
  685. <div id="myt-snr-setting-color-scheme" class="x-myt-list-item">
  686. <div><!--
  687. --><span class="x-nobr"><!--
  688. --><input id="myt-snr-setting-color-scheme-light"
  689. class="x-myt-hidden-radio x-myt-hidden-input x-myt-setting-color-scheme" type="radio"
  690. data-value="light" name="color-scheme"><!--
  691. --><label for="myt-snr-setting-color-scheme-light" data-color-scheme="light"
  692. class="x-myt-hidden-radio-button x-myt-button">浅色</label><!--
  693. --><input id="myt-snr-setting-color-scheme-dark"
  694. class="x-myt-hidden-radio x-myt-hidden-input x-myt-setting-color-scheme" type="radio"
  695. data-value="dark" name="color-scheme"><!--
  696. --><label for="myt-snr-setting-color-scheme-dark" data-color-scheme="dark"
  697. class="x-myt-hidden-radio-button x-myt-button">深色</label><!--
  698. --></span><!--
  699. --><wbr><!--
  700. --><span class="x-nobr"><!--
  701. --><input id="myt-snr-setting-color-scheme-sepia"
  702. class="x-myt-hidden-radio x-myt-hidden-input x-myt-setting-color-scheme" type="radio"
  703. data-value="sepia" name="color-scheme"><!--
  704. --><label for="myt-snr-setting-color-scheme-sepia" data-color-scheme="sepia"
  705. class="x-myt-hidden-radio-button x-myt-button">纸墨</label><!--
  706. --><input id="myt-snr-setting-color-scheme-ex-dark"
  707. class="x-myt-hidden-radio x-myt-hidden-input x-myt-setting-color-scheme" type="radio"
  708. data-value="ex-dark" name="color-scheme"><!--
  709. --><label for="myt-snr-setting-color-scheme-ex-dark" data-color-scheme="ex-dark"
  710. class="x-myt-hidden-radio-button x-myt-button">极黑</label><!--
  711. --></span><!--
  712. --><wbr><!--
  713. --><span class="x-nobr"><!--
  714. --><input id="myt-snr-setting-color-scheme-sepia2"
  715. class="x-myt-hidden-radio x-myt-hidden-input x-myt-setting-color-scheme" type="radio"
  716. data-value="sepia2" name="color-scheme"><!--
  717. --><label for="myt-snr-setting-color-scheme-sepia2" data-color-scheme="sepia2"
  718. class="x-myt-hidden-radio-button x-myt-button">纸墨2</label><!--
  719. --><input id="myt-snr-setting-color-scheme-auto"
  720. class="x-myt-hidden-radio x-myt-hidden-input x-myt-setting-color-scheme" type="radio"
  721. data-value="auto" name="color-scheme"><!--
  722. --><label for="myt-snr-setting-color-scheme-auto" class="x-myt-hidden-radio-button x-myt-button"
  723. data-color-scheme="auto">自动</label><!--
  724. --></span><!--
  725. --></div>
  726. </div>
  727. <div class="x-myt-list-item">
  728. <div><!--
  729. --><label for="myt-snr-setting-custom-style">自定义样式</label><!--
  730. --><br><!--
  731. --><input id="myt-snr-setting-custom-style" type="text"><!--
  732. --></div>
  733. <div><!--
  734. --><input id="myt-snr-setting-custom-style-import" class="x-myt-hidden-input" type="file"
  735. accept="text/css"><!--
  736. --><label for="myt-snr-setting-custom-style-import" class="x-myt-button">导入</label><!--
  737. --><wbr><!--
  738. --><button id="myt-snr-setting-custom-style-apply" class="x-myt-button">应用</button><!--
  739. --><wbr><!--
  740. --><button id="myt-snr-setting-custom-style-disable" class="x-myt-button">停用</button><!--
  741. --></div>
  742. </div>
  743. </div>
  744. </header>
  745. <nav id="myt-snr-nav" class="x-myt-content-style"><!--
  746. --><button id="myt-snr-prev" class="x-myt-button x-left">上一章</button><!--
  747. --><button id="myt-snr-info" class="x-myt-button x-middle"><span class="x-nobr">章节</span><span
  748. class="x-nobr">列表</span></button><!--
  749. --><button id="myt-snr-next" class="x-myt-button x-right">下一章</button><!--
  750. --></nav><!--
  751. --><article id="myt-snr-content" class="x-myt-content-style"><!--
  752. --></article><!--
  753. --><footer id="myt-snr-footer" class="x-myt-content-style">
  754. <iframe id="myt-snr-preload"></iframe>
  755. </footer>
  756. </div>
  757. <style>
  758. #myt-snr-root {
  759. box-sizing: border-box;
  760. position: fixed;
  761. width: 100%;
  762. height: 100%;
  763. top: 200%;
  764. left: 0;
  765. z-index: 2001;
  766. background: var(--x-snr-background-level-0);
  767. color: var(--x-snr-foreground-level-0);
  768. font-family: sans-serif;
  769. font-size: medium;
  770. line-height: 1.5;
  771. --x-max-width: 40em;
  772. }
  773.  
  774. #myt-snr-root * {
  775. transition: all 0.2s ease;
  776. }
  777.  
  778. .x-myt-content-style,
  779. #myt-snr-root div {
  780. background: inherit;
  781. color: inherit;
  782. }
  783.  
  784. #myt-snr-root *::selection {
  785. background: var(--x-snr-background-selected-text);
  786. color: var(--x-snr-foreground-selected-text);
  787. }
  788.  
  789. #myt-snr-root a {
  790. background: var(--x-snr-background-link);
  791. color: var(--x-snr-foreground-link);
  792. text-decoration: underline var(--x-snr-foreground-level-0);
  793. }
  794.  
  795. #myt-snr-root a:visited {
  796. color: var(--x-snr-foreground-visited-link);
  797. }
  798.  
  799. #myt-snr-root a::selection {
  800. background: var(--x-snr-background-selected-link);
  801. color: var(--x-snr-foreground-selected-link);
  802. }
  803.  
  804. #myt-snr-root input[type=text] {
  805. background: var(--x-snr-background-level-1);
  806. border: 2px solid var(--x-snr-border);
  807. color: var(--x-snr-foreground-level-1);
  808. padding: revert;
  809. margin: .2em;
  810. }
  811.  
  812. #myt-snr-root label {
  813. display: inline-block;
  814. max-width: unset;
  815. margin-bottom: 6px;
  816. font-weight: revert;
  817. }
  818.  
  819. .x-myt-button {
  820. display: unset;
  821. border: none;
  822. background: transparent no-repeat center center;
  823. padding: .5em 1em;
  824. border-radius: .3em;
  825. margin: .5em 1em;
  826. cursor: pointer;
  827. background: var(--x-snr-background-button);
  828. color: var(--x-snr-foreground-button);
  829. fill: var(--x-snr-foreground-button);
  830. -webkit-touch-callout: none;
  831. -webkit-user-select: none;
  832. -khtml-user-select: none;
  833. -moz-user-select: none;
  834. -ms-user-select: none;
  835. user-select: none;
  836. }
  837.  
  838. #myt-snr-content * {
  839. color: inherit;
  840. font-family: inherit;
  841. font-size: inherit;
  842. line-height: inherit;
  843. }
  844.  
  845. .x-minus,
  846. .x-plus {
  847. width: 2em;
  848. height: 2em;
  849. padding: 0;
  850. }
  851.  
  852. .x-myt-button:enabled:hover,
  853. label.x-myt-button:hover {
  854. background: var(--x-snr-background-button-hover);
  855. color: var(--x-snr-foreground-button-hover);
  856. fill: var(--x-snr-foreground-button-hover);
  857. }
  858.  
  859. .x-myt-button:enabled:active,
  860. label.x-myt-button:active {
  861. background: var(--x-snr-background-button-active);
  862. color: var(--x-snr-foreground-button-active);
  863. fill: var(--x-snr-foreground-button-active);
  864. }
  865.  
  866. .x-middle {
  867. margin: auto;
  868. }
  869.  
  870. .x-scroll-container {
  871. overflow-x: clip;
  872. overflow-y: auto;
  873. }
  874.  
  875. #myt-snr-header {
  876. position: unset;
  877. text-align: center;
  878. max-width: var(--x-max-width);
  879. margin: auto;
  880. padding: 1em;
  881. height: unset;
  882. background: unset;
  883. line-height: revert;
  884. border-bottom: unset;
  885. }
  886.  
  887. #myt-snr-header .x-myt-button {
  888. margin: .2em;
  889. }
  890.  
  891. #myt-snr-tools {
  892. margin-top: 1em;
  893. }
  894.  
  895. #myt-snr-root #myt-snr-setting-items {
  896. position: fixed;
  897. background: var(--x-snr-background-level-0);
  898. border: 1px solid var(--x-snr-border);
  899. left: 0;
  900. top: 0;
  901. z-index: 2333;
  902. max-height: 100%;
  903. box-sizing: border-box;
  904. padding: 0;
  905. margin: 0;
  906. }
  907.  
  908. #myt-snr-setting-font-family-custom-name {
  909. width: 8em;
  910. }
  911.  
  912. #myt-snr-setting-custom-style {
  913. width: 100%;
  914. box-sizing: border-box;
  915. margin: .2em 0;
  916. }
  917.  
  918. #myt-snr-setting-items .x-myt-list-item {
  919. margin: .5em;
  920. }
  921.  
  922. #myt-snr-setting-font-family .x-myt-button {
  923. width: 5em;
  924. padding: .5em .1em;
  925. }
  926.  
  927. #myt-snr-setting-color-scheme .x-myt-button {
  928. width: 4em;
  929. padding: .5em .1em;
  930. }
  931.  
  932. .x-setting-short-item {
  933. margin: .5em;
  934. display: inline-block;
  935. width: 9em;
  936. }
  937.  
  938. .x-setting-short-item>div {
  939. display: flex;
  940. }
  941.  
  942. .x-myt-hidden-radio-button {
  943. display: inline-block;
  944. position: relative;
  945. background: var(--x-snr-background-level-1);
  946. color: var(--x-snr-foreground-level-1);
  947. box-sizing: border-box;
  948. border-radius: 2px;
  949. border: 2px solid var(--x-snr-background-level-1);
  950. font-weight: revert;
  951. max-width: revert;
  952. }
  953.  
  954. #myt-snr-close-settings {
  955. position: absolute;
  956. width: 1.5em;
  957. height: 1.5em;
  958. right: 0;
  959. top: 0;
  960. }
  961.  
  962. #myt-snr-close-settings:hover {
  963. rotate: 90deg;
  964. }
  965.  
  966. .x-myt-hidden-radio-button[checked] {
  967. border-color: var(--x-snr-selected-border);
  968. }
  969.  
  970. .x-myt-hidden-radio-button:hover::after {
  971. content: "";
  972. display: block;
  973. border-bottom: 2px solid var(--x-snr-selected-border);
  974. border-radius: 4px;
  975. width: calc(100% + 4px);
  976. position: absolute;
  977. bottom: -6px;
  978. inset-inline-start: -2px;
  979. }
  980.  
  981. .x-myt-hidden-input {
  982. pointer-events: none;
  983. position: absolute;
  984. opacity: 0;
  985. }
  986.  
  987. #myt-snr-nav {
  988. display: flex;
  989. position: sticky;
  990. top: 0;
  991. border: 1px solid var(--x-snr-border);
  992. border-left: none;
  993. border-right: none;
  994. padding: 0;
  995. background: var(--x-snr-background-level-1);
  996. color: var(--x-snr-foreground-level-1);
  997. }
  998.  
  999. #myt-snr-nav .x-left {
  1000. margin-right: 0;
  1001. }
  1002.  
  1003. .x-nobr {
  1004. white-space: nowrap;
  1005. }
  1006.  
  1007. #myt-snr-nav .x-right {
  1008. margin-left: 0;
  1009. }
  1010.  
  1011. #myt-snr-content {
  1012. padding: 1em;
  1013. max-width: var(--x-max-width);
  1014. margin: auto;
  1015. }
  1016.  
  1017. #myt-snr-root h1 {
  1018. margin: 0;
  1019. font-size: revert;
  1020. line-height: revert;
  1021. font-weight: revert;
  1022. color: var(--x-snr-foreground-level-0);
  1023. }
  1024.  
  1025. #myt-snr-root h6 {
  1026. margin: 0;
  1027. font-size: smaller;
  1028. color: var(--x-snr-foreground-level-0);
  1029. }
  1030.  
  1031. #myt-snr-root p {
  1032. text-indent: 2em;
  1033. margin: revert;
  1034. padding: revert;
  1035. }
  1036.  
  1037. #myt-snr-root svg {
  1038. stroke: var(--x-snr-border);
  1039. stroke-linecap: round;
  1040. stroke-width: 1.5;
  1041. fill: none;
  1042. }
  1043.  
  1044. #myt-snr-content * {
  1045. max-width: var(--x-max-width);
  1046. }
  1047.  
  1048. #myt-snr-content p>img:first-child:last-child,
  1049. #myt-snr-content p>video:first-child:last-child,
  1050. #myt-snr-content p>audio:first-child:last-child {
  1051. margin-left: -2em;
  1052. }
  1053.  
  1054. #myt-snr-preload {
  1055. display: none;
  1056. }
  1057.  
  1058. [data-color-scheme=light] {
  1059. --x-snr-background-level-0: #fff;
  1060. --x-snr-background-level-1: #eee;
  1061. --x-snr-background-button: var(--x-snr-background-level-1);
  1062. --x-snr-background-button-hover: #ddd;
  1063. --x-snr-background-button-active: #ccc;
  1064. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  1065. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1066. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1067. --x-snr-background-link: inherit;
  1068. --x-snr-background-visited-link: inherit;
  1069. --x-snr-foreground-level-0: rgb(21, 20, 26);
  1070. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1071. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1072. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1073. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1074. --x-snr-foreground-selected-text: inherit;
  1075. --x-snr-foreground-selected-link: #333;
  1076. --x-snr-foreground-link: rgb(0, 97, 224);
  1077. --x-snr-foreground-visited-link: #b5007f;
  1078. --x-snr-foreground-disabled: rgba(91, 91, 102, 0.4);
  1079. --x-snr-border: #ccc;
  1080. --x-snr-selected-border: var(--x-snr-foreground-link);
  1081. }
  1082.  
  1083. [data-color-scheme=dark] {
  1084. --x-snr-background-level-0: rgb(28, 27, 34);
  1085. --x-snr-background-level-1: rgb(66, 65, 77);
  1086. --x-snr-background-button: var(--x-snr-background-level-1);
  1087. --x-snr-background-button-hover: rgb(82, 82, 94);
  1088. --x-snr-background-button-active: rgb(91, 91, 102);
  1089. --x-snr-background-selected: rgba(0, 221, 255, 0.3);
  1090. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1091. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1092. --x-snr-background-link: inherit;
  1093. --x-snr-background-visited-link: inherit;
  1094. --x-snr-foreground-level-0: rgb(251, 251, 254);
  1095. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1096. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1097. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1098. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1099. --x-snr-foreground-selected-text: inherit;
  1100. --x-snr-foreground-selected-link: #fff;
  1101. --x-snr-foreground-link: rgb(0, 221, 255);
  1102. --x-snr-foreground-visited-link: #e675fd;
  1103. --x-snr-foreground-disabled: rgba(251, 251, 254, 0.4);
  1104. --x-snr-border: #ccc;
  1105. --x-snr-selected-border: var(--x-snr-foreground-link);
  1106. }
  1107.  
  1108. [data-color-scheme=sepia] {
  1109. --x-snr-background-level-0: rgb(244, 236, 216);
  1110. --x-snr-background-level-1: rgb(229, 219, 200);
  1111. --x-snr-background-button: var(--x-snr-background-level-1);
  1112. --x-snr-background-button-hover: rgb(200, 190, 170);
  1113. --x-snr-background-button-active: rgb(170, 160, 140);
  1114. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  1115. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1116. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1117. --x-snr-background-link: inherit;
  1118. --x-snr-background-visited-link: inherit;
  1119. --x-snr-foreground-level-0: rgb(91, 70, 54);
  1120. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1121. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1122. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1123. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1124. --x-snr-foreground-selected-text: inherit;
  1125. --x-snr-foreground-selected-link: #333;
  1126. --x-snr-foreground-link: rgb(0, 97, 224);
  1127. --x-snr-foreground-visited-link: #b5007f;
  1128. --x-snr-foreground-disabled: rgba(91, 70, 54, 0.4);
  1129. --x-snr-border: rgb(91, 70, 54);
  1130. --x-snr-selected-border: var(--x-snr-foreground-link);
  1131. }
  1132.  
  1133. [data-color-scheme=ex-dark] {
  1134. --x-snr-background-level-0: #000;
  1135. --x-snr-background-level-1: rgb(20, 20, 20);
  1136. --x-snr-background-button: var(--x-snr-background-level-1);
  1137. --x-snr-background-button-hover: rgb(40, 40, 40);
  1138. --x-snr-background-button-active: rgb(60, 60, 60);
  1139. --x-snr-background-selected: rgba(0, 180, 200, 0.3);
  1140. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1141. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1142. --x-snr-background-link: inherit;
  1143. --x-snr-background-visited-link: inherit;
  1144. --x-snr-foreground-level-0: rgb(180, 180, 180);
  1145. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1146. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1147. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1148. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1149. --x-snr-foreground-selected-text: inherit;
  1150. --x-snr-foreground-selected-link: rgb(200, 200, 200);
  1151. --x-snr-foreground-link: rgb(0, 180, 200);
  1152. --x-snr-foreground-visited-link: rgb(190, 100, 200);
  1153. --x-snr-foreground-disabled: rgba(180, 180, 180, 0.4);
  1154. --x-snr-border: #555;
  1155. --x-snr-selected-border: var(--x-snr-foreground-link);
  1156. }
  1157.  
  1158. [data-color-scheme=sepia2] {
  1159. --x-snr-background-level-0: rgb(230, 200, 170);
  1160. --x-snr-background-level-1: rgb(200, 170, 120);
  1161. --x-snr-background-button: var(--x-snr-background-level-1);
  1162. --x-snr-background-button-hover: rgb(180, 150, 100);
  1163. --x-snr-background-button-active: rgb(140, 120, 80);
  1164. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  1165. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1166. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1167. --x-snr-background-link: inherit;
  1168. --x-snr-background-visited-link: inherit;
  1169. --x-snr-foreground-level-0: rgb(91, 70, 54);
  1170. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1171. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1172. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1173. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1174. --x-snr-foreground-selected-text: inherit;
  1175. --x-snr-foreground-selected-link: #333;
  1176. --x-snr-foreground-link: rgb(224, 126, 0);
  1177. --x-snr-foreground-visited-link: #b5007f;
  1178. --x-snr-foreground-disabled: rgba(91, 70, 54, 0.4);
  1179. --x-snr-border: rgb(91, 70, 54);
  1180. --x-snr-selected-border: var(--x-snr-foreground-link);
  1181. }
  1182.  
  1183. @media (prefers-color-scheme: light) {
  1184. [data-color-scheme=auto] {
  1185. --x-snr-background-level-0: #fff;
  1186. --x-snr-background-level-1: #eee;
  1187. --x-snr-background-button: var(--x-snr-background-level-1);
  1188. --x-snr-background-button-hover: #ddd;
  1189. --x-snr-background-button-active: #ccc;
  1190. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  1191. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1192. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1193. --x-snr-background-link: inherit;
  1194. --x-snr-background-visited-link: inherit;
  1195. --x-snr-foreground-level-0: rgb(21, 20, 26);
  1196. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1197. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1198. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1199. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1200. --x-snr-foreground-selected-text: inherit;
  1201. --x-snr-foreground-selected-link: #333;
  1202. --x-snr-foreground-link: rgb(0, 97, 224);
  1203. --x-snr-foreground-visited-link: #b5007f;
  1204. --x-snr-foreground-disabled: rgba(91, 91, 102, 0.4);
  1205. --x-snr-border: #ccc;
  1206. --x-snr-selected-border: var(--x-snr-foreground-link);
  1207. }
  1208. }
  1209.  
  1210. @media (prefers-color-scheme: dark) {
  1211. [data-color-scheme=auto] {
  1212. --x-snr-background-level-0: rgb(28, 27, 34);
  1213. --x-snr-background-level-1: rgb(66, 65, 77);
  1214. --x-snr-background-button: var(--x-snr-background-level-1);
  1215. --x-snr-background-button-hover: rgb(82, 82, 94);
  1216. --x-snr-background-button-active: rgb(91, 91, 102);
  1217. --x-snr-background-selected: rgba(0, 221, 255, 0.3);
  1218. --x-snr-background-selected-text: var(--x-snr-background-selected);
  1219. --x-snr-background-selected-link: var(--x-snr-background-selected);
  1220. --x-snr-background-link: inherit;
  1221. --x-snr-background-visited-link: inherit;
  1222. --x-snr-foreground-level-0: rgb(251, 251, 254);
  1223. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  1224. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  1225. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  1226. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  1227. --x-snr-foreground-selected-text: inherit;
  1228. --x-snr-foreground-selected-link: #fff;
  1229. --x-snr-foreground-link: rgb(0, 221, 255);
  1230. --x-snr-foreground-visited-link: #e675fd;
  1231. --x-snr-foreground-disabled: rgba(251, 251, 254, 0.4);
  1232. --x-snr-border: #ccc;
  1233. --x-snr-selected-border: var(--x-snr-foreground-link);
  1234. }
  1235. }
  1236. </style>
  1237. <style id="myt-snr-content-style">
  1238. </style>
  1239. <style id="myt-snr-custom-style">
  1240. </style>
  1241. `;
  1242. GM_registerMenuCommand("切换阅读模式", toggle);
  1243. GM_registerMenuCommand("切换设置界面", toggleSettingDisplay);
  1244. GM_registerMenuCommand("删除样式数据", deleteData);
  1245. SimpleNovelReader.querySelector("#myt-snr-exit").addEventListener("click", hide);
  1246. SimpleNovelReader.querySelector("#myt-snr-settings").addEventListener("click", toggleSettingDisplay);
  1247. SimpleNovelReader.querySelector("#myt-snr-close-settings").addEventListener("click", toggleSettingDisplay);
  1248. SimpleNovelReader.querySelector("#myt-snr-prev").addEventListener("click", switchChapter);
  1249. SimpleNovelReader.querySelector("#myt-snr-info").addEventListener("click", viewInfo);
  1250. SimpleNovelReader.querySelector("#myt-snr-next").addEventListener("click", switchChapter);
  1251.  
  1252. SimpleNovelReader.querySelector("#myt-snr-setting-font-size-minus").addEventListener("click", () => updateFontSize(-1));
  1253. SimpleNovelReader.querySelector("#myt-snr-setting-font-size-plus").addEventListener("click", () => updateFontSize(1));
  1254. SimpleNovelReader.querySelector("#myt-snr-setting-line-height-minus").addEventListener("click", () => updateLineSpace(-0.1));
  1255. SimpleNovelReader.querySelector("#myt-snr-setting-line-height-plus").addEventListener("click", () => updateLineSpace(0.1));
  1256. SimpleNovelReader.querySelector("#myt-snr-setting-max-width-minus").addEventListener("click", () => updateMaxWidth(-1));
  1257. SimpleNovelReader.querySelector("#myt-snr-setting-max-width-plus").addEventListener("click", () => updateMaxWidth(1));
  1258.  
  1259. SimpleNovelReader.querySelector("#myt-snr-setting-custom-style-import").addEventListener("change", importCustomStyle);
  1260. SimpleNovelReader.querySelector("#myt-snr-setting-custom-style-apply").addEventListener("click", applyCustomStyle);
  1261. SimpleNovelReader.querySelector("#myt-snr-setting-custom-style-disable").addEventListener("click", disableCustomStyle);
  1262.  
  1263. for (const btn of SimpleNovelReader.querySelectorAll(".x-myt-hidden-radio")) {
  1264. btn.addEventListener("change", updateRadioButton);
  1265. }
  1266. for (const btn of SimpleNovelReader.querySelectorAll(".x-myt-snr-setting-font-family")) {
  1267. btn.addEventListener("change", updateFontFamilyByRadio);
  1268. }
  1269. for (const btn of SimpleNovelReader.querySelectorAll(".x-myt-setting-color-scheme")) {
  1270. btn.addEventListener("change", updateColorScheme);
  1271. }
  1272.  
  1273. /** @type {HTMLInputElement} */
  1274. const customFontFamily = SimpleNovelReader.querySelector("#myt-snr-setting-font-family-custom-name");
  1275. customFontFamily.value = GM_getValue("config.font-family.custom", "");
  1276. customFontFamily.addEventListener("input", updateFontFamilyByInput);
  1277.  
  1278. SimpleNovelReader.querySelector("#myt-snr-preload").addEventListener("load", loadPreload);
  1279.  
  1280. updateRadioButtonGroup("font-family", GM_getValue("config.font-family", "sans-serif"));
  1281. updateRadioButtonGroup("color-scheme", GM_getValue("config.color-scheme", "auto"));
  1282. updateCustomFontButtonStyle();
  1283. updateContentStyle();
  1284. updateCustomStyle();
  1285. if (!IsDynamicPage) {
  1286. loadUrl(new URL(window.location.href));
  1287. }
  1288. if (window.location.hash == "#simple-novel-reader") {
  1289. SimpleNovelReader.style.top = "0";
  1290. show();
  1291. }
  1292. document.body.appendChild(SimpleNovelReader);
  1293. if (IsDynamicPage) {
  1294. loadUrl(new URL(window.location.href));
  1295. }
  1296. window.addEventListener("hashchange", detectHashChange);
  1297. }
  1298.  
  1299. if (!window.location.hash.includes("disable-simple-novel-reader")) {
  1300. main();
  1301. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址