sexstories.com stories downloader and searcher

from any author's profile on sexstories.com, can select stories to download in a zip or as a series in one aggregated .html file, can also search stories and display frequency of search terms for each story

// ==UserScript==
// @name         sexstories.com stories downloader and searcher
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  from any author's profile on sexstories.com, can select stories to download in a zip or as a series in one aggregated .html file, can also search stories and display frequency of search terms for each story
// @author       DUVish
// @match        https://www.sexstories.com/profile*/*
// @grant        none
// ==/UserScript==

let totalScripts = 3;
let scriptsLoaded = 0;

let scriptTag = document.createElement("script");
scriptTag.src = "https://code.jquery.com/jquery-3.3.1.min.js";
scriptTag.async = false;
scriptTag.type = "text/javascript";
scriptTag.onload = function() {
    scriptsLoaded++;
    //changing title of page
    document.querySelector("title").innerText = document.querySelector("h3").children[0].innerText + "'s profile on sexstories.com";

    //adding button for asyncStatus and search input itself
    let asyncStatus = true;
    document.getElementsByTagName("h3")[1].innerHTML += "<input class='searchStories' placeholder='Search stories'></input><button class='syncButton' title='Switch synchronicity status'>Asynchronous</button><button class='reset'>Reset Search Results</button>";
    document.querySelector("head").innerHTML += `<style>
      .searchStories {
        margin-left: 10px;
      }
      .syncButton {
        margin-left: 10px;
      }
      .tableDataRow {
        font-size: 11px;
        margin-left: 5px;
        margin-right: 5px;
      }
    </style>`;

    $(".syncButton").on("click", function(e) {
      //console.log("async status", asyncStatus);
      asyncStatus = !asyncStatus;
      let status = asyncStatus ? "Asynchronous" : "Synchronous";
      $(".syncButton")[0].innerText = status;
    });

    $(".reset").on("click", function(e) {
      Array.from($(".tableDataRow")).forEach(el => el.remove());
    });

    $(".searchStories").on("keydown", function(e) {
      if (e.key === "Enter") {
        let searchTerm = $(".searchStories").val();
        //console.log("starting event with search term and asyncStatus", searchTerm, asyncStatus);
        Array.prototype.forEach.call(document.getElementsByTagName("tr"), function(node, i) {
          if (i === 0) return;
          let link = node.children[0].children[0].href;
          $.ajax({
            type: "GET",
            async: asyncStatus,
            url: link,
            success: function(data) {
              //console.log("success in ajax call");
              let reg = new RegExp(searchTerm, "gi");
              let idx = $(data).find(".block_panel").length === 2 ? 0 : 1;
              let wordCount = $(data).find(".block_panel")[idx].innerText.match(/\S\s\S/g).length;
              let searchCount = $(data).find(".block_panel")[idx].innerText.match(reg).length;
              //console.log("reg, wordCount, and searchCount", reg, wordCount, searchCount);
              if (node.children.length === 5) node.innerHTML += `<td class='tableDataRow'>${wordCount} words</td>`;
              node.children[0].innerHTML += `<span class='tableDataRow'>(${searchCount} results for ${searchTerm})</span>`;
            },
            error: function(error) {
              //console.log("error in ajax call", error);
            }
          });
        });
      }
    });
};
document.querySelector("head").appendChild(scriptTag);

let zipScr = document.createElement("script");
zipScr.src = "https://cdn.jsdelivr.net/gh/Stuk/jszip/dist/jszip.min.js";
zipScr.async = false;
zipScr.type = "text/javascript";
zipScr.onload = function() {
  scriptsLoaded++;
  if (scriptsLoaded === totalScripts) scriptFunc();
};
document.querySelector("head").appendChild(zipScr);

let fileScr = document.createElement("script");
fileScr.src = "https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js/src/FileSaver.js";
fileScr.async = false;
fileScr.type = "text/javascript";
fileScr.onload = function() {
  scriptsLoaded++;
  if (scriptsLoaded === totalScripts) scriptFunc();
};
document.querySelector("head").appendChild(fileScr);

let currentNumberGlobal = 1;
const author = document.querySelector(".notice").innerText.match(/[^\n]+/)[0];

document.querySelector("head").innerHTML += `
<style>
  .checkOrder {
    width: 30px
  }
  .dlTogether {
    margin-left: 60px;
  }
  .dlSeperately {
    margin-left: 4px;
  }
  .resetSelections {
    margin-left: 6px;
    margin-right: 60px
  }
</style>
`;

function scriptFunc() {
  Array.from(document.querySelector("tbody").children).forEach(el => el.innerHTML += `<div style="display: flex; position: relative; top: 10px;"><input class="checkOrder" type="checkbox"><span class="selectionorder"></span></div>`);

  Array.from(document.getElementsByClassName("checkOrder")).forEach(el => el.addEventListener("click", function(e) {
      if (e.target.checked) e.target.parentNode.children[1].innerText = currentNumberGlobal++;
      else e.preventDefault();
  }));

  document.querySelectorAll(".notice")[1].innerHTML += `<button class="dlTogether">Download Selected chapters as one story (in selection order)</button> <button class="dlSeparately">Download selected stories individually</button> <button class="resetSelections">Reset Selections</button>`;

  //console.log("current dom nodes", document.querySelector(".resetSelections"), document.querySelector(".dlSeparately"));

  document.getElementsByClassName("resetSelections")[0].addEventListener("click", function(e) {
    Array.from(document.getElementsByClassName("checkOrder")).forEach(el => {
      el.checked = false;
      el.parentNode.children[1].innerText = "";
      currentNumberGlobal = 1;
    });
  });

  document.getElementsByClassName("dlSeparately")[0].addEventListener("click", function(e) {
    //console.log("regostered downloading all separately");
    var zip = new JSZip();
    Array.from(document.querySelectorAll(".checkOrder:checked")).forEach(checkedBox => {
      let link = checkedBox.parentNode.parentNode.children[0].children[0].href;
      let htmlFile = "";
      let bodyStr = "";
      let descStr = "";
      let titleStr = "";
      let tagsStr = "";
      $.ajax({
        type: "GET",
        url: link,
        async: false,
        success: (data) => {
          bodyStr = $(data).find(".block_panel")[1].innerHTML.replace(/<!-- VOTES -->[\s\S]+>/, "").trim();
          descStr = $(data).find(".block_panel")[0].innerText.trim().match(/Introduction:[\s\n]+(.+)/i)[1];
          titleStr = $($(data).find(".story_info")[0]).find("h2")[0].innerText.trim().match(/[^\n]+/)[0].trim();
          tagsStr = $($(data).find(".story_info")[0]).find(".top_info")[0].innerText.trim();
        }
      });
      htmlFile = `
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${titleStr}</title>
</head>
<body>
<div style="text-align:center;font-size: 22px;">
${titleStr}
</div>
<br>
<div style="text-align:center;font-size: 18px;font-style: italic">
${descStr}
</div>
<br>
<div style="text-align:center;font-size: 12px;">
${tagsStr}
</div>
<br>
<br>
${bodyStr}
</body>
`;
      zip.file(titleStr + ".html", htmlFile);
    });
    zip.generateAsync({type:"blob"})
        .then(function (blob) {
        saveAs(blob, `${author}'s Stories`);
    });
  });

    document.getElementsByClassName("dlTogether")[0].addEventListener("click", function(e) {
      let htmlDoc = "";
      let bodyStr = "";
      let lastTitle = "";
      let firstTitle = "";
      let anchorsStr = "";
      Array.from(document.querySelectorAll(".checkOrder:checked")).sort((a, b) => Number(a.parentNode.children[1].innerText) - Number(b.parentNode.children[1].innerText)).forEach((el, idx) => {
        let link = el.parentNode.parentNode.children[0].children[0].href;
        $.ajax({
          type: "GET",
          url: link,
          async: false,
          success: (data) => {
            localBodyStr = $(data).find(".block_panel")[1].innerHTML.replace(/<!-- VOTES -->[\s\S]+>/, "").trim();
            localDescStr = $(data).find(".block_panel")[0].innerText.trim().match(/Introduction:[\s\n]+(.+)/i)[1];
            localTitleStr = $($(data).find(".story_info")[0]).find("h2")[0].innerText.trim().match(/[^\n]+/)[0].trim();
            localTagsStr = $($(data).find(".story_info")[0]).find(".top_info")[0].innerText.trim();
            if (idx === 0) firstTitle = localTitleStr;
            anchorsStr += `<a href="#${idx}" style="text-align:center;">${localTitleStr}</a><br>`;
            bodyStr += `
<div style="text-align:center;font-size: 22px;" id="${idx}">
${localTitleStr}
</div>
<br>
<div style="text-align:center;font-size: 18px;font-style: italic;">
${localDescStr}
</div>
<br>
<div style="text-align:center;font-size: 12px;">
${localTagsStr}
</div>
<br>
<br>
${localBodyStr}
<br>
<br>
`;
            lastTitle = localTitleStr;
          }
        });
      });
      htmlDoc = `
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${firstTitle} - ${lastTitle}</title>
</head>
<body>
<div style="text-align:center;font-size: 24px;">Table of Contents</div>
<br>
<div style="font-size: 15px; width: 100%; text-align: center; line-height: 1.6">${anchorsStr}</div>
<br>
<br>
${bodyStr}
</body>
`;
      let blob = new Blob([htmlDoc], {type: "text/plain;charset=utf8"});
      saveAs(blob, `${firstTitle} - ${lastTitle} (${currentNumberGlobal - 1} Chapters).html`);
    });
}