VoidVerified

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

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

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

QingJ © 2025

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