1const homeUrl = "https://lotrapi.co/";
2
3const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4
5/**
6 * @returns {Promise<HTMLAnchorElement[]>}
7 */
8async function waitQuerySelectorAll(selector, timeout = 5000, interval = 100) {
9 let counter = 0;
10 while (counter++ < timeout / interval) {
11 const elements = await document.querySelectorAll(selector);
12 if (elements.length > 0) {
13 return Array.from(elements);
14 }
15 await sleep(interval);
16 }
17 throw new Error(
18 `Element with selector "${selector}" not found within ${timeout}ms`,
19 );
20}
21
22/**
23 * @template T
24 * @param {function(): T | Promise<T>} callback
25 * @param {function(T): Promise<boolean>} condition
26 * @return {Promise<T>}
27 */
28async function doUntil(callback, condition, timeout = 5000, interval = 100) {
29 let counter = 0;
30 while (counter++ < timeout / interval) {
31 const result = await callback();
32 if (await condition(result)) {
33 return result;
34 }
35 await sleep(interval);
36 }
37 throw new Error(`Condition not met within ${timeout}ms`);
38}
39
40async function clickBooksLink() {
41 const booksLink = (await waitQuerySelectorAll('a[class*="hint"]')).find(
42 (link) => link.textContent.includes("books/1"),
43 );
44 await doUntil(
45 () => booksLink.click(),
46 async () => {
47 const [apiCallResult] = await waitQuerySelectorAll("pre#apiSpan");
48 return apiCallResult.textContent.includes("The Fellowship of the Ring");
49 },
50 );
51}
52
53window.onload = async function () {
54 if (window.location.href === homeUrl) {
55 await clickBooksLink();
56 }
57};
58