// ==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 // @downloadURL none // ==/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; } }