MSPFA extras

Adds custom features to MSPFA.

目前為 2020-07-02 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         MSPFA extras
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Adds custom features to MSPFA.
// @author       seymour schlong
// @match        https://mspfa.com/*
// @grant        none
// ==/UserScript==


(function() {
    'use strict';

    const pageLoad = (fn) => {
        let interval = setInterval(() => {
            if (fn()) clearInterval(interval);
        }, 500);
    };

    const saveData = (data) => {
        localStorage.mspfaextra = JSON.stringify(data);
        console.log("Saved cookies under mspfaextra.");
    };

    let settings = {};

    if (localStorage.mspfaextra) {
        settings = JSON.parse(localStorage.mspfaextra);
    } else {
        settings.autospoiler = false;
        settings.style = 0;
        settings.styleURL = "";
        settings.night = false;
        settings.auto502 = true;
        saveData(settings);
    }

    if (typeof settings.autospoiler === "undefined") {
        settings.autospoiler = false;
    }
    if (typeof settings.style === "undefined") {
        settings.style = 0;
    }
    if (typeof settings.styleURL === "undefined") {
        settings.styleURL = "";
    }
    if (typeof settings.night === "undefined") {
        settings.night = false;
    }
    if (typeof settings.auto502 === "undefined") {
        settings.auto502 = true;
    }

    //console.log(settings);

    let styleOptions = ["Standard", "Low Contrast", "Light", "Dark", "Felt", "Trickster", "Custom"];

    let myLink = document.querySelector('nav a[href="/my/"]');
    let dropDiv = document.createElement('div');
    dropDiv.className = 'dropdown';
    Object.assign(dropDiv.style, {
        position: 'relative',
        display: 'inline-block',
        backgroundColor: 'inherit'
    });

    let dropContent = document.createElement('div');
    dropContent.className = 'dropdown-content';
    Object.assign(dropContent.style, {
        display: 'none',
        backgroundColor: 'inherit',
        position: 'absolute',
        textAlign: 'left',
        minWidth: '100px',
        marginLeft: '-5px',
        padding: '2px',
        zIndex: '1',
        borderRadius: '0 0 5px 5px'
    });

    dropDiv.addEventListener('mouseenter', evt => {
        dropContent.style.display = 'block';
        dropContent.style.color = getComputedStyle(myLink).color;
    });
    dropDiv.addEventListener('mouseleave', evt => {
        dropContent.style.display = 'none';
    });

    if (myLink) {
        myLink.parentNode.insertBefore(dropDiv, myLink);
        dropDiv.appendChild(myLink);
        dropDiv.appendChild(dropContent);

        let dLinks = [];
        dLinks[0] = [ 'Messages', 'My Adventures', 'Settings' ];
        dLinks[1] = [ '/my/messages/', '/my/stories/', '/my/settings/' ];

        for (let i = 0; i < dLinks[0].length; i++) {
            let newLink = document.createElement('a');
            newLink.textContent = dLinks[0][i];
            newLink.href = dLinks[1][i];
            dropContent.appendChild(newLink);
        }
    }

    window.addEventListener("load", () => {
        // Reload the page if 502 CloudFlare error page appears
        if (settings.auto502 && document.querySelector('.cf-error-overview')) {
            window.location.reload();
        }

        // Append "My Profile" to the dropdown list if you're signed in
        pageLoad(() => {
            if (window.MSPFA) {
                if (window.MSPFA.me.n) {
                    let newLink = document.createElement('a');
                    newLink.textContent = "My Profile";
                    newLink.href = `/user/?u=${window.MSPFA.me.i}`;
                    dropContent.appendChild(newLink);
                    return true;
                }
                return true;
            }
        });
    });

    let dropStyleText = '#notification { z-index: 2; } .dropdown-content a { color: inherit; padding: 2px; text-decoration: underline; display: block;}';
    let dropStyle = document.createElement('style');
    dropStyle.id = 'dropdown-style';
    dropStyle.textContent = dropStyleText;
    //dropdownStyle.textContent = '#notification {    z-index: 2;}.dropdown:hover .dropdown-content {	display: block;}.dropdown {    position: relative;    display: inline-block;    background-color: inherit;}.dropdown-content {    display: none;    position: absolute;    text-align: left;    background-color: inherit;    min-width: 100px;    margin-left: -5px;    padding: 2px;    z-index: 1;    border-radius: 0 0 5px 5px;}.dropdown-content a {    color: #fffa36;    padding: 2px 2px;    text-decoration: underline;    display: block;}';

    let theme = document.createElement('link');
    Object.assign(theme, { id: 'theme', type: 'text/css', rel: 'stylesheet' });

    if (!document.querySelector('#theme') && !/^\/css\/|^\/js\//.test(location.pathname)) {
        document.querySelector('head').appendChild(theme);
    }
    if (!document.querySelector('#dropdown-style')) {
        document.querySelector('head').appendChild(dropStyle);
    }

    const updateTheme = (src) => {
        theme.href = src;
    }

    if (settings.night) {
        updateTheme('/css/theme3.css');
    } else if (settings.style > 0) {
        updateTheme(settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL);
    }

    pageLoad(() => {
        if (window.MSPFA) {
            if (window.MSPFA.story && window.MSPFA.story.y.length > 0) {
                updateTheme("");
            }
            return true;
        }
    });

    pageLoad(() => {
        if (document.querySelector('footer .mspfalogo')) {
            document.querySelector('footer .mspfalogo').addEventListener('dblclick', evt => {
                if (evt.button === 0) {
                    settings.night = !settings.night;
                    saveData(settings);

                    if (settings.night) {
                        updateTheme('/css/theme3.css');
                    } else {
                        updateTheme(settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL);
                    }

                    dropStyle.textContent = dropStyleText + '*{transition:1s}';
                    setTimeout(() => {
                        dropStyle.textContent = dropStyleText;
                    }, 1000);

                    console.log(`Night mode turned ${settings.night ? 'on' : 'off'}.`);
                }
            });
            return true;
        }
    });

    if (location.pathname === "/" || location.pathname === "/preview/") {
        if (settings.autospoiler) {
            window.MSPFA.slide.push((p) => {
                document.querySelectorAll('#slide .spoiler:not(.open) > div:first-child > input').forEach(sb => sb.click());
            });
        }
        if (location.search) {
            pageLoad(() => {
                if (document.querySelector('#infobox tr td:nth-child(2)')) {
                    document.querySelector('#infobox tr td:nth-child(2)').appendChild(document.createTextNode('Creation date: ' + new Date(window.MSPFA.story.d).toString().split(' ').splice(1, 3).join(' ')));
                    return true;
                }
            });
            pageLoad(() => {
                let infoButton = document.querySelector('.edit.major');
                if (infoButton) {
                    let editPages = document.createElement('button');
                    Object.assign(editPages, { className: 'editpages major edit', title: 'Edit pages'});
                    //infoButton.parentNode.insertBefore(editPages, infoButton);
                    return true;
                }
            });
        }
    }
    else if (location.pathname === "/my/settings/") { // Custom settings
        let saveBtn = document.querySelector('#savesettings');

        let table = document.querySelector("#editsettings tbody");
        let saveTr = table.querySelectorAll("tr");
        saveTr = saveTr[saveTr.length - 1];

        let headerTr = document.createElement('tr');
        let header = document.createElement('th');
        header.textContent = "Extra Settings";
        headerTr.appendChild(header);

        let settingsTr = document.createElement('tr');
        let localMsg = document.createElement('span');
        let settingsTd = document.createElement('td');
        localMsg.innerHTML = "Because this is an extension, any data saved is only <b>locally</b> on this device.<br>Don't forget to <b>save</b> when you've finished making changes!";
        let plusTable = document.createElement('table');
        let plusTbody = document.createElement('tbody');
        plusTable.appendChild(plusTbody);
        settingsTd.appendChild(localMsg);
        settingsTd.appendChild(document.createElement('br'));
        settingsTd.appendChild(document.createElement('br'));
        settingsTd.appendChild(plusTable);
        settingsTr.appendChild(settingsTd);

        let spoilerTr = plusTbody.insertRow(0);
        let spoilerTextTd = spoilerTr.insertCell(0);
        let spoilerInputTd = spoilerTr.insertCell(1);
        let spoilerInput = document.createElement('input');
        spoilerInputTd.appendChild(spoilerInput);

        let errorTr = plusTbody.insertRow(1);
        let errorTextTd = errorTr.insertCell(0);
        let errorInputTd = errorTr.insertCell(1);
        let errorInput = document.createElement('input');
        errorInputTd.appendChild(errorInput);

        let cssTr = plusTbody.insertRow(2);
        let cssTextTd = cssTr.insertCell(0);
        let cssSelectTd = cssTr.insertCell(1);
        let cssSelect = document.createElement('select');
        cssSelectTd.appendChild(cssSelect);

        let customTr = plusTbody.insertRow(3);
        let customTextTd = customTr.insertCell(0);
        let customCssTd = customTr.insertCell(1);
        let customCssInput = document.createElement('input');
        customCssTd.appendChild(customCssInput);

        plusTable.style = "text-align: center;";
        spoilerTextTd.textContent = "Automatically open spoilers:";
        spoilerInput.name = "p1";
        spoilerInput.type = "checkbox";
        spoilerInput.checked = settings.autospoiler;

        errorTextTd.textContent = "Automatically reload Cloudflare 502 error pages:";
        errorInput.name = "p2";
        errorInput.type = "checkbox";
        errorInput.checked = settings.auto502;

        cssTextTd.textContent = "Change style:";

        customTextTd.textContent = "Custom CSS URL:";
        customCssInput.style.width = "99px";
        customCssInput.value = settings.styleURL;

        styleOptions.forEach(o => cssSelect.appendChild(new Option(o, o)));

        // Enable the save button
        spoilerInput.addEventListener("mouseup", () => {
            saveBtn.disabled = false;
        });
        errorInput.addEventListener("mouseup", () => {
            saveBtn.disabled = false;
        });
        cssSelect.addEventListener("mouseup", () => {
            saveBtn.disabled = false;
        });
        customCssInput.addEventListener("mouseup", () => {
            saveBtn.disabled = false;
        });

        saveTr.parentNode.insertBefore(headerTr, saveTr);
        saveTr.parentNode.insertBefore(settingsTr, saveTr);
        cssSelect.selectedIndex = settings.style;

        saveBtn.addEventListener('mouseup', () => {
            settings.autospoiler = spoilerInput.checked;
            settings.style = cssSelect.selectedIndex;
            settings.styleURL = customCssInput.value;
            settings.auto502 = errorInput.checked;
            theme.href = settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL;
            settings.night = false;
            console.log(settings);
            saveData(settings);

            dropStyle.textContent = dropStyleText + '*{transition:1s}';
            setTimeout(() => {
                dropStyle.textContent = dropStyleText;
            }, 1000);
        });
    }
    else if (location.pathname === "/my/messages/") { // New buttons
        let btnStyle = "margin: 10px 5px;";

        // Select all read messages button.
        const selRead = document.createElement('input');
        selRead.style = btnStyle;
        selRead.value = "Select Read";
        selRead.id = "selectread";
        selRead.classList.add("major");
        selRead.type = "button";

        // On click, select all messages with the style attribute indicating it as read.
        selRead.addEventListener('mouseup', () => {
            document.querySelectorAll('td[style="border-left: 8px solid rgb(221, 221, 221);"] > input').forEach((m) => m.click());
        });

        // Select duplicate message (multiple update notifications).
        const selDupe = document.createElement('input');
        selDupe.style = btnStyle;
        selDupe.value = "Select Same";
        selDupe.id = "selectdupe";
        selDupe.classList.add("major");
        selDupe.type = "button";

        selDupe.addEventListener('mouseup', evt => {
            let temp = document.querySelectorAll('#messages > tr');
            let msgs = [];
            for (let i = temp.length - 1; i >= 0; i--) {
                msgs.push(temp[i]);
            }
            let titles = [];
            msgs.forEach((msg) => {
                let title = msg.querySelector('a.major').textContent;
                if (/^New update: /.test(title)) { // Select only adventure updates
                    if (titles.indexOf(title) === -1) {
                        if (msg.querySelector('td').style.cssText !== "border-left: 8px solid rgb(221, 221, 221);") {
                            titles.push(title);
                        }
                    } else {
                        msg.querySelector('input').click();
                    }
                }
            });
        });

        // Add buttons to the page.
        let del = document.querySelector('#deletemsgs');
        del.parentNode.appendChild(document.createElement('br'));
        del.parentNode.appendChild(selRead);
        del.parentNode.appendChild(selDupe);
    }
    else if (location.pathname === "/my/stories/") {
        let guides = ["MSPFA Etiquette", "Fanventure Guide for Dummies", "CSS Guide", "HTML and CSS Things"];
        let ids = ["27631", "29299", "21099", "23711"];
        let authors = ["Radical Dude 42", "nzar", "MadCreativity", "seymour schlong"];

        let parentTd = document.querySelector('.container > tbody > tr:last-child > td');
        let unofficial = parentTd.querySelector('span');
        unofficial.textContent = "Unofficial Guides";
        let guideTable = document.createElement('table');
        let guideTbody = document.createElement('tbody');
        guideTable.style.width = "100%";
        guideTable.style.textAlign = "center";

        guideTable.appendChild(guideTbody);
        parentTd.appendChild(guideTable);

        for (let i = 0; i < guides.length; i++) {
            let guideTr = guideTbody.insertRow(i);
            let guideTd = guideTr.insertCell(0);
            let guideLink = document.createElement('a');
            guideLink.href = '/?s='+ids[i];
            guideLink.textContent = guides[i];
            guideLink.className = "major";
            guideTd.appendChild(guideLink);
            guideTd.appendChild(document.createElement('br'));
            guideTd.appendChild(document.createTextNode('by '+authors[i]));
            guideTd.appendChild(document.createElement('br'));
            guideTd.appendChild(document.createElement('br'));
        }
    }
    else if (location.pathname === "/user/") {
        pageLoad(() => {
            if (window.MSPFA) {
                window.MSPFA.request(0, {
                    do: "user",
                    u: location.search.slice(3)
                }, user => {
                    if (typeof user !== "undefined") {
                        let stats = document.querySelector('#userinfo table');
                        let joinTr = stats.insertRow(1);
                        let joinTextTd = joinTr.insertCell(0);
                        joinTextTd.appendChild(document.createTextNode("Account created:"));
                        let d = new Date(user.d).toString().split(' ').splice(1, 4).join(' ');
                        let joinDate = joinTr.insertCell(1);
                        let joinTime = document.createElement('b');
                        joinTime.appendChild(document.createTextNode(d));
                        joinDate.appendChild(joinTime);
                    }
                }, status => {
                    console.log(status);
                }, true);
                return true;
            }
        });
    }
})();