Greasy Fork

Greasy Fork is available in English.

AO3LucideIcons

Reusable library that initialized lucide icons and serves functions to turn stats and menus into icons

当前为 2025-06-12 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/539241/1606701/AO3LucideIcons.js

作者
genusslicht
版本
1.0.0
创建于
2025-06-12
更新于
2025-06-12
大小
13.0 KB
许可证
MIT

// ==UserScript== // @exclude * // @author Yours Truly // @version 1.0.0 // @require https://unpkg.com/lucide@latest

// ==UserLibrary== // @name AO3LucideIcons // @description Reusable library that initialized lucide icons and serves functions to turn stats and menus into icons // @license MIT

// ==/UserScript==

// ==/UserLibrary==

/** *

  • @param {Object} settings
  • @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 { icon: 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.challengesOptions
  • @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 { icon: 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 = {}; mergeSettings(settings, customSettings);

/**

  • Initialises lucide.dev */ function initLucide() { lucide.createIcons(); const lucideCSS = document.createElement("style"); lucideCSS.setAttribute("type", "text/css"); lucideCSS.innerHTML = .lucide { width: 1em; height: 1em; margin-right: .3em; }; document.head.appendChild(lucideCSS); }

/**

  • Creates a new element with the icon class added to the classList. *
  • @param {Object} options
  • @param {String} options.icon Name of the icon to use.
  • @returns Element with the neccessary classes for a boxicons icon. */ function getNewIconElement(options = {}) { const i = document.createElement("i"); i.setAttribute("data-lucide", options.icon);

    return i; }

/**

  • Prepends the given boxicons class to the given element.
  • Note: If the element is an tag, nothing will happen, as we assume that the is already an icon. *
  • @param {HTMLElement} element Parent element that the icon class should be prepended to.
  • @param {Object} options
  • @param {String} options.icon Name of the icon to use
  • @param {String} options.tooltip Adds a tooltip to the element
  • @param {Boolean} options.addTooltip Indicates if a tooltip should be added to the element.
  • tooltip needs to be present in options. */ function setIcon(element, options = {}) { if (element.tagName !== "I") element.prepend(getNewIconElement(options)); if (options?.addTooltip && 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.icon Name of the icon to use.
  • @param {String} options.tooltip Adds a tooltip to the element.
  • @param {Boolean} options.addTooltip Indicates if a tooltip should be added to the element.
  • tooltip needs to be present in options. */ 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 TotalWords = "dl.statistics dd.words"; const TotalHits = "dl.statistics dd.hits"; const TotalKudos = "dl.statistics dd.kudos"; const TotalBookmarks = "dl.statistics dd.bookmarks"; const TotalCommentThreads = "dl.statistics dd.comment.thread"; const TotalSubscribersWorks = "dl.statistics dd[class=subscriptions]"; const TotalSubscribersAuthor = "dl.statistics dd.user.subscriptions";

    const WorkLanguage = "dl.stats dd.language"; const WorkWords = "dl.stats dd.words"; const WorkChapters = "dl.stats dd.chapters"; const WorkHits = "dl.stats dd.hits"; const WorkKudos = "dl.stats dd.kudos"; const WorkComments = "dl.stats dd.comments"; const WorkBookmarks = "dl.stats dd.bookmarks"; const WorkCollections = "dl.stats dd.collections"; const WorkSubscribers = "dl.stats dd.subscriptions"; const WorkKudos2Hits = "dl.stats dd.kudos-hits-ratio"; const WorkReadingTime = "dl.stats dd.reading-time"; const WorkDatePublished = "dl.work dl.stats dd.published";

    const SeriesWords = ".series.group dl.stats>dd.words"; const SeriesWorks = ".series.group dl.stats>dd.works"; const SeriesBookmarks = ".series.group dl.stats>dd.bookmarks"; const SeriesComplete = ".series.group dl.stats>dd[title~=Complete]";

    const CollectionWorks = ".collection.group dl.stats dd a[href$=works]"; const CollectionFandoms = ".collection.group dl.stats dd a[href$=fandoms]"; const CollectionRequests = ".collection.group dl.stats dd a[href$=requests]"; const CollectionBookmarks = ".collection.group dl.stats dd a[href$=bookmarks]"; const CollectionChallenges = ".collection.group dl.stats dd a[href$=collections]";

    const DateStatusTitle = "dl.work dl.stats dt.status"; const DateStatusWork = "dl.work dl.stats dd.status";

    const localSettings = { languageOptions: { tooltip: "Language", icon: "languages" }, wordCountOptions: { tooltip: "Word Count", icon: "pencil-line" }, chaptersOptions: { tooltip: "Chapters", icon: "notebook" }, collectionsOptions: { tooltip: "Collections", icon: "square-library" }, commentsOptions: { tooltip: "Comments", icon: "message-square" }, kudosOptions: { tooltip: "Kudos", icon: "heart" }, bookmarksOptions: { tooltip: "Bookmarks", icon: "bookmark" }, hitsOptions: { tooltip: "Hits", icon: "eye" }, workSubsOptions: { tooltip: "Subscriptions", icon: "mail-check" }, authorSubsOptions: { tooltip: "User Subscriptions", icon: "user-round-check" }, commentThreadsOptions: { tooltip: "Comment Threads", icon: "messages-square" }, challengesOptions: { tooltip: "Challenges/Subcollections", icon: "swords" }, fandomsOptions: { tooltip: "Fandoms", icon: "crown" }, requestsOptions: { tooltip: "Prompts", icon: "cake-slice" }, workCountOptions: { tooltip: "Work Count", icon: "library" }, seriesCompleteOptions: { tooltip: "Series Complete", icon: "circle-check-big" }, kudos2HitsOptions: { tooltip: "Kudos to Hits", icon: "flame" }, timeToReadOptions: { tooltip: "Time to Read", icon: "hourglass" }, dateWorkPublishedOptions: { tooltip: "Published", icon: "calendar-plus" }, dateWorkUpdateOptions: { tooltip: "Updated", icon: "calendar-clock" }, dateWorkCompleteOptions: { tooltip: "Completed", icon: "calendar-check-2" }, }; // 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(WorkLanguage, localSettings.languageOptions); findElementsAndSetIcon(${TotalWords}, ${WorkWords}, ${SeriesWords}, localSettings.wordCountOptions); findElementsAndSetIcon(WorkChapters, localSettings.chaptersOptions); findElementsAndSetIcon(WorkCollections, localSettings.collectionsOptions); findElementsAndSetIcon(WorkComments, localSettings.commentsOptions); findElementsAndSetIcon(${TotalKudos}, ${WorkKudos}, localSettings.kudosOptions); findElementsAndSetIcon(${TotalBookmarks}, ${WorkBookmarks}, ${CollectionBookmarks}, ${SeriesBookmarks}, localSettings.bookmarksOptions); findElementsAndSetIcon(${TotalHits}, ${WorkHits}, localSettings.hitsOptions); findElementsAndSetIcon(${TotalSubscribersWorks}, ${WorkSubscribers}, localSettings.workSubsOptions); findElementsAndSetIcon(TotalSubscribersAuthor, localSettings.authorSubsOptions); findElementsAndSetIcon(TotalCommentThreads, localSettings.commentThreadsOptions); findElementsAndSetIcon(CollectionChallenges, localSettings.challengesOptions); findElementsAndSetIcon(CollectionFandoms, localSettings.fandomsOptions); findElementsAndSetIcon(CollectionRequests, localSettings.requestsOptions); findElementsAndSetIcon(${CollectionWorks}, ${SeriesWorks}, localSettings.workCountOptions); findElementsAndSetIcon(SeriesComplete, localSettings.seriesCompleteOptions);

    // AO3E elements findElementsAndSetIcon(WorkKudos2Hits, localSettings.kudos2HitsOptions); findElementsAndSetIcon(WorkReadingTime, localSettings.timeToReadOptions);

    // calendar icons at works page findElementsAndSetIcon(WorkDatePublished, 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, icon: "circle-user-round" }, postNewOptions: { tooltip: "New Work", addTooltip: true, icon: "book-plus" }, logoutOptions: { tooltip: "Logout", addTooltip: true, icon: "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)); }

if (settings?.iconifyStats) iconifyStats(); if (settings?.iconifyUserNav) iconifyUserNav(); initLucide(); }