Audible Add "shelves" to the library

Adding a shelving system to Audibles Library

// ==UserScript==
// @name        Audible Add "shelves" to the library
// @version     1.1.3
// @include     https://www.audible.com/lib*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @grant       GM.setValue
// @grant       GM.getValue
// @grant       GM.listValues
// @description Adding a shelving system to Audibles Library
// @namespace   https://www.reddit.com/user/Coolgamer7/
// ==/UserScript==
/*global document: false */
/*global async: false */
/* changelog
       Version 1.0 
            - First Release
       Version 1.1 
            - Button corrections, everything should update properly now
            - Changed logic to detect how many books are displayed on each page and query correctly
            - Added Import/Export
            - Small performance Improvements
       Version 1.1.1
            - Fixed Bug when adding a new shelf
       Version 1.1.2
            - Fixed Bugs when adding and removing shelves
            - Dropdown and displayed books now maintain and update when a book is added or removed from a shelf
       Version 1.1.3
            - Correcting White space left after putting books on a shelf
                 - This seems to have corrects the ratings not showing up as well
            - Fixed bug with Shelf list not updating
            

  */

var table = document.getElementById("adbl-library-content-main").getElementsByTagName('table')[0].getElementsByTagName('tbody')[0];
var rows = table.getElementsByTagName('tr');
var page = document.getElementById("center-5").getElementsByTagName('form')[0].getElementsByTagName('div')[0].getElementsByTagName('div')[0].getElementsByTagName('div')[1].getElementsByTagName('div')[0].getElementsByTagName('span')[0].getElementsByTagName('ul')[0].getElementsByTagName('li');
var pageCount = parseInt(page[page.length - 2].getElementsByTagName('a')[0].innerHTML, 10);
var progress = 2;
var bookID = "";
var titlesPerPage = document.getElementById("center-5").getElementsByTagName('select')[0].value;
var form = $("[data-form-id=filtering]")[0];

function createButton(text, id, functionCall){
  var shelfCellSpan = document.createElement("span");
  var shelfCellSpan2 = document.createElement("span");
  var link = document.createElement("a");
  link.appendChild(document.createTextNode(text));
  link.setAttribute("class","bc-button-text");
  link.setAttribute("id",id);
  shelfCellSpan.setAttribute("class", "bc-text bc-button-text-inner bc-size-small bc-button-text");
  shelfCellSpan2.setAttribute("class", "bc-button bc-button-secondary adbl-lib-action-download  bc-button-small");
  link.onclick = function() { functionCall() };
  shelfCellSpan.appendChild(link);
  shelfCellSpan2.appendChild(shelfCellSpan);
  return shelfCellSpan2;
}

function exportShelves(textArea){
  (async() => {
    var jsonVars = {};
    var allVars = await GM.listValues();
    for(var i = allVars.length - 1; i >= 0; i--){
      console.log(allVars[i]);
      jsonVars[allVars[i]] = await GM.getValue(allVars[i], "");
    }
    textArea.value = JSON.stringify(jsonVars); 
  })();
}

function importShelves(textArea){
  var allShelves = textArea.value;
  (async() => {
    var obj = JSON.parse(allShelves); 
    for (var key in obj) {
      await GM.setValue(key, obj[key]);
    }
    location.reload();
  })();
}

function addExportShelvesButton() {
  (async() => {
    var exportPopUpContent = document.createElement("div");
    var importExportBox = document.createElement("TEXTAREA");
    var importExportButton = createButton("Import/Export Shelves", "importExportShelves", function(){ return togglePopUpDiv(exportPopUpContent); });
    var importButton = createButton("Import Shelves", "importShelves", function(){ return importShelves(importExportBox); });
    var exportButton = createButton("Export Shelves", "exportShelves", function(){ return exportShelves(importExportBox); });
    var closeImportExportButton = createButton("Close", "closeShelves", function(){ return showDiv(bookID); });
    importExportButton.style.float = "inline-end";
    exportPopUpContent.appendChild(importExportBox);
    exportPopUpContent.appendChild(document.createElement("br"));
    exportPopUpContent.appendChild(document.createElement("br"));
    exportPopUpContent.appendChild(importButton);
    exportPopUpContent.appendChild(document.createElement("br"));
    exportPopUpContent.appendChild(exportButton);
    exportPopUpContent.appendChild(document.createElement("br"));
    exportPopUpContent.appendChild(closeImportExportButton);
    form.appendChild(importExportButton);
  })();
}

function addShelfDropDown() {
  (async() => {
    let shelves = (await GM.getValue("shelves", "")).split(";");
    var shelfListSpan = document.getElementById("ShelfFilter");
    var shelfListSelect = document.getElementById("shelfSelect");
    var selectedItem = "";
    if (shelfListSpan == null) {
      shelfListSpan = document.createElement("span");
      shelfListSelect = document.createElement("select");
    } else {
      selectedItem = shelfListSelect.value;
      shelfListSpan.innerHTML = "";
      shelfListSelect.innerHTML = "";
      
    }
    shelfListSpan.setAttribute("class", "bc-dropdown bc-dropdown-inline bc-dropdown-small");
    shelfListSpan.setAttribute("id", "ShelfFilter");
    
    shelfListSelect.setAttribute("class", "bc-input bc-color-base bc-color-border-focus bc-color-background-base bc-color-border-base refinementFormDropDown refinementDropdown-purchaseDateFilter  bc-input-inline bc-input-small");
    shelfListSelect.setAttribute("name","shelfFilter");
    shelfListSelect.setAttribute("id", "shelfSelect");
    shelfListSelect.onchange = function() { hideShleves(shelfListSelect.value); };
    var option = document.createElement("option");
    option.text = "All Shelves";
    shelfListSelect.add(option)
    option = document.createElement("option");
    option.text = "Unshelved";
    shelfListSelect.add(option);
    shelves.forEach(function(shelf) {
      if (shelf != "") {
        option = document.createElement("option");
        option.text = shelf;
        shelfListSelect.add(option);
      }
    });
    shelfListSpan.appendChild(shelfListSelect);
    if(selectedItem != ""){
      shelfListSelect.value = selectedItem;
      hideShleves(selectedItem);
    }
    form.appendChild(shelfListSpan);
    shelfListSpan.insertAdjacentHTML('beforeend', '<i class="bc-icon bc-icon-chevron-down" aria-hidden="true"></i>');
  })();
}


addShelfDropDown();
addExportShelvesButton();

for (var i = rows.length - 2; i > 0; i--) {
  bookID = rows[i].getAttribute("id");
  addShelfText(bookID);
}
while (progress <= pageCount) {
  var url = "https://www.audible.com/lib?purchaseDateFilter=all&programFilter=all&sortBy=PURCHASE_DATE.dsc&page=" + progress + "&pageSize=" + titlesPerPage;
  request(url, "page"+progress);
  progress++;
}

function request(url, pageNum) {
  $.get(url, function(data, status) {
    var gatheredRows = new DOMParser().parseFromString(data, "text/html").getElementById("adbl-library-content-main").getElementsByTagName('table')[0].getElementsByTagName('tr');
    for (var i = gatheredRows.length - 2; i > 0; i--) {
      table.appendChild(gatheredRows[i].cloneNode(true));
      addShelfText(gatheredRows[i].getAttribute("id"));
    }
  });
}

function addShelfText(bookID) {
  (async() => {
    let shelf = await GM.getValue(bookID, "");
    var bookRow = document.getElementById(bookID);
    bookRow.setAttribute("GM_Shelf_Value",shelf);
    var columns = bookRow.getElementsByTagName("td");
    var shelfCellSpan2;
    if(shelf == "") {
      shelfCellSpan2 = createButton("Add to Shelf", "addToShelf" + bookID, function(){ return showDiv(bookID); });
    }else{
      shelfCellSpan2 = createButton("Change Shelf", "addToShelf" + bookID, function(){ return showDiv(bookID); });
    }
    columns[7].appendChild(shelfCellSpan2);
  })();
}

function addShelvesToDiv(shelfDiv, bookID) {
  (async() => {
    let existingShelf = await GM.getValue(bookID, "");
    let shelves = (await GM.getValue("shelves", "")).split(";");
    shelfDiv.style.height = (170 + shelves.length * 25) + "px";
    var newShelfToAdd = document.createElement("span");
    var link = document.createElement("a");

    shelves.forEach(function(shelf) {
      link.setAttribute("id",bookID + shelf);
      if (shelf != "") {
        newShelfToAdd.appendChild(document.createTextNode(shelf));
        if (shelf == existingShelf) {
          link.appendChild(document.createTextNode(" (remove)"));
          link.onclick = function() { removeFromShelf(shelf, bookID); };
        } else {
          link.appendChild(document.createTextNode(" (add)"));
          link.onclick = function() { addToShelf(shelf, bookID); };
        }

        newShelfToAdd.appendChild(link);
        link = document.createElement("a");
        link.appendChild(document.createTextNode(" (Delete Shelf)"));
        link.onclick = function() { deleteShelf(shelf, bookID); };
        newShelfToAdd.appendChild(link);
        newShelfToAdd.appendChild(document.createElement("br"));
        shelfDiv.appendChild(newShelfToAdd);
        link = document.createElement("a");
        newShelfToAdd = document.createElement("span");
      }
    });
    shelfDiv.appendChild(document.createElement("br"));
    var newShelfInput = document.createElement("input");
    newShelfInput.setAttribute("type","search");
    newShelfInput.setAttribute("class","bc-input bc-color-border-focus bc-color-base bc-color-background-base bc-color-border-base");
    newShelfInput.setAttribute("id","newShelfToAdd");
    newShelfInput.setAttribute("placeholder","New Shelf Name");
    shelfDiv.appendChild(newShelfInput);
    var newShelfButton = createButton("Add to New Shelf", "addToNewShelf", function(){ createNewShelf(newShelfInput,bookID); });
    shelfDiv.appendChild(newShelfButton);
    shelfDiv.appendChild(document.createElement("br"));
    var closeButton = createButton("Close", "closeShelves", function(){ return showDiv(bookID); });
    shelfDiv.appendChild(closeButton);
  })();
}

function deleteShelf(shelfToRemove, bookID) {
  (async() => {
    let shelves = (await GM.getValue("shelves", "")).split(";");
    var newShelves = [];
    shelves.forEach(function(shelf) {
      if (shelfToRemove != shelf) {
        newShelves.push(shelf);
      }
    });
    await GM.setValue("shelves", newShelves.join(";"));
    showDiv(bookID);
    showDiv(bookID);
    addShelfDropDown();
    var rows = table.getElementsByTagName('tr');
    var addToShelfButton;
    for (var i = 1; i <= rows.length - 1; i++) {
      if(rows[i].getAttribute("GM_Shelf_Value") == shelfToRemove ) {
        rows[i].setAttribute("GM_Shelf_Value","");
        await GM.setValue(rows[i].getAttribute("id"), "");
        addToShelfButton = document.getElementById("addToShelf"+rows[i].getAttribute("id"));
        addToShelfButton.textContent="Add to Shelf";
      }
    }

  })();
}

function createNewShelf(newShelfInput, bookID) {
  (async() => {
    await GM.setValue(bookID, newShelfInput.value);
    let shelves = (await GM.getValue("shelves", "")).split(";");
    var n = shelves.includes(newShelfInput.value);
    if (!n) {
      shelves.push(newShelfInput.value);
      await GM.setValue("shelves", shelves.join(";"));
    }
    refreshShelfSelectMenu(bookID);
    var addToShelfButton = document.getElementById("addToShelf" + bookID);
    addToShelfButton.textContent = "Change Shelf";
    addShelfDropDown();
    var row = document.getElementById(bookID);
    row.setAttribute("GM_Shelf_Value", newShelfInput.value);
  })();
}

function refreshShelfSelectMenu(bookID) {
  showDiv(bookID);
  showDiv(bookID);
}

function addToShelf(shelf, book) {
  console.log("Adding to Shelf");
  var link = document.getElementById(book+shelf);
  var row = document.getElementById(book);
  link.innerHTML = " (remove)";
  (async() => {
    await GM.setValue(book, shelf);
    row.setAttribute("GM_Shelf_Value",shelf);
    var addToShelfButton = document.getElementById("addToShelf" + book);
    addToShelfButton.textContent = "Change Shelf";
    refreshShelfSelectMenu(book);
    addShelfDropDown();
  })();
}

function removeFromShelf(shelf,book) {
  console.log("Removing From Shelf");
  var link = document.getElementById(book+shelf);
  var row = document.getElementById(book);
  link.innerHTML = " (add)";
  (async() => {
    await GM.setValue(book, "");
    row.setAttribute("GM_Shelf_Value","");
    var addToShelfButton = document.getElementById("addToShelf"+book);
    addToShelfButton.textContent = "Add to Shelf";
    refreshShelfSelectMenu(book);
    addShelfDropDown();
  })();
}

function togglePopUpDiv(element) {
  var shelfDiv = document.getElementById("GM_popUpDiv");
  if (shelfDiv == null) {
    shelfDiv = document.createElement("div");
    document.getElementsByTagName('body')[0].appendChild(shelfDiv);
    shelfDiv.style.position = "fixed";
    shelfDiv.style.zIndex = "15";
    shelfDiv.style.top = "30%";
    shelfDiv.style.right = "50%";
    shelfDiv.style.margin = "-100px 0 0 -150px";
    shelfDiv.style.border = "1px solid #cdcdcd";
    shelfDiv.style.boxShadow = "2px 2px 6px 0 rgba(51,51,51,.25)";
    shelfDiv.style.background = "rgb(255, 255, 255)";
    shelfDiv.style.padding = "20px";
    shelfDiv.setAttribute("id","GM_popUpDiv");
  } else {
    shelfDiv.innerHTML = "";
  }
  toggleVisible(shelfDiv);
  shelfDiv.appendChild(element);
  toggleBlackOutDiv();
}

function showDiv(bookShelfSelect) {
  var absShelfDiv = document.createElement("div");
  var shelfDivider = document.createElement("div");
  var columns = document.getElementById(bookShelfSelect).getElementsByTagName("td");
  shelfDivider.setAttribute("class", "bc-divider");
  absShelfDiv.appendChild(document.createTextNode("Select a Shelf for this book:"));
  absShelfDiv.appendChild(document.createElement("br"));
  absShelfDiv.appendChild(document.createElement("br"));
  absShelfDiv.appendChild(shelfDivider);
  absShelfDiv.appendChild(document.createElement("br"));
  addShelvesToDiv(absShelfDiv, bookShelfSelect);
  togglePopUpDiv(absShelfDiv);
}

function toggleBlackOutDiv() {
  var blackOut = document.getElementById("GM_blackOut");
  if (blackOut == null) {
    blackOut = document.createElement("div");
    document.getElementsByTagName('body')[0].appendChild(blackOut);
    blackOut.style.background = "rgb(0, 0, 0)";
    blackOut.style.position = "fixed";
    blackOut.style.height = "100%";
    blackOut.style.width = "100%";
    blackOut.style.zIndex = "14";
    blackOut.setAttribute("id", "GM_blackOut");
    blackOut.style.top = "0px";
    blackOut.style.opacity = ".5";
  }
  toggleVisible(blackOut);
}

function toggleVisible(element){
  if (element.style.visibility != "visible") {
    element.style.visibility = "visible";
  } else {
    element.style.visibility = "hidden";
  }
}


function hideShleves(selectedShelf) {
  var rows = table.getElementsByTagName('tr');
  if (selectedShelf == "Unshelved") {
    for (var i = rows.length - 1; i > 0; i--) {
      if (rows[i].getAttribute("GM_Shelf_Value") == "Unshelved" || rows[i].getAttribute("GM_Shelf_Value") == "") {
        rows[i].style.visibility = 'visible';
        rows[i].style.display = '';
      } else {
        rows[i].style.visibility = 'collapse';
        rows[i].style.display = 'none';
      }
    }
  } else if (selectedShelf == "All Shelves") {
    for (var i = rows.length - 1; i > 0; i--) {
      rows[i].style.visibility = 'visible';
      rows[i].style.display = '';
    }
  } else {
    for (var i = rows.length - 1; i > 0; i--) {
      if (rows[i].getAttribute("GM_Shelf_Value") != selectedShelf) {
        rows[i].style.visibility = 'collapse';
        rows[i].style.display = 'none';
      } else {
        rows[i].style.visibility = 'visible';
        rows[i].style.display = '';
      }
    }
  }
}

function addGlobalStyle(css) {
  var head, style;
  head = document.getElementsByTagName('head')[0];
  if (!head) { return; }
  style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = css;
  head.appendChild(style);
}