Anilist VA filter

Filters the list of characters voiced by a VA to show only the characters from anime in your completed and current watchlists.

// ==UserScript==
// @name         Anilist VA filter
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Filters the list of characters voiced by a VA to show only the characters from anime in your completed and current watchlists.
// @author       Arunato
// @match        https://anilist.co/*
// @grant        none
// @require      https://code.jquery.com/jquery-3.3.1.min.js
// ==/UserScript==

(function() {
    'use strict';
    var $ = window.jQuery;

    // Retrieves user name
    function getUser(){
        const profileLink = document.querySelector(".links").childNodes[2].href;
        const re = new RegExp("https://anilist.co/user/(.*)/");
        const user = profileLink.match(re)[1];
        return user;
    }

    function getWatchlist(user, status){
        // Query for completed anime watchlist of the user
var query = `
query ($userName: String, $listStatus: MediaListStatus) { # Define which variables will be used in the query (id)
  MediaListCollection(userName: $userName, type: ANIME, status: $listStatus) {
    user {
      id
    }
    lists {
      name
      entries {
        media {
          id
        }
      }
    }
  }
}
`;

        // Define our query variables and values that will be used in the query request
        var variables = {
            userName: user,
            listStatus: status
        };

        // Define the config we'll need for our Api request
        var url = 'https://graphql.anilist.co',
            options = {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',
                },
                body: JSON.stringify({
                    query: query,
                    variables: variables
                })
            };

        // Make the HTTP Api request
        fetch(url, options).then(handleResponse)
            .then(handleData)
            .catch(handleError);
    }

    function handleResponse(response) {
        return response.json().then(function (json) {
            return response.ok ? json : Promise.reject(json);
        });
    }

    // Extracts anime id's from the data retrieved with the API request
    var animeIdList = [];
    function handleData(data) {
        var animeList = data.data.MediaListCollection.lists[0].entries;
        for (let i = 0; i<animeList.length; i++) {
            animeIdList.push(animeList[i].media.id);
        }
    }

    function handleError(error) {
        alert('Error, check console');
        console.error(error);
    }

    // Autoscrolls the page to the bottom until all data is loaded, then scrolls to the top
    var count = 0;
    var lastScrollHeight = 0;
    function loadPage(){
        var sh = document.documentElement.scrollHeight;
        if (sh != lastScrollHeight) {
            lastScrollHeight = sh;
            document.documentElement.scrollTop = sh;
            count = 0;
        } else {
            count++;
        }
        if (count === 5){
            clearInterval(loadingInterval);
            document.documentElement.scrollTop = 0;
            filterCharacters(animeIdList);
        }

    }

    // Filters the characters of a VA to only show characters in media present on the watchlist
    function filterCharacters(watchlist) {
        var characterList = document.querySelector(".character-roles .grid-wrap").childNodes;
        var characterArray = Array.from(characterList);
        var characterIdList = [];
        // For each character entry, checks if its media is in the watchlist. If it is not, the entry is removed.
        characterArray.forEach(function(item){
            var animeRe = /^https:\/\/anilist\.co\/anime\/(.*)\/.+/;
            var charRe = /^https:\/\/anilist\.co\/character\/(.*)\/.+/;
            var animeId = Number(item.querySelector(".media .content").href.match(animeRe)[1]);
            var charMatch = item.querySelector(".character .content").href.match(charRe);
            if (charMatch) {
                var charId = Number(charMatch[1]);
                if (animeIdList.indexOf(animeId) >= 0 && characterIdList.indexOf(charId) < 0){
                    characterIdList.push(charId);
                } else {
                    item.parentNode.removeChild(item);
                }
            } else {
                item.parentNode.removeChild(item);
            }
        });
    }

    // Creates buttons and displays it on the staff page
    var loadingInterval
    function addButtons(){
        // Create filter button
        /*
        var filterButton = document.createElement("BUTTON");
        filterButton.textContent = 'Filter';
        filterButton.setAttribute("style", "float:right");
        filterButton.addEventListener("click", function() {
            filterCharacters(animeIdList);
        }, false);
        */

        // Create loading button
        var loadingButton = document.createElement("BUTTON");
        loadingButton.textContent = 'Load page & filter';
        loadingButton.setAttribute("style", "float:right");
        loadingButton.addEventListener("click", function() {
            loadingInterval = window.setInterval(loadPage, 100);
            loadingButton.disabled = true;
        }, false);

        // Select character-roles header of staff page and add the buttons
        var charHeader = document.querySelector(".staff .character-roles h2");
        charHeader.appendChild(loadingButton);
        // charHeader.appendChild(filterButton);
    }

    // Script which filters the characters of a VA
    function filterScript(){
        addButtons();
        // TODO add sorting buttons
    }

    // Handles which script runs on which page
    function handleScripts(url){
        if (url.match(/^https:\/\/anilist\.co\/staff\/.+/)) {
            filterScript();
        };
    }

    $( window ).on( "load", function() {
        const user = getUser();
        getWatchlist(user, 'COMPLETED');
        getWatchlist(user, 'CURRENT');

        // Checks if the page url has changed and runs scripts accordingly
        // TODO change to event trigger if possible
        var current = "";
        var handle = 0;
        setInterval(function(){
            if(document.URL != current){
                clearTimeout(handle);
                current = document.URL;
                handle = setTimeout(function(){
                    handleScripts(current)
                }, 1000);
            };
        },200);
    });
})();