AO3: [Wrangling] Count Wranglers on Subfandoms

On a fandom tag's Landing Page, writes out how many wranglers are currently assigned to the listed subfandoms

当前为 2022-06-29 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         AO3: [Wrangling] Count Wranglers on Subfandoms
// @namespace    https://greasyfork.org/en/users/906106-escctrl
// @description  On a fandom tag's Landing Page, writes out how many wranglers are currently assigned to the listed subfandoms
// @author       escctrl
// @version      0.1
// @match        *://*.archiveofourown.org/tags/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @license      MIT
// ==/UserScript==

(function($) {
    'use strict';

    // wait duration between checking individual bins - set this number higher if you often run into Retry Later
    //   this applies especially to huge fandom trees like DCU!! 3sec is NOT enough for the full list! make this number huge and let it run in the background
    //   defined in milliseconds, e.g. 3000 = 3 seconds
    const interval = 5000;

    const subfandoms = document.getElementsByClassName('sub')[0];
    if ($('div.parent.fandom').length == 0) { console.log('Count Wranglers on Subfandoms script says: this is not a fandom tag'); return true; } // bow out gracefully if we're not looking at a fandom
    if (subfandoms == null) { console.log('Count Wranglers on Subfandoms script says: fandom doesn\'t have subfandoms'); return true; } // bow out gracefully if the fandom has no subfandoms

    // some code to add *) hidden span to track ao3jail *) span for progress bar/report global results, *) button to start the checking
    $(subfandoms).children('h3.heading').before('<span id="countwranglers-jail" style="display: none;"></span>' +
                                                '<span id="countwranglers-unwrangled" style="display: none;">0</span>' +
                                                '<span id="countwranglers-results" style="float:right;"><button id="countwranglers">Count Wranglers</button></span>');

    // assign the function to the click event (will trigger only once user clicked the link)
    $(subfandoms).find('button#countwranglers').click(countWranglers);


    // a function that gets run on button push
    function countWranglers() {

        // checking that the click event actually worked
        var progress = document.getElementById('countwranglers-results');
        $(progress).css("padding", "0.3em 0.25em");
        $(progress).html('Counting wranglers... grab a drink, this may take a while!');

        // find all the subfandoms
        var fandoms = $(subfandoms).find('ul.tree a');

        // immediately add a span placeholder next to each fandom for the # of wranglers, show TBD until the fandom was checked
        // this is basically our progress bar while the script slowly works down the fandom tree
        $(fandoms).after('<span style="font-size: smaller;"> <em>... wranglers TBD</em></span>');

        // loop through the unwrangled bins
        $(fandoms).each(function(i, fandom) {

            // set a delay between bin checks to reduce chances of jail
            setTimeout(function() {

                // if previous loops hit Ao3 Jail, don't try looking for wranglers anymore
                if ( document.getElementById('countwranglers-jail').innerText == "true") {
                    console.log('previously received "Retry later" response, skipped check on subfandom');
                    return false;
                }

                var assignedCount = 0;
                var unwrangledTotal = document.getElementById('countwranglers-unwrangled');

                // retrieve the data from the fandom tag's edit page
                $.get(fandom.href + '/edit', function(response) {
                    // nothing to do here, all interactions are in done() and failed()

                // do stuff on success (write out how many wranglers are on a fandom tag, add an Edit link when 0, and count global unwrangled's up)
                }).done(function(response) {
                    // pick the correct field in the form containing the assigned wranglers
                    var assignedWranglers = $(response).find('#edit_tag fieldset:first dd:nth-of-type(4)').text();
                    var unwrangledCount = parseInt(unwrangledTotal.innerText);

                    // unwrangled fandoms have a Sign Up link here
                    if (assignedWranglers == 'Sign Up') {
                        // count up the global total of unwrangled subfandoms
                        unwrangledCount++;
                        unwrangledTotal.innerText = unwrangledCount.toString();

                        // update the fandom tag with that information and put in an Edit & Wrangle child tags button for convenience
                        $(fandom).next().html(' ... unwrangled [<a href="'+ fandom.href + '/edit">Edit</a>] [<a href="'+ fandom.href + '/wrangle">Wrangle</a>]');

                        // highlight the fandom tag to make it easily findable in a long list
                        $(fandom).parent().css("background-color", "yellow");
                    }
                    // assigned wranglers are plaintext
                    else {
                        // split the a comma-seperated list into an array and count its length (that's the number of wranglers)
                        assignedCount = assignedWranglers.split(', ').length;

                        // update the fandom tag with that information
                        $(fandom).next().html(' ... '+ assignedCount +' wrangler' + ((assignedCount > 1) ? "s" : ""));
                    }

                    // if last loop also succeeded, update global progress with "all wrangled" or "# unwrangled"
                    if (fandoms.length == i+1) {
                        if (unwrangledCount == 0) {
                            $(progress).html('all subfandoms are wrangled \\o/');
                        }
                        else {
                            $(progress).html(unwrangledCount + ' subfandoms unwrangled');
                        }
                    }

                // thanks to Przemysław Sienkiewicz on Stackoverflow for the code to catch error responses https://stackoverflow.com/a/40256829
                // do other stuff on fail (set the ao3jail marker)
                }).fail(function(data, textStatus, xhr) {
                    // update user on the issue
                    $(progress).html('Wrangler Count has hit "Retry later", sorry!');
                    document.getElementById('countwranglers-jail').innerText = "true"; // set it in DOM so next delayed loops can skip

                    //This shows status code eg. 429
                    console.log("Wrangler Count has hit Retry later", data.status);
                    //This shows status message eg. Too Many Requests
                    //console.log("bin #"+i+" STATUS: "+xhr);

                    return false;
                });

            // the each() loops immediately and creates all timeout calls (async) at once, so we need to stagger them
            // by multiplying the 3s delay by the loop number (bin #0 = 3000*0, bin #1 = 3000*1, bin #2 = 3000*2, etc)
            }, interval*i);
        }); // end each
    } // end function
})(jQuery);