// ==UserScript==
// @name AO3 Bookmark Maker (Adds title, author, workID, summary to bookmark notes automatically)
// @namespace Ellililunch AO3 Bookmark Maker
// @description Automatically populates fic info in bookmark notes! Adds title, author, workID, and summary to any existing bookmark notes (but not if the summary has already been added!) Great for tracking bookmarks. Helpful for re-readers! Or to figure out what that deleted fic used to be!
// @version 0.7
// @history 0.0 modified Bairdel's Bookmark Maker to autopopulate title, author, and summary
// @history 0.1 added workID to autopopulated info (on suggestion of oliver t)
// @history 0.2 added if-else if-else statement so existing notes that don't include fic info will get fic info added but if editing an existing bookmark that already has the fic info/summary in the notes it won't be duplicated (friendlier to rereaders)
// @history 0.3 patched error where Anonymous accounts break the author variable on the suggestions of w4tchdoge on greasyfork
// @history 0.4 added nested if-else statement where if summary has already been added but without workID, workID will be added in front of the reread date.
// @history 0.5 modified bookmark maker to be based off the title and author variables instead of "Summary:" // reformatted it all to lose the heading formatting + make it easier to read.
// @history 0.6 added automatic word count range tags with the help of w4tchdoge
// @history 0.7 fixed issues with automatic word count range tags, put fic info (title, author, summary, and workID) in collapsed element.
// @author Ellililunch
// @match *archiveofourown.org/works/*
// @match *archiveofourown.org/series/*
// note oliver t has found that it did NOT work if you use the typical userscripts extension app (they downloaded Stay - Userscripts Extension which is a “Tampermonkey for Safari” thing) but also found changing @match to @include worked as well
// @license GNU GPLv3
// ==/UserScript==
////////////////////////////////////////////////////////////
//////////////// ALL BUTTON CLONING HERE ///////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//////////// recreate subscribe button at end of works by scriptfairy
(function($) {
$(document).ready(function() {
var sub = $('li.subscribe').clone();
$('#new_kudo').parent().after(sub);
});
})(window.jQuery);
//////////////////////////AO3 clone "mark for later" button at bottom//////////////////////
///// this is useful for indicating you've read the fic once you get to the bottom! (ie/ if you "Marked for later" at the top of the fic and read to the bottom, at the bottom this would be a "Mark as read" button :)
(function($) {
$(document).ready(function() {
var mfl = $('li.mark').clone();
$('#new_kudo').parent().after(mfl);
});
})(window.jQuery);
//////////////////// Add AO3 Mark for Later Button to works list ///////////////////
// description Puts the "Marked for Later" button in a fic blurb when browsing AO3 from "JaneBuzJane but indebted to Bat, always"
// This script adds a button to each work on archiveofourown.org (AO3) when browsing tags that allows a user to add a work to their "Marked for Later" list without having to open the fic in a separate tab.
var $j = jQuery.noConflict();
$j(document).ready(function() {
for (var i = 0, link, links = $("li[role=article] > .header > .heading:first-child > a[href*='/works']"); i < links.length; i++) {
(link = $(links[i])).closest(".header")
.nextAll(".stats")
.before("<span class=\"actions\" style=\"float: left; clear: right;\"><a href=\"" + link.attr("href") + "/mark_for_later\">Mark For Later</a></span>");
}
});
/////////FOR REKUDOS//////////////////
//ACNOWLEDGEMENT: most of the method is cribbed from "ao3 no rekudos" by scriptfairy
//Rest is cribbed from "Change Ao3 Kudos button text to Glory" by AlectoPerdita
//I do not know enough JS to do shit like this on my own
//https://greasyfork.org/en/scripts/406616-ao3-no-rekudos
//https://greasyfork.org/en/scripts/390197-change-ao3-kudos-button-text-to-glory/code
//SETUP//
var auto = false;
//Set to "true" if you want to skip the confirmation automatically.
var comments = Array(
"Extra Kudos <3 ",
"This is an extra kudos, since I've already left one. :) ",
"I just wanted to leave another kudos <3 ",
"Kudos! ♥ ",
"I loved this! ",
"This was great!! ♥ ",
"♥ ♥ ♥ ",
"LOVE LOVE LOVE! ",
"<3 <3 <3 ",
"This is great!! ♥ ",
"Loved this <3 ",
"♥ ",
"Kudos ♥ ",
"I loved this so much I reread it and now im leaving an extra kudos! ",
"Kissing you on your forehead kudos ",
"Kissing you on your forehead, MWAH ",
"Reread kudos :) ",
"Reread kudos <3 ",
"Reread kudos ♥ "
//ellililunch added some more array options for more random rekudos messages
);
//Remember to keep your message between the quotation marks.
//Remember to separate comments with a comma!
//Message max length: 10000 characters
var lat = 500;
//Delay in milliseconds, waiting for reply from OTW servers. (Check with CTRL+SHIFT+K)
var verify = true;
//Set to "false" to turn off anti-spam verification. (Not recommended.)
//Definitions
var work_id, kudos, banner, kudo_btn, cmnt_btn, cmnt_field, id;
work_id = window.location.pathname;
work_id = work_id.substring(work_id.lastIndexOf('/')+1);
banner = document.getElementById('kudos_message');
kudo_btn = document.getElementById('new_kudo');
cmnt_btn = document.getElementById('comment_submit_for_'+work_id);
cmnt_field = document.getElementById('comment_content_for_'+work_id);
//Message randomiser
var random = Math.floor(Math.random() * comments.length);
var message = comments[random];
// ID
if (verify == true) {
var d = new Date();
id = d.toISOString();
id = id.substring(0,10);
message = message +'<br></br><sub>Sent with love from the rekudos machine on '+id+'</sub>'
}
//Comment-sending with button press rather than form submit
function send() {
cmnt_field.value = message;
cmnt_btn.click();
}
//Change kudos button behaviour
function change() {
kudo_btn.addEventListener("click", send);
}
//Extra click for confirmation
var active = 'Rekudos?';
function rename() {
'use strict';
var kudo_text = document.querySelector('#kudo_submit');
kudo_text.value = active;
change();
}
//New method
function isAuto(){
if (auto == false || window.AssistMode == true) {
rename();}
else {
send();}
}
function makeitwork() {
console.log('Rekudo latency check');
if (banner.classList.contains("kudos_error") == true) {
isAuto();}
}
function delay(){
setTimeout(makeitwork,lat);
}
kudo_btn.addEventListener("click", delay);
//////////////////////////////////////////////
//////////////////////////////////////////////
//////// THIS IS THE BOOKMARK AREA ///////////
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////THE BOOKMARK SAVIOR/////////////
////////////automatically adds title, author, workID and summary above exisiting notes in bookmark////////////
////this is all modified from Bairdel's AO3 Bookmarking Records, with immense help from w4tchdoge
(function() {
// get the current date. should be in local time. you could add HH:MM if you wanted.
var currdate = new Date();
var dd = String(currdate.getDate()).padStart(2, '0');
var mm = String(currdate.getMonth() + 1).padStart(2, '0'); //January is 0
var yyyy = currdate.getFullYear();
var hh = String(currdate.getHours()).padStart(2, '0');
var mins = String(currdate.getMinutes()).padStart(2, '0');
// change to preferred date format
var date;
//date = dd + '/' + mm + '/' + yyyy + " " + hh + ":" + mins;
date = mm + '/' + dd + '/' + yyyy; //this is the USA standard date format
console.log(date);
/// these are all the variables I'm going to use
var author;
var words;
var word_tag;
var wordsINT;
var status; // not in use atm
var title;
var summary;
var series_notes; // not in use atm
var lastChapter; // not in use atm
var workID = window.location.href.split("/")[4]; //the code was suggested by oliver t, and is useful if a work is deleted and you want to recreate the URL for the wayback machine
var oldBookmarkNotes = (document.getElementById("bookmark_notes").innerHTML);
var tags;
///// sooooo..... technically doesn't work for series at the moment. I will work on this soon (promised 7/18/23)
// checks if series
var seriesTrue = document.getElementsByClassName("current")[0];
if (seriesTrue != undefined) {
// options for series bookmark notes
title = document.getElementsByTagName("h2")[0].innerHTML.trim();
words = document.getElementsByClassName("stats")[2].getElementsByTagName("dd")[0].textContent; // will have to see if these words glith out like all the issues I had for works
author = document.querySelectorAll('[rel="author"]')[0].innerHTML.trim(); // fic author
summary = document.getElementsByClassName("series meta group.userstuff")[0].innerHTML; // series notes "summary" attempt //attempted new way: document.getElementsByClassName("series meta group.userstuff")[0].innerText.substring(10, document.getElementsByClassName("series meta group.userstuff")[0].innerText.length); //old way:
/*var complete = document.getElementsByClassName("stats")[2].getElementsByTagName("dd")[2].textContent;
var updated = document.getElementsByClassName("series meta group")[0].getElementsByTagName("dd")[2].textContent
// var status
if (complete == "No") {
status = "Updated: " + updated;
} else if (complete == "Yes") {
status = "Completed: " + updated;
}*/
} else {
// options for fics
lastChapter = "Chapter " + document.getElementsByClassName("chapters")[1].innerHTML.split("/")[0];
title = document.getElementsByClassName("title heading")[0].innerHTML.trim(); // fic name
words = document.getElementsByClassName("words")[1].innerHTML; // fic wordcount
author = document.querySelector("#workskin > .preface .byline").textContent.trim(); // new way of finding fic author regardless of Anonymous suggested by w4tchdog on greasyfork, this was the old way if that breaks it for you: document.querySelectorAll('[rel="author"]')[0].innerHTML.trim(); // old way of finding fic author
summary = document.getElementsByClassName("summary")[0].innerText.substring(10, document.getElementsByClassName("summary")[0].innerText.length); // old way to get summmary that had all sorts of OG formatting document.getElementsByClassName("summary")[0].innerHTML; // summary attempt
/*// status i.e. Completed: 2020-08-23, Updated: 2022-05-08, Published: 2015-06-29
if (document.getElementsByClassName("status").length != 0) {
// for multichapters
status = document.getElementsByClassName("status")[0].innerHTML + " " + document.getElementsByClassName("status")[1].innerHTML;
} else{
// for single chapter fics
status = document.getElementsByClassName("published")[0].innerHTML + " " + document.getElementsByClassName("published")[1].innerHTML;
}
*/
}
/////now for automatically adding tags (want to automatically add word count ///with help from w4tchdoge
// ao3 seems to alternate between wordcount having a space or comma between the thousands and hundreds place depending on how the page loads. no idea why. seems to lean towards thte space on my phone. still no idea why
// new attempt for version 1.1.1.2 with w4tchdoge suggestion for weird words issue:
var words_XPath = './/*[@id="main"]//dl[contains(concat(" ",normalize-space(@class)," ")," stats ")]//dt[text()="Words:"]/following-sibling::*[1]/self::dd';
words = document.evaluate(words_XPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.innerText.toString();
// words = words.replaceAll(/,| /gi, ''); this was w4tchdoge's code, broke with the weird words
if (words.includes(",")) {
words = words.replace(",", ""); //https://www.w3schools.com/jsref/jsref_string_replaceall.asp
}
if (words.includes(" ")) {
words = words.replace(" ", "");// words = words.slice(0, 3); //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
}
if (words.includes(" ")) {
words = words.replace(" ", ""); //https://www.w3schools.com/jsref/jsref_string_replaceall.asp, https://futurestud.io/tutorials/remove-all-whitespace-from-a-string-in-javascript
}
wordsINT = parseInt(words);
// have been informed switch statements have better performance than if else statements, so commenting out my if else statements at using switch statements from w4tchdoge instead:
/* //this was my stuff:
word_tag = "if statement not executed";
if (isNaN(wordsINT)) { word_tag = " nan error"; }
else if (wordsINT < 1000) { word_tag = "<1K words"; }
else if (wordsINT < 5000) { word_tag = "1-5K words"; }
else if (wordsINT < 10000) { word_tag = "5-10K words"; }
else if (wordsINT < 20000) { word_tag = "10-20K words"; }
else if (wordsINT < 40000) { word_tag = "20-40K words"; }
else if (wordsINT < 60000) { word_tag = "40-60K words"; }
else if (wordsINT < 100000) { word_tag = "60-100K words"; }
else if (wordsINT >= 100000) { word_tag = ">100K words"; }
else { word_tag = "wc fucked up"; } */
// w4tchdoge's switch statements: (modified to add case >100K words + if words is NaN. tested all cases, and when wordINT parses correctly, this all works great.
switch (true) { // constructed from example shown in https://stackoverflow.com/a/48969351/11750206
case wordsINT < 100: // number chose arbitrarily
word_tag = " <100 words, ";
// word_tag = word_tag + ", words: " + words + " , wordsINT: " + wordsINT;
break;
case wordsINT < 1000:
word_tag = "<1K words";
break;
case wordsINT >= 1000 && wordsINT < 5000:
word_tag = "1-5K words";
break;
case wordsINT >= 5000 && wordsINT < 10000:
word_tag = "5-10K words";
break;
case wordsINT >= 10000 && wordsINT < 20000:
word_tag = "10-20K words";
break;
case wordsINT >= 20000 && wordsINT < 40000:
word_tag = "20-40K words";
break;
case wordsINT >= 40000 && wordsINT < 60000:
word_tag = "40-60K words";
break;
case wordsINT >= 60000 && wordsINT < 100000:
word_tag = "60-100K words";
break;
case wordsINT >= 100000 && !(isNaN(wordsINT)):
word_tag = ">100K words";
break;
case (isNaN(wordsINT) && words == "Words:"): // I think this occurs when "data-ao3e-original" + need this to work on mobile (I use Kiwi browswer with tampermonkey)
word_tag = "word count NaN error";
// word_tag = word_tag + ", words: " + words + " , wordsINT: " + wordsINT;
break;
default:
word_tag = "switch statement not executed";
break;
}
// This here is the code that adds the tags to the bookmark notes automatically
var tag_input_box = document.querySelector('.input #bookmark_tag_string_autocomplete');
tag_input_box.value = word_tag; // you can also add your own default automatic tags like this: + "tag 1, tag 2"
// also now (sarcasm) that i'm getting ambitous, it would be cool to automatically tag incomplete works with "WIP".
//////////////////// THIS IS THE BIT FOR YOU TO LOOK AT /////////////////////////////////////////////////////////////////////////////////////
// puts it all together. feel free to change this format to whatever you like.
// <br> puts the next text on a new line
// options for variables are:
// date = current date
// title = title of fic
// author = author of fic
// summary = fic summary
// oldBookmarkNotes = existing bookmark notes
// newBookmarkNotes = this is the variable for the new bookmark notes
// status of fic i.e. Completed: 2020-08-23, Updated: 2022-05-08, Published: 2015-06-29
// Want a collabsible element to hide fic info and keep your bookmarks uncluttered? use the <detail> and <summary> elements. read more below:
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary
/////////////////SELECT YOUR OPTIONS//////////////////////////////
///// if currently no bookmark notes, populates fic info. if there are bookmark notes that already has the fic info, keep the existing info + adds the current date as the reread date. if there are already bookmark notes, but no fic info, adds the fic info in front of the bookmark notes
var newBookmarkNotes; //this is the variable that will set the new bookmark note
var fic_info; //this is the variable that will contain the fic info and how it's ordered and formatted
fic_info = "<details><summary>Fic Info</summary><b>" + title + " by " + author + "</b> (workID: " + workID + ")<blockquote>Summary: <br>" + summary + "</blockquote></details>";
if (oldBookmarkNotes === "") { //learned this from: https://stackoverflow.com/questions/154059/how-do-i-check-for-an-empty-undefined-null-string-in-javascript
// if no existing bookmarks, then add all info:
newBookmarkNotes = fic_info;
// automatically checks the Private Bookmark checkbox. Set to false if you don't want this.
document.getElementById("bookmark_private").checked = true;
} else if (oldBookmarkNotes.includes(workID)) { //learned this from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
// if existing bookmark with fic info (determined by the presence of the work ID), then make no changes to the bookmark notes
newBookmarkNotes = oldBookmarkNotes; ///trying to see if I can add reread date to existing bookmark notes... we'll see babes
} else { ///learned this from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
//if there is an existing bookmark note, but it does not yet contain the fic info
newBookmarkNotes = fic_info + oldBookmarkNotes;
}
// fills in the bookmark notes box.
document.getElementById("bookmark_notes").innerHTML = newBookmarkNotes;// + "<br>Read: " + date; //I decided I personally didn't like the read date because I already have 7000+ bookmarks and I guess I'm not changing now
// is there any way to remove this "<img alt="(Restricted)" title="Restricted" src="/images/lockblue.png" width="15" height="15">" from the info every time?
})();
//////////////////// backup AO3 clone fic title, author, and summary at bottom of page (helpful when author is anonymous and the modifed Bairdel's Bookmark Maker doesn't help)////// ... I think I can finally get rid of this
(function($) {
$(document).ready(function() {
// new attempt
/*var summary = $('div.preface .summary').clone();
$('#feedback').parent().after(summary);
var author = $('div.preface .byline').clone();
//$('#feedback').parent().after(author);
var format = " by "
//$('#feedback').parent().after(format);
var title = $('div.preface .title.heading').clone();
$('#feedback').parent().after(author); + $('#feedback').parent().after(format); + $('#feedback').parent().after(title);
//document.write("test text") #completely overwrote entire page //more research has been done, looks like this is mostly jquery lol */
var wordcount = $('.meta .stats dl dd.words').clone();
$('#feedback').parent().after(wordcount);
//var wordlabel = "Words: "
//$('#feedback').parent().after("Words: ");
});
})(window.jQuery);