SimpleNovelReader

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

当前为 2023-11-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name SimpleNovelReader
  3. // @namespace net.myitian.js.SimpleNovelReader
  4. // @version 0.3
  5. // @description 简单的笔趣阁类网站小说阅读器
  6. // @source https://github.com/Myitian/SimpleNovelReader
  7. // @author Myitian
  8. // @license MIT
  9. // @match *://*.xiaoshuohu.com/*/*/*.html*
  10. // @match *://*.bqgpp.com/read/*/*.html*
  11. // @match *://*.52bqg.org/book_*/*.html*
  12. // @match *://*.bqg78.cc/book/*/*.html*
  13. // @match *://*.beqege.com/*/*.html*
  14. // @match *://*.beqege.cc/*/*.html*
  15. // @match *://*.biquge66.net/book/*/*.html*
  16. // @match *://*.wxsc8.com/book/*/*.html*
  17. // @match *://*.zhenhunxiaoshuo.com/*.html*
  18. // @grant GM_getValue
  19. // @grant GM_setValue
  20. // @grant GM_deleteValue
  21. // @grant GM_registerMenuCommand
  22. // ==/UserScript==
  23.  
  24. /**
  25. * @param {Document} doc
  26. */
  27. function extractPageData(doc) {
  28. /**
  29. * @type {string}
  30. */
  31. var title = (
  32. doc.querySelector(".bookname>h1") ??
  33. doc.querySelector(".article-title,.bookname,#nr_title,.title,.zhong,.cont-title")
  34. )?.innerText;
  35. /**
  36. * @type {string}
  37. */
  38. var content = (
  39. doc.querySelector("#cont-body") ??
  40. doc.querySelector(".article-content,#content,#chaptercontent,#nr")
  41. )?.innerHTML.replace(" ", "");
  42. /**
  43. * @type {?string}
  44. */
  45. var prev = (
  46. doc.querySelector("[rel=prev],#prev_url,#pb_prevz,#link-preview") ??
  47. doc.querySelector(".bottem1>a:nth-child(1),.col-md-6.text-center>a[href]:nth-child(1)")
  48. )?.href;
  49. /**
  50. * @type {?string}
  51. */
  52. var info = (
  53. doc.querySelector("[rel='category tag'],#info_url,#pb_mulu,#link-index") ??
  54. doc.querySelector(".bottem1>a:nth-child(2),.col-md-6.text-center>a[href]:nth-child(2)")
  55. )?.href;
  56. /**
  57. * @type {?string}
  58. */
  59. var next = (
  60. doc.querySelector("[rel=next],#next_url,#pb_next,#link-next") ??
  61. doc.querySelector(".bottem1>a:nth-child(3),.col-md-6.text-center>a[href]:nth-child(3)")
  62. )?.href;
  63. return {
  64. pageTitle: doc.title.trim(),
  65. title: title?.trim() ?? "",
  66. content: content?.trim() ?? "",
  67. prev: prev?.includes(".html") ? prev.trim() : "",
  68. info: info?.trim() ?? "",
  69. next: next?.includes(".html") ? next.trim() : ""
  70. }
  71. }
  72. /**
  73. * @param {{pageTitle:string,title:string,content:string,prev:string,info:string,next:string}} data
  74. */
  75. function loadPageData(data) {
  76. document.title = data.pageTitle;
  77. SimpleNovelReader.querySelector("#myt-snr-title").innerText = data.title;
  78. SimpleNovelReader.querySelector("#myt-snr-content").innerHTML = data.content;
  79. var prev = SimpleNovelReader.querySelector("#myt-snr-prev");
  80. prev.dataset.href = data.prev;
  81. prev.disabled = !data.prev;
  82. SimpleNovelReader.querySelector("#myt-snr-info").dataset.href = data.info;
  83. var next = SimpleNovelReader.querySelector("#myt-snr-next");
  84. next.dataset.href = data.next;
  85. next.disabled = !data.next;
  86. }
  87.  
  88. /**
  89. * @param {string} url
  90. */
  91. function loadUrl(url) {
  92. SimpleNovelReader.querySelector("#myt-snr-content").scrollTop = 0;
  93. get(url).then(
  94. xhr => {
  95. loadPageData(extractPageData(xhr.response));
  96. }
  97. );
  98. }
  99.  
  100. /**
  101. * GET 请求
  102. * @param {string} url 请求地址
  103. * @param {string} responseType 响应类型
  104. * @param {number} timeout 超时
  105. * @returns {Promise<XMLHttpRequest>} Promise 对象,其 resolve 和 reject 均传入请求所用的 XMLHttpRequest 对象
  106. */
  107. function get(url, responseType = "document", timeout = 0) {
  108. return new Promise(function (resolve, reject) {
  109. var xhr = new XMLHttpRequest();
  110. xhr.open("GET", url, true);
  111. xhr.timeout = timeout;
  112. xhr.withCredentials = true;
  113. xhr.responseType = responseType;
  114. xhr.send();
  115. xhr.onload = () => {
  116. if (xhr.status < 300) {
  117. resolve(xhr);
  118. } else {
  119. reject(xhr);
  120. }
  121. }; xhr.ontimeout = () => reject(timeout);
  122. });
  123. }
  124.  
  125. function detectHashChange() {
  126. if (window.location.hash == "#simple-novel-reader") {
  127. SimpleNovelReader.style.top = "0";
  128. } else {
  129. SimpleNovelReader.style.top = "200%";
  130. }
  131. }
  132.  
  133. function toggle() {
  134. if (window.location.hash == "#simple-novel-reader") {
  135. hide();
  136. } else {
  137. show();
  138. }
  139. }
  140.  
  141. function show(url = undefined) {
  142. window.location.hash = "#simple-novel-reader";
  143. document.documentElement.style.overflow = "hidden";
  144. document.body.style.overflow = "hidden";
  145. if (url) {
  146. var newUrl = new URL(url);
  147. newUrl.hash = "#simple-novel-reader";
  148. history.pushState(null, "", newUrl.toString());
  149. SimpleNovelReader.scrollTop = 0;
  150. loadUrl(url);
  151. }
  152. }
  153.  
  154. function hide() {
  155. var newUrl = window.location.origin + window.location.pathname + window.location.search;
  156. if (newUrl != OriginalUrl) {
  157. window.location = newUrl;
  158. } else {
  159. window.location.hash = "";
  160. document.documentElement.style.overflow = "";
  161. document.body.style.overflow = "";
  162. }
  163. }
  164.  
  165. /**
  166. * @param {Event} event
  167. */
  168. function toggleSettingDisplay(event) {
  169. /**
  170. * @type {HTMLDivElement}
  171. */
  172. var settings = document.querySelector("#myt-snr-setting-items");
  173. if (settings.toggleAttribute("hidden")) {
  174. document.querySelector("#myt-snr-settings").innerText = "展开样式设置";
  175. } else {
  176. document.querySelector("#myt-snr-settings").innerText = "收起样式设置";
  177. }
  178. }
  179.  
  180. /**
  181. * @param {Event} event
  182. */
  183. function switchChapter(event) {
  184. /**
  185. * @type {HTMLButtonElement}
  186. */
  187. var btn = event.target;
  188. if (btn.dataset.href) {
  189. show(btn.dataset.href);
  190. }
  191. }
  192.  
  193. function updateCustomFontButtonStyle() {
  194. SimpleNovelReader.querySelector("[for=myt-snr-setting-font-family-custom]").style.fontFamily = GM_getValue("config.font-family.custom", "sans-serif");
  195. }
  196.  
  197. function updateContentStyle() {
  198. SimpleNovelReader.querySelector("#myt-snr-content-style").innerHTML = `
  199. #myt-snr-root .x-myt-content-style {
  200. font-family: ${GM_getValue("config.font-family.name", "sans-serif")};
  201. font-size: ${GM_getValue("config.font-size", "medium")};
  202. line-height: ${GM_getValue("config.line-height", "1.5")};
  203. }
  204.  
  205. #myt-snr-root {
  206. --width-limit: ${GM_getValue("config.width-limit", "40em")};
  207. }
  208. `;
  209. }
  210.  
  211. /**
  212. * @param {boolean} useCustomStyle
  213. */
  214. function updateCustomStyle(useCustomStyle) {
  215. if (useCustomStyle) {
  216. SimpleNovelReader.querySelector("#myt-snr-custom-style").innerHTML = GM_getValue("config.custom-style", "");
  217. } else {
  218. SimpleNovelReader.querySelector("#myt-snr-custom-style").innerHTML = "";
  219. }
  220. }
  221.  
  222. function updateRadioButtonGroup(name, value) {
  223. /**
  224. * @type {HTMLInputElement}
  225. */
  226. var radio = SimpleNovelReader.querySelector(`input[name=${name}][data-value=${CSS.escape(value)}]`);
  227. radio.checked = true;
  228. radio.dispatchEvent(new Event('change'));
  229. }
  230.  
  231. /**
  232. * @param {Event} event
  233. */
  234. function updateRadioButton(event) {
  235. /**
  236. * @type {HTMLInputElement}
  237. */
  238. var radio = event.target;
  239. SimpleNovelReader.querySelector(`label[for=${radio.id}]`).toggleAttribute("checked", true);
  240. for (var r of SimpleNovelReader.querySelectorAll(`input[name=${radio.name}]:not([id=${radio.id}])`)) {
  241. SimpleNovelReader.querySelector(`label[for=${r.id}]`).toggleAttribute("checked", false);
  242. }
  243. }
  244.  
  245. /**
  246. * @param {Event} event
  247. */
  248. function updateFontFamilyByRadio(event) {
  249. /**
  250. * @type {HTMLInputElement}
  251. */
  252. var radio = event.target;
  253. var custom = SimpleNovelReader.querySelector("#myt-snr-setting-font-family-custom-name").value;
  254. GM_setValue("config.font-family.custom", custom);
  255. GM_setValue("config.font-family", radio.dataset.value);
  256. if (radio.dataset.value == "custom") {
  257. GM_setValue("config.font-family.name", custom);
  258. } else {
  259. GM_setValue("config.font-family.name", radio.dataset.value);
  260. }
  261. updateCustomFontButtonStyle();
  262. updateContentStyle();
  263. }
  264. /**
  265. * @param {Event} event
  266. */
  267. function updateFontFamilyByInput(event) {
  268. /**
  269. * @type {HTMLInputElement}
  270. */
  271. var input = event.target;
  272. if (GM_getValue("config.font-family", "sans-serif") == "custom") {
  273. GM_setValue("config.font-family.name", input.value);
  274. updateContentStyle();
  275. }
  276. GM_setValue("config.font-family.custom", input.value);
  277. updateCustomFontButtonStyle();
  278. }
  279.  
  280. /**
  281. * @param {Event} event
  282. */
  283. function updateColorScheme(event) {
  284. /**
  285. * @type {HTMLInputElement}
  286. */
  287. var radio = event.target;
  288. GM_setValue("config.color-scheme", radio.dataset.value);
  289. SimpleNovelReader.dataset.colorScheme = radio.dataset.value;
  290. }
  291.  
  292. function deleteData() {
  293. if (confirm("确认删除储存的样式数据?")) {
  294. GM_deleteValue("config.font-family");
  295. GM_deleteValue("config.font-family.name");
  296. GM_deleteValue("config.font-family.custom");
  297. GM_deleteValue("config.font-size");
  298. GM_deleteValue("config.line-height");
  299. GM_deleteValue("config.width-limit");
  300. GM_deleteValue("config.custom-style");
  301. GM_deleteValue("config.color-scheme");
  302. }
  303. }
  304.  
  305. function main() {
  306. SimpleNovelReader.id = "myt-snr-root";
  307. SimpleNovelReader.className = "x-scroll-container";
  308. SimpleNovelReader.innerHTML = `
  309. <div id="myt-snr-main">
  310. <header id="myt-snr-header" class="x-myt-content-style">
  311. <h1 id="myt-snr-title"></h1>
  312. <div id="myt-snr-tools">
  313. <button id="myt-snr-exit" class="x-myt-button">退出阅读模式</button>
  314. <button id="myt-snr-settings" class="x-myt-button">展开样式设置</button>
  315. </div>
  316. <div id="myt-snr-setting-items" class="x-scroll-container" hidden>
  317. <div class="x-myt-list-item">
  318. <div id="myt-snr-close-settings">
  319. <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 24 24">
  320. <path stroke-linecap="round" d="M6 6l12 12m0-12L6 18" fill="none" stroke-width="1.5"></path>
  321. </svg>
  322. </div>
  323. <h6>字体</h6>
  324. <div>
  325. <span class="x-nobr">
  326. <input id="myt-snr-setting-font-family-sans-serif"
  327. class="x-myt-hidden-radio x-myt-snr-setting-font-family" type="radio"
  328. data-value="sans-serif" name="font-family" checked>
  329. <label for="myt-snr-setting-font-family-sans-serif"
  330. class="x-myt-hidden-radio-button x-myt-button" style="font-family: sans-serif;"
  331. checked>无衬线体</label>
  332. <input id="myt-snr-setting-font-family-serif"
  333. class="x-myt-hidden-radio x-myt-snr-setting-font-family" type="radio" data-value="serif"
  334. name="font-family">
  335. <label for="myt-snr-setting-font-family-serif" class="x-myt-hidden-radio-button x-myt-button"
  336. style="font-family: serif;">衬线体</label>
  337. </span>
  338. <input id="myt-snr-setting-font-family-custom"
  339. class="x-myt-hidden-radio x-myt-snr-setting-font-family" type="radio" data-value="custom"
  340. name="font-family">
  341. <label for="myt-snr-setting-font-family-custom"
  342. class="x-myt-hidden-radio-button x-myt-button">自定义</label>
  343. <input id="myt-snr-setting-font-family-custom-name">
  344. </div>
  345. </div>
  346. <div class="x-myt-list-item">
  347. <div class="x-setting-short-item">
  348. <h6>字号</h6>
  349. <div>
  350. <button id="myt-snr-setting-font-familysize-minus" class="x-myt-button">-</button>
  351. <span id="myt-snr-setting-font-familysize-value" class="x-middle" data-v="3">中</span>
  352. <button id="myt-snr-setting-font-familysize-plus" class="x-myt-button">+</button>
  353. </div>
  354. </div>
  355. <div class="x-setting-short-item">
  356. <h6>行间距</h6>
  357. <div>
  358. <button id="myt-snr-setting-line-space-minus" class="x-myt-button">-</button>
  359. <span id="myt-snr-setting-line-space-value" class="x-middle" data-v="1.5">1.5</span>
  360. <button id="myt-snr-setting-line-space-plus" class="x-myt-button">+</button>
  361. </div>
  362. </div>
  363. <div class="x-setting-short-item">
  364. <h6>最大内容宽度</h6>
  365. <div>
  366. <button id="myt-snr-setting-max-width-minus" class="x-myt-button">-</button>
  367. <span id="myt-snr-setting-max-width-value" class="x-middle" data-v="40">40em</span>
  368. <button id="myt-snr-setting-max-width-plus" class="x-myt-button">+</button>
  369. </div>
  370. </div>
  371. </div>
  372. <div class="x-myt-list-item">
  373. <div>
  374. <span class="x-nobr">
  375. <input id="myt-snr-setting-color-scheme-light"
  376. class="x-myt-hidden-radio x-myt-setting-color-scheme" type="radio" data-value="light"
  377. name="color-scheme">
  378. <label for="myt-snr-setting-color-scheme-light" data-color-scheme="light"
  379. class="x-myt-hidden-radio-button x-myt-button">浅色</label>
  380. <input id="myt-snr-setting-color-scheme-dark"
  381. class="x-myt-hidden-radio x-myt-setting-color-scheme" type="radio" data-value="dark"
  382. name="color-scheme">
  383. <label for="myt-snr-setting-color-scheme-dark" data-color-scheme="dark"
  384. class="x-myt-hidden-radio-button x-myt-button">深色</label>
  385. </span>
  386. <span class="x-nobr">
  387. <input id="myt-snr-setting-color-scheme-sepia"
  388. class="x-myt-hidden-radio x-myt-setting-color-scheme" type="radio" data-value="sepia"
  389. name="color-scheme">
  390. <label for="myt-snr-setting-color-scheme-sepia" data-color-scheme="sepia"
  391. class="x-myt-hidden-radio-button x-myt-button">纸墨</label>
  392. <input id="myt-snr-setting-color-scheme-ex-dark"
  393. class="x-myt-hidden-radio x-myt-setting-color-scheme" type="radio" data-value="ex-dark"
  394. name="color-scheme">
  395. <label for="myt-snr-setting-color-scheme-ex-dark" data-color-scheme="ex-dark"
  396. class="x-myt-hidden-radio-button x-myt-button">极黑</label>
  397. </span>
  398. <span class="x-nobr">
  399. <input id="myt-snr-setting-color-scheme-sepia2"
  400. class="x-myt-hidden-radio x-myt-setting-color-scheme" type="radio" data-value="sepia2"
  401. name="color-scheme">
  402. <label for="myt-snr-setting-color-scheme-sepia2" data-color-scheme="sepia2"
  403. class="x-myt-hidden-radio-button x-myt-button">纸墨2</label>
  404. <input id="myt-snr-setting-color-scheme-auto"
  405. class="x-myt-hidden-radio x-myt-setting-color-scheme" type="radio" data-value="auto"
  406. name="color-scheme" checked>
  407. <label for="myt-snr-setting-color-scheme-auto" class="x-myt-hidden-radio-button x-myt-button"
  408. data-color-scheme="auto" checked>自动</label>
  409. </span>
  410. </div>
  411. </div>
  412. <div class="x-myt-list-item">
  413. <div>
  414. <label for="myt-snr-setting-style-custom">自定义样式</label>
  415. <input id="myt-snr-setting-style-custom">
  416. </div>
  417. <div>
  418. <button id="myt-snr-setting-style-custom-import" class="x-myt-button">导入</button>
  419. <button id="myt-snr-setting-style-custom-confirm" class="x-myt-button">应用</button>
  420. <button id="myt-snr-setting-style-custom-confirm" class="x-myt-button">停用</button>
  421. </div>
  422. </div>
  423. </div>
  424. </header>
  425. <nav id="myt-snr-nav">
  426. <button id="myt-snr-prev" class="x-myt-button x-left">上一章</button>
  427. <button id="myt-snr-info" class="x-myt-button x-middle" disabled><span class="x-nobr">章节</span><span
  428. class="x-nobr">列表</span></button>
  429. <button id="myt-snr-next" class="x-myt-button x-right">下一章</button>
  430. </nav>
  431. <article id="myt-snr-content" class="x-myt-content-style">
  432. </article>
  433. <footer id="myt-snr-footer" class="x-myt-content-style">
  434. </footer>
  435. </div>
  436. <style>
  437. #myt-snr-root {
  438. box-sizing: border-box;
  439. position: fixed;
  440. width: 100%;
  441. height: 100%;
  442. top: 200%;
  443. left: 0;
  444. z-index: 2001;
  445. background: var(--x-snr-background-level-0);
  446. color: var(--x-snr-foreground-level-0);
  447. font-family: sans-serif;
  448. font-size: medium;
  449. line-height: 1.5;
  450. --width-limit: 40em;
  451. }
  452.  
  453. #myt-snr-content * {
  454. color: inherit;
  455. font-family: inherit;
  456. font-size: inherit;
  457. line-height: inherit;
  458. }
  459.  
  460. #myt-snr-root *::selection {
  461. background: var(--x-snr-background-selected-text);
  462. color: var(--x-snr-foreground-selected-text);
  463. }
  464.  
  465. #myt-snr-root a {
  466. background: var(--x-snr-background-link);
  467. color: var(--x-snr-foreground-link);
  468. text-decoration: underline var(--x-snr-foreground-level-0);
  469. }
  470.  
  471. #myt-snr-root a:visited {
  472. color: var(--x-snr-foreground-visited-link);
  473. }
  474.  
  475. #myt-snr-root a::selection {
  476. background: var(--x-snr-background-selected-link);
  477. color: var(--x-snr-foreground-selected-link);
  478. }
  479.  
  480. .x-myt-button {
  481. border: none;
  482. background: transparent no-repeat center center;
  483. padding: .5em 1em;
  484. border-radius: .3em;
  485. margin: .5em 1em;
  486. cursor: pointer;
  487. transition: all 0.2s ease;
  488. background: var(--x-snr-background-button);
  489. color: var(--x-snr-foreground-button);
  490. fill: var(--x-snr-foreground-button);
  491. -webkit-touch-callout: none;
  492. -webkit-user-select: none;
  493. -khtml-user-select: none;
  494. -moz-user-select: none;
  495. -ms-user-select: none;
  496. user-select: none;
  497. }
  498.  
  499. .x-myt-button:enabled:hover {
  500. background: var(--x-snr-background-button-hover);
  501. color: var(--x-snr-foreground-button-hover);
  502. fill: var(--x-snr-foreground-button-hover);
  503. }
  504.  
  505. .x-myt-button:enabled:active {
  506. background: var(--x-snr-background-button-active);
  507. color: var(--x-snr-foreground-button-active);
  508. fill: var(--x-snr-foreground-button-active);
  509. }
  510.  
  511. .x-middle {
  512. margin: auto;
  513. }
  514.  
  515. .x-scroll-container {
  516. overflow-x: clip;
  517. overflow-y: auto;
  518. }
  519.  
  520. #myt-snr-header {
  521. text-align: center;
  522. max-width: var(--width-limit);
  523. margin: auto;
  524. padding: 1em;
  525. height: unset;
  526. background: unset;
  527. line-height: revert;
  528. border-bottom: unset;
  529. }
  530.  
  531. #myt-snr-tools {
  532. margin-top: 1em;
  533. }
  534.  
  535. #myt-snr-tools .x-myt-button {
  536. margin: 0;
  537. margin-top: .2em;
  538. }
  539.  
  540. #myt-snr-setting-items {
  541. position: fixed;
  542. background: var(--x-snr-background-level-0);
  543. border: 1px solid var(--x-snr-border);
  544. left: 0;
  545. top: 0;
  546. z-index: 2333;
  547. max-height: 100%;
  548. box-sizing: border-box;
  549. padding: 0;
  550. margin: 0;
  551. }
  552.  
  553. #myt-snr-setting-items .x-myt-list-item {
  554. margin: .5em;
  555. }
  556.  
  557. #myt-snr-setting-items .x-myt-button {
  558. margin: .2em;
  559. }
  560.  
  561. .x-setting-short-item {
  562. margin: .5em;
  563. display: inline-block;
  564. width: 9em;
  565. }
  566.  
  567. .x-setting-short-item>div {
  568. display: flex;
  569. }
  570.  
  571. .x-myt-hidden-radio-button {
  572. display: inline-block;
  573. position: relative;
  574. background: var(--x-snr-background-level-1);
  575. color: var(--x-snr-foreground-level-1);
  576. box-sizing: border-box;
  577. border-radius: 2px;
  578. border: 2px solid var(--x-snr-background-level-1);
  579. font-weight: revert;
  580. max-width: revert;
  581. }
  582.  
  583. #myt-snr-close-settings {
  584. position: absolute;
  585. width: 1.5em;
  586. height: 1.5em;
  587. right: 0;
  588. top: 0;
  589. }
  590.  
  591. .x-myt-hidden-radio-button[checked] {
  592. border-color: var(--x-snr-selected-border);
  593. }
  594.  
  595. .x-myt-hidden-radio-button:hover::after {
  596. content: "";
  597. display: block;
  598. border-bottom: 2px solid var(--x-snr-selected-border);
  599. border-radius: 4px;
  600. width: calc(100% + 4px);
  601. position: absolute;
  602. bottom: -6px;
  603. inset-inline-start: -2px;
  604. }
  605.  
  606. .x-myt-hidden-radio {
  607. pointer-events: none;
  608. position: absolute;
  609. visibility: hidden;
  610. }
  611.  
  612. #myt-snr-nav {
  613. display: flex;
  614. position: sticky;
  615. top: 0;
  616. border: 1px solid var(--x-snr-border);
  617. border-left: none;
  618. border-right: none;
  619. padding: 0;
  620. background: var(--x-snr-background-level-1);
  621. color: var(--x-snr-foreground-level-1);
  622. }
  623.  
  624. #myt-snr-nav .x-left {
  625. margin-right: 0;
  626. }
  627.  
  628. .x-nobr {
  629. white-space: nowrap;
  630. }
  631.  
  632. #myt-snr-nav .x-right {
  633. margin-left: 0;
  634. }
  635.  
  636. #myt-snr-content {
  637. padding: 1em;
  638. max-width: var(--width-limit);
  639. margin: auto;
  640. }
  641.  
  642. #myt-snr-root h1 {
  643. margin: 0;
  644. font-size: revert;
  645. line-height: revert;
  646. font-weight: revert;
  647. }
  648.  
  649. #myt-snr-root h6 {
  650. margin: 0;
  651. font-size: smaller;
  652. }
  653.  
  654. #myt-snr-root p {
  655. text-indent: 2em;
  656. margin: revert;
  657. padding: revert;
  658. }
  659.  
  660. svg {
  661. stroke: var(--x-snr-border);
  662. }
  663.  
  664. [data-color-scheme=light] {
  665. --x-snr-background-level-0: #fff;
  666. --x-snr-background-level-1: #eee;
  667. --x-snr-background-button: var(--x-snr-background-level-1);
  668. --x-snr-background-button-hover: #ddd;
  669. --x-snr-background-button-active: #ccc;
  670. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  671. --x-snr-background-selected-text: var(--x-snr-background-selected);
  672. --x-snr-background-selected-link: var(--x-snr-background-selected);
  673. --x-snr-background-link: inherit;
  674. --x-snr-background-visited-link: inherit;
  675. --x-snr-foreground-level-0: rgb(21, 20, 26);
  676. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  677. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  678. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  679. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  680. --x-snr-foreground-selected-text: inherit;
  681. --x-snr-foreground-selected-link: #333;
  682. --x-snr-foreground-link: rgb(0, 97, 224);
  683. --x-snr-foreground-visited-link: #b5007f;
  684. --x-snr-foreground-disabled: rgba(91, 91, 102, 0.4);
  685. --x-snr-border: #ccc;
  686. --x-snr-selected-border: var(--x-snr-foreground-link);
  687. }
  688.  
  689. [data-color-scheme=dark] {
  690. --x-snr-background-level-0: rgb(28, 27, 34);
  691. --x-snr-background-level-1: rgb(66, 65, 77);
  692. --x-snr-background-button: var(--x-snr-background-level-1);
  693. --x-snr-background-button-hover: rgb(82, 82, 94);
  694. --x-snr-background-button-active: rgb(91, 91, 102);
  695. --x-snr-background-selected: rgba(0, 221, 255, 0.3);
  696. --x-snr-background-selected-text: var(--x-snr-background-selected);
  697. --x-snr-background-selected-link: var(--x-snr-background-selected);
  698. --x-snr-background-link: inherit;
  699. --x-snr-background-visited-link: inherit;
  700. --x-snr-foreground-level-0: rgb(251, 251, 254);
  701. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  702. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  703. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  704. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  705. --x-snr-foreground-selected-text: inherit;
  706. --x-snr-foreground-selected-link: #fff;
  707. --x-snr-foreground-link: rgb(0, 221, 255);
  708. --x-snr-foreground-visited-link: #e675fd;
  709. --x-snr-foreground-disabled: rgba(251, 251, 254, 0.4);
  710. --x-snr-border: #ccc;
  711. --x-snr-selected-border: var(--x-snr-foreground-link);
  712. }
  713.  
  714. [data-color-scheme=sepia] {
  715. --x-snr-background-level-0: rgb(244, 236, 216);
  716. --x-snr-background-level-1: rgb(229, 219, 200);
  717. --x-snr-background-button: var(--x-snr-background-level-1);
  718. --x-snr-background-button-hover: #ddd;
  719. --x-snr-background-button-active: #ccc;
  720. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  721. --x-snr-background-selected-text: var(--x-snr-background-selected);
  722. --x-snr-background-selected-link: var(--x-snr-background-selected);
  723. --x-snr-background-link: inherit;
  724. --x-snr-background-visited-link: inherit;
  725. --x-snr-foreground-level-0: rgb(91, 70, 54);
  726. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  727. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  728. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  729. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  730. --x-snr-foreground-selected-text: inherit;
  731. --x-snr-foreground-selected-link: #333;
  732. --x-snr-foreground-link: rgb(0, 97, 224);
  733. --x-snr-foreground-visited-link: #b5007f;
  734. --x-snr-foreground-disabled: rgba(91, 70, 54, 0.4);
  735. --x-snr-border: rgb(91, 70, 54);
  736. --x-snr-selected-border: var(--x-snr-foreground-link);
  737. }
  738.  
  739. [data-color-scheme=ex-dark] {
  740. --x-snr-background-level-0: #000;
  741. --x-snr-background-level-1: rgb(20, 20, 20);
  742. --x-snr-background-button: var(--x-snr-background-level-1);
  743. --x-snr-background-button-hover: rgb(40, 40, 40);
  744. --x-snr-background-button-active: rgb(60, 60, 60);
  745. --x-snr-background-selected: rgba(0, 180, 200, 0.3);
  746. --x-snr-background-selected-text: var(--x-snr-background-selected);
  747. --x-snr-background-selected-link: var(--x-snr-background-selected);
  748. --x-snr-background-link: inherit;
  749. --x-snr-background-visited-link: inherit;
  750. --x-snr-foreground-level-0: rgb(180, 180, 180);
  751. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  752. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  753. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  754. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  755. --x-snr-foreground-selected-text: inherit;
  756. --x-snr-foreground-selected-link: rgb(200, 200, 200);
  757. --x-snr-foreground-link: rgb(0, 180, 200);
  758. --x-snr-foreground-visited-link: rgb(190, 100, 200);
  759. --x-snr-foreground-disabled: rgba(180, 180, 180, 0.4);
  760. --x-snr-border: #555;
  761. --x-snr-selected-border: var(--x-snr-foreground-link);
  762. }
  763.  
  764. [data-color-scheme=sepia2] {
  765. --x-snr-background-level-0: rgb(230, 200, 170);
  766. --x-snr-background-level-1: rgb(200, 170, 120);
  767. --x-snr-background-button: var(--x-snr-background-level-1);
  768. --x-snr-background-button-hover: rgb(180, 150, 100);
  769. --x-snr-background-button-active: rgb(140, 120, 80);
  770. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  771. --x-snr-background-selected-text: var(--x-snr-background-selected);
  772. --x-snr-background-selected-link: var(--x-snr-background-selected);
  773. --x-snr-background-link: inherit;
  774. --x-snr-background-visited-link: inherit;
  775. --x-snr-foreground-level-0: rgb(91, 70, 54);
  776. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  777. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  778. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  779. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  780. --x-snr-foreground-selected-text: inherit;
  781. --x-snr-foreground-selected-link: #333;
  782. --x-snr-foreground-link: rgb(224, 126, 0);
  783. --x-snr-foreground-visited-link: #b5007f;
  784. --x-snr-foreground-disabled: rgba(91, 70, 54, 0.4);
  785. --x-snr-border: rgb(91, 70, 54);
  786. --x-snr-selected-border: var(--x-snr-foreground-link);
  787. }
  788.  
  789. @media (prefers-color-scheme: light) {
  790. [data-color-scheme=auto] {
  791. --x-snr-background-level-0: #fff;
  792. --x-snr-background-level-1: #eee;
  793. --x-snr-background-button: var(--x-snr-background-level-1);
  794. --x-snr-background-button-hover: #ddd;
  795. --x-snr-background-button-active: #ccc;
  796. --x-snr-background-selected: rgba(0, 97, 224, 0.3);
  797. --x-snr-background-selected-text: var(--x-snr-background-selected);
  798. --x-snr-background-selected-link: var(--x-snr-background-selected);
  799. --x-snr-background-link: inherit;
  800. --x-snr-background-visited-link: inherit;
  801. --x-snr-foreground-level-0: rgb(21, 20, 26);
  802. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  803. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  804. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  805. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  806. --x-snr-foreground-selected-text: inherit;
  807. --x-snr-foreground-selected-link: #333;
  808. --x-snr-foreground-link: rgb(0, 97, 224);
  809. --x-snr-foreground-visited-link: #b5007f;
  810. --x-snr-foreground-disabled: rgba(91, 91, 102, 0.4);
  811. --x-snr-border: #ccc;
  812. --x-snr-selected-border: var(--x-snr-foreground-link);
  813. }
  814. }
  815.  
  816. @media (prefers-color-scheme: dark) {
  817. [data-color-scheme=auto] {
  818. --x-snr-background-level-0: rgb(28, 27, 34);
  819. --x-snr-background-level-1: rgb(66, 65, 77);
  820. --x-snr-background-button: var(--x-snr-background-level-1);
  821. --x-snr-background-button-hover: rgb(82, 82, 94);
  822. --x-snr-background-button-active: rgb(91, 91, 102);
  823. --x-snr-background-selected: rgba(0, 221, 255, 0.3);
  824. --x-snr-background-selected-text: var(--x-snr-background-selected);
  825. --x-snr-background-selected-link: var(--x-snr-background-selected);
  826. --x-snr-background-link: inherit;
  827. --x-snr-background-visited-link: inherit;
  828. --x-snr-foreground-level-0: rgb(251, 251, 254);
  829. --x-snr-foreground-level-1: var(--x-snr-foreground-level-0);
  830. --x-snr-foreground-button: var(--x-snr-foreground-level-0);
  831. --x-snr-foreground-button-hover: var(--x-snr-foreground-level-0);
  832. --x-snr-foreground-button-active: var(--x-snr-foreground-link);
  833. --x-snr-foreground-selected-text: inherit;
  834. --x-snr-foreground-selected-link: #fff;
  835. --x-snr-foreground-link: rgb(0, 221, 255);
  836. --x-snr-foreground-visited-link: #e675fd;
  837. --x-snr-foreground-disabled: rgba(251, 251, 254, 0.4);
  838. --x-snr-border: #ccc;
  839. --x-snr-selected-border: var(--x-snr-foreground-link);
  840. }
  841. }
  842. </style>
  843. <style id="myt-snr-content-style">
  844. </style>
  845. <style id="myt-snr-custom-style">
  846. </style>
  847. `;
  848. GM_registerMenuCommand("切换阅读模式", toggle);
  849. GM_registerMenuCommand("删除样式数据", deleteData);
  850. SimpleNovelReader.querySelector("#myt-snr-exit").addEventListener("click", hide);
  851. SimpleNovelReader.querySelector("#myt-snr-settings").addEventListener("click", toggleSettingDisplay);
  852. SimpleNovelReader.querySelector("#myt-snr-close-settings").addEventListener("click", toggleSettingDisplay);
  853. SimpleNovelReader.querySelector("#myt-snr-prev").addEventListener("click", switchChapter);
  854. SimpleNovelReader.querySelector("#myt-snr-next").addEventListener("click", switchChapter);
  855. for (var btn of SimpleNovelReader.querySelectorAll(".x-myt-hidden-radio")) {
  856. btn.addEventListener("change", updateRadioButton);
  857. }
  858. for (var btn of SimpleNovelReader.querySelectorAll(".x-myt-snr-setting-font-family")) {
  859. btn.addEventListener("change", updateFontFamilyByRadio);
  860. }
  861. for (var btn of SimpleNovelReader.querySelectorAll(".x-myt-setting-color-scheme")) {
  862. btn.addEventListener("change", updateColorScheme);
  863. }
  864. SimpleNovelReader.querySelector("#myt-snr-setting-font-family-custom-name").value = GM_getValue("config.font-family.custom", "");
  865. SimpleNovelReader.querySelector("#myt-snr-setting-font-family-custom-name").addEventListener("input", updateFontFamilyByInput);
  866. updateRadioButtonGroup("font-family", GM_getValue("config.font-family", "sans-serif"));
  867. updateRadioButtonGroup("color-scheme", GM_getValue("config.color-scheme", "auto"));
  868. updateCustomFontButtonStyle();
  869. updateContentStyle();
  870. updateCustomStyle();
  871. loadUrl(window.location.href);
  872. if (window.location.hash == "#simple-novel-reader") {
  873. SimpleNovelReader.style.top = "0";
  874. show();
  875. }
  876. window.addEventListener("hashchange", detectHashChange);
  877. document.body.appendChild(SimpleNovelReader);
  878. }
  879.  
  880. const FontSizes = [
  881. ["xx-small", "极小"],
  882. ["x-small", "小"],
  883. ["small", "较小"],
  884. ["medium", "中"],
  885. ["large", "较大"],
  886. ["x-large", "大"],
  887. ["xx-large", "极大"]
  888. ];
  889. const SimpleNovelReader = document.createElement("div");
  890. const OriginalUrl = window.location.origin + window.location.pathname + window.location.search;
  891. main();