// ==UserScript==
// @name Ao3 Auto Bookmarker
// @description Allows for autofilled bookmark summary and tags on Ao3.
// @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;
}
}