AO3: [Wrangling] Search my canonicals for illegal characters

automatically runs a fandom-specific tag search over all your assigned fandoms, to find any canonicals with 'illegal' characters such as curly quotes or Chinese pipes

As of 2022-12-05. See the latest version.

// ==UserScript==
// @name         AO3: [Wrangling] Search my canonicals for illegal characters
// @namespace    https://greasyfork.org/en/users/906106-escctrl
// @version      2.0
// @description  automatically runs a fandom-specific tag search over all your assigned fandoms, to find any canonicals with 'illegal' characters such as curly quotes or Chinese pipes
// @author       escctrl
// @match        https://archiveofourown.org/tags/search
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const DEBUG = false;

    var found = 0; // counting found results

    var node_ul = document.getElementById('wranglerbuttons') || document.querySelector('#main ul.navigation.actions');
    const node_li = document.createElement('li');

    node_li.id = 'checkillegal';
    node_li.className = 'reindex';
    node_li.innerHTML = "<a href='#'>Check Illegal Characters</a>";
    node_li.addEventListener("click", startBackgroundCheck);
    node_ul.appendChild(node_li);

    function startBackgroundCheck() {
        // who am I logged in as?
        const user = document.querySelector('#greeting ul.user.navigation li.dropdown>a.dropdown-toggle[href*="/users/"]').href.match(/\/([A-Za-z0-9_]+)\/?$/i)[1];
        if (DEBUG) console.log("Illegal Characters: Logged in as user "+user);

        // get a list of all my wrangled fandoms
        pageload('https://archiveofourown.org/tag_wranglers/'+user, 'fandoms');
    }

    function retrieveFandomList(me) {
        // find all the assigned fandoms in the table (URL and name)
        const fandomlist = me.querySelectorAll('table tbody tr th a');

        // add a tracker for how many open XHR there will be
        window.openXHR = fandomlist.length;
        if (DEBUG) console.log("Illegal Characters: There are "+window.openXHR+" fandoms assigned, to be checked");
        document.getElementById('checkillegal').firstChild.innerText = "Checking " + window.openXHR + " fandoms";

        fandomlist.forEach( (f, i) => {
            var url = 'https://archiveofourown.org/tags/search?tag_search%5Bname%5D=*%E2%80%99*+OR+*%E2%80%98*+OR+*%E2%80%9C*+OR+*%E2%80%9D*+OR+*%EF%BD%9C*+OR+*%E2%80%93*+OR+*%E2%80%94*+OR+*%E2%80%95*+OR+*%EF%BC%88*+OR+*%EF%BC%89*+OR+*%5C%27%5C%27*&tag_search%5Btype%5D=&tag_search%5Bcanonical%5D=T&tag_search%5Bsort_column%5D=name&tag_search%5Bsort_direction%5D=asc&commit=Search+Tags&tag_search%5Bfandoms%5D=';
            url += encodeURI(f.innerText);

            if (DEBUG) console.log("Illegal Characters: Fandom "+f.innerText+" will be checked in "+3000*i+" second");
            if (DEBUG) url = 'https://archiveofourown.org/tags/search?tag_search%5Bname%5D=*POV*&tag_search%5Btype%5D=&tag_search%5Bcanonical%5D=T&tag_search%5Bsort_column%5D=name&tag_search%5Bsort_direction%5D=asc&commit=Search+Tags&tag_search%5Bfandoms%5D='+ encodeURI(f.innerText);

            // collect the search results for specifically those fandoms
            setTimeout(function() {
                pageload(url, 'search');
            }, 3000*i);
        });
    }

    function retrieveSearchResults(me) {
        window.openXHR--;
        document.getElementById('checkillegal').firstChild.innerText = "Checking " + window.openXHR + " fandoms";
        if (DEBUG) console.log("Illegal Characters: There are "+window.openXHR+" fandoms remaining to be checked");

        const results = me.querySelectorAll('ol.tag.index li span');
        const fandom = me.querySelector('#tag_search_fandoms').value;

        if (DEBUG) console.log("Illegal Characters: Fandom "+fandom+" has "+results.length+" tags with illegal characters");

        // adds the found nodelist to the object
        var printtext = "";
        if (results.length > 0) {
            printtext = '<h4 class="heading">'+fandom+'</h4><ol class="tag index group">';
            found = found + results.length;
            for (let n of results.values()) { printtext += '<li>'+n.outerHTML+'</li>'; }
            printtext += '</ol>';

            const node_parent = document.querySelector('#main');
            node_parent.innerHTML += printtext;
        }

        if (window.openXHR == 0) {
            if (DEBUG) console.log("Illegal Characters: Last fandom was checked, tallying up the result tags");
            document.getElementById('checkillegal').firstChild.innerText = "Check for illegal characters finished";
            // this was the last open XHR so we can print the results!
            printResults();
        }
    }

    function printResults() {
        const heading = document.createElement('h3');
        heading.className = "heading"
        heading.innerText = found+' Found';
        const node_parent = document.querySelector('#new_tag_search');
        node_parent.insertAdjacentElement('afterend', heading);
        if (DEBUG) console.log("Illegal Characters: Script completed.");
    }

    function saySorry(me) {
        window.openXHR--;
        console.log("Sorry, this didn't work. The background page load ran into an issue: "+me.statusText);
    }

    function pageload(url, what) {
        if (DEBUG) console.log("Illegal Characters: Loading page "+url);
        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
            if (what == 'fandoms') retrieveFandomList(xhr.response);
            else retrieveSearchResults(xhr.response);
        };
        xhr.onerror = () => { saySorry(xhr); };
        xhr.open("GET", url);
        xhr.responseType = "document";
        xhr.send();
    }

})();