misaalanshori

userscript patched

Aug 11th, 2023
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name       AO3 Reader - Vite
  3. // @namespace  https://misa.pw
  4. // @version    0.0.0
  5. // @author     monkey
  6. // @icon       https://www.google.com/s2/favicons?sz=64&domain=archiveofourown.org
  7. // @match      https://archiveofourown.org/*
  8. // @require    https://cdn.jsdelivr.net/npm/[email protected]/dist/preact.min.js
  9. // @connect    download.archiveofourown.org
  10. // @grant      GM.deleteValue
  11. // @grant      GM.getValue
  12. // @grant      GM.listValues
  13. // @grant      GM.setValue
  14. // @grant      GM_xmlhttpRequest
  15. // @grant      unsafeWindow
  16. // @run-at     document-idle
  17. // ==/UserScript==
  18.  
  19. (i=>{const r=document.createElement("style");r.dataset.source="vite-plugin-monkey",r.textContent=i,document.head.append(r)})(" div.a3r .modal{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:1000}div.a3r .modal>.box{background-color:#fff;padding:20px;border-radius:8px;box-shadow:0 0 10px #0003}div.a3r .modal>.box>.contents{display:flex;flex-direction:column}div.a3r .modal>.box>.actions{margin-top:20px;display:flex;flex-direction:row;justify-content:center;gap:4px}div.a3r .modal>.box>.actions>.button{background:initial;color:#fff;padding:8px 16px;border:1px solid #bdbdbd;border-radius:12px;cursor:pointer}div.a3r .modal>.box>.actions>.button:hover{background-color:#0003}div.a3r .workList{display:flex;flex-direction:column;width:100%;height:100%}div.a3r .workItem *{transition:all .1s linear}div.a3r .workItem{display:flex;flex-direction:column;border-bottom:var(--divider-color) 1px solid;margin:8px 0;padding-bottom:16px;align-items:stretch;text-align:start;white-space:pre-line}div.a3r .workItem>*{padding:0 16px}div.a3r .workItem .header{display:flex;flex-direction:row;align-items:center}div.a3r .workItem .header>.title{display:flex;flex-direction:column;font-size:large;padding-top:8px;padding-bottom:8px;width:100%}div.a3r .workItem .header>.title>*{margin:0}div.a3r .workItem .header:hover{background-color:#0000001a}div.a3r .workItem .tags{display:flex;flex-direction:column}div.a3r .workItem .tags:hover{background-color:#0000001a}div.a3r .workItem .tags .main,div.a3r .workItem .tags .expanded{display:flex;flex-direction:column}div.a3r .workItem .tags .main h5{margin:1px}div.a3r .workItem .summary{display:flex;flex-direction:column;padding-bottom:8px}div.a3r .workItem .summary:hover{background-color:#0000001a}div.a3r .workItem .trash{display:flex;flex-direction:column;align-items:center;justify-content:center}div.a3r .workItem .trash>.button{all:initial;display:flex;align-items:center;justify-content:center;height:56px;width:56px;border-radius:64px}div.a3r .workItem .trash>.button:hover{background-color:#0000001a}div.a3r .container{--dark-primary-color: #512da8;--light-primary-color: #d1c4e9;--primary-color: #673ab7;--text-color: #ffffff;--accent-color: #e040fb;--primary-text-color: #212121;--secondary-text-color: #757575;--divider-color: #bdbdbd;--background-color: #f5f5f5}div.a3r .container{display:flex;flex-direction:column;height:100%;color:var(--primary-text-color)}div.a3r .appbar{display:flex;flex-direction:row;height:56px;justify-content:center;background-color:var(--primary-color);color:var(--background-color);align-items:center;filter:drop-shadow(0px 0px 4px rgba(0,0,0,.5))}div.a3r .appbar .left{width:100%;display:flex;flex-direction:row;justify-content:flex-start;align-items:flex-start;align-items:stretch;padding:0 16px}div.a3r .appbar .right{width:100%;display:flex;flex-direction:row;justify-content:flex-end;align-items:flex-end;align-items:stretch;padding:0 16px}div.a3r .appbar h3{margin:0;padding:0;font-size:x-large}div.a3r .contents{display:flex;flex-direction:column;overflow:scroll;height:100%;width:100%}div.a3r .contents *{color:var(--primary-text-color)!important}div.a3r .icons{fill:var(--text-color);width:24px}div.a3r .icons.white{filter:invert(100%)}div.a3r .appbar .button{all:initial;display:flex;justify-content:center;height:56px;width:56px}div.a3r .appbar .button:hover{background-color:#0000001a}div.a3r .workDetails{display:flex;flex-direction:column;padding:16px 32px;gap:16px;border-top:1px solid var(--divider-color);background-color:#fff;filter:drop-shadow(0px 0px 4px rgba(0,0,0,.5))}div.a3r .workDetails .headers{display:flex;flex-direction:column;text-align:left}div.a3r .workDetails .headers>*{margin:0}div.a3r .workDetails .headers>h4{font-size:xx-large}div.a3r .workDetails .headers>h5{font-size:medium}div.a3r .workInfo{padding:8px 16px;display:flex;flex-direction:column;text-align:left;gap:8px;white-space:pre-line}div.a3r .workInfo{display:flex;height:100%}div.a3r .buttons{display:flex;flex-direction:column}div.a3r .buttons>.row{width:100%;display:flex;flex-direction:row;justify-content:center}div.a3r .buttons>.row>a{all:initial;display:block;text-decoration:none;width:100%;height:100%;padding:4px 12px;margin:2px!important;border:none;border-radius:2em!important;text-align:center!important;font-size:medium!important;background-color:var(--primary-color)!important;color:var(--text-color)!important}div.a3r .buttons>.row>a:hover{filter:brightness(85%)!important}div.a3r{all:revert;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#fff;z-index:100}div.a3r *{font-family:Arial,Helvetica,sans-serif!important}div.a3r *:after{all:unset} ");
  20.  
  21. function addScript(src) {
  22.   return new Promise((resolve, reject) => {
  23.       const s = document.createElement('script');
  24.  
  25.       s.setAttribute('src', src);
  26.       s.addEventListener('load', resolve);
  27.       s.addEventListener('error', reject);
  28.  
  29.       document.body.appendChild(s);
  30.   });
  31. }
  32.  
  33. await (async function loadLibrary() {
  34.     await addScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js");
  35.     await addScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/epub.min.js");
  36. })();
  37.  
  38. (async function (preact, ePub, jszip) {
  39.   'use strict';
  40.  
  41.   var _a;
  42.   var t, r, u, i, o$1 = 0, f = [], c = [], e = preact.options.__b, a = preact.options.__r, v = preact.options.diffed, l = preact.options.__c, m = preact.options.unmount;
  43.   function d(t2, u2) {
  44.     preact.options.__h && preact.options.__h(r, t2, o$1 || u2), o$1 = 0;
  45.     var i2 = r.__H || (r.__H = { __: [], __h: [] });
  46.     return t2 >= i2.__.length && i2.__.push({ __V: c }), i2.__[t2];
  47.   }
  48.   function h(n) {
  49.     return o$1 = 1, s(B, n);
  50.   }
  51.   function s(n, u2, i2) {
  52.     var o2 = d(t++, 2);
  53.     if (o2.t = n, !o2.__c && (o2.__ = [i2 ? i2(u2) : B(void 0, u2), function(n2) {
  54.       var t2 = o2.__N ? o2.__N[0] : o2.__[0], r2 = o2.t(t2, n2);
  55.       t2 !== r2 && (o2.__N = [r2, o2.__[1]], o2.__c.setState({}));
  56.     }], o2.__c = r, !r.u)) {
  57.       var f2 = function(n2, t2, r2) {
  58.         if (!o2.__c.__H)
  59.           return true;
  60.         var u3 = o2.__c.__H.__.filter(function(n3) {
  61.           return n3.__c;
  62.         });
  63.         if (u3.every(function(n3) {
  64.           return !n3.__N;
  65.         }))
  66.           return !c2 || c2.call(this, n2, t2, r2);
  67.         var i3 = false;
  68.         return u3.forEach(function(n3) {
  69.           if (n3.__N) {
  70.             var t3 = n3.__[0];
  71.             n3.__ = n3.__N, n3.__N = void 0, t3 !== n3.__[0] && (i3 = true);
  72.           }
  73.         }), !(!i3 && o2.__c.props === n2) && (!c2 || c2.call(this, n2, t2, r2));
  74.       };
  75.       r.u = true;
  76.       var c2 = r.shouldComponentUpdate, e2 = r.componentWillUpdate;
  77.       r.componentWillUpdate = function(n2, t2, r2) {
  78.         if (this.__e) {
  79.           var u3 = c2;
  80.           c2 = void 0, f2(n2, t2, r2), c2 = u3;
  81.         }
  82.         e2 && e2.call(this, n2, t2, r2);
  83.       }, r.shouldComponentUpdate = f2;
  84.     }
  85.     return o2.__N || o2.__;
  86.   }
  87.   function p(u2, i2) {
  88.     var o2 = d(t++, 3);
  89.     !preact.options.__s && z(o2.__H, i2) && (o2.__ = u2, o2.i = i2, r.__H.__h.push(o2));
  90.   }
  91.   function _$1(n) {
  92.     return o$1 = 5, F(function() {
  93.       return { current: n };
  94.     }, []);
  95.   }
  96.   function F(n, r2) {
  97.     var u2 = d(t++, 7);
  98.     return z(u2.__H, r2) ? (u2.__V = n(), u2.i = r2, u2.__h = n, u2.__V) : u2.__;
  99.   }
  100.   function b() {
  101.     for (var t2; t2 = f.shift(); )
  102.       if (t2.__P && t2.__H)
  103.         try {
  104.           t2.__H.__h.forEach(k), t2.__H.__h.forEach(w), t2.__H.__h = [];
  105.         } catch (r2) {
  106.           t2.__H.__h = [], preact.options.__e(r2, t2.__v);
  107.         }
  108.   }
  109.   preact.options.__b = function(n) {
  110.     r = null, e && e(n);
  111.   }, preact.options.__r = function(n) {
  112.     a && a(n), t = 0;
  113.     var i2 = (r = n.__c).__H;
  114.     i2 && (u === r ? (i2.__h = [], r.__h = [], i2.__.forEach(function(n2) {
  115.       n2.__N && (n2.__ = n2.__N), n2.__V = c, n2.__N = n2.i = void 0;
  116.     })) : (i2.__h.forEach(k), i2.__h.forEach(w), i2.__h = [], t = 0)), u = r;
  117.   }, preact.options.diffed = function(t2) {
  118.     v && v(t2);
  119.     var o2 = t2.__c;
  120.     o2 && o2.__H && (o2.__H.__h.length && (1 !== f.push(o2) && i === preact.options.requestAnimationFrame || ((i = preact.options.requestAnimationFrame) || j)(b)), o2.__H.__.forEach(function(n) {
  121.       n.i && (n.__H = n.i), n.__V !== c && (n.__ = n.__V), n.i = void 0, n.__V = c;
  122.     })), u = r = null;
  123.   }, preact.options.__c = function(t2, r2) {
  124.     r2.some(function(t3) {
  125.       try {
  126.         t3.__h.forEach(k), t3.__h = t3.__h.filter(function(n) {
  127.           return !n.__ || w(n);
  128.         });
  129.       } catch (u2) {
  130.         r2.some(function(n) {
  131.           n.__h && (n.__h = []);
  132.         }), r2 = [], preact.options.__e(u2, t3.__v);
  133.       }
  134.     }), l && l(t2, r2);
  135.   }, preact.options.unmount = function(t2) {
  136.     m && m(t2);
  137.     var r2, u2 = t2.__c;
  138.     u2 && u2.__H && (u2.__H.__.forEach(function(n) {
  139.       try {
  140.         k(n);
  141.       } catch (n2) {
  142.         r2 = n2;
  143.       }
  144.     }), u2.__H = void 0, r2 && preact.options.__e(r2, u2.__v));
  145.   };
  146.   var g = "function" == typeof requestAnimationFrame;
  147.   function j(n) {
  148.     var t2, r2 = function() {
  149.       clearTimeout(u2), g && cancelAnimationFrame(t2), setTimeout(n);
  150.     }, u2 = setTimeout(r2, 100);
  151.     g && (t2 = requestAnimationFrame(r2));
  152.   }
  153.   function k(n) {
  154.     var t2 = r, u2 = n.__c;
  155.     "function" == typeof u2 && (n.__c = void 0, u2()), r = t2;
  156.   }
  157.   function w(n) {
  158.     var t2 = r;
  159.     n.__c = n.__(), r = t2;
  160.   }
  161.   function z(n, t2) {
  162.     return !n || n.length !== t2.length || t2.some(function(t3, r2) {
  163.       return t3 !== n[r2];
  164.     });
  165.   }
  166.   function B(n, t2) {
  167.     return "function" == typeof t2 ? t2(n) : t2;
  168.   }
  169.   var _GM = /* @__PURE__ */ (() => typeof GM != "undefined" ? GM : void 0)();
  170.   var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  171.   var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  172.   var classes;
  173.   class StorageWrapper {
  174.     /**
  175.      * @constructor
  176.      * @param {string} storageSpace name of the storage space
  177.      */
  178.     constructor(storageSpace) {
  179.       this.storageSpace = storageSpace;
  180.     }
  181.     set(key, value) {
  182.       return _GM.setValue(`${this.storageSpace}.${key}`, value);
  183.     }
  184.     get(key) {
  185.       return _GM.getValue(`${this.storageSpace}.${key}`);
  186.     }
  187.     delete(key) {
  188.       return _GM.deleteValue(`${this.storageSpace}.${key}`);
  189.     }
  190.     list() {
  191.       return _GM.listValues().then((values) => {
  192.         return values.filter((value) => {
  193.           return value.startsWith(this.storageSpace);
  194.         }).map((value) => {
  195.           return value.slice((this.storageSpace + ".").length);
  196.         });
  197.       });
  198.     }
  199.   }
  200.   const blobSerializer = {
  201.     toBase64: (blobject) => {
  202.       return new Promise((resolve, reject) => {
  203.         const reader = new FileReader();
  204.         reader.onloadend = () => {
  205.           resolve(reader.result);
  206.         };
  207.         reader.onerror = reject;
  208.         reader.readAsDataURL(blobject);
  209.       });
  210.     },
  211.     toBlob: (base64) => {
  212.       return fetch(base64).then((res) => res.blob());
  213.     }
  214.   };
  215.   const objectSerializer = {
  216.     datePattern: /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/i,
  217.     serialize(classInstance) {
  218.       return JSON.stringify(classInstance, (key, value) => {
  219.         if (value && typeof value === "object") {
  220.           value.__type = value.constructor.name;
  221.         }
  222.         return value;
  223.       });
  224.     },
  225.     deserialize(jsonString) {
  226.       return JSON.parse(jsonString, (key, value) => {
  227.         if (value && typeof value === "object" && value.__type) {
  228.           const DynamicClass = classes[value.__type] || Object;
  229.           value = Object.assign(new DynamicClass(), value);
  230.           delete value.__type;
  231.         } else if (this.datePattern.test(value)) {
  232.           value = new Date(value);
  233.         }
  234.         return value;
  235.       });
  236.     }
  237.   };
  238.   const AO3Parser = {
  239.     /**
  240.      * @param {HTMLElement} html
  241.      * @returns {?AO3Work}
  242.      */
  243.     parseWorkPage(html) {
  244.       if (!html.querySelector("div.wrapper > dl.work.meta.group")) {
  245.         return null;
  246.       }
  247.       return new AO3Work(html.querySelector("li.share > a").attributes.href.value.split("/")[2].split("?")[0], html.querySelector("h2.title.heading").textContent.trim(), Array.from(html.querySelectorAll("h3.byline.heading > a")).map((e2) => {
  248.         return new AO3Author(e2.textContent.trim(), e2.attributes.href.value);
  249.       }), Array.from(html.querySelectorAll("dd.rating.tags > ul > li > a")).map((e2) => {
  250.         return new AO3Tag(e2.textContent.trim(), e2.attributes.href.value);
  251.       }), Array.from(html.querySelectorAll("dd.warning.tags > ul > li > a")).map((e2) => {
  252.         return new AO3Tag(e2.textContent.trim(), e2.attributes.href.value);
  253.       }), Array.from(html.querySelectorAll("dd.fandom.tags > ul > li > a")).map((e2) => {
  254.         return new AO3Tag(e2.textContent.trim(), e2.attributes.href.value);
  255.       }), Array.from(html.querySelectorAll("dd.relationship.tags > ul > li > a")).map((e2) => {
  256.         return new AO3Tag(e2.textContent.trim(), e2.attributes.href.value);
  257.       }), Array.from(html.querySelectorAll("dd.character.tags > ul > li > a")).map((e2) => {
  258.         return new AO3Tag(e2.textContent.trim(), e2.attributes.href.value);
  259.       }), Array.from(html.querySelectorAll("dd.freeform.tags > ul > li > a")).map((e2) => {
  260.         return new AO3Tag(e2.textContent.trim(), e2.attributes.href.value);
  261.       }), html.querySelector("div.summary.module > blockquote.userstuff") ? Array.from(html.querySelector("div.summary.module > blockquote.userstuff").childNodes).map((e2) => Array.from(e2.childNodes).map((v2) => v2.nodeName != "BR" ? v2.textContent : "\n").join("")).join("\n\n").trim() : "", parseInt(html.querySelector("dd.chapters").textContent.split("/")[0].trim()), parseInt(html.querySelector("dd.chapters").textContent.split("/")[1].trim()) || null, new Date(html.querySelector("dd.published").textContent.trim()), new Date((html.querySelector("dd.status") || html.querySelector("dd.published")).textContent.trim()), html.querySelector("dd.words") ? parseInt(html.querySelector("dd.words").textContent.trim()) : 0, html.querySelector("dd.comments") ? parseInt(html.querySelector("dd.comments").textContent.trim()) : 0, html.querySelector("dd.kudos") ? parseInt(html.querySelector("dd.kudos").textContent.trim()) : 0, html.querySelector("dd.bookmarks") ? parseInt(html.querySelector("dd.bookmarks").textContent.trim()) : 0, html.querySelector("dd.hits") ? parseInt(html.querySelector("dd.hits").textContent.trim()) : 0, html.querySelector("dd.language") ? html.querySelector("dd.language").textContent.trim() : "", Array.from(html.querySelectorAll("dd.collections > ul > li > a")).map((e2) => {
  262.         return new AO3Collection(e2.textContent.trim(), e2.attributes.href.value);
  263.       }), html.querySelector("div.series.module") ? new AO3WorkSeries(new AO3Series(html.querySelector("div.series.module > ul > li > span.series > span.position > a").textContent.trim(), html.querySelector("div.series.module > ul > li > span.series > span.position > a").attributes.href.value), parseInt(html.querySelector("div.series.module > ul > li > span.series > span.position").textContent.split(" ")[1]), Array.from(document.body.querySelectorAll("div.series.module > ul > li > span.series > a.next")).map((e2) => e2.attributes.href.value)[0] || "", Array.from(document.body.querySelectorAll("div.series.module > ul > li > span.series > a.previous")).map((e2) => e2.attributes.href.value)[0] || "") : null);
  264.     }
  265.   };
  266.   const AO3AccessObject = {
  267.     /**
  268.      * fetches the html of the work page for the given id using fetch and calls the parser to parse it.
  269.      * set fetch to send all cookies, so it should work for logged in users.
  270.      * @param {string} id
  271.      * @returns {Promise<AO3Work>}
  272.      */
  273.     async getWork(id) {
  274.       var response = await fetch(`https://archiveofourown.org/works/${id}?view_adult=true`, {
  275.         credentials: "include"
  276.       });
  277.       var html = await response.text();
  278.       return AO3Parser.parseWorkPage(new DOMParser().parseFromString(html, "text/html"));
  279.     },
  280.     /**
  281.      *
  282.      * @param {string} id
  283.      * @returns {Promise<Blob>} The epub file
  284.      */
  285.     getWorkEpub(id) {
  286.       return new Promise((resolve, reject) => {
  287.         _GM_xmlhttpRequest({
  288.           method: "GET",
  289.           url: `https://archiveofourown.org/downloads/${id}/book.epub?updated_at=${Math.floor(Date.now() / 1e3)}`,
  290.           responseType: "blob",
  291.           redirect: "follow",
  292.           fetch: true,
  293.           onload: (response) => {
  294.             resolve(response.response);
  295.           },
  296.           onerror: (err) => {
  297.             reject(err);
  298.           }
  299.         });
  300.       });
  301.     }
  302.   };
  303.   const LibraryStorageObject = {
  304.     libraryStore: new StorageWrapper("libraryStore"),
  305.     epubStore: new StorageWrapper("epubStore"),
  306.     locationStore: new StorageWrapper("locationStore"),
  307.     /**
  308.      *
  309.      * @param {LibraryWork} work
  310.      * @returns {Promise<void>}
  311.      */
  312.     storeWork(work) {
  313.       return this.libraryStore.set(work.id, objectSerializer.serialize(work));
  314.     },
  315.     /**
  316.      *
  317.      * @param {string} id
  318.      * @returns {Promise<LibraryWork>}
  319.      */
  320.     async getWork(id) {
  321.       return objectSerializer.deserialize(await this.libraryStore.get(id));
  322.     },
  323.     /**
  324.      *
  325.      * @param {LibraryWork | string} work id or object
  326.      */
  327.     deleteWork(work) {
  328.       if (typeof work === "string") {
  329.         return this.libraryStore.delete(work);
  330.       } else {
  331.         return this.libraryStore.delete(work.id);
  332.       }
  333.     },
  334.     listWorks() {
  335.       return this.libraryStore.list();
  336.     },
  337.     async storeEpub(id, date, blob) {
  338.       return this.epubStore.set(`${id}_${date.getTime()}`, await blobSerializer.toBase64(blob));
  339.     },
  340.     async getEpub(id, date) {
  341.       return blobSerializer.toBlob(await this.epubStore.get(`${id}_${date.getTime()}`));
  342.     },
  343.     deleteEpub(id, date) {
  344.       return this.epubStore.delete(`${id}_${date.getTime()}`);
  345.     },
  346.     async listEpubs(id) {
  347.       var epubs = await this.epubStore.list();
  348.       if (id) {
  349.         return epubs.filter((e2) => e2.startsWith(id));
  350.       } else {
  351.         return epubs;
  352.       }
  353.     },
  354.     async storeLocation(id, location) {
  355.       return this.locationStore.set(id, location);
  356.     },
  357.     async getLocation(id) {
  358.       return this.locationStore.get(id);
  359.     },
  360.     deleteLocation(id) {
  361.       return this.locationStore.delete(id);
  362.     }
  363.   };
  364.   class AO3Tag {
  365.     /**
  366.      * @param {string} name Name of the tag
  367.      * @param {string} url URL of the tag
  368.      */
  369.     constructor(name, url) {
  370.       this.name = name;
  371.       this.url = url;
  372.     }
  373.   }
  374.   class AO3Author {
  375.     /**
  376.      * @param {string} name Name of the author/pseud
  377.      * @param {string} url URL of the author/pseud
  378.      */
  379.     constructor(name, url) {
  380.       this.name = name;
  381.       this.url = url;
  382.     }
  383.   }
  384.   class AO3Collection {
  385.     /**
  386.      * @param {string} name Title of the collection
  387.      * @param {string} url URL of the collection
  388.      */
  389.     constructor(name, url) {
  390.       this.name = name;
  391.       this.url = url;
  392.     }
  393.   }
  394.   class AO3Series {
  395.     /**
  396.      * @param {string} name Title of the series
  397.      * @param {string} url URL of the series
  398.      */
  399.     constructor(name, url) {
  400.       this.name = name;
  401.       this.url = url;
  402.     }
  403.   }
  404.   class AO3WorkSeries {
  405.     /**
  406.      * @constructor
  407.      * @param {AO3Series} series Series of the work
  408.      * @param {number} position Position of the work in the series
  409.      * @param {string} nextWork URL of the next work in the series
  410.      * @param {string} previousWork URL of the previous work in the series
  411.      */
  412.     constructor(series, position, nextWork, previousWork) {
  413.       this.series = series;
  414.       this.position = position;
  415.       this.nextWork = nextWork;
  416.       this.previousWork = previousWork;
  417.     }
  418.   }
  419.   class AO3Work {
  420.     /**
  421.      * @constructor
  422.      * @param {string} id ID of the work;
  423.      * @param {string} title Title of the work
  424.      * @param {AO3Author[]} authors Authors of the work
  425.      * @param {AO3Tag[]} ratings ratings of the work
  426.      * @param {AO3Tag[]} warnings Warnings of the work
  427.      * @param {AO3Tag[]} fandoms fandoms of the work
  428.      * @param {AO3Tag[]} relationships Relationships of the work
  429.      * @param {AO3Tag[]} characters Characters of the work
  430.      * @param {AO3Tag[]} tags Tags of the work
  431.      * @param {string} summary Summary of the work
  432.      * @param {number} chapters Number of chapters
  433.      * @param {?number} totalChapters Total number of chapters, undefined if unknown
  434.      * @param {date} published Date of work published
  435.      * @param {date} updated Date of work updated
  436.      * @param {number} wordCount Number of words in work
  437.      * @param {number} commentCount Number of comments
  438.      * @param {number} kudosCount Number of kudos
  439.      * @param {number} bookmarkCount Number of bookmarks
  440.      * @param {number} hitCount Number of hits
  441.      * @param {string} language Language of the work
  442.      * @param {AO3Collection[]} collections Collections the work is in
  443.      * @param {?AO3WorkSeries} series Series the work is in
  444.      */
  445.     constructor(id, title, author, ratings, warnings, fandoms, relationships, characters, tags, summary, chapters, totalChapters, published, updated, wordCount, commentCount, kudosCount, bookmarkCount, hitCount, language, collections, series) {
  446.       this.id = id;
  447.       this.title = title;
  448.       this.authors = author;
  449.       this.ratings = ratings;
  450.       this.warnings = warnings;
  451.       this.fandoms = fandoms;
  452.       this.relationships = relationships;
  453.       this.characters = characters;
  454.       this.tags = tags;
  455.       this.summary = summary;
  456.       this.chapterCount = chapters;
  457.       this.totalChapters = totalChapters;
  458.       this.published = published;
  459.       this.updated = updated;
  460.       this.wordCount = wordCount;
  461.       this.commentCount = commentCount;
  462.       this.kudosCount = kudosCount;
  463.       this.bookmarkCount = bookmarkCount;
  464.       this.hitCount = hitCount;
  465.       this.language = language;
  466.       this.collections = collections;
  467.       this.series = series;
  468.     }
  469.     getEpub() {
  470.       return LibraryStorageObject.getEpub(this.id, this.updated);
  471.     }
  472.   }
  473.   class LibraryWork {
  474.     /**
  475.      * @constructor
  476.      * @param {string} id ID of the work
  477.      * @param {date} lastChecked Date and Time of last check
  478.      * @param {date} lastAccessed Date and Time of last read/access
  479.      * @param {AO3Work[]} works Array of all versions of a work
  480.      */
  481.     constructor(id, lastChecked, lastAccessed, works) {
  482.       this.id = id;
  483.       this.lastChecked = lastChecked;
  484.       this.lastAccessed = lastAccessed;
  485.       this.works = works || [];
  486.     }
  487.     /**
  488.      * Checks for the latest version of the work and adds it to the works array. Also gets the epub and stores it
  489.      * @param {boolean} force force get latest version
  490.      * @returns {Promise<boolean>} True if the work was updated, false if not
  491.      */
  492.     async updateWork(force = false) {
  493.       var newWork;
  494.       newWork = await AO3AccessObject.getWork(this.id);
  495.       if (this.works.length == 0 || newWork.updated > this.getLatest().updated) {
  496.         this.works.push(newWork);
  497.         await LibraryStorageObject.storeEpub(this.id, newWork.updated, await AO3AccessObject.getWorkEpub(this.id));
  498.         this.lastChecked = /* @__PURE__ */ new Date();
  499.         return true;
  500.       } else if (force) {
  501.         this.works[this.works.length - 1] = newWork;
  502.         await LibraryStorageObject.storeEpub(this.id, newWork.updated, await AO3AccessObject.getWorkEpub(this.id));
  503.         this.lastChecked = /* @__PURE__ */ new Date();
  504.         return true;
  505.       }
  506.       return false;
  507.     }
  508.     deleteWorkVersion(date) {
  509.       var index2 = this.works.findIndex((work) => work.updated == date);
  510.       if (index2 != -1) {
  511.         this.works.splice(index2, 1);
  512.         LibraryStorageObject.deleteEpub(this.id, date);
  513.       }
  514.     }
  515.     /**
  516.      * Removes all versions of the work except the latest
  517.      */
  518.     purge() {
  519.       while (this.works.length > 1) {
  520.         this.deleteWorkVersion(this.works[0].updated);
  521.       }
  522.     }
  523.     /**
  524.      * Gets the latest version of the work
  525.      * @returns {AO3Work}
  526.      */
  527.     getLatest() {
  528.       return this.works[this.works.length - 1];
  529.     }
  530.     updateLastAccessed() {
  531.       this.lastAccessed = /* @__PURE__ */ new Date();
  532.     }
  533.     getReadingLocation() {
  534.       return LibraryStorageObject.getLocation(this.id);
  535.     }
  536.     setReadingLocation(location) {
  537.       LibraryStorageObject.storeLocation(this.id, location);
  538.     }
  539.     resetReadingLocation() {
  540.       LibraryStorageObject.deleteLocation(this.id);
  541.     }
  542.   }
  543.   const Library = {
  544.     /**@type {string[]} */
  545.     ids: [],
  546.     /**@type {LibraryWork[]} */
  547.     works: [],
  548.     /**@type {number} */
  549.     page: 0,
  550.     /**@type {number} */
  551.     pageSize: 20,
  552.     async refresh() {
  553.       this.ids = await LibraryStorageObject.listWorks();
  554.       this.works = await Promise.all(this.ids.slice(this.page * this.pageSize, this.page * this.pageSize + this.pageSize).map((id) => LibraryStorageObject.getWork(id)));
  555.     },
  556.     setPage(num) {
  557.       if (num > 0) {
  558.         this.page = num - 1;
  559.       }
  560.     },
  561.     getPage() {
  562.       return this.page + 1;
  563.     },
  564.     setPageSize(size) {
  565.       this.pageSize = size;
  566.     },
  567.     getPageCount() {
  568.       return Math.ceil(this.ids.length / this.pageSize);
  569.     },
  570.     /**
  571.      * Creates a new Library work, and retrieves latest work from AO3. Returns the new LibraryWork
  572.      * @param {string} id
  573.      * @returns {Promise<LibraryWork>}
  574.      */
  575.     async addWork(id) {
  576.       var work = new LibraryWork(id, null, null, null);
  577.       await work.updateWork();
  578.       work.lastAccessed = /* @__PURE__ */ new Date(0);
  579.       this.updateWork(work);
  580.       return work;
  581.     },
  582.     /**
  583.      * Deletes a library work, deletes all the metadata and epub files.
  584.      * @param {string} id
  585.      * @returns {Promise<void>}
  586.      */
  587.     async deleteWork(id) {
  588.       var epubs = await LibraryStorageObject.listEpubs(id);
  589.       for (var epub of epubs) {
  590.         await LibraryStorageObject.deleteEpub(id, new Date(parseInt(epub.split("_")[1])));
  591.       }
  592.       await LibraryStorageObject.deleteWork(id);
  593.     },
  594.     async getWorkUpdate(work) {
  595.       var newWork = await AO3AccessObject.getWork(work.id);
  596.       if (work.getLatest().updated.toString() == newWork.updated.toString()) {
  597.         work.works.pop();
  598.       }
  599.       work.works.push(newWork);
  600.       await LibraryStorageObject.storeEpub(work.id, newWork.updated, await AO3AccessObject.getWorkEpub(work.id));
  601.       work.lastChecked = /* @__PURE__ */ new Date();
  602.       await this.updateWork(work);
  603.     },
  604.     /**
  605.      * Updates the stored library work
  606.      * @param {LibraryWork} work
  607.      * @returns {Promise<void>}
  608.      */
  609.     async updateWork(work) {
  610.       await LibraryStorageObject.storeWork(work);
  611.     }
  612.   };
  613.   classes = {
  614.     AO3Tag,
  615.     AO3Author,
  616.     AO3Collection,
  617.     AO3Series,
  618.     AO3WorkSeries,
  619.     AO3Work,
  620.     LibraryWork,
  621.     StorageWrapper
  622.   };
  623.   _unsafeWindow.ao3reader = {
  624.     class: classes,
  625.     objects: {
  626.       blobSerializer,
  627.       objectSerializer,
  628.       AO3Parser,
  629.       AO3AccessObject,
  630.       LibraryStorageObject,
  631.       Library
  632.     },
  633.     TM: {
  634.       GM: _GM,
  635.       GM_xmlhttpRequest: _GM_xmlhttpRequest
  636.     }
  637.   };
  638.   var _ = 0;
  639.   function o(o2, e2, n, t2, f2, l2) {
  640.     var s2, u2, a2 = {};
  641.     for (u2 in e2)
  642.       "ref" == u2 ? s2 = e2[u2] : a2[u2] = e2[u2];
  643.     var i2 = { type: o2, props: a2, key: n, ref: s2, __k: null, __: null, __b: 0, __e: null, __d: void 0, __c: null, __h: null, constructor: void 0, __v: --_, __source: f2, __self: l2 };
  644.     if ("function" == typeof o2 && (s2 = o2.defaultProps))
  645.       for (u2 in s2)
  646.         void 0 === a2[u2] && (a2[u2] = s2[u2]);
  647.     return preact.options.vnode && preact.options.vnode(i2), i2;
  648.   }
  649.   function Modal({
  650.     isOpen,
  651.     children
  652.   }) {
  653.     if (!isOpen)
  654.       return o(preact.Fragment, {});
  655.     return o("div", {
  656.       className: "modal",
  657.       children: o("div", {
  658.         className: "box",
  659.         children
  660.       })
  661.     });
  662.   }
  663.   function ModalContents({
  664.     children
  665.   }) {
  666.     return o("div", {
  667.       className: "contents",
  668.       children
  669.     });
  670.   }
  671.   function ModalActions({
  672.     children
  673.   }) {
  674.     return o("div", {
  675.       className: "actions",
  676.       children
  677.     });
  678.   }
  679.   function ModalButton({
  680.     onClick,
  681.     children
  682.   }) {
  683.     return o("button", {
  684.       className: "button",
  685.       onClick,
  686.       children
  687.     });
  688.   }
  689.   const exitIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiPgo8Zz4KCTxwYXRoIGQ9Ik0xNzAuNjk4LDQ0OEg3Mi43NTdjLTQuODE0LTAuMDEyLTguNzE0LTMuOTExLTguNzI1LTguNzI1VjcyLjcyNWMwLjAxMi00LjgxNCwzLjkxMS04LjcxNCw4LjcyNS04LjcyNWg5Ny45NDEgICBjMTcuNjczLDAsMzItMTQuMzI3LDMyLTMycy0xNC4zMjctMzItMzItMzJINzIuNzU3QzMyLjYxMSwwLjA0NywwLjA3OSwzMi41OCwwLjAzMiw3Mi43MjV2MzY2LjU0OSAgIEMwLjA3OSw0NzkuNDIsMzIuNjExLDUxMS45NTMsNzIuNzU3LDUxMmg5Ny45NDFjMTcuNjczLDAsMzItMTQuMzI3LDMyLTMyUzE4OC4zNzEsNDQ4LDE3MC42OTgsNDQ4eiIvPgoJPHBhdGggZD0iTTQ4My45MTQsMTg4LjExN2wtODIuODE2LTgyLjc1MmMtMTIuNTAxLTEyLjQ5NS0zMi43NjQtMTIuNDktNDUuMjU5LDAuMDExcy0xMi40OSwzMi43NjQsMC4wMTEsNDUuMjU5bDcyLjc4OSw3Mi43NjggICBMMTM4LjY5OCwyMjRjLTE3LjY3MywwLTMyLDE0LjMyNy0zMiwzMnMxNC4zMjcsMzIsMzIsMzJsMCwwbDI5MS4xMTUtMC41MzNsLTczLjk2Myw3My45NjMgICBjLTEyLjA0MiwxMi45MzYtMTEuMzE3LDMzLjE4NCwxLjYxOCw0NS4yMjZjMTIuMjk1LDExLjQ0NSwzMS4zNDYsMTEuNDM2LDQzLjYzLTAuMDIxbDgyLjc1Mi04Mi43NTIgICBjMzcuNDkxLTM3LjQ5LDM3LjQ5MS05OC4yNzQsMC4wMDEtMTM1Ljc2NGMwLDAtMC4wMDEtMC4wMDEtMC4wMDEtMC4wMDFMNDgzLjkxNCwxODguMTE3eiIvPgo8L2c+CgoKCgoKCgoKCgoKCgoKCjwvc3ZnPgo=";
  690.   const refreshIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTMuODA2IDUxMy44MDYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMy44MDYgNTEzLjgwNjsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIj4KPGc+Cgk8cGF0aCBkPSJNNjYuMDc0LDIyOC43MzFDODEuNTc3LDEyMy4zNzksMTc5LjU0OSw1MC41NDIsMjg0LjkwMSw2Ni4wNDVjMzUuOTQ0LDUuMjg5LDY5LjY2MiwyMC42MjYsOTcuMjcsNDQuMjQ0bC0yNC44NTMsMjQuODUzICAgYy04LjMzLDguMzMyLTguMzI4LDIxLjg0LDAuMDA1LDMwLjE3YzMuOTk5LDMuOTk4LDkuNDIzLDYuMjQ1LDE1LjA3OCw2LjI0Nmg5Ny44MzVjMTEuNzgyLDAsMjEuMzMzLTkuNTUxLDIxLjMzMy0yMS4zMzNWNTIuMzkgICBjLTAuMDAzLTExLjc4Mi05LjU1Ni0yMS4zMzEtMjEuMzM4LTIxLjMyOWMtNS42NTUsMC4wMDEtMTEuMDc5LDIuMjQ4LTE1LjA3OCw2LjI0Nkw0MjcuNDE4LDY1LjA0ICAgQzMyMS42NTgtMjkuMjM1LDE1OS40OTctMTkuOTI1LDY1LjIyMiw4NS44MzVjLTMzLjM5OSwzNy40NjctNTUuMDczLDgzLjkwOS02Mi4zMzcsMTMzLjU3MyAgIGMtMi44NjQsMTcuNjA3LDkuMDg3LDM0LjIwMiwyNi42OTMsMzcuMDY2YzEuNTg2LDAuMjU4LDMuMTg4LDAuMzk3LDQuNzk1LDAuNDE3QzUwLjQ4MSwyNTYuNzE3LDY0LjAwMiwyNDQuNzA2LDY2LjA3NCwyMjguNzMxeiIvPgoJPHBhdGggZD0iTTQ3OS40MjksMjU2Ljg5MWMtMTYuMTA4LDAuMTc0LTI5LjYyOSwxMi4xODUtMzEuNzAxLDI4LjE2QzQzMi4yMjUsMzkwLjQwMywzMzQuMjUzLDQ2My4yNCwyMjguOTAxLDQ0Ny43MzggICBjLTM1Ljk0NC01LjI4OS02OS42NjItMjAuNjI2LTk3LjI3LTQ0LjI0NGwyNC44NTMtMjQuODUzYzguMzMtOC4zMzIsOC4zMjgtMjEuODQtMC4wMDUtMzAuMTcgICBjLTMuOTk5LTMuOTk4LTkuNDIzLTYuMjQ1LTE1LjA3OC02LjI0Nkg0My41NjhjLTExLjc4MiwwLTIxLjMzMyw5LjU1MS0yMS4zMzMsMjEuMzMzdjk3LjgzNSAgIGMwLjAwMywxMS43ODIsOS41NTYsMjEuMzMxLDIxLjMzOCwyMS4zMjljNS42NTUtMC4wMDEsMTEuMDc5LTIuMjQ4LDE1LjA3OC02LjI0NmwyNy43MzMtMjcuNzMzICAgYzEwNS43MzUsOTQuMjg1LDI2Ny44ODQsODUuMDA0LDM2Mi4xNy0yMC43MzJjMzMuNDE3LTM3LjQ3NSw1NS4xMDEtODMuOTMzLDYyLjM2My0xMzMuNjE1ICAgYzIuODc2LTE3LjYwNS05LjA2NC0zNC4yMDgtMjYuNjY4LTM3LjA4NEM0ODIuNjU1LDI1Ny4wNTEsNDgxLjA0NCwyNTYuOTEsNDc5LjQyOSwyNTYuODkxeiIvPgo8L2c+CgoKCgoKCgoKCgoKCgoKCjwvc3ZnPgo=";
  691.   const trashIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiPgo8Zz4KCTxwYXRoIGQ9Ik00OTAuNjY3LDk2YzAtMTcuNjczLTE0LjMyNy0zMi0zMi0zMmgtODAuNTU1QzM2NC42MzIsMjUuNzU3LDMyOC41NDksMC4xMywyODgsMGgtNjQgICBjLTQwLjU0OSwwLjEzLTc2LjYzMiwyNS43NTctOTAuMTEyLDY0SDUzLjMzM2MtMTcuNjczLDAtMzIsMTQuMzI3LTMyLDMybDAsMGMwLDE3LjY3MywxNC4zMjcsMzIsMzIsMzJINjR2MjY2LjY2NyAgIEM2NCw0NTkuNDY4LDExNi41MzIsNTEyLDE4MS4zMzMsNTEyaDE0OS4zMzNDMzk1LjQ2OCw1MTIsNDQ4LDQ1OS40NjgsNDQ4LDM5NC42NjdWMTI4aDEwLjY2NyAgIEM0NzYuMzQsMTI4LDQ5MC42NjcsMTEzLjY3Myw0OTAuNjY3LDk2eiBNMzg0LDM5NC42NjdDMzg0LDQyNC4xMjIsMzYwLjEyMiw0NDgsMzMwLjY2Nyw0NDhIMTgxLjMzMyAgIEMxNTEuODc4LDQ0OCwxMjgsNDI0LjEyMiwxMjgsMzk0LjY2N1YxMjhoMjU2VjM5NC42Njd6Ii8+Cgk8cGF0aCBkPSJNMjAyLjY2NywzODRjMTcuNjczLDAsMzItMTQuMzI3LDMyLTMyVjIyNGMwLTE3LjY3My0xNC4zMjctMzItMzItMzJzLTMyLDE0LjMyNy0zMiwzMnYxMjggICBDMTcwLjY2NywzNjkuNjczLDE4NC45OTQsMzg0LDIwMi42NjcsMzg0eiIvPgoJPHBhdGggZD0iTTMwOS4zMzMsMzg0YzE3LjY3MywwLDMyLTE0LjMyNywzMi0zMlYyMjRjMC0xNy42NzMtMTQuMzI3LTMyLTMyLTMycy0zMiwxNC4zMjctMzIsMzJ2MTI4ICAgQzI3Ny4zMzMsMzY5LjY3MywyOTEuNjYsMzg0LDMwOS4zMzMsMzg0eiIvPgo8L2c+CgoKCgoKCgoKCgoKCgoKCjwvc3ZnPgo=";
  692.   function WorkItemHeader({
  693.     work,
  694.     openWork
  695.   }) {
  696.     return o("div", {
  697.       className: "title",
  698.       onClick: openWork,
  699.       children: [o("h4", {
  700.         children: o("strong", {
  701.           children: work.getLatest().title
  702.         })
  703.       }), o("h5", {
  704.         children: ["by ", work.getLatest().authors.map((e2) => e2.name).join(" ,")]
  705.       })]
  706.     });
  707.   }
  708.   function WorkItemTags({
  709.     work
  710.   }) {
  711.     const [isExpanded, setIsExpanded] = h(false);
  712.     function toggleExpanded() {
  713.       setIsExpanded(!isExpanded);
  714.     }
  715.     return o("div", {
  716.       className: "tags",
  717.       onClick: toggleExpanded,
  718.       children: [o("div", {
  719.         className: "main",
  720.         children: [o("h5", {
  721.           children: work.getLatest().ratings.map((e2) => e2.name).join(", ")
  722.         }), o("h5", {
  723.           children: work.getLatest().fandoms.map((e2) => e2.name).join(", ")
  724.         })]
  725.       }), o("div", {
  726.         className: "expanded",
  727.         style: {
  728.           display: isExpanded ? "flex" : "none"
  729.         },
  730.         children: [o("h5", {
  731.           children: work.getLatest().warnings.map((e2) => e2.name).join(", ")
  732.         }), o("h5", {
  733.           children: work.getLatest().relationships.map((e2) => e2.name).join(", ")
  734.         }), o("h5", {
  735.           children: work.getLatest().characters.map((e2) => e2.name).join(", ")
  736.         }), o("h5", {
  737.           children: work.getLatest().tags.map((e2) => e2.name).join(", ")
  738.         })]
  739.       })]
  740.     });
  741.   }
  742.   function WorkItemSummary({
  743.     work
  744.   }) {
  745.     const [isExpanded, setIsExpanded] = h(false);
  746.     function toggleExpanded() {
  747.       setIsExpanded(!isExpanded);
  748.     }
  749.     return o("div", {
  750.       className: "summary",
  751.       onClick: toggleExpanded,
  752.       children: [o("h5", {
  753.         children: o("strong", {
  754.           children: "Summary:"
  755.         })
  756.       }), o("p", {
  757.         children: isExpanded ? work.getLatest().summary : work.getLatest().summary.split("\n")[0].split(" ").slice(0, 32).join(" ") + "..."
  758.       })]
  759.     });
  760.   }
  761.   function WorkList({
  762.     works,
  763.     openWork,
  764.     deleteWork
  765.   }) {
  766.     const [isOpen, setIsOpen] = h(false);
  767.     const [workDelete, setWorkDelete] = h(null);
  768.     return o("div", {
  769.       className: "workList",
  770.       children: [works.map((work) => o("div", {
  771.         className: "workItem",
  772.         children: [o("div", {
  773.           className: "header",
  774.           children: [o(WorkItemHeader, {
  775.             work,
  776.             openWork: () => {
  777.               openWork(work);
  778.             }
  779.           }), o("div", {
  780.             className: "trash",
  781.             children: o("button", {
  782.               class: "button",
  783.               onClick: () => {
  784.                 setWorkDelete(work);
  785.                 setIsOpen(true);
  786.               },
  787.               children: o("img", {
  788.                 class: "icons",
  789.                 src: trashIcon,
  790.                 alt: "refresh"
  791.               })
  792.             })
  793.           })]
  794.         }), o(WorkItemTags, {
  795.           work
  796.         }), o(WorkItemSummary, {
  797.           work
  798.         })]
  799.       })), o(Modal, {
  800.         isOpen,
  801.         children: [o(ModalContents, {
  802.           children: [o("h3", {
  803.             children: "Delete Work?"
  804.           }), o("p", {
  805.             children: ["This work and its version history will be deleted. ", o("br", {}), "This action is irreversible! "]
  806.           })]
  807.         }), o(ModalActions, {
  808.           children: [o(ModalButton, {
  809.             onClick: () => {
  810.               deleteWork(workDelete);
  811.               setIsOpen(false);
  812.             },
  813.             children: "Delete"
  814.           }), o(ModalButton, {
  815.             onClick: () => setIsOpen(false),
  816.             children: "Close"
  817.           })]
  818.         })]
  819.       })]
  820.     });
  821.   }
  822.   function App({
  823.     closeApp,
  824.     setLoadedWork,
  825.     setCurrentApp
  826.   }) {
  827.     const [works, setWorks] = h([]);
  828.     async function refresh() {
  829.       await Library.refresh();
  830.       setWorks(Library.works);
  831.     }
  832.     function openWork(work) {
  833.       setLoadedWork(work);
  834.       setCurrentApp("workDetails");
  835.     }
  836.     async function deleteWork(work) {
  837.       await Library.deleteWork(work.id);
  838.       refresh();
  839.     }
  840.     p(() => {
  841.       refresh();
  842.     }, []);
  843.     return o("div", {
  844.       class: "container",
  845.       children: [o("div", {
  846.         className: "appbar",
  847.         children: [o("div", {
  848.           className: "left",
  849.           children: o("h3", {
  850.             children: "AO3 Reader"
  851.           })
  852.         }), o("div", {
  853.           className: "right",
  854.           children: o("button", {
  855.             class: "button",
  856.             onClick: closeApp,
  857.             children: o("img", {
  858.               class: "icons white",
  859.               src: exitIcon,
  860.               alt: "refresh"
  861.             })
  862.           })
  863.         })]
  864.       }), o("div", {
  865.         className: "contents",
  866.         children: o(WorkList, {
  867.           works,
  868.           openWork,
  869.           deleteWork
  870.         })
  871.       }), o("div", {
  872.         className: "appbar",
  873.         children: [o("div", {
  874.           className: "left",
  875.           children: o("h3", {
  876.             children: "Library"
  877.           })
  878.         }), o("div", {
  879.           className: "right",
  880.           children: o("button", {
  881.             class: "button",
  882.             onClick: refresh,
  883.             children: o("img", {
  884.               class: "icons white",
  885.               src: refreshIcon,
  886.               alt: "refresh"
  887.             })
  888.           })
  889.         })]
  890.       })]
  891.     });
  892.   }
  893.   const arrowLeftIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJCb2xkIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIj48cGF0aCBkPSJNNC45NDMsNS42MDYsMS4wMjQsOS41MjVhMy41ODUsMy41ODUsMCwwLDAsMCw0Ljk1bDMuOTE5LDMuOTE5YTEuNSwxLjUsMCwxLDAsMi4xMjEtMi4xMjFMNC4yODUsMTMuNDkybDE4LjI1LS4wMjNhMS41LDEuNSwwLDAsMCwxLjUtMS41djBhMS41LDEuNSwwLDAsMC0xLjUtMS41TDQuMywxMC40OTIsNy4wNjQsNy43MjdBMS41LDEuNSwwLDAsMCw0Ljk0Myw1LjYwNloiLz48L3N2Zz4K";
  894.   function DetailsApp({
  895.     closeApp,
  896.     work,
  897.     setLoadedEpub,
  898.     setCurrentApp
  899.   }) {
  900.     const [isUpdateButtonDisabled, setIsUpdateButtonDisabled] = h(false);
  901.     const [updateButtonText, setUpdateButtonText] = h("Update Work");
  902.     p(() => {
  903.     }, []);
  904.     function back() {
  905.       setCurrentApp("");
  906.     }
  907.     async function openWork() {
  908.       setLoadedEpub(await work.getLatest().getEpub());
  909.       setCurrentApp("workReader");
  910.     }
  911.     async function updateWork() {
  912.       setUpdateButtonText("Updating...");
  913.       setIsUpdateButtonDisabled(true);
  914.       try {
  915.         await Library.getWorkUpdate(work);
  916.         setUpdateButtonText("Updated!");
  917.         setTimeout(() => {
  918.           setUpdateButtonText("Update Work");
  919.           setIsUpdateButtonDisabled(false);
  920.         }, 1e3);
  921.       } catch (error) {
  922.         setUpdateButtonText("Update Work Failed!");
  923.         setIsUpdateButtonDisabled(false);
  924.       }
  925.     }
  926.     return o("div", {
  927.       class: "container",
  928.       children: [o("div", {
  929.         className: "appbar",
  930.         children: [o("div", {
  931.           className: "left",
  932.           children: o("h3", {
  933.             children: "AO3 Reader"
  934.           })
  935.         }), o("div", {
  936.           className: "right",
  937.           children: o("button", {
  938.             class: "button",
  939.             onClick: closeApp,
  940.             children: o("img", {
  941.               class: "icons white",
  942.               src: exitIcon,
  943.               alt: "refresh"
  944.             })
  945.           })
  946.         })]
  947.       }), o("div", {
  948.         className: "contents",
  949.         children: [o("div", {
  950.           className: "workInfo",
  951.           children: [o("h4", {
  952.             children: o("strong", {
  953.               children: "Summary:"
  954.             })
  955.           }), o("p", {
  956.             children: work.getLatest().summary
  957.           }), o("p", {
  958.             children: [o("strong", {
  959.               children: "Last Updated: "
  960.             }), work.getLatest().updated.toString()]
  961.           })]
  962.         }), o("div", {
  963.           className: "workDetails",
  964.           children: [o("div", {
  965.             className: "headers",
  966.             children: [o("h4", {
  967.               children: o("strong", {
  968.                 children: work.getLatest().title
  969.               })
  970.             }), o("h5", {
  971.               children: ["by ", work.getLatest().authors.map((e2) => e2.name).join(" ,")]
  972.             })]
  973.           }), o("div", {
  974.             className: "actions",
  975.             children: o("div", {
  976.               className: "buttons",
  977.               children: [o("div", {
  978.                 className: "row",
  979.                 children: [o("a", {
  980.                   href: "#",
  981.                   onClick: openWork,
  982.                   children: "Read in Reader"
  983.                 }), o("a", {
  984.                   href: `/works/${work.getLatest().id}`,
  985.                   target: "_blank",
  986.                   children: "Read in AO3"
  987.                 })]
  988.               }), o("div", {
  989.                 className: "row",
  990.                 children: o("a", {
  991.                   href: "#",
  992.                   onClick: isUpdateButtonDisabled ? () => {
  993.                   } : updateWork,
  994.                   children: updateButtonText
  995.                 })
  996.               })]
  997.             })
  998.           }), o("div", {
  999.             className: "info"
  1000.           })]
  1001.         })]
  1002.       }), o("div", {
  1003.         className: "appbar",
  1004.         children: [o("div", {
  1005.           className: "left",
  1006.           children: o("h3", {
  1007.             children: "Details"
  1008.           })
  1009.         }), o("div", {
  1010.           className: "right",
  1011.           children: o("button", {
  1012.             class: "button",
  1013.             onClick: back,
  1014.             children: o("img", {
  1015.               class: "icons white",
  1016.               src: arrowLeftIcon,
  1017.               alt: "back"
  1018.             })
  1019.           })
  1020.         })]
  1021.       })]
  1022.     });
  1023.   }
  1024.   const angleRightIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJCb2xkIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIj48cGF0aCBkPSJNNi4wNzksMjIuNWExLjUsMS41LDAsMCwxLC40NC0xLjA2bDcuNjcyLTcuNjcyYTIuNSwyLjUsMCwwLDAsMC0zLjUzNkw2LjUyOSwyLjU2NUExLjUsMS41LDAsMCwxLDguNjUuNDQ0bDcuNjYyLDcuNjYxYTUuNTA2LDUuNTA2LDAsMCwxLDAsNy43NzlMOC42NCwyMy41NTZBMS41LDEuNSwwLDAsMSw2LjA3OSwyMi41WiIvPjwvc3ZnPgo=";
  1025.   const angleLeftIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJCb2xkIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIj48cGF0aCBkPSJNMTcuOTIxLDEuNTA1YTEuNSwxLjUsMCwwLDEtLjQ0LDEuMDZMOS44MDksMTAuMjM3YTIuNSwyLjUsMCwwLDAsMCwzLjUzNmw3LjY2Miw3LjY2MmExLjUsMS41LDAsMCwxLTIuMTIxLDIuMTIxTDcuNjg4LDE1LjlhNS41MDYsNS41MDYsMCwwLDEsMC03Ljc3OUwxNS4zNi40NDRhMS41LDEuNSwwLDAsMSwyLjU2MSwxLjA2MVoiLz48L3N2Zz4K";
  1026.   const getChapter = (href, rendition) => {
  1027.     href = href.replace(" ", "%20");
  1028.     function flatten(items) {
  1029.       return [].concat.apply([], items.map((item) => [].concat.apply([item], flatten(item.subitems))));
  1030.     }
  1031.     console.log(href, rendition);
  1032.     var matches = flatten(rendition.book.navigation.toc).filter((item) => rendition.book.canonical(item.href) == rendition.book.canonical(href));
  1033.     if (matches.length != 1) {
  1034.       console.log("Unable to get chapter title", event, matches);
  1035.       return null;
  1036.     }
  1037.     return matches[0].label.trim();
  1038.   };
  1039.   const EpubViewer = ({
  1040.     epub,
  1041.     location,
  1042.     onLocationChange,
  1043.     getRendition
  1044.   }) => {
  1045.     async function initReader() {
  1046.       const bookData = await epub.arrayBuffer();
  1047.       console.log("Epub Loaded, byte length: ", bookData.byteLength);
  1048.       const book = ePub(bookData);
  1049.       console.log("Initializing epub viewer");
  1050.       const rendition = book.renderTo("a3r-epub-viewer", {
  1051.         width: "100%",
  1052.         height: "100%"
  1053.       });
  1054.       _unsafeWindow.epubrendition = rendition;
  1055.       rendition.display();
  1056.       console.log("Epub Displayed");
  1057.       if (location) {
  1058.         rendition.display(location);
  1059.       }
  1060.       getRendition(rendition);
  1061.       rendition.on("relocated", (location2) => {
  1062.         onLocationChange(location2);
  1063.       });
  1064.       return () => {
  1065.         book.destroy();
  1066.         _unsafeWindow.epubrendition = null;
  1067.       };
  1068.     }
  1069.     p(() => {
  1070.       initReader();
  1071.     }, []);
  1072.     return o("div", {
  1073.       id: "a3r-epub-viewer",
  1074.       style: {
  1075.         width: "100%",
  1076.         height: "100%"
  1077.       }
  1078.     });
  1079.   };
  1080.   function ReaderApp({
  1081.     closeApp,
  1082.     work,
  1083.     epub,
  1084.     setCurrentApp
  1085.   }) {
  1086.     const [chapterTitle, setChapterTitle] = h("");
  1087.     const [epubLocation, setEpubLocation] = h(0);
  1088.     const renditionRef = _$1(null);
  1089.     p(() => {
  1090.       return () => {
  1091.       };
  1092.     }, []);
  1093.     function back() {
  1094.       setCurrentApp("workDetails");
  1095.     }
  1096.     function updateLocation(location) {
  1097.       work.setReadingLocation(location);
  1098.       setEpubLocation(location);
  1099.     }
  1100.     function nextPage() {
  1101.       renditionRef.current.next();
  1102.     }
  1103.     function prevPage() {
  1104.       renditionRef.current.prev();
  1105.     }
  1106.     function loadRendition(rendition) {
  1107.       renditionRef.current = rendition;
  1108.       renditionRef.current.on("relocated", (location) => {
  1109.         const chapter = getChapter(location.start.href, renditionRef.current) || "";
  1110.         setChapterTitle(chapter);
  1111.       });
  1112.     }
  1113.     return o("div", {
  1114.       class: "container",
  1115.       children: [o("div", {
  1116.         className: "contents",
  1117.         children: o(EpubViewer, {
  1118.           epub,
  1119.           location: epubLocation,
  1120.           onLocationChange: updateLocation,
  1121.           getRendition: loadRendition
  1122.         })
  1123.       }), o("div", {
  1124.         className: "appbar",
  1125.         children: [o("div", {
  1126.           className: "left",
  1127.           children: o("h4", {
  1128.             children: chapterTitle
  1129.           })
  1130.         }), o("div", {
  1131.           className: "right",
  1132.           children: [o("button", {
  1133.             class: "button",
  1134.             onClick: back,
  1135.             children: o("img", {
  1136.               class: "icons white",
  1137.               src: arrowLeftIcon,
  1138.               alt: "back"
  1139.             })
  1140.           }), o("button", {
  1141.             class: "button",
  1142.             onClick: prevPage,
  1143.             children: o("img", {
  1144.               class: "icons white",
  1145.               src: angleLeftIcon,
  1146.               alt: "back"
  1147.             })
  1148.           }), o("button", {
  1149.             class: "button",
  1150.             onClick: nextPage,
  1151.             children: o("img", {
  1152.               class: "icons white",
  1153.               src: angleRightIcon,
  1154.               alt: "back"
  1155.             })
  1156.           })]
  1157.         })]
  1158.       })]
  1159.     });
  1160.   }
  1161.   console.log("JSLib Version: ", jszip.version);
  1162.   const AddButton = ({
  1163.     workID
  1164.   }) => {
  1165.     const [buttonText, setButtonText] = h("Add to Library");
  1166.     const [isButtonDisabled, setButtonDisabled] = h(false);
  1167.     const handleClick = async () => {
  1168.       try {
  1169.         setButtonDisabled(true);
  1170.         setButtonText("Adding Work...");
  1171.         await Library.addWork(workID);
  1172.         setButtonText("Work Added!");
  1173.       } catch (error) {
  1174.         console.log("AO3 Reader Error: ", error, workID);
  1175.         setButtonText("Failed, Retry?");
  1176.         setButtonDisabled(false);
  1177.       } finally {
  1178.       }
  1179.     };
  1180.     p(() => {
  1181.       if (Library.ids.includes(workID)) {
  1182.         setButtonText("Work Already in Library");
  1183.         setButtonDisabled(true);
  1184.       }
  1185.     }, []);
  1186.     return o("button", {
  1187.       onClick: !isButtonDisabled ? handleClick : void 0,
  1188.       disabled: isButtonDisabled,
  1189.       type: "button",
  1190.       class: "action",
  1191.       style: {
  1192.         marginTop: "1em"
  1193.       },
  1194.       children: buttonText
  1195.     });
  1196.   };
  1197.   function insertButtons() {
  1198.     for (const button of document.querySelectorAll("div.a3r-actions")) {
  1199.       button.remove();
  1200.     }
  1201.     for (const work of document.querySelectorAll("li.blurb.group")) {
  1202.       const id = work.querySelector("h4.heading > a").attributes.href.value.split("/")[2];
  1203.       const header = work.querySelector("div.header.module");
  1204.       const buttonDiv = document.createElement("div");
  1205.       buttonDiv.className = "a3r-actions";
  1206.       header.append(buttonDiv);
  1207.       preact.render(o(AddButton, {
  1208.         workID: id
  1209.       }), buttonDiv);
  1210.     }
  1211.   }
  1212.   const AppController = ({
  1213.     closeApp
  1214.   }) => {
  1215.     const [currentApp, setCurrentApp] = h("");
  1216.     const [loadedWork, setLoadedWork] = h(null);
  1217.     const [loadedEpub, setLoadedEpub] = h(null);
  1218.     switch (currentApp) {
  1219.       case "workDetails":
  1220.         if (!loadedWork) {
  1221.           setCurrentApp("");
  1222.         }
  1223.         return o(DetailsApp, {
  1224.           closeApp,
  1225.           work: loadedWork,
  1226.           setLoadedEpub,
  1227.           setCurrentApp
  1228.         });
  1229.       case "workReader":
  1230.         if (!loadedEpub) {
  1231.           setCurrentApp("workDetails");
  1232.         }
  1233.         return o(ReaderApp, {
  1234.           closeApp,
  1235.           work: loadedWork,
  1236.           epub: loadedEpub,
  1237.           setCurrentApp
  1238.         });
  1239.       default:
  1240.         return o(App, {
  1241.           closeApp,
  1242.           setLoadedWork,
  1243.           setCurrentApp
  1244.         });
  1245.     }
  1246.   };
  1247.   const AppWrapper = () => {
  1248.     const [isAppVisible, setIsAppVisible] = h(false);
  1249.     const openApp = () => {
  1250.       document.body.style.overflow = "hidden";
  1251.       document.body.style.overscrollBehavior = "none";
  1252.       document.querySelector("html").style.overscrollBehavior = "none";
  1253.       setIsAppVisible(true);
  1254.     };
  1255.     const closeApp = () => {
  1256.       document.body.style.overflow = "visible";
  1257.       document.body.style.overscrollBehavior = "";
  1258.       document.querySelector("html").style.overscrollBehavior = "";
  1259.       insertButtons();
  1260.       setIsAppVisible(false);
  1261.     };
  1262.     return o(preact.Fragment, {
  1263.       children: [o("a", {
  1264.         href: "#",
  1265.         onClick: openApp,
  1266.         children: "AO3 Reader"
  1267.       }), o("div", {
  1268.         className: "a3r",
  1269.         style: {
  1270.           display: isAppVisible ? "block" : "none"
  1271.         },
  1272.         children: o(AppController, {
  1273.           closeApp
  1274.         })
  1275.       })]
  1276.     });
  1277.   };
  1278.   try {
  1279.     console.log("=== AO3 Reader Starting! ===");
  1280.     _unsafeWindow.library = Library;
  1281.     (await Library.refresh());
  1282.   } catch (error) {
  1283.     console.log("AO3 Reader Error: ", error);
  1284.   } finally {
  1285.     insertButtons();
  1286.     (_a = document.querySelector(".a3r-open")) == null ? void 0 : _a.remove();
  1287.     const nav = document.querySelector("ul.primary.navigation.actions");
  1288.     const navItem = document.createElement("li");
  1289.     navItem.className = "a3r-open dropdown";
  1290.     nav.append(navItem);
  1291.     preact.render(o(AppWrapper, {}), navItem);
  1292.   }
  1293.  
  1294. })(preact, unsafeWindow.ePub, unsafeWindow.JSZip);
  1295.  
Add Comment
Please, Sign In to add comment