您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Reusable library that initialized the boxicons css and serves functions to turn stats and menus into icons
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/497064/1433536/AO3Boxicons.js
// ==UserScript== // @exclude * // @author Yours Truly // @version 1.2 // ==UserLibrary== // @name AO3Boxicons // @description Reusable library that initialized the boxicons css and serves functions to turn stats and menus into icons // @license MIT // ==/UserScript== // ==/UserLibrary== /** * * @param {Object} settings * @param {String} settings.boxiconsVersion Used version of https://boxicons.com/ * @param {Boolean} settings.iconifyStats Flag that indicates if the AO3 work stat names should be turned into icons * @param {Object} settings.statsSettings Individual settings for stat icons * that typically consist of { iconClass: string, solid: boolean, tooltip: string } * @param {Object} settings.statsSettings.wordCountOptions * @param {Object} settings.statsSettings.chaptersOptions * @param {Object} settings.statsSettings.collectionsOptions * @param {Object} settings.statsSettings.commentsOptions * @param {Object} settings.statsSettings.kudosOptions * @param {Object} settings.statsSettings.bookmarksOptions * @param {Object} settings.statsSettings.hitsOptions * @param {Object} settings.statsSettings.workSubsOptions * @param {Object} settings.statsSettings.authorSubsOptions * @param {Object} settings.statsSettings.commentThreadsOptions * @param {Object} settings.statsSettings.fandomsOptions * @param {Object} settings.statsSettings.requestOptions * @param {Object} settings.statsSettings.workCountOptions * @param {Object} settings.statsSettings.seriesCompleteOptions * @param {Object} settings.statsSettings.kudos2HitsOptions * @param {Object} settings.statsSettings.timeToReadOptions * @param {Object} settings.statsSettings.dateWorkPublishedOptions * @param {Object} settings.statsSettings.dateWorkUpdateOptions * @param {Object} settings.statsSettings.dateWorkCompleteOptions * @param {Object} settings.iconifyUserNav Flag that indicates if the AO3 user navigation should be turned into icons * @param {Object} settings.userNavSettings Individual settings for user nav icons * that typically consist of { iconClass: string, solid: boolean, tooltip: string, addTooltip: boolean } * @param {Object} settings.accountOptions * @param {Object} settings.postNewOptions * @param {Object} settings.logoutOptions * */ function IconifyAO3(customSettings = {}) { /** * Merges the second object into the first * If a value is in `a` but not in `b`, the value stays like it is. * If a value is in `b` but not in `a`, it gets copied over. * If a value is in both `a` and `b`, the value of `b` takes preference. * * @param {Object} a original settings * @param {Object} b user settings overwrite * @param {*} c used for temp storage, don't worry about it */ function mergeSettings(a, b, c) { for (c in b) b.hasOwnProperty(c) && ((typeof a[c])[0] == "o" ? m(a[c], b[c]) : (a[c] = b[c])); } // set global settings and overwrite with incoming settings const settings = { boxiconsVersion: "2.1.4", }; mergeSettings(settings, customSettings); /** * Initialises boxicons.com css and adds a small css to add some space between icon and stats count. */ function initBoxicons() { // load boxicon style const boxicons = document.createElement("link"); boxicons.setAttribute("href", `https://unpkg.com/boxicons@${settings?.boxiconsVersion}/css/boxicons.min.css`); boxicons.setAttribute("rel", "stylesheet"); document.head.appendChild(boxicons); // css that adds margin for icons const boxiconsCSS = document.createElement("style"); boxiconsCSS.setAttribute("type", "text/css"); boxiconsCSS.innerHTML = ` i.bx { margin-right: .3em; }`; document.head.appendChild(boxiconsCSS); } /** * Creates a new element with the icon class added to the classList. * * @param {Object} options * @param {String} options.iconClass Name of the boxicons class to use. (The "bx(s)" prefix can be omitted) * @param {String} options.tooltip Adds an optional tooltip to the element. * Only if `addTooltip = true`. * @param {Boolean} options.addTooltip Indicates if a tooltip should be added to the element. * `tooltip` needs to be present in `options`. * @param {Boolean} options.solid Indicates if the icon should be of the "solid" variant. * Will be ignored if `iconClass` has "bx(s)" prefix. * @returns <i> Element with the neccessary classes for a boxicons icon. */ function getNewIconElement(options = {}) { const i = document.createElement("i"); i.classList.add("bx"); if (options?.addTooltip && options?.tooltip) i.setAttribute("title", options.tooltip); if (/^bxs?-/i.test(options.iconClass)) { // check if the icon class has the bx(s) prefix and simply set it, ignoring any settings for solid i.classList.add(options.iconClass); } else { // else, add the fittings prefix i.classList.add(options?.solid ? "bxs-" + options.iconClass : "bx-" + options.iconClass); } return i; } /** * Prepends the given boxicons class to the given element. * Note: If the element is an <i> tag, nothing will happen, as we assume that the <i> is already an icon. * * @param {HTMLElement} element Parent element that the icon class should be prepended to. * @param {Object} options * @param {String} options.iconClass Name of the boxicons class to use. (The "bx(s)" prefix can be omitted) * @param {String} options.tooltip Adds a tooltip to the element * @param {Boolean} options.solid Indicates if the icon should be of the "solid" variant. * Will be ignored if iconClass has "bx(s)" prefix. */ function setIcon(element, options = {}) { if (element.tagName !== "I") element.prepend(getNewIconElement(options)); if (options?.tooltip) element.setAttribute("title", options.tooltip); } /** * Iterates through all elements that apply to the given querySelector and adds an element with the given icon class to it. * * @param {String} querySelector CSS selector for the elements to find and iconify. * @param {Object} options * @param {String} options.iconClass Name of the boxicons class to use. (The "bx(s)" prefix can be omitted) * @param {String} options.tooltip Adds a tooltip to the element * @param {Boolean} options.solid Indicates if the icon should be of the "solid" variant. * Will be ignored if iconClass has "bx(s)" prefix. */ function findElementsAndSetIcon(querySelector, options = {}) { const els = document.querySelectorAll(querySelector); els.forEach((el) => (el.firstChild.nodeType === Node.ELEMENT_NODE ? setIcon(el.firstChild, options) : setIcon(el, options))); } /** * Adds an CSS that will hide the stats titles and prepends an icon to all stats. */ function iconifyStats() { const WordsTotal = "dl.statistics dd.words"; const WordsWork = "dl.stats dd.words"; const WordsSeries = ".series.meta.group dl.stats>dd:nth-of-type(1)"; const ChaptersWork = "dl.stats dd.chapters"; const CollectionsWork = "dl.stats dd.collections"; const CommentsWork = "dl.stats dd.comments"; const KudosTotal = "dl.statistics dd.kudos"; const KudosWork = "dl.stats dd.kudos"; const BookmarksTotal = "dl.statistics dd.bookmarks"; const BookmarksWork = "dl.stats dd.bookmarks"; const BookmarksSeries = ".series.meta.group dl.stats>dd:nth-of-type(4)"; const BookmarksCollection = "li.collection dl.stats dd a[href$=bookmarks]"; const HitsTotal = "dl.statistics dd.hits"; const HitsWork = "dl.stats dd.hits"; const SubscribersTotal = "dl.statistics dd[class=subscriptions]"; const SubscribersWork = "dl.stats dd.subscriptions"; const FandomsCollection = "li.collection dl.stats dd a[href$=fandoms]"; const RequestsCollection = "li.collection dl.stats dd a[href$=requests]"; const AuthorSubscribers = "dl.statistics dd.user.subscriptions"; const CommentThreads = "dl.statistics dd.comment.thread"; const WorksCollection = "li.collection dl.stats dd a[href$=works]"; const WorksSeries = ".series.meta.group dl.stats>dd:nth-of-type(2)"; const SeriesComplete = ".series.meta.group dl.stats>dd:nth-of-type(3)"; const Kudos2HitsWork = "dl.stats dd.kudos-hits-ratio"; const ReadingTimeWork = "dl.stats dd.reading-time"; const DatePublishedWork = "dl.work dl.stats dd.published"; const DateStatusTitle = "dl.work dl.stats dt.status"; const DateStatusWork = "dl.work dl.stats dd.status"; const localSettings = { wordCountOptions: { tooltip: "Word Count", iconClass: "pen", solid: true }, chaptersOptions: { tooltip: "Chapters", iconClass: "food-menu" }, collectionsOptions: { tooltip: "Collections", iconClass: "collection", solid: true }, commentsOptions: { tooltip: "Comments", iconClass: "chat", solid: true }, kudosOptions: { tooltip: "Kudos", iconClass: "heart", solid: true }, bookmarksOptions: { tooltip: "Bookmarks", iconClass: "bookmarks", solid: true }, hitsOptions: { tooltip: "Hits", iconClass: "show-alt" }, workSubsOptions: { tooltip: "Subscriptions", iconClass: "bell", solid: true }, authorSubsOptions: { tooltip: "User Subscriptions", iconClass: "bell-ring", solid: true }, commentThreadsOptions: { tooltip: "Comment Threads", iconClass: "conversation", solid: true }, fandomsOptions: { tooltip: "Fandoms", iconClass: "crown", solid: true }, requestsOptions: { tooltip: "Prompts", iconClass: "invader", solid: true }, workCountOptions: { tooltip: "Work Count", iconClass: "library" }, seriesCompleteOptions: { tooltip: "Series Complete", iconClass: "flag-checkered", solid: true }, kudos2HitsOptions: { tooltip: "Kudos to Hits", iconClass: "hot", solid: true }, timeToReadOptions: { tooltip: "Time to Read", iconClass: "hourglass", solid: true }, dateWorkPublishedOptions: { tooltip: "Published", iconClass: "calendar-plus" }, dateWorkUpdateOptions: { tooltip: "Updated", iconClass: "calendar-edit" }, dateWorkCompleteOptions: { tooltip: "Completed", iconClass: "calendar-check" }, }; // merge incoming settings into local settings (overwrite) mergeSettings(localSettings, settings?.statsSettings); // css to hide stats titles const statsCSS = document.createElement("style"); statsCSS.setAttribute("type", "text/css"); statsCSS.innerHTML = ` dl.stats dt { display: none !important; }`; document.head.appendChild(statsCSS); findElementsAndSetIcon(`${WordsTotal}, ${WordsWork}, ${WordsSeries}`, localSettings.wordCountOptions); findElementsAndSetIcon(ChaptersWork, localSettings.chaptersOptions); findElementsAndSetIcon(CollectionsWork, localSettings.collectionsOptions); findElementsAndSetIcon(CommentsWork, localSettings.commentsOptions); findElementsAndSetIcon(`${KudosTotal}, ${KudosWork}`, localSettings.kudosOptions); findElementsAndSetIcon(`${BookmarksTotal}, ${BookmarksWork}, ${BookmarksCollection}, ${BookmarksSeries}`, localSettings.bookmarksOptions); findElementsAndSetIcon(`${HitsTotal}, ${HitsWork}`, localSettings.hitsOptions); findElementsAndSetIcon(`${SubscribersTotal}, ${SubscribersWork}`, localSettings.workSubsOptions); findElementsAndSetIcon(AuthorSubscribers, localSettings.authorSubsOptions); findElementsAndSetIcon(CommentThreads, localSettings.commentThreadsOptions); findElementsAndSetIcon(FandomsCollection, localSettings.fandomsOptions); findElementsAndSetIcon(`${WorksCollection}, ${WorksSeries}`, localSettings.workCountOptions); findElementsAndSetIcon(SeriesComplete, localSettings.seriesCompleteOptions); // AO3E elements findElementsAndSetIcon(Kudos2HitsWork, localSettings.kudos2HitsOptions); findElementsAndSetIcon(ReadingTimeWork, localSettings.timeToReadOptions); // calendar icons at works page findElementsAndSetIcon(DatePublishedWork, localSettings.dateWorkPublishedOptions); const workStatus = document.querySelector(DateStatusTitle); if (workStatus && workStatus.innerHTML.startsWith("Updated")) { setIcon(document.querySelector(DateStatusWork), localSettings.dateWorkUpdateOptions); } else if (workStatus && workStatus.innerHTML.startsWith("Completed")) { setIcon(document.querySelector(DateStatusWork), localSettings.dateWorkCompleteOptions); } } /** * Replaces the "Hi, {user}!", "Post" and "Log out" text at the top of the page with icons. */ function iconifyUserNav() { const localSettings = { accountOptions: { tooltip: "User Area", addTooltip: true, iconClass: "user-circle", solid: true }, postNewOptions: { tooltip: "New Work", addTooltip: true, iconClass: "book-add", solid: true }, logoutOptions: { tooltip: "Logout", addTooltip: true, iconClass: "log-out" }, }; // merge incoming settings into local settings (overwrite) mergeSettings(localSettings, settings?.userNavSettings); const AccountUserNav = "#header a.dropdown-toggle[href*='/users/']"; const PostUserNav = "#header a.dropdown-toggle[href*='/works/new']"; const LogoutUserNav = "#header a[href*='/users/logout']"; // add css for user navigation icons const userNavCss = document.createElement("style"); userNavCss.setAttribute("type", "text/css"); userNavCss.innerHTML = ` ${LogoutUserNav}, ${AccountUserNav}, ${PostUserNav} { /* font size needs to be higher to make icons the right size */ font-size: 1.25rem; /* left and right padding for a slightly bigger hover hitbox */ padding: 0 .3rem; } ${LogoutUserNav} i.bx { /* overwrite the right margin for logout icon */ margin-right: 0; /* add left margin instead to add more space to user actions */ margin-left: .3em; }`; document.head.appendChild(userNavCss); // replace text with icons document.querySelector(AccountUserNav).replaceChildren(getNewIconElement(localSettings.accountOptions)); document.querySelector(PostUserNav).replaceChildren(getNewIconElement(localSettings.postNewOptions)); document.querySelector(LogoutUserNav).replaceChildren(getNewIconElement(localSettings.logoutOptions)); } initBoxicons(); if (settings?.iconifyStats) iconifyStats(); if (settings?.iconifyUserNav) iconifyUserNav(); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址