Greasy Fork is available in English.

AO3: Full Navigation Bar

Adds full navigation bar to AO3.

// ==UserScript==
// @name         AO3: Full Navigation Bar
// @namespace    https://greasyfork.org/
// @version      1.0
// @description  Adds full navigation bar to AO3.
// @author       sopens
// @match        https://archiveofourown.org/works/*/chapters/*
// @grant        none
// ==/UserScript==

// selectedTag exists only when there are several chapters.
var selectedTag = document.getElementById("selected_id");
if (!selectedTag) return;

var ddValue = selectedTag.selectedIndex;
var ddSize = selectedTag.length;
var ddInnerHTML = selectedTag.innerHTML;

var TOP_AND_BOTTOM = false;

// Make hrefs to first and last chapter.
var mainChapterURL = window.location.pathname.split('/').slice(0, 4).join("/") + "/";
var firstHref = mainChapterURL + selectedTag.children[0].value + "#workskin";
var lastHref = mainChapterURL + selectedTag.children[ddSize - 1].value + "#workskin";
var fullPageHref = window.location.pathname.split('/').slice(0, 3).join("/") + "/navigate";

showNavigatorBar();

// Main Functions -------------------------------------------------------------------------

function showNavigatorBar() {
  let topNavBar = document.getElementById("top");
  let btmNavBar = document.getElementById("bottom");

  if (topNavBar != null && btmNavBar != null) return;

  topNavBar = getTopNavBar();
  btmNavBar = getBtmNavBar();

  let navigationTags;

  if (TOP_AND_BOTTOM) {
    navigationTags = [topNavBar, btmNavBar];
  } else {
    showButtonPanel();
    navigationTags = [btmNavBar];
  }

  for (let i = 0; i < navigationTags.length; i++) {
    let firstChapterBtm = createFirstChapterBtm();
    let firstChapterListItem = createListItem(firstChapterBtm, "first");

    let chapterDropdown = createChapterDropdown();
    let chapterDropdownListItem = createListItem(chapterDropdown, "dropdown");

    let lastChapterBtm = createLastChapterBtm();
    let lastChapterListItem = createListItem(lastChapterBtm, "last");

    let fullPageIndexBtm = createFullPageIndexBtm();
    let fullPageIndexListItem = createListItem(fullPageIndexBtm, "full-page-index");

    let navigatorBar = navigationTags[i];
    let entireWorkListItem = navigatorBar.getElementsByClassName("chapter entire")[0];
    let prevChapterListItem = navigatorBar.getElementsByClassName("chapter previous")[0];
    let nextChapterListItem = navigatorBar.getElementsByClassName("chapter next")[0];

    if (ddValue > 1) {
      entireWorkListItem.insertAdjacentElement("afterend", firstChapterListItem);
    }

    if (ddValue == 0) {
      entireWorkListItem.insertAdjacentElement("afterend", chapterDropdownListItem);
    } else {
      prevChapterListItem.insertAdjacentElement("afterend", chapterDropdownListItem);
    }

    if (ddValue < (ddSize - 2)) {
      nextChapterListItem.insertAdjacentElement("afterend", lastChapterListItem);
    }

    if (ddValue == (ddSize - 2)) {
      nextChapterListItem.insertAdjacentElement("afterend", fullPageIndexListItem);
    }

    if (ddValue < (ddSize - 1)) {
      lastChapterListItem.insertAdjacentElement("afterend", fullPageIndexListItem);
    }
  }
}

function showButtonPanel() {
  let buttonPanel = document.getElementById("button_panel");

  if (buttonPanel != null) return;

  let listHeader = document.createElement("dt");
  listHeader.appendChild(document.createTextNode("Navigation Panel:"));

  let listContent = document.createElement("dd");
  let buttonList = document.createElement("ul");
  listContent.appendChild(buttonList);

  buttonPanel = document.getElementsByClassName("work meta group")[0];
  buttonPanel.id = "button_panel";
  buttonPanel.appendChild(listHeader);
  buttonPanel.appendChild(listContent);

  let entireWorkBtm = createEntireWorkBtm();
  addStyle(entireWorkBtm);

  let entireWorkListItem = createListItem(entireWorkBtm, "entire");
  buttonList.appendChild(entireWorkListItem);

  if (ddValue > 1) {
    let firstChapterBtm = createFirstChapterBtm();
    addStyle(firstChapterBtm);

    let firstChapterListItem = createListItem(firstChapterBtm, "first");
    buttonList.appendChild(firstChapterListItem);
  }

  if (ddValue > 0) {
    let prevChapterBtm = createPrevChapterBtm();
    addStyle(prevChapterBtm);

    let prevChapterListItem = createListItem(prevChapterBtm, "previous");
    buttonList.appendChild(prevChapterListItem);
  }

  let chapterDropdown = createChapterDropdown();
  let chapterDropdownListItem = createListItem(chapterDropdown, "dropdown");

  buttonList.appendChild(chapterDropdownListItem);

  if (ddValue < (ddSize - 1)) {
    let nextChapterBtm = createNextChapterBtm();
    addStyle(nextChapterBtm);

    let nextChapterListItem = createListItem(nextChapterBtm, "previous");
    buttonList.appendChild(nextChapterListItem);
  }

  if (ddValue < (ddSize - 2)) {
    let lastChapterBtm = createLastChapterBtm();
    addStyle(lastChapterBtm);

    let lastChapterListItem = createListItem(lastChapterBtm, "last");
    buttonList.appendChild(lastChapterListItem);
  }

  let fullPageIndexBtm = createFullPageIndexBtm();
  addStyle(fullPageIndexBtm);

  let fullPageIndexListItem = createListItem(fullPageIndexBtm, "full-page-index");
  buttonList.appendChild(fullPageIndexListItem);
}

// Global Functions ------------------------------------------------------------------

function getTopNavBar() {
  let navBar = document.getElementById("chapter_index").parentElement.parentElement;
  navBar.id = "top";

  if (!TOP_AND_BOTTOM) {
    let entireWorkListItem = navBar.getElementsByClassName("chapter entire")[0];
    entireWorkListItem.remove();

    let prevChapterListItem = navBar.getElementsByClassName("chapter previous")[0];
    if (prevChapterListItem != null) prevChapterListItem.remove();

    let nextChapterListItem = navBar.getElementsByClassName("chapter next")[0];
    if (nextChapterListItem != null) nextChapterListItem.remove();
  }

  let topListItems = navBar.getElementsByTagName("li");

  for (let i = 0; i < topListItems.length; i++) {
    let topLink = topListItems[i].getElementsByTagName("a")[0];

    if (topLink.text.search("Chapter Index") != -1) {
      topListItems[i].remove();
      break;
    }
  }

  return navBar;
}

function getBtmNavBar() {
  let navBar = document.getElementById("feedback").getElementsByTagName("ul")[0];
  navBar.id = "bottom";

  let btmListItems = navBar.getElementsByTagName("li");

  for (let i = 0; i < btmListItems.length; i++) {
    let btmLink = btmListItems[i].getElementsByTagName("a")[0];

    if (btmLink == null) continue;

    if (btmLink.text.search("Top") != -1) {
      btmListItems[i].className = "chapter entire";
      continue;
    }

    if (btmLink.text.search("Previous") != -1) {
      btmListItems[i].className = "chapter previous";
      continue;
    }

    if (btmLink.text.search("Next") != -1) {
      btmListItems[i].className = "chapter next";
      break;
    }
  }

  return navBar;
}

function createListItem(element, className) {
  let listItem = document.createElement("li");
  listItem.className = "chapter " + className;
  listItem.appendChild(document.createTextNode("\n\n"));
  listItem.appendChild(element);
  listItem.appendChild(document.createTextNode("\n\n"));

  return listItem;
}

function createChapterDropdown() {
  let dropdown = document.createElement("select");
  addStyle(dropdown);
  dropdown.id = "chapter_list";
  dropdown.innerHTML = ddInnerHTML;
  dropdown.style.padding = ".18em .75em";
  dropdown.onchange = function() {
    let selectedValue = this.options[this.selectedIndex].value;
    window.location.href = mainChapterURL + selectedValue + "#workskin";
  }

  return dropdown;
}

function createEntireWorkBtm() {
  let href = window.location.pathname.split('/').slice(0, 3).join("/") + "?view_full_work=true";

  return createBtm(href, "Entire Work");
}  

function createFirstChapterBtm() {
  return createBtm(firstHref, "<< First Chapter");
}  

function createPrevChapterBtm() {
  let href = mainChapterURL + selectedTag.children[ddValue - 1].value + "#workskin";

  return createBtm(href, "< Previous Chapter");
}  

function createNextChapterBtm() {
  let href = mainChapterURL + selectedTag.children[ddValue + 1].value + "#workskin";

  return createBtm(href, "Next Chapter >");
}  

function createLastChapterBtm() {  
  return createBtm(lastHref, "Last Chapter >>");
}  

function createFullPageIndexBtm() {  
  return createBtm(fullPageHref, "Full-page Index");
}  

function createBtm(href, text) {
  let link = document.createElement("a");
  link.href = href;
  link.appendChild(document.createTextNode(text));

  return link;
}

function addStyle(element) {
  element.style.display = "inline-block";
  element.style.verticalAlign = "middle";
  element.style.backgroundColor = "#eee";
  element.style.color = "#444";
  element.style.width = "auto";
  element.style.fontSize = "100%";
  element.style.lineHeight = "1.286";
  element.style.padding = ".25em .75em";
  element.style.whiteSpace = "nowrap";
  element.style.overflow = "visible";
  element.style.position = "relative";
  element.style.cursor = "pointer";
  element.style.textDecoration = "none";
  element.style.backgroundImage = "linear-gradient(#fff 2%,#ddd 95%,#bbb 100%)";
  element.style.border = "1px solid #bbb";
  element.style.borderRadius = ".25em";
  element.style.boxShadow = "none";
  element.style.margin = "0px";
  element.style.font = "inherit";
}