BA Planner Enhancer

Add some desirable functionality to BA Resource Planner

目前為 2023-07-17 提交的版本,檢視 最新版本

// ==UserScript==
// @name        BA Planner Enhancer
// @author      Dia
// @description Add some desirable functionality to BA Resource Planner
// @version     0.1.0
// @icon        https://i.imgur.com/oA2E4CJ.png
// @grant       none
// @match       https://justin163.com/planner/
// @namespace   wxw.moe/@dia
// @run-at      document-idle
// @license     Unlicense
// ==/UserScript==

const MINIMUM_AUTOSAVE = 130000; //minimum required delay for autosave, do not change this
const DEBUG_MODE_AUTOSAVE = 30000; //30 sec autosave in debug mode (for debug only; does not actually save anything to cloud)
const DEBUG_MODE = false; //set to true to debug
const AUTOSAVE_PERIOD_OFFSET = 170000; //millisec delay to autosave to cloud so we don't overwhelm the api- this is added to MINIMUM_AUTOSAVE

let isFirstTime = true;
let lastSaveExport = localStorage.getItem('save-data');
let autosaveFreq = MINIMUM_AUTOSAVE + AUTOSAVE_PERIOD_OFFSET;

function exec(fn) {
    let script = document.createElement('script');
    script.setAttribute("type", "application/javascript");
    script.textContent = '(' + fn + ')();';
    document.body.appendChild(script); // run the script
    document.body.removeChild(script); // clean up
}
function waitForElm(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(mutations => {
            if (document.querySelector(selector)) {
                resolve(document.querySelector(selector));
                observer.disconnect();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}
function log(msg) {
    let currTime = new Date();
    let currH = String(currTime.getHours()).padStart(2, 0);
    let currM = String(currTime.getMinutes()).padStart(2, 0);
    let currS = String(currTime.getSeconds()).padStart(2, 0);
    console.log("[BAPE-" + currH + ":" + currM + ":" + currS + "] " + msg);
}

function autoSave(forceSaving) {
    let xferUsername = document.getElementById("input-transfer-username").value;
    let xferAuthkey = document.getElementById("input-transfer-authkey").value;
    let currTime = new Date();
    let currSaveExport = localStorage.getItem('save-data');
    log("Autosave fired");
    if (xferUsername == "" || xferAuthkey == "") {
        log("Cannot autosave to cloud because there's no username and/or authkey found - please login first")
    } else {
        if (lastSaveExport === currSaveExport && !forceSaving) {
            log("There are no changes to save. Skipping this round of autosave...");
        } else {
            if(forceSaving){
                log("Force saving...");
            }
            else{
                log("Changes detected. Saving changes...");
            }
            lastSaveExport = currSaveExport;
            if (DEBUG_MODE) {
                console.log("Saved to cloud (fake)");
            } else {
                exec(function() {
                    saveRequest(false);
                });
            }
        }
    }
}

function autoSaveToCloud(autosaveFreq) {
    setInterval(function() {
        autoSave(isFirstTime);
        isFirstTime = false;
    }, autosaveFreq);
}

function loadImprovement() {
    let loadBtn = document.getElementById("transfer-load-button");
    loadBtn.onclick = ";";
    loadBtn.addEventListener("click", confirmLoad);
}

function confirmLoad() {
    if (confirm("Do you really want to load? This will overwrite any unsaved data!")) {
        exec(function() {
            return loadClick();
        });
    }
}
function reorderMats(){
    const dummyIdStr = "mat-zzz-";
    const dummyIdEnd = "-dummyid-";
    let resourceBox = document.getElementById('table-parent-1').parentNode;
    let table1 = document.getElementById('table-parent-1');
    let table2 = document.getElementById('table-parent-2');
    let table3 = document.getElementById('table-parent-3');
    let table4 = document.getElementById('table-parent-4');
    let table5 = document.getElementById('table-parent-5');
    resourceBox.insertBefore(table1, null); //put school resources after the artifacts
    let schoolResourceRows = table1.getElementsByTagName("tr");
    let artifactLeftRows = table2.getElementsByTagName("tr");
    let artifactRightRows = table3.getElementsByTagName("tr");
    let gearRows = table4.getElementsByTagName("tr");
    let ueRows = table5.getElementsByTagName("tr");
    if(schoolResourceRows.length > 0 && artifactLeftRows.length > 0 && artifactRightRows.length > 0 && gearRows.length > 0){
        clearInterval(reorderInterval);
        //blurays & notes
        for(let i=0;i<schoolResourceRows.length; i++){
            let colIds = [];
            let cols = schoolResourceRows[i].getElementsByTagName("td");
            for(let j=1; j<cols.length; j++){ //skip the first column because that's the "name" column
                if(!cols[j].id.startsWith("mat")){ //additional check to make sure that the column ids begin with 'mat' (the site broke it before)
                    cols[j].id = dummyIdStr + "sr-"+ i + "-" + j + dummyIdEnd + cols[j].id; //dummy id for sorting
                }
                colIds.push(cols[j].id);
            }
            colIds.sort();
            for(let j=0; j<colIds.length; j++){
                let sortingResource = document.getElementById(colIds[j]);
                schoolResourceRows[i].insertBefore(sortingResource, null);
                let dummyIdCheck = colIds[j].indexOf(dummyIdEnd);
                if(dummyIdCheck > 0){
                    sortingResource.id = sortingResource.id.substr(dummyIdCheck + dummyIdEnd.length, sortingResource.id.length - 1);
                }
            }
        }
        //do the same for artifact (left)
        for(let i=0; i<artifactLeftRows.length; i++){
            let colIds = [];
            let cols = artifactLeftRows[i].getElementsByTagName("td");
            for(let j=1; j<cols.length; j++){
                if(!cols[j].id.startsWith("mat")){
                    cols[j].id = dummyIdStr + "al-"+ i + "-" + j + dummyIdEnd + cols[j].id;
                }
                colIds.push(cols[j].id);
            }
            colIds.sort();
            for(let j=0; j<colIds.length; j++){
                let sortingResource = document.getElementById(colIds[j]);
                artifactLeftRows[i].insertBefore(sortingResource, null);
                let dummyIdCheck = colIds[j].indexOf(dummyIdEnd);
                if(dummyIdCheck > 0){
                    sortingResource.id = sortingResource.id.substr(dummyIdCheck + dummyIdEnd.length, sortingResource.id.length - 1);
                }
            }
        }
        //do the same for artifact (right)
        for(let i=0; i<artifactRightRows.length; i++){
            let colIds = [];
            let cols = artifactRightRows[i].getElementsByTagName("td");
            for(let j=1; j<cols.length; j++){
                if(!cols[j].id.startsWith("mat")){
                    cols[j].id = dummyIdStr + "ar-"+ i + "-" + j + dummyIdEnd + cols[j].id;
                }
                colIds.push(cols[j].id);
            }
            colIds.sort();
            for(let j=0; j<colIds.length; j++){
                let sortingResource = document.getElementById(colIds[j]);
                artifactRightRows[i].insertBefore(sortingResource, null);
                let dummyIdCheck = colIds[j].indexOf(dummyIdEnd);
                if(dummyIdCheck > 0){
                    sortingResource.id = sortingResource.id.substr(dummyIdCheck + dummyIdEnd.length, sortingResource.id.length - 1);
                }
            }
        }
        //do the same for gears
        for(let i=0; i<gearRows.length; i++){
            let colIds = [];
            let cols = gearRows[i].getElementsByTagName("td");
            for(let j=1; j<cols.length; j++){
                if(!cols[j].id.startsWith("mat")){
                    cols[j].id = dummyIdStr + "gr-"+ i + "-" + j + dummyIdEnd + cols[j].id;
                }
                colIds.push(cols[j].id);
            }
            colIds.sort();
            for(let j=0; j<colIds.length; j++){
                let sortingResource = document.getElementById(colIds[j]);
                gearRows[i].insertBefore(sortingResource, null);
                let dummyIdCheck = colIds[j].indexOf(dummyIdEnd);
                if(dummyIdCheck > 0){
                    sortingResource.id = sortingResource.id.substr(dummyIdCheck + dummyIdEnd.length, sortingResource.id.length - 1);
                }
            }
        }
        //do the same for ues
        //except we need to also exclude the last 2 columns, and they don't have id in <td>, only in the <p> inside them
        for(let i=0; i<ueRows.length; i++){
            let colIds = [];
            let cols = ueRows[i].getElementsByTagName("p");
            let anchors = [];
            for(let j=0; j<cols.length - 2; j++){
                if(!cols[j].id.startsWith("T")){
                    cols[j].id = dummyIdStr + "ue-"+ i + "-" + j + dummyIdEnd + cols[j].id;
                }
                colIds.push(cols[j].id);
                anchors[i] = cols[j].parentNode.nextElementSibling;
            }
            colIds.sort();
            for(let j=0; j<colIds.length; j++){
                let idResource = document.getElementById(colIds[j]);
                let sortingResource = idResource.parentNode;
                ueRows[i].insertBefore(sortingResource, anchors[i]);
                let dummyIdCheck = colIds[j].indexOf(dummyIdEnd);
                if(dummyIdCheck > 0){
                    idResource.id = idResource.id.substr(dummyIdCheck + dummyIdEnd.length, idResource.id.length - 1);
                }
            }
        }
    }
}

log("Document has finished loading. Starting the userscript...");
if (DEBUG_MODE) {
    autosaveFreq = DEBUG_MODE_AUTOSAVE;
    log("Warning! This script is currently run with debug mode on. The autosave functionality does not actually save anything to cloud in this mode.")
    log("You can turn off the debug mode by setting DEBUG_MODE to false in the script and refresh the page!")
}
log("Autosave duration is currently every " + (autosaveFreq / 1000) + " seconds. First autosave will be forced, even if there are no changes.")

//feature #1: ask first if you really want to load from cloud, preventing accidental click that wipes local data
loadImprovement();
//feature #2: autosave every 5 minute (will not save if there are no changes)
autoSaveToCloud(autosaveFreq);
//feature #3: reorder resources to match how the game sorts them for convenience
let reorderInterval = setInterval(reorderMats, 1000);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址