Greasy Fork

来自缓存

Greasy Fork is available in English.

Ao3 Auto Bookmarker

Allows for autofilled bookmark summary and tags on Ao3. Currently does not include series bookmarks but is planned. Feel free to request features: I will implement if I have the time & ability.

当前为 2025-03-31 提交的版本,查看 最新版本

// ==UserScript==
// @name        Ao3 Auto Bookmarker
// @description Allows for autofilled bookmark summary and tags on Ao3. Currently does not include series bookmarks but is planned. Feel free to request features: I will implement if I have the time & ability.
// @namespace   Ao3
// @match       http*://archiveofourown.org/works/*
// @match       http*://archiveofourown.org/series/*
// @grant       none
// @version     1.0
// @author      Legovil
// @license     MIT
// @downloadURL
// ==/UserScript==

// Set the true or false value of each setting to change whether the bookmark auto generates the option.
const Settings = new Map([
  ["GenerateTitleNote", true],
  ["GenerateSummaryNote", true],
  ["CheckRecBox", false],
  ["CheckPrivateBox", false],
  ["GetRating", true],
  ["GetArchiveWarnings", true],
  ["GetCategoryTags", true],
  ["GetFandomTags", true],
  ["GetRelationshipTags", true],
  ["GetCharacterTags", true],
  ["GetAdditionalTags", true],
  ["GenerateWordCountTag", true],
  ["AppendToExistingNote", true],
  ["AppendToExistingTags", false],
  ["UsingAo3Extensions", true]
]);

// Edit these comma delimited numbers to change the auto word count tag boundaries (e.g. 1000 - 5000).
const WordCountBounds = [1000, 5000, 10000, 50000, 100000, 500000];

// Bookmark type enum.
const BookmarkType = Object.freeze({
  Work: 'Work',
  Series: 'Series'
});

(function() {
  'use strict';

  const bookmarkType = CheckIfWorkOrSeries(window.location.href);

  if (Object.is(bookmarkType, BookmarkType.Series)) {
      console.error("Not generating bookmark. Series bookmarks are not yet implemented.");
      return;
    }

  if (Object.is(bookmarkType, null)) {
    console.error("Bookmark type not found. Cancelling bookmark generation.");
    return;
  }

  GenerateNotes();
  GenerateTags();
  HandleCheckBoxes();
})();

function CheckIfWorkOrSeries(url) {
  if (url.includes("/works/")) {
    console.log("Found Work Bookmark.");
    return BookmarkType.Work;
  }
  if (url.includes("/series/")) {
    console.log("Found Series Bookmark.");
    return BookmarkType.Series;
  }
  return null;
}

function GenerateNotes() {
  const notesElement = document.getElementById("bookmark_notes");
  let note = "";

  if (Object.is(notesElement, null)) {
    console.error("Notes element not found. Cancelling notes generation.");
    return;
  }

  note += GenerateTitleNote();
  note += GenerateSummaryNote();
  if (Settings.get("AppendToExistingNote")) {
    notesElement.value += `\n\n${note}`;
  } else {
    notesElement.value = note;
  }
}

function GenerateTitleNote() {
  if (!Settings.get("GenerateTitleNote")) {
    console.log("Not generating title note.");
    return "";
  }

  console.log("Generating title note.");
  const title = document.getElementsByClassName("title heading")[0];
  if (Object.is(title, undefined)) {
    console.warn("Title not found. Cancelling Title Note generation.");
    return "";
  }

  const byline = document.getElementsByClassName("byline heading")[0].getElementsByTagName('a')[0];
  if (Object.is(byline, undefined)) {
    console.warn("Byline not found. Cancelling Title Note generation.");
    return "";
  }
  return `${title.innerHTML.link(window.location.href)} by ${byline.outerHTML}.`;
}

function GenerateSummaryNote() {
  if (!Settings.get("GenerateSummaryNote")) {
    console.log("Not generating summary note.");
    return;
  }

  const summary = document.getElementsByClassName("summary module")[0].getElementsByClassName("userstuff")[0];
  if (Object.is(summary, undefined)) {
    console.warn("No summary found. Cancelling summary note generation.");
    return "";
  }
  return `\n\nSummary: ${summary.innerText}`;
}

function GenerateTags() {
  let tagsElement = document.getElementById("bookmark_tag_string_autocomplete");
  if (Object.is(tagsElement, null)) {
    console.error("Tags element not found. Cancelling bookmark tag generation.");
    return;
  }
  let tags = Settings.get("AppendToExistingTags") ? `${tagsElement.value}, ` : "";
  tags += Settings.get("GetArchiveWarnings") ? GetTagsFromString("warning tags") : "";
  tags += Settings.get("GetCategoryTags") ? GetTagsFromString("category tags") : "";
  tags += Settings.get("GetFandomTags") ? GetTagsFromString("fandom tags") : "";
  tags += Settings.get("GetRelationshipTags") ? GetTagsFromString("relationship tags") : "";
  tags += Settings.get("GetCharacterTags") ? GetTagsFromString("character tags") : "";
  tags += Settings.get("GetAdditionalTags") ? GetTagsFromString("freeform tags") : "";
  tags += GenerateWordCountTag();
  tagsElement.value = tags;
}

function GetTagsFromString(tagClassName) {
  const tagList = document.getElementsByClassName(tagClassName)[1].getElementsByClassName("tag");
  if (Object.is(tagList, null) || tagList.length == 0) {
    console.error(`Tags element not found. Cancelling ${tagClassName} generation.`);
    return "";
  }
  let tags = "";
  for (const tag of tagList) {
    tags += `${tag.text}, `;
  }
  return tags;
}

function GenerateWordCountTag() {
  if (!Settings.get("GenerateWordCountTag")) {
    console.log("Not generating word count tag.")
    return;
  }

  const wordCountElement = Settings.get("UsingAo3Extensions") ? document.getElementsByClassName("words")[2] : document.getElementsByClassName("words")[1];
  if (Object.is(wordCountElement, null) || wordCountElement.innerText === "Words:") {
    console.error("Word count not found. Cancelling word count tag generation. Check to see if Ao3 Extensions setting is toggled correctly.");
    return;
  }
  const wordCount = wordCountElement.innerText.replaceAll(",", "");
  let lowerBound = WordCountBounds[0];
  if (wordCount < lowerBound) {
    return `< ${lowerBound}`;
  }

  for (const upperBound of WordCountBounds) {
    if (wordCount < upperBound) {
      return `${lowerBound} - ${upperBound - 1}`
    }
    lowerBound = upperBound;
  }
  return `> ${WordCountBounds[WordCountBounds.length - 1]}`;
}

function HandleCheckBoxes() {
  const recBox = document.getElementById("bookmark_rec");
  if (Settings.get("CheckRecBox") && !Object.is(recBox, undefined)) {
    console.log("Checking rec box.");
    recBox.checked = true;
  }

  const privateBox = document.getElementById("bookmark_private");
  if (Settings.get("CheckPrivateBox") && !Object.is(privateBox, undefined)) {
    console.log("Checking private box.");
    privateBox.checked = true;
  }
}