VoidVerified

Display a verified sign next to user's name in AniList.

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

  1. // ==UserScript==
  2. // @name VoidVerified
  3. // @version 1.1.0
  4. // @namespace http://tampermonkey.net/
  5. // @author voidnyan
  6. // @description Display a verified sign next to user's name in AniList.
  7. // @homepageURL https://github.com/voidnyan/void-verified#voidverified
  8. // @supportURL https://github.com/voidnyan/void-verified/issues
  9. // @grant none
  10. // @match https://anilist.co/*
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. "use strict";
  16.  
  17. class StyleHandler {
  18. Settings;
  19. usernameStyles = "";
  20. highlightStyles = "";
  21. otherStyles = "";
  22.  
  23. profileLink = this.createStyleLink("", "profile");
  24.  
  25. constructor(settings) {
  26. this.Settings = settings;
  27. }
  28.  
  29. refreshStyles() {
  30. this.createStyles();
  31. this.createStyleLink(this.usernameStyles, "username");
  32. this.createStyleLink(this.highlightStyles, "highlight");
  33. this.createStyleLink(this.otherStyles, "other");
  34. }
  35.  
  36. createStyles() {
  37. this.usernameStyles = "";
  38. this.highlightStyles = "";
  39. this.otherStyles = `a[href="/settings/developer" i]::after{content: " & Void"}`;
  40.  
  41. for (const user of this.Settings.VerifiedUsers) {
  42. if (
  43. this.Settings.getOptionValue(
  44. this.Settings.Options.enabledForUsername
  45. ) ||
  46. user.enabledForUsername
  47. ) {
  48. this.createUsernameCSS(user);
  49. }
  50.  
  51. if (
  52. this.Settings.getOptionValue(
  53. this.Settings.Options.highlightEnabled
  54. ) ||
  55. user.highlightEnabled
  56. ) {
  57. this.createHighlightCSS(
  58. user,
  59. `div.wrap:has( div.header > a.name[href*="${user.username}" i] )`
  60. );
  61. this.createHighlightCSS(
  62. user,
  63. `div.wrap:has( div.details > a.name[href*="${user.username}" i] )`
  64. );
  65. }
  66.  
  67. if (
  68. this.Settings.getOptionValue(
  69. this.Settings.Options.highlightEnabledForReplies
  70. ) ||
  71. user.highlightEnabledForReplies
  72. ) {
  73. this.createHighlightCSS(
  74. user,
  75. `div.reply:has( a.name[href*="${user.username}" i] )`
  76. );
  77. }
  78. }
  79.  
  80. this.disableHighlightOnSmallCards();
  81.  
  82. if (
  83. this.Settings.getOptionValue(
  84. this.Settings.Options.moveSubscribeButtons
  85. )
  86. ) {
  87. this.otherStyles += `
  88. .has-label::before {
  89. top: -30px !important;
  90. left: unset !important;
  91. right: -10px;
  92. }
  93. .has-label[label="Unsubscribe"],
  94. .has-label[label="Subscribe"] {
  95. font-size: 0.875em !important;
  96. }
  97. .has-label[label="Unsubscribe"] {
  98. color: rgba(var(--color-green),.8);
  99. }
  100. `;
  101. }
  102.  
  103. if (
  104. this.Settings.getOptionValue(
  105. this.Settings.Options.hideLikeCount
  106. )
  107. ) {
  108. this.otherStyles += `
  109. .like-wrap .count {
  110. display: none;
  111. }
  112. `;
  113. }
  114. }
  115.  
  116. createUsernameCSS(user) {
  117. this.usernameStyles += `
  118. a.name[href*="${user.username}" i]::after {
  119. content: "${
  120. this.stringIsEmpty(user.sign) ??
  121. this.Settings.getOptionValue(
  122. this.Settings.Options.defaultSign
  123. )
  124. }";
  125. color: ${
  126. this.getUserColor(user) ?? "rgb(var(--color-blue))"
  127. }
  128. }
  129. `;
  130. }
  131.  
  132. createHighlightCSS(user, selector) {
  133. this.highlightStyles += `
  134. ${selector} {
  135. margin-right: -${this.Settings.getOptionValue(
  136. this.Settings.Options.highlightSize
  137. )};
  138. border-right: ${this.Settings.getOptionValue(
  139. this.Settings.Options.highlightSize
  140. )} solid ${
  141. this.getUserColor(user) ?? this.getDefaultHighlightColor()
  142. };
  143. border-radius: 5px;
  144. }
  145. `;
  146. }
  147.  
  148. disableHighlightOnSmallCards() {
  149. this.highlightStyles += `
  150. div.wrap:has(div.small) {
  151. margin-right: 0px !important;
  152. border-right: 0px solid black !important;
  153. }
  154. `;
  155. }
  156.  
  157. refreshHomePage() {
  158. if (
  159. !this.Settings.getOptionValue(
  160. this.Settings.Options.highlightEnabled
  161. )
  162. ) {
  163. return;
  164. }
  165.  
  166. this.createStyleLink(this.highlightStyles, "highlight");
  167. }
  168.  
  169. clearProfileVerify() {
  170. this.profileLink.href =
  171. "data:text/css;charset=UTF-8," + encodeURIComponent("");
  172. }
  173.  
  174. clearStyles(id) {
  175. const styles = document.getElementById(
  176. `void-verified-${id}-styles`
  177. );
  178. styles?.remove();
  179. }
  180.  
  181. verifyProfile() {
  182. if (
  183. !this.Settings.getOptionValue(
  184. this.Settings.Options.enabledForProfileName
  185. )
  186. ) {
  187. return;
  188. }
  189.  
  190. const usernameHeader = document.querySelector("h1.name");
  191. const username = usernameHeader.innerHTML.trim();
  192.  
  193. const user = this.Settings.VerifiedUsers.find(
  194. (u) => u.username === username
  195. );
  196. if (!user) {
  197. this.clearProfileVerify();
  198. return;
  199. }
  200.  
  201. const profileStyle = `
  202. h1.name::after {
  203. content: "${
  204. this.stringIsEmpty(user.sign) ??
  205. this.Settings.getOptionValue(
  206. this.Settings.Options.defaultSign
  207. )
  208. }"
  209. }
  210. `;
  211. this.profileLink.href =
  212. "data:text/css;charset=UTF-8," +
  213. encodeURIComponent(profileStyle);
  214. }
  215.  
  216. copyUserColor() {
  217. const usernameHeader = document.querySelector("h1.name");
  218. const username = usernameHeader.innerHTML.trim();
  219. const user = this.Settings.VerifiedUsers.find(
  220. (u) => u.username === username
  221. );
  222.  
  223. if (!user) {
  224. return;
  225. }
  226.  
  227. if (
  228. !(
  229. user.copyColorFromProfile ||
  230. this.Settings.getOptionValue(
  231. this.Settings.Options.copyColorFromProfile
  232. )
  233. )
  234. ) {
  235. return;
  236. }
  237.  
  238. const color =
  239. getComputedStyle(usernameHeader).getPropertyValue(
  240. "--color-blue"
  241. );
  242.  
  243. this.Settings.updateUserOption(user.username, "color", color);
  244. }
  245.  
  246. getUserColor(user) {
  247. return (
  248. user.colorOverride ??
  249. (user.color &&
  250. (user.copyColorFromProfile ||
  251. this.Settings.getOptionValue(
  252. this.Settings.Options.copyColorFromProfile
  253. ))
  254. ? `rgb(${user.color})`
  255. : undefined)
  256. );
  257. }
  258.  
  259. getDefaultHighlightColor() {
  260. if (
  261. this.Settings.getOptionValue(
  262. this.Settings.Options.useDefaultHighlightColor
  263. )
  264. ) {
  265. return this.Settings.getOptionValue(
  266. this.Settings.Options.defaultHighlightColor
  267. );
  268. }
  269. return "rgb(var(--color-blue))";
  270. }
  271.  
  272. createStyleLink(styles, id) {
  273. const oldLink = document.getElementById(
  274. `void-verified-${id}-styles`
  275. );
  276. const link = document.createElement("link");
  277. link.setAttribute("id", `void-verified-${id}-styles`);
  278. link.setAttribute("rel", "stylesheet");
  279. link.setAttribute("type", "text/css");
  280. link.setAttribute(
  281. "href",
  282. "data:text/css;charset=UTF-8," + encodeURIComponent(styles)
  283. );
  284. document.head?.append(link);
  285. oldLink?.remove();
  286. return link;
  287. }
  288.  
  289. stringIsEmpty(string) {
  290. if (!string || string.length === 0) {
  291. return undefined;
  292. }
  293. return string;
  294. }
  295. }
  296.  
  297. class GlobalCSS {
  298. settings;
  299. styleHandler;
  300.  
  301. styleId = "global-css";
  302. isCleared = false;
  303.  
  304. cssInLocalStorage = "void-verified-global-css";
  305. constructor(settings) {
  306. this.settings = settings;
  307. this.styleHandler = new StyleHandler(settings);
  308.  
  309. this.css = localStorage.getItem(this.cssInLocalStorage) ?? "";
  310. }
  311.  
  312. createCss() {
  313. if (
  314. !this.settings.getOptionValue(
  315. this.settings.Options.globalCssEnabled
  316. )
  317. ) {
  318. this.styleHandler.clearStyles(this.styleId);
  319. return;
  320. }
  321.  
  322. if (!this.shouldRender()) {
  323. return;
  324. }
  325.  
  326. this.isCleared = false;
  327. this.styleHandler.createStyleLink(this.css, this.styleId);
  328. }
  329.  
  330. updateCss(css) {
  331. this.css = css;
  332. this.createCss();
  333. localStorage.setItem(this.cssInLocalStorage, css);
  334. }
  335.  
  336. clearCssForProfile() {
  337. if (this.isCleared) {
  338. return;
  339. }
  340. if (!this.shouldRender()) {
  341. this.styleHandler.clearStyles(this.styleId);
  342. this.isCleared = true;
  343. }
  344. }
  345.  
  346. shouldRender() {
  347. if (window.location.pathname.startsWith("/settings/")) {
  348. return false;
  349. }
  350.  
  351. if (
  352. !this.settings.getOptionValue(
  353. this.settings.Options.globalCssAutoDisable
  354. )
  355. ) {
  356. return true;
  357. }
  358.  
  359. const profileCustomCss = document.getElementById(
  360. "customCSS-automail-styles"
  361. );
  362. const shouldRender = profileCustomCss.innerHTML.trim().length === 0;
  363. return shouldRender;
  364. }
  365. }
  366.  
  367. class Settings {
  368. LocalStorageUsers = "void-verified-users";
  369. LocalStorageSettings = "void-verified-settings";
  370. Version = "DEV";
  371.  
  372. Options = {
  373. copyColorFromProfile: {
  374. defaultValue: true,
  375. description: "Copy user color from their profile when visited.",
  376. },
  377. moveSubscribeButtons: {
  378. defaultValue: false,
  379. description:
  380. "Move activity subscribe button next to comments and likes.",
  381. },
  382. hideLikeCount: {
  383. defaultValue: false,
  384. description: "Hide activity and reply like counts.",
  385. },
  386. enabledForUsername: {
  387. defaultValue: true,
  388. description: "Display a verified sign next to usernames.",
  389. },
  390. enabledForProfileName: {
  391. defaultValue: false,
  392. description: "Display a verified sign next to a profile name.",
  393. },
  394. defaultSign: {
  395. defaultValue: "✔",
  396. description: "The default sign displayed next to a username.",
  397. },
  398. highlightEnabled: {
  399. defaultValue: true,
  400. description: "Highlight user activity with a border.",
  401. },
  402. highlightEnabledForReplies: {
  403. defaultValue: true,
  404. description: "Highlight replies with a border.",
  405. },
  406. highlightSize: {
  407. defaultValue: "5px",
  408. description: "Width of the highlight border.",
  409. },
  410. useDefaultHighlightColor: {
  411. defaultValue: false,
  412. description:
  413. "Use fallback highlight color when user color is not specified.",
  414. },
  415. defaultHighlightColor: {
  416. defaultValue: "#FFFFFF",
  417. description: "Fallback highlight color.",
  418. },
  419. globalCssEnabled: {
  420. defaultValue: false,
  421. description: "Enable custom global CSS.",
  422. },
  423. globalCssAutoDisable: {
  424. defaultValue: true,
  425. description:
  426. "Disable global CSS when a profile has custom CSS.",
  427. },
  428. };
  429.  
  430. VerifiedUsers = [];
  431.  
  432. constructor() {
  433. this.VerifiedUsers =
  434. JSON.parse(localStorage.getItem(this.LocalStorageUsers)) ?? [];
  435.  
  436. const settingsInLocalStorage =
  437. JSON.parse(localStorage.getItem(this.LocalStorageSettings)) ??
  438. {};
  439.  
  440. for (const [key, value] of Object.entries(settingsInLocalStorage)) {
  441. if (!this.Options[key]) {
  442. continue;
  443. }
  444. this.Options[key].value = value.value;
  445. }
  446. }
  447.  
  448. getOptionValue(object) {
  449. if (object.value === "") {
  450. return object.defaultValue;
  451. }
  452. return object.value ?? object.defaultValue;
  453. }
  454.  
  455. verifyUser(username) {
  456. if (this.VerifiedUsers.find((user) => user.username === username)) {
  457. return;
  458. }
  459.  
  460. this.VerifiedUsers.push({ username });
  461. localStorage.setItem(
  462. this.LocalStorageUsers,
  463. JSON.stringify(this.VerifiedUsers)
  464. );
  465. }
  466.  
  467. updateUserOption(username, key, value) {
  468. this.VerifiedUsers = this.VerifiedUsers.map((u) =>
  469. u.username === username
  470. ? {
  471. ...u,
  472. [key]: value,
  473. }
  474. : u
  475. );
  476. localStorage.setItem(
  477. this.LocalStorageUsers,
  478. JSON.stringify(this.VerifiedUsers)
  479. );
  480. }
  481.  
  482. removeUser(username) {
  483. this.VerifiedUsers = this.VerifiedUsers.filter(
  484. (user) => user.username !== username
  485. );
  486. localStorage.setItem(
  487. this.LocalStorageUsers,
  488. JSON.stringify(this.VerifiedUsers)
  489. );
  490. }
  491.  
  492. saveSettingToLocalStorage(key, value) {
  493. let localSettings = JSON.parse(
  494. localStorage.getItem(this.LocalStorageSettings)
  495. );
  496.  
  497. this.Options[key].value = value;
  498.  
  499. if (localSettings === null) {
  500. const settings = {
  501. [key]: value,
  502. };
  503. localStorage.setItem(
  504. this.LocalStorageSettings,
  505. JSON.stringify(settings)
  506. );
  507. return;
  508. }
  509.  
  510. localSettings[key] = { value };
  511. localStorage.setItem(
  512. this.LocalStorageSettings,
  513. JSON.stringify(localSettings)
  514. );
  515. }
  516. }
  517.  
  518. class ActivityHandler {
  519. settings;
  520. constructor(settings) {
  521. this.settings = settings;
  522. }
  523.  
  524. moveAndDisplaySubscribeButton() {
  525. if (
  526. !this.settings.getOptionValue(
  527. this.settings.Options.moveSubscribeButtons
  528. )
  529. ) {
  530. return;
  531. }
  532.  
  533. const subscribeButtons = document.querySelectorAll(
  534. "span[label='Unsubscribe'], span[label='Subscribe']"
  535. );
  536. for (const subscribeButton of subscribeButtons) {
  537. if (subscribeButton.parentNode.classList.contains("actions")) {
  538. continue;
  539. }
  540.  
  541. const container = subscribeButton.parentNode.parentNode;
  542. const actions = container.querySelector(".actions");
  543. actions.append(subscribeButton);
  544. }
  545. }
  546. }
  547.  
  548. class SettingsUserInterface {
  549. Settings;
  550. StyleHandler;
  551. globalCSS;
  552. AnilistBlue = "120, 180, 255";
  553.  
  554. constructor(settings, styleHandler, globalCSS) {
  555. this.Settings = settings;
  556. this.StyleHandler = styleHandler;
  557. this.globalCSS = globalCSS;
  558. }
  559.  
  560. renderSettingsUi() {
  561. const container = document.querySelector(
  562. ".settings.container > .content"
  563. );
  564. const settingsContainer = document.createElement("div");
  565. settingsContainer.setAttribute("id", "voidverified-settings");
  566. this.renderSettingsHeader(settingsContainer);
  567.  
  568. const settingsListContainer = document.createElement("div");
  569. settingsListContainer.style.display = "flex";
  570. settingsListContainer.style.flexDirection = "column";
  571. settingsListContainer.style.gap = "5px";
  572. for (const [key, setting] of Object.entries(
  573. this.Settings.Options
  574. )) {
  575. this.renderSetting(setting, settingsListContainer, key);
  576. }
  577.  
  578. settingsContainer.append(settingsListContainer);
  579.  
  580. this.renderUserTable(settingsContainer);
  581.  
  582. this.renderCustomCssEditor(settingsContainer);
  583.  
  584. container.append(settingsContainer);
  585. }
  586.  
  587. removeSettingsUi() {
  588. const settings = document.querySelector("#voidverified-settings");
  589. settings?.remove();
  590. }
  591.  
  592. renderSettingsHeader(settingsContainer) {
  593. const headerContainer = document.createElement("div");
  594. const header = document.createElement("h1");
  595. header.style.marginTop = "30px";
  596. header.innerText = "VoidVerified Settings";
  597.  
  598. const versionInfo = document.createElement("p");
  599. versionInfo.append("Version: ");
  600. const versionNumber = document.createElement("span");
  601. versionNumber.style.color = `rgb(${this.AnilistBlue})`;
  602. versionNumber.append(this.Settings.Version);
  603.  
  604. versionInfo.append(versionNumber);
  605.  
  606. headerContainer.append(header);
  607. headerContainer.append(versionInfo);
  608. settingsContainer.append(headerContainer);
  609. }
  610.  
  611. renderUserTable(settingsContainer) {
  612. const oldTableContainer = document.querySelector(
  613. "#void-verified-user-table"
  614. );
  615. const tableContainer =
  616. oldTableContainer ?? document.createElement("div");
  617. tableContainer.innerHTML = "";
  618.  
  619. tableContainer.setAttribute("id", "void-verified-user-table");
  620.  
  621. const table = document.createElement("table");
  622. const head = document.createElement("thead");
  623. const headrow = document.createElement("tr");
  624. headrow.append(this.createCell("Username", "th"));
  625. headrow.append(this.createCell("Sign", "th"));
  626. headrow.append(this.createCell("Color", "th"));
  627.  
  628. head.append(headrow);
  629.  
  630. const body = document.createElement("tbody");
  631.  
  632. for (const user of this.Settings.VerifiedUsers) {
  633. body.append(this.createUserRow(user));
  634. }
  635.  
  636. table.append(head);
  637. table.append(body);
  638. tableContainer.append(table);
  639.  
  640. const inputForm = document.createElement("form");
  641. inputForm.addEventListener("submit", (event) =>
  642. this.handleVerifyUserForm(event, this.Settings)
  643. );
  644. const label = document.createElement("label");
  645. label.innerText = "Add user";
  646. inputForm.append(label);
  647. const textInput = document.createElement("input");
  648. textInput.setAttribute("id", "voidverified-add-user");
  649.  
  650. inputForm.append(textInput);
  651. tableContainer.append(inputForm);
  652.  
  653. oldTableContainer || settingsContainer.append(tableContainer);
  654. }
  655.  
  656. createUserRow(user) {
  657. const row = document.createElement("tr");
  658. const userLink = document.createElement("a");
  659. userLink.innerText = user.username;
  660. userLink.setAttribute(
  661. "href",
  662. `https://anilist.co/user/${user.username}/`
  663. );
  664. userLink.setAttribute("target", "_blank");
  665. row.append(this.createCell(userLink));
  666.  
  667. const signInput = document.createElement("input");
  668. signInput.value = user.sign ?? "";
  669. signInput.style.width = "100px";
  670. signInput.addEventListener("input", (event) =>
  671. this.updateUserOption(user.username, "sign", event.target.value)
  672. );
  673. const signCell = this.createCell(signInput);
  674. signCell.append(
  675. this.createUserCheckbox(
  676. user.enabledForUsername,
  677. user.username,
  678. "enabledForUsername",
  679. this.Settings.getOptionValue(
  680. this.Settings.Options.enabledForUsername
  681. )
  682. )
  683. );
  684.  
  685. row.append(this.createCell(signCell));
  686.  
  687. const colorInput = document.createElement("input");
  688. colorInput.setAttribute("type", "color");
  689. colorInput.style.border = "0";
  690. colorInput.style.height = "24px";
  691. colorInput.style.width = "40px";
  692. colorInput.style.padding = "0";
  693. colorInput.style.backgroundColor = "unset";
  694. colorInput.value = this.getUserColorPickerColor(user);
  695. colorInput.addEventListener(
  696. "change",
  697. (event) => this.handleUserColorChange(event, user.username),
  698. false
  699. );
  700.  
  701. const colorInputContainer = document.createElement("span");
  702.  
  703. const colorCell = this.createCell(colorInput);
  704.  
  705. colorInputContainer.append(
  706. this.createUserCheckbox(
  707. user.copyColorFromProfile,
  708. user.username,
  709. "copyColorFromProfile",
  710. this.Settings.getOptionValue(
  711. this.Settings.Options.copyColorFromProfile
  712. )
  713. )
  714. );
  715.  
  716. colorInputContainer.append(
  717. this.createUserCheckbox(
  718. user.highlightEnabled,
  719. user.username,
  720. "highlightEnabled",
  721. this.Settings.getOptionValue(
  722. this.Settings.Options.highlightEnabled
  723. )
  724. )
  725. );
  726.  
  727. colorInputContainer.append(
  728. this.createUserCheckbox(
  729. user.highlightEnabledForReplies,
  730. user.username,
  731. "highlightEnabledForReplies",
  732. this.Settings.getOptionValue(
  733. this.Settings.Options.highlightEnabledForReplies
  734. )
  735. )
  736. );
  737.  
  738. colorCell.append(colorInputContainer);
  739.  
  740. const resetColorBtn = document.createElement("button");
  741. resetColorBtn.innerText = "🔄";
  742. resetColorBtn.addEventListener("click", () =>
  743. this.handleUserColorReset(user.username)
  744. );
  745.  
  746. colorCell.append(resetColorBtn);
  747. row.append(colorCell);
  748.  
  749. const deleteButton = document.createElement("button");
  750. deleteButton.innerText = "❌";
  751. deleteButton.addEventListener("click", () =>
  752. this.removeUser(user.username)
  753. );
  754. row.append(this.createCell(deleteButton));
  755. return row;
  756. }
  757.  
  758. getUserColorPickerColor(user) {
  759. if (user.colorOverride) {
  760. return user.colorOverride;
  761. }
  762.  
  763. if (
  764. user.color &&
  765. (user.copyColorFromProfile ||
  766. this.Settings.getOptionValue(
  767. this.Settings.Options.copyColorFromProfile
  768. ))
  769. ) {
  770. return this.rgbToHex(user.color);
  771. }
  772.  
  773. if (
  774. this.Settings.getOptionValue(
  775. this.Settings.Options.useDefaultHighlightColor
  776. )
  777. ) {
  778. return this.Settings.getOptionValue(
  779. this.Settings.Options.defaultHighlightColor
  780. );
  781. }
  782.  
  783. return this.rgbToHex(this.AnilistBlue);
  784. }
  785.  
  786. createUserCheckbox(isChecked, username, settingKey, disabled) {
  787. const checkbox = document.createElement("input");
  788. if (disabled) {
  789. checkbox.setAttribute("disabled", "");
  790. }
  791.  
  792. checkbox.setAttribute("type", "checkbox");
  793. checkbox.checked = isChecked;
  794. checkbox.addEventListener("change", (event) => {
  795. this.updateUserOption(
  796. username,
  797. settingKey,
  798. event.target.checked
  799. );
  800. this.refreshUserTable();
  801. });
  802.  
  803. checkbox.style.marginLeft = "5px";
  804.  
  805. checkbox.title = this.Settings.Options[settingKey].description;
  806. return checkbox;
  807. }
  808.  
  809. handleUserColorReset(username) {
  810. this.updateUserOption(username, "colorOverride", undefined);
  811. this.refreshUserTable();
  812. }
  813.  
  814. handleUserColorChange(event, username) {
  815. const color = event.target.value;
  816. this.updateUserOption(username, "colorOverride", color);
  817. }
  818.  
  819. handleVerifyUserForm(event, settings) {
  820. event.preventDefault();
  821.  
  822. const usernameInput = document.getElementById(
  823. "voidverified-add-user"
  824. );
  825. const username = usernameInput.value;
  826. settings.verifyUser(username);
  827. usernameInput.value = "";
  828. this.refreshUserTable();
  829. }
  830.  
  831. refreshUserTable() {
  832. const container = document.querySelector(
  833. ".settings.container > .content"
  834. );
  835. this.renderUserTable(container);
  836. }
  837.  
  838. updateUserOption(username, key, value) {
  839. this.Settings.updateUserOption(username, key, value);
  840. this.StyleHandler.refreshStyles();
  841. }
  842.  
  843. removeUser(username) {
  844. this.Settings.removeUser(username);
  845. this.refreshUserTable();
  846. this.StyleHandler.refreshStyles();
  847. }
  848.  
  849. verifyUser(username) {
  850. this.Settings.verifyUser(username);
  851. this.StyleHandler.refreshStyles();
  852. }
  853.  
  854. createCell(content, elementType = "td") {
  855. const cell = document.createElement(elementType);
  856. cell.append(content);
  857. return cell;
  858. }
  859.  
  860. renderSetting(
  861. setting,
  862. settingsContainer,
  863. settingKey,
  864. disabled = false
  865. ) {
  866. const value = this.Settings.getOptionValue(setting);
  867. const type = typeof value;
  868.  
  869. const container = document.createElement("div");
  870. const input = document.createElement("input");
  871.  
  872. if (type === "boolean") {
  873. input.setAttribute("type", "checkbox");
  874. } else if (settingKey == "defaultHighlightColor") {
  875. input.setAttribute("type", "color");
  876. input.style.border = "0";
  877. input.style.height = "15px";
  878. input.style.width = "25px";
  879. input.style.padding = "0";
  880. input.style.backgroundColor = "unset";
  881. } else if (type === "string") {
  882. input.setAttribute("type", "text");
  883. input.style.width = "50px";
  884. }
  885.  
  886. if (disabled) {
  887. input.setAttribute("disabled", "");
  888. }
  889.  
  890. input.setAttribute("id", settingKey);
  891. input.addEventListener("change", (event) =>
  892. this.handleOption(event, settingKey, type)
  893. );
  894.  
  895. if (type === "boolean" && value) {
  896. input.setAttribute("checked", true);
  897. } else if (type === "string") {
  898. input.value = value;
  899. }
  900.  
  901. container.append(input);
  902.  
  903. const label = document.createElement("label");
  904. label.setAttribute("for", settingKey);
  905. label.innerText = setting.description;
  906. label.style.marginLeft = "5px";
  907. container.append(label);
  908. settingsContainer.append(container);
  909. }
  910.  
  911. handleOption(event, settingKey, type) {
  912. const value =
  913. type === "boolean" ? event.target.checked : event.target.value;
  914. this.Settings.saveSettingToLocalStorage(settingKey, value);
  915. this.StyleHandler.refreshStyles();
  916. this.refreshUserTable();
  917. }
  918.  
  919. renderCustomCssEditor(settingsContainer) {
  920. const container = document.createElement("div");
  921. const label = document.createElement("label");
  922. label.innerText = "Custom Global CSS";
  923. label.setAttribute("for", "void-verified-global-css-editor");
  924. label.style.marginTop = "20px";
  925. label.style.fontSize = "2rem";
  926. label.style.display = "inline-block";
  927. container.append(label);
  928.  
  929. const textarea = document.createElement("textarea");
  930. textarea.setAttribute("id", "void-verified-global-css-editor");
  931.  
  932. textarea.value = this.globalCSS.css;
  933. textarea.style.width = "100%";
  934. textarea.style.height = "200px";
  935. textarea.style.resize = "vertical";
  936. textarea.style.background = "#14191f";
  937. textarea.style.color = "white";
  938.  
  939. textarea.addEventListener("change", (event) => {
  940. this.handleCustomCssEditor(event, this);
  941. });
  942.  
  943. container.append(textarea);
  944.  
  945. const notice = document.createElement("div");
  946. notice.innerText =
  947. "Please note that Custom CSS is disabled in the settings. \nIn the event that you accidentally disable rendering of critical parts of AniList, navigate to the settings by URL";
  948. notice.style.fontSize = "11px";
  949. container.append(notice);
  950.  
  951. settingsContainer.append(container);
  952. }
  953.  
  954. handleCustomCssEditor(event, settingsUi) {
  955. const value = event.target.value;
  956. settingsUi.globalCSS.updateCss(value);
  957. }
  958.  
  959. rgbToHex(rgb) {
  960. const [r, g, b] = rgb.split(",");
  961. const hex = this.generateHex(r, g, b);
  962. return hex;
  963. }
  964.  
  965. generateHex(r, g, b) {
  966. return (
  967. "#" +
  968. [r, g, b]
  969. .map((x) => {
  970. const hex = Number(x).toString(16);
  971. return hex.length === 1 ? "0" + hex : hex;
  972. })
  973. .join("")
  974. );
  975. }
  976. }
  977.  
  978. class IntervalScriptHandler {
  979. styleHandler;
  980. settingsUi;
  981. activityHandler;
  982. settings;
  983. globalCSS;
  984. constructor(settings) {
  985. this.settings = settings;
  986.  
  987. this.styleHandler = new StyleHandler(settings);
  988. this.globalCSS = new GlobalCSS(settings);
  989. this.settingsUi = new SettingsUserInterface(
  990. settings,
  991. this.styleHandler,
  992. this.globalCSS
  993. );
  994. this.activityHandler = new ActivityHandler(settings);
  995. }
  996.  
  997. currentPath = "";
  998. evaluationIntervalInSeconds = 1;
  999. hasPathChanged(path) {
  1000. if (path === this.currentPath) {
  1001. return false;
  1002. }
  1003. this.currentPath = path;
  1004. return true;
  1005. }
  1006.  
  1007. handleIntervalScripts(intervalScriptHandler) {
  1008. const path = window.location.pathname;
  1009.  
  1010. intervalScriptHandler.activityHandler.moveAndDisplaySubscribeButton();
  1011. intervalScriptHandler.globalCSS.clearCssForProfile();
  1012.  
  1013. if (path === "/home") {
  1014. intervalScriptHandler.styleHandler.refreshHomePage();
  1015. }
  1016.  
  1017. if (!path.startsWith("/settings/developer")) {
  1018. intervalScriptHandler.settingsUi.removeSettingsUi();
  1019. }
  1020.  
  1021. if (!intervalScriptHandler.hasPathChanged(path)) {
  1022. return;
  1023. }
  1024.  
  1025. intervalScriptHandler.styleHandler.clearProfileVerify();
  1026. intervalScriptHandler.globalCSS.createCss();
  1027.  
  1028. if (path.startsWith("/user/")) {
  1029. intervalScriptHandler.styleHandler.verifyProfile();
  1030. intervalScriptHandler.styleHandler.copyUserColor();
  1031. return;
  1032. }
  1033.  
  1034. if (path.startsWith("/settings/developer")) {
  1035. intervalScriptHandler.settingsUi.renderSettingsUi();
  1036. return;
  1037. }
  1038. }
  1039.  
  1040. enableScriptIntervalHandling() {
  1041. setInterval(() => {
  1042. this.handleIntervalScripts(this);
  1043. }, this.evaluationIntervalInSeconds * 1000);
  1044. }
  1045. }
  1046.  
  1047. const settings = new Settings();
  1048. const styleHandler = new StyleHandler(settings);
  1049. const intervalScriptHandler = new IntervalScriptHandler(settings);
  1050.  
  1051. styleHandler.refreshStyles();
  1052. intervalScriptHandler.enableScriptIntervalHandling();
  1053.  
  1054. console.log(`VoidVerified ${settings.Version} loaded.`);
  1055. })();

QingJ © 2025

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