AO3: Add button to Show Bookmark

Adds a "Show Bookmark" button before the "Edit Bookmark" button on the page where you view a work's bookmarks

  1. // ==UserScript==
  2. // @name AO3: Add button to Show Bookmark
  3. // @namespace https://github.com/w4tchdoge
  4. // @version 2.1.0-20250320_085258
  5. // @description Adds a "Show Bookmark" button before the "Edit Bookmark" button on the page where you view a work's bookmarks
  6. // @author w4tchdoge
  7. // @homepage https://github.com/w4tchdoge/MISC-UserScripts
  8. // @match *://archiveofourown.org/*chapters/*
  9. // @match *://archiveofourown.org/*works/*
  10. // @match *://archiveofourown.org/*series/*
  11. // @match *://archiveofourown.org/*/bookmarks
  12. // @match *://archiveofourown.org/bookmarks*
  13. // @exclude *://archiveofourown.org/*works/*/navigate
  14. // @license AGPL-3.0-or-later
  15. // @history 2.1.0 — Add "User Bookmark" button to the series page
  16. // @history 2.0.2 — Move the detection of whether the work is bookmarked to the start of the script instead of making it part of what the bookmarks page fetch does. This is to make sure the bookmarks page is only fetched when the user has the work bookmarked
  17. // @history 2.0.1 — Add exclude rule so that userscript doesn't run on /navigate pages
  18. // @history 2.0.0 — Add a link to view a user's bookmark in the stats area that is present when viewing a chapter (https://i.imgur.com/a5O1lpD.png)
  19. // @history 1.0.3 — Make sure script works on bookmark searches. Add a check to make sure that the script only executes if the existing_edit_btns array has elements in it
  20. // @history 1.0.2 — Make sure script only adds a "Show Bookmark" button when there is an "Edit Bookmark" button as opposed to a "Bookmark" button that is used to make new bookmarks
  21. // @history 1.0.1 — Fix issue caused by data-remote attribute where bookmark could not be opened in the current tab
  22. // @history 1.0.0 — Initial commit
  23. // ==/UserScript==
  24.  
  25. (async function () {
  26. `use strict`;
  27.  
  28. async function respText(url) {
  29. const fetch_resp = await fetch(url);
  30. const resp_text = await fetch_resp.text();
  31. return resp_text;
  32. }
  33.  
  34. function respText2bkmrkUrl(resp) {
  35. const html_parser = new DOMParser();
  36. const html_bookmark_page_html = html_parser.parseFromString(resp, `text/html`);
  37. // console.log(html_bookmark_page_html);
  38. const edit_bookmark_elem = html_bookmark_page_html.querySelector(`a[id^="bookmark_form_trigger"]`).cloneNode();
  39. const show_bookmark_href = edit_bookmark_elem.getAttribute(`href`).split(/\/edit/i).at(0);
  40. return show_bookmark_href;
  41. }
  42.  
  43. function genUserBookmarkElms(bkmrk_href) {
  44. const dt_elem = Object.assign(document.createElement(`dt`), {
  45. id: `show_user_bookmark_dt`,
  46. className: `user_bookmark`,
  47. innerHTML: `User Bookmark:`
  48. });
  49.  
  50. const dd_elem = Object.assign(document.createElement(`dd`), {
  51. className: `user_bookmark`,
  52. id: `show_user_bookmark_dd`,
  53. innerHTML: (() => {
  54. const element = Object.assign(document.createElement(`a`), {
  55. href: `${bkmrk_href}`,
  56. id: `show_user_bookmark_a`,
  57. innerHTML: `View`
  58. });
  59. return element.outerHTML;
  60. })()
  61. });
  62.  
  63. return [dt_elem, dd_elem];
  64. }
  65.  
  66.  
  67. const current_page_url = new URL(window.location);
  68.  
  69. const
  70. is_pg_wrk = (current_page_url.pathname.includes(`works`) || current_page_url.pathname.includes(`chapters`)),
  71. is_pg_srs = current_page_url.pathname.includes(`series`);
  72.  
  73. const bkmrk_page_check = current_page_url.pathname.includes(`bookmarks`);
  74.  
  75. const is_work_bookmarked = (() => {
  76. try {
  77.  
  78. const element_iwb = document.querySelector(`a.bookmark_form_placement_open`).textContent.toLowerCase().includes(`edit`);
  79. return element_iwb;
  80.  
  81. } catch (error) {
  82.  
  83. return false;
  84.  
  85. }
  86. })();
  87.  
  88.  
  89. if (is_pg_wrk && bkmrk_page_check && Boolean(document.querySelector(`a[id^="bookmark_form_trigger"]`))) {
  90.  
  91. // create array of the parent elements of the li element that makes up the native edit bookmark buttons
  92. const existing_edit_btns = Array.from(document.querySelectorAll(`*:has(> li > [id^="bookmark_form_trigger"][href*="edit"])`));
  93.  
  94. // Check if the array exists and has elements
  95. if (Array.isArray(existing_edit_btns) && Boolean(existing_edit_btns.length)) {
  96. // console.log(`existing edit buttons exist on this page`);
  97. console.log(`
  98. Add "Show Bookmark" Button userscript:
  99. One or more "Edit Bookmark" buttons exist on this page.
  100. Proceeding to add "Show Bookmark" buttons next to them.`
  101. );
  102.  
  103. // proceed to iterate on each element in existing_edit_btns to make and insert a new show bookmark button
  104. existing_edit_btns.forEach(function (elm, i, arr) {
  105. // get the original li element which the link is a child of and make it a const so it cant be changed
  106. // orig is needed as a reference for the new "Show Bookmark" li elm to be added before
  107. const orig = elm.querySelector(`li`);
  108.  
  109. let
  110. // clone orig to modify it and make the new show bookmark button element
  111. li_elm = orig.cloneNode(true),
  112. // make a variable specifically for the a element so i dont have to call a querySelector everytime
  113. a_elm = li_elm.querySelector(`a`);
  114.  
  115. // remove the data-remote as it prevents the bookmark from being opened in the current tab
  116. a_elm.removeAttribute(`data-remote`);
  117.  
  118. // get the original a element id and change it for the new one
  119. const a_elm_id = a_elm.getAttribute(`id`).replace(`bookmark_form`, `show_bookmark`);
  120.  
  121. // get the original a element href and change it for the new one
  122. const a_elm_link_href = a_elm.getAttribute(`href`).replace(`/edit`, ``);
  123.  
  124. // get the original a element textContent and change it for the new one
  125. const a_elm_text = a_elm.textContent.replace(/edit/i, `Show`);
  126.  
  127. // modify the cloned element using the a_elm_* variables to make the new "Show Bookmark" button
  128. a_elm.setAttribute(`id`, a_elm_id);
  129. a_elm.setAttribute(`href`, a_elm_link_href);
  130. a_elm.textContent = a_elm_text;
  131.  
  132. // add the new "Show Bookmark" button before the edit bookmark button
  133. orig.before(li_elm);
  134. });
  135.  
  136. } else {
  137. console.log(`
  138. Add "Show Bookmark" Button userscript:
  139. No "Edit Bookmark" buttons exist on this page.`
  140. );
  141. }
  142. }
  143.  
  144. if (is_pg_wrk && !bkmrk_page_check && Boolean(document.querySelector(`dd.bookmarks a`)) && !is_work_bookmarked) {
  145.  
  146. // console.log(`branch B`);
  147.  
  148. console.log(`
  149. Add "Show Bookmark" Button userscript:
  150. User does not have the work bookmarked.`
  151. );
  152. }
  153.  
  154. if (is_pg_wrk && !bkmrk_page_check && Boolean(document.querySelector(`dd.bookmarks a`)) && is_work_bookmarked) {
  155.  
  156. // console.log(`branch A`);
  157.  
  158. const bookmark_page_href = document.querySelector(`dd.bookmarks a`).getAttribute(`href`);
  159. const bkmrk_fetch_resp_txt = await respText(bookmark_page_href);
  160. // console.log(bkmrk_fetch_resp_txt);
  161.  
  162. console.log(`
  163. Add "Show Bookmark" Button userscript:
  164. User has the work bookmarked.`
  165. );
  166.  
  167. const
  168. show_bookmark_href = respText2bkmrkUrl(bkmrk_fetch_resp_txt),
  169. all_public_bkmrks_link = document.querySelector(`.work.meta.group dl.stats dd[class^="bookmark"]:has(>a)`);
  170.  
  171. // console.log(show_bookmark_href);
  172.  
  173. const [dt_user_bookmark_elem, dd_user_bookmark_elem] = genUserBookmarkElms(show_bookmark_href);
  174.  
  175. all_public_bkmrks_link.after(dt_user_bookmark_elem, dd_user_bookmark_elem);
  176.  
  177. }
  178.  
  179. if (is_pg_srs && Boolean(document.querySelector(`dl.series.meta.group > dd.stats dd.bookmarks > a`)) && !is_work_bookmarked) {
  180. console.log(`
  181. Add "Show Bookmark" Button userscript:
  182. User does not have the series bookmarked.`
  183. );
  184. }
  185.  
  186. if (is_pg_srs && Boolean(document.querySelector(`dl.series.meta.group > dd.stats dd.bookmarks > a`)) && is_work_bookmarked) {
  187.  
  188. const bookmark_page_href = document.querySelector(`dl.series.meta.group > dd.stats dd.bookmarks > a`).getAttribute(`href`);
  189. const bkmrk_fetch_resp_txt = await respText(bookmark_page_href);
  190. // console.log(bkmrk_fetch_resp_txt);
  191.  
  192. console.log(`
  193. Add "Show Bookmark" Button userscript:
  194. User has the work bookmarked.`
  195. );
  196.  
  197. const
  198. show_bookmark_href = respText2bkmrkUrl(bkmrk_fetch_resp_txt),
  199. all_public_bkmrks_link = document.querySelector(`dl.series.meta.group dl.stats > dd[class^="bookmark"]:has(>a)`);
  200.  
  201. // console.log(show_bookmark_href);
  202.  
  203. const [dt_user_bookmark_elem, dd_user_bookmark_elem] = genUserBookmarkElms(show_bookmark_href);
  204.  
  205. all_public_bkmrks_link.after(dt_user_bookmark_elem, dd_user_bookmark_elem);
  206.  
  207. }
  208.  
  209. })();