NeopetsUserscripts/scripts/move-training-pets-to-top.u...

70 lines
3.4 KiB
JavaScript

// ==UserScript==
// @name Move Training Pets To Top
// @namespace https://hollymcfarland.com
// @version 1.0
// @description At the three battledome training areas in Neopia, move pets currently in a course or waiting for input to the top of the list
// @author monorail
// @match https://www.neopets.com/pirates/academy.phtml?type=status
// @match https://www.neopets.com/island/training.phtml?type=status
// @match https://www.neopets.com/island/fight_training.phtml?type=status
// @icon https://www.google.com/s2/favicons?sz=64&domain=neopets.com
// @grant none
// ==/UserScript==
/*
Note that this has not been tested in the Secret Ninja Training School, simply
because I don't have any pets even close to level 250. But the DOM looks the
same at the Swashbuckling Academy and the Mystery Island Training School so,
I assume it'll work there too? If someone let me know I'd appreciate it haha
*/
(function() {
'use strict';
function getPairs(iter) {
/*
Given an iterable (something that can be converted to an array, anyway), return
an array of two-length arrays containing each element once, in the same order
e.g. getPairs([1, 2, 3, 4, 5, 6]) => [[1, 2], [3, 4], [5, 6]]
*/
// The function getting mapped here behaves differently depending on the parity
// of the index.
// Odd index => Empty array
// Even index => Array of array(!!) of the element at that index and the one after it
// Then because it's getting _flat_mapped, the whole thing is flattened by one level.
// Empty arrays disappear and the arrays of arrays become just two-length arrays
// https://stackoverflow.com/a/57851980/2114129
return [...iter].flatMap((_, i, a) => i % 2 ? [] : [a.slice(i, i + 2)]);
}
// Table that shows each pet
const statusTable = document.querySelector(".content > p:nth-child(6) > table:nth-child(1) > tbody:nth-child(1)");
for (let [header, pet] of getPairs(statusTable.children)) {
// Each "pet" here refers to a <tr> element representing that pet's status.
// Every pet has at exactly two <td> elements as children. The first displays
// their image and stats and the second shows any applicable status (i.e. the
// pet is waiting for payment, currently training, or waiting for "course
// complete" confirmation). This second element is empty for pets with no status
// at all.
// Any pet that has any kind of status at all should be moved to the top, and
// ideally we'd also like to otherwise preserve the order. To accomplish this,
// what we're actually going to do is take every pet that _doesn't_ have a
// status (i.e. the second <td> child is empty) and move it to the bottom. We
// do this on a copy of the list of children so we don't hit any pet more than
// once or skip any, and it's all done in the correct order.
// To move an element to the bottom, we just have to append it as a child to
// the element it's already a child of. Because Neopets uses tables for layout
// in current year, the header above each pet is actually a completely separate
// element, so those are handled at the same time.
if (pet.lastChild.childElementCount === 0) {
statusTable.appendChild(header);
statusTable.appendChild(pet);
}
}
})();