// ==UserScript==
// @name MSPFA extras
// @namespace http://tampermonkey.net/
// @version 1.5.1
// @description Adds custom features to MSPFA.
// @author seymour schlong
// @icon https://raw.githubusercontent.com/GrantGryczan/MSPFA/master/www/images/ico.svg
// @icon64 https://raw.githubusercontent.com/GrantGryczan/MSPFA/master/www/images/ico.svg
// @match https://mspfa.com/
// @match https://mspfa.com/*/
// @match https://mspfa.com/*/?*
// @match https://mspfa.com/?s=*
// @match https://mspfa.com/my/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const currentVersion = "1.5.1";
console.log(`MSPFA extras script v${currentVersion} by seymour schlong`);
/**
* https://github.com/GrantGryczan/MSPFA/projects/1?fullscreen=true
* Github to-do completion list
*
* https://github.com/GrantGryczan/MSPFA/issues/26 - Dropdown menu - February 23rd, 2020
* https://github.com/GrantGryczan/MSPFA/issues/18 - MSPFA themes - February 23rd, 2020
* https://github.com/GrantGryczan/MSPFA/issues/32 - Adventure creation dates - February 23rd, 2020
* https://github.com/GrantGryczan/MSPFA/issues/32 - User creation dates - February 23rd, 2020
* https://github.com/GrantGryczan/MSPFA/issues/40 - Turn certain buttons into links - July 21st, 2020
* https://github.com/GrantGryczan/MSPFA/issues/41 - Word and character count - July 21st, 2020
* https://github.com/GrantGryczan/MSPFA/issues/57 - Default spoiler values - August 7th, 2020
* https://github.com/GrantGryczan/MSPFA/issues/62 - Buttonless spoilers - August 7th, 2020
*
* Note: At this point, I think I've done pretty much all that I /can/ do from the Github list. However, that doesn't mean I'll stop working on this.
* So long as people have ideas for features to add, I can continue adding things.
*
* Extension to-do... maybe...
*
* If trying to save a page and any other save button is not disabled, ask the user if they would rather Save All instead, or prompt to disable update notifications.
*/
// A general function that allows for waiting until a certain element appears on the page.
const pageLoad = (fn) => {
let interval = setInterval(() => {
if (fn()) clearInterval(interval);
}, 500);
};
// Saves the options data for the script.
const saveData = (data) => {
localStorage.mspfaextra = JSON.stringify(data);
};
// Encases an element within a link
const addLink = (elm, url) => {
let link = document.createElement('a');
link.href = url;
elm.parentNode.insertBefore(link, elm);
link.appendChild(elm);
};
// Returns true if version 2 is newer
const compareVer = (ver1, ver2) => {
ver1 = ver1.split(/\./); // current version
ver2 = ver2.split(/\./); // new version
ver1.push(0);
ver2.push(0);
if (parseInt(ver2[0]) > parseInt(ver1[0])) { // 1.x.x
return true;
} else if (parseInt(ver2[1]) > parseInt(ver1[1])) { // x.1.x
return true;
} else if (parseInt(ver2[2]) > parseInt(ver1[2])) { // x.x.1
return true;
}
return false;
}
let settings = {};
let defaultSettings = {
autospoiler: false,
style: 0,
styleURL: "",
night: false,
auto502: true,
textFix: false,
pixelFix: false,
intro: false,
autoUpdate: true,
version: currentVersion,
spoilerValues: {}
}
// Load any previous settings from localStorage
if (localStorage.mspfaextra) {
settings = JSON.parse(localStorage.mspfaextra);
}
// If any settings are undefined, re-set to their default state. (For older users when new things get stored)
Object.assign(defaultSettings, settings);
saveData(settings);
// Update saved version to the version used in the script to prevent unnecessary notifications
if (compareVer(settings.version, currentVersion)) {
settings.version = currentVersion;
saveData(settings);
}
window.MSPFAe = {}
window.MSPFAe.getSettings = () => {
return settings;
}
window.MSPFAe.getSettingsString = () => {
console.log(JSON.stringify(settings));
}
window.MSPFAe.changeSettings = (newSettings) => {
console.log('Settings updated');
console.log(settings);
Object.assign(settings, newSettings);
saveData(settings);
}
//console.log(settings);
let styleOptions = ["Standard", "Low Contrast", "Light", "Dark", "Felt", "Trickster", "Custom"];
let styleUrls = ['', '/css/theme1.css', '/css/theme2.css', '/css/?s=36237', '/css/theme4.css', '/css/theme5.css'];
// Dropdown menu
let myLink = document.querySelector('nav a[href="/my/"]');
if (myLink) {
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';
});
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);
}
// 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;
}
}
});
}
// Error reloading
window.addEventListener("load", () => {
// Reload the page if 502 CloudFlare error page appears
if (settings.auto502 && document.querySelector('.cf-error-overview')) {
window.location.reload();
}
// Wait five seconds, then refresh the page
if (document.body.textContent === "Your client is sending data to MSPFA too quickly. Wait a moment before continuing.") {
setTimeout(() => {
window.location.reload();
}, 5000);
}
});
// Message that shows when you first get the script
const showIntroDialog = () => {
let msg = window.MSPFA.parseBBCode('Hi! Thanks for installing this script!\n\nBe sure to check the [url=https://greasyfork.org/en/scripts/396798-mspfa-extras#additional-info]GreasyFork[/url] page to see a full list of features, and don\'t forget to check out your [url=https://mspfa.com/my/settings/#extraSettings]settings[/url] page to tweak things to how you want.\n\nIf you have any suggestions, or you find a bug, please be sure to let me know on Discord at [url=discord://discordapp.com/users/277928549866799125]@seymour schlong#3669[/url].\n\n[size=12]This dialog will only appear once. To view it again, click "View Script Message" at the bottom of the site.[/size]');
window.MSPFA.dialog("MSPFA extras message", msg, ["Okay"]);
}
// Check for updates by comparing currentVersion to text data from an adventure that has update text and info
const checkForUpdates = (evt) => {
window.MSPFA.request(0, {
do: "story",
s: "36596"
}, story => {
if (typeof story !== "undefined") {
let ver = settings.version.split(/\./);
let newVer = story.p[1].c.split(/\./);
// compare versions
if (compareVer(settings.version, story.p[1].c) || (evt && evt.type === 'click')) {
let msg = window.MSPFA.parseBBCode(story.p[1].b);
settings.version = story.p[1].c;
saveData(settings);
window.MSPFA.dialog(`MSPFA extras update! (${story.p[1].c})`, msg, ["Opt-out", "Dismiss", "Update"], (output, form) => {
if (output === "Update") {
window.open('https://greasyfork.org/en/scripts/396798-mspfa-extras', '_blank').focus();
} else if (output === "Opt-out") {
settings.autoUpdate = false;
saveData(settings);
}
});
}
}
});
};
// Check for updates and show intro dialog if needed
pageLoad(() => {
if (window.MSPFA) {
if (settings.autoUpdate) {
checkForUpdates();
}
if (!settings.intro) {
showIntroDialog();
settings.intro = true;
saveData(settings);
}
return true;
}
});
let details = document.querySelector('#details');
// Add 'link' at the bottom to show the intro dialog again
let introLink = document.createElement('a');
introLink.textContent = 'View Script Message';
introLink.style = 'cursor: pointer; color: #00E; text-decoration: underline;';
introLink.className = 'intro-link';
introLink.addEventListener('click', showIntroDialog);
details.appendChild(introLink);
// vbar!!!!
let vbar = document.createElement('span');
vbar.className = 'vbar';
vbar.style = 'padding: 0 5px';
vbar.textContent = '|';
details.appendChild(vbar);
// Add 'link' at the bottom to show the update dialog again
let updateLink = document.createElement('a');
updateLink.textContent = 'View Update';
updateLink.style = 'cursor: pointer; color: #00E; text-decoration: underline;';
updateLink.className = 'intro-link';
updateLink.addEventListener('click', checkForUpdates);
details.appendChild(updateLink);
// Theme stuff
let theme = document.createElement('link');
Object.assign(theme, { id: 'theme', type: 'text/css', rel: 'stylesheet' });
const updateTheme = (src) => {
theme.href = src;
}
if (!document.querySelector('#theme') && !/^\/css\/|^\/js\//.test(location.pathname)) {
document.querySelector('head').appendChild(theme);
if (settings.night) {
updateTheme('/css/?s=36237');
} else {
updateTheme(settings.style == styleOptions.length - 1 ? settings.styleURL : styleUrls[settings.style]);
}
}
// Dropdown menu and pixelated scaling
let dropStyle = document.createElement('style');
let pixelFixText = 'img, .mspfalogo, .major, .arrow, #flashytitle, .heart, .fav, .notify, .edit, .rss, input, #loading { image-rendering: pixelated !important; }';
let dropStyleText = `#notification { z-index: 2; } .dropdown-content a { color: inherit; padding: 2px; text-decoration: underline; display: block;}`;
if (!document.querySelector('#dropdown-style')) {
dropStyle.id = 'dropdown-style';
dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '');
//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;}';
document.querySelector('head').appendChild(dropStyle);
}
// Remove the current theme if the adventure has CSS (to prevent conflicts);
pageLoad(() => {
if (window.MSPFA) {
if (window.MSPFA.story && window.MSPFA.story.y && window.MSPFA.story.y.length > 0) {
updateTheme('');
}
return true;
}
});
// Enabling night mode.
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/?s=36237');
} else {
updateTheme(settings.style == styleOptions.length - 1 ? settings.styleURL : styleUrls[settings.style]);
}
dropStyle.textContent = dropStyleText + '';
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/") {
// Automatic spoiler opening
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) {
// Show creation date
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;
}
});
// Attempt to fix text errors
if (settings.textFix) {
pageLoad(() => {
if (window.MSPFA.story && window.MSPFA.story.p) {
// russian/bulgarian is not possible =(
let currentPage = parseInt(/^\?s(?:.*?)&p=([\d]*)$/.exec(location.search)[1]);
let library = [
["�", "'"],
["Ã�", "Ñ"],
["ñ", "ñ"],
["ó", "ó"],
["á", "á"],
["í", "í"],
["ú", "ú"],
["é", "é"],
["¡", "¡"],
["¿", "¿"],
["Nº", "#"]
];
// https://mspfa.com/?s=5280&p=51 -- unknown error
const replaceTerms = (p) => {
library.forEach(term => {
if (window.MSPFA.story.p[p]) {
window.MSPFA.story.p[p].c = window.MSPFA.story.p[p].c.replace(new RegExp(term[0], 'g'), term[1]);
window.MSPFA.story.p[p].b = window.MSPFA.story.p[p].b.replace(new RegExp(term[0], 'g'), term[1]);
}
});
};
replaceTerms(currentPage-1);
window.MSPFA.slide.push(p => {
replaceTerms(p);
replaceTerms(p-2);
});
window.MSPFA.page(currentPage);
return true;
}
});
}
// Turn buttons into links
pageLoad(() => {
let infoButton = document.querySelector('.edit.major');
if (infoButton) {
pageLoad(() => {
if (window.MSPFA.me.i) {
addLink(infoButton, `/my/stories/info/${location.search.split('&p=')[0]}`);
return;
}
});
addLink(document.querySelector('.rss.major'), `/rss/${location.search.split('&p=')[0]}`);
return true;
}
});
// Add "Reply" button to comment gear
document.body.addEventListener('click', evt => {
if (evt.toElement.classList.contains('gear')) {
let userID = evt.path[2].classList[2].replace('u', '');
let reportButton = document.querySelector('#dialog button[data-value="Report"]');
let replyButton = document.createElement('button');
replyButton.classList.add('major');
replyButton.type = 'submit';
replyButton.setAttribute('data-value', 'Reply');
replyButton.textContent = 'Reply';
replyButton.style = 'margin-right: 9px';
reportButton.parentNode.insertBefore(replyButton, reportButton);
replyButton.addEventListener('click', evt => {
document.querySelector('#dialog button[data-value="Cancel"]').click();
let commentBox = document.querySelector('#commentbox textarea');
commentBox.value = `[user]${userID}[/user], ${commentBox.value}`;
commentBox.focus();
});
} else {
return;
}
});/**/
}
}
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');
headerTr.id = "extraSettings";
header.textContent = "Extra Settings";
headerTr.appendChild(header);
let moreTr = document.createElement('tr');
let more = document.createElement('td');
more.textContent = "* This only applies to a select few older adventures that have had their text corrupted. Some punctuation is fixed, as well as regular characters with accents. Currently only some spanish/french is fixable. Russian/Bulgarian is not possible.";
moreTr.appendChild(more);
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);
plusTable.style = "text-align: center;";
let spoilerTr = plusTbody.insertRow(plusTbody.childNodes.length);
let spoilerTextTd = spoilerTr.insertCell(0);
let spoilerInputTd = spoilerTr.insertCell(1);
let spoilerInput = document.createElement('input');
spoilerInputTd.appendChild(spoilerInput);
spoilerTextTd.textContent = "Automatically open spoilers:";
spoilerInput.type = "checkbox";
spoilerInput.checked = settings.autospoiler;
let errorTr = plusTbody.insertRow(plusTbody.childNodes.length);
let errorTextTd = errorTr.insertCell(0);
let errorInputTd = errorTr.insertCell(1);
let errorInput = document.createElement('input');
errorInputTd.appendChild(errorInput);
errorTextTd.textContent = "Automatically reload Cloudflare 502 error pages:";
errorInput.type = "checkbox";
errorInput.checked = settings.auto502;
let updateTr = plusTbody.insertRow(plusTbody.childNodes.length);
let updateTextTd = updateTr.insertCell(0);
let updateInputTd = updateTr.insertCell(1);
let updateInput = document.createElement('input');
updateInputTd.appendChild(updateInput);
updateTextTd.textContent = "Automatically check for updates:";
updateInput.type = "checkbox";
updateInput.checked = settings.autoUpdate;
let pixelFixTr = plusTbody.insertRow(plusTbody.childNodes.length);
let pixelFixTextTd = pixelFixTr.insertCell(0);
let pixelFixInputTd = pixelFixTr.insertCell(1);
let pixelFixInput = document.createElement('input');
pixelFixInputTd.appendChild(pixelFixInput);
pixelFixTextTd.textContent = "Change pixel scaling to nearest neighbour:";
pixelFixInput.type = "checkbox";
pixelFixInput.checked = settings.pixelFix;
let textFixTr = plusTbody.insertRow(plusTbody.childNodes.length);
let textFixTextTd = textFixTr.insertCell(0);
let textFixInputTd = textFixTr.insertCell(1);
let textFixInput = document.createElement('input');
textFixInputTd.appendChild(textFixInput);
textFixTextTd.textContent = "Attempt to fix text errors (experimental)*:";
textFixInput.type = "checkbox";
textFixInput.checked = settings.textFix;
let cssTr = plusTbody.insertRow(plusTbody.childNodes.length);
let cssTextTd = cssTr.insertCell(0);
let cssSelectTd = cssTr.insertCell(1);
let cssSelect = document.createElement('select');
cssSelectTd.appendChild(cssSelect);
cssTextTd.textContent = "Change style:";
let customTr = plusTbody.insertRow(plusTbody.childNodes.length);
let customTextTd = customTr.insertCell(0);
let customCssTd = customTr.insertCell(1);
let customCssInput = document.createElement('input');
customCssTd.appendChild(customCssInput);
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
saveTr.parentNode.insertBefore(headerTr, saveTr);
saveTr.parentNode.insertBefore(settingsTr, saveTr);
saveTr.parentNode.insertBefore(moreTr, saveTr);
cssSelect.selectedIndex = settings.style;
// Add event listeners
plusTbody.querySelectorAll('input, select').forEach(elm => {
elm.addEventListener("change", () => {
saveBtn.disabled = false;
});
});
saveBtn.addEventListener('mouseup', () => {
settings.autospoiler = spoilerInput.checked;
settings.style = cssSelect.selectedIndex;
settings.styleURL = customCssInput.value;
settings.auto502 = errorInput.checked;
settings.textFix = textFixInput.checked;
settings.pixelFix = pixelFixInput.checked;
settings.autoUpdate = updateInput.checked;
settings.night = false;
console.log(settings);
saveData(settings);
updateTheme(settings.style == styleOptions.length - 1 ? settings.styleURL : styleUrls[settings.style]);
dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '');
dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '') + ' *{transition:1s}';
setTimeout(() => {
dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '');
}, 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/") {
// Add links to buttons
pageLoad(() => {
let adventures = document.querySelectorAll('#stories tr');
if (adventures.length > 0) {
adventures.forEach(story => {
let buttons = story.querySelectorAll('input.major');
let id = story.querySelector('a').href.replace('https://mspfa.com/', '').replace('&p=1', '');
if (id) {
addLink(buttons[0], `/my/stories/info/${id}`);
addLink(buttons[1], `/my/stories/pages/${id}`);
}
});
return true;
}
});
// Add user guides
let guides = ["A Guide To Uploading Your Comic To MSPFA", "MSPFA Etiquette", "Fanventure Guide for Dummies", "CSS Guide", "HTML and CSS Things", ];
let links = ["https://docs.google.com/document/d/17QI6Cv_BMbr8l06RrRzysoRjASJ-ruWioEtVZfzvBzU/edit?usp=sharing", "/?s=27631", "/?s=29299", "/?s=21099", "/?s=23711"];
let authors = ["Farfrom Tile", "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 = links[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 === "/my/stories/info/" && location.search) {
// Button links
addLink(document.querySelector('#userfavs'), `/readers/${location.search}`);
addLink(document.querySelector('#editpages'), `/my/stories/pages/${location.search}`);
}
else if (location.pathname === "/my/stories/pages/" && location.search) {
let adventureID = /\?s=(\d{5})/.exec(location.search)[1];
// Button links
addLink(document.querySelector('#editinfo'), `/my/stories/info/${adventureID}`);
// Default spoiler values
let replaceButton = document.querySelector('#replaceall');
let spoilerButton = document.createElement('input');
spoilerButton.classList.add('major');
spoilerButton.value = 'Default Spoiler Values';
spoilerButton.type = 'button';
replaceButton.parentNode.insertBefore(spoilerButton, replaceButton);
replaceButton.parentNode.insertBefore(document.createElement('br'), replaceButton);
replaceButton.parentNode.insertBefore(document.createElement('br'), replaceButton);
let spoilerSpan = document.createElement('span');
let spoilerOpen = document.createElement('input');
let spoilerClose = document.createElement('input');
spoilerSpan.appendChild(document.createTextNode('Open button text:'));
spoilerSpan.appendChild(document.createElement('br'));
spoilerSpan.appendChild(spoilerOpen);
spoilerSpan.appendChild(document.createElement('br'));
spoilerSpan.appendChild(document.createElement('br'));
spoilerSpan.appendChild(document.createTextNode('Close button text:'));
spoilerSpan.appendChild(document.createElement('br'));
spoilerSpan.appendChild(spoilerClose);
if (!settings.spoilerValues[adventureID]) {
settings.spoilerValues[adventureID] = {
open: 'Show',
close: 'Hide'
}
}
spoilerOpen.value = settings.spoilerValues[adventureID].open;
spoilerClose.value = settings.spoilerValues[adventureID].close;
spoilerButton.addEventListener('click', evt => {
window.MSPFA.dialog('Default Spoiler Values', spoilerSpan, ['Save', 'Cancel'], (output, form) => {
if (output === 'Save') {
settings.spoilerValues[adventureID].open = spoilerOpen.value === '' ? 'Show' : spoilerOpen.value;
settings.spoilerValues[adventureID].close = spoilerClose.value === '' ? 'Hide' : spoilerClose.value;
if (settings.spoilerValues[adventureID].open === 'Show' && settings.spoilerValues[adventureID].close === 'Hide') {
delete settings.spoilerValues[adventureID];
}
saveData(settings);
}
});
});
document.querySelector('input[title="Spoiler"]').addEventListener('click', evt => {
document.querySelector('#dialog input[name="open"]').value = settings.spoilerValues[adventureID].open;
document.querySelector('#dialog input[name="close"]').value = settings.spoilerValues[adventureID].close;
});
// Buttonless spoilers
let flashButton = document.querySelector('input[title="Flash');
let newSpoilerButton = document.createElement('input');
newSpoilerButton.setAttribute('data-tag', 'Buttonless Spoiler');
newSpoilerButton.title = 'Buttonless Spoiler';
newSpoilerButton.type = 'button';
newSpoilerButton.style = 'background-image: url("https://pipe.miroware.io/5b52ba1d94357d5d623f74aa/HTML%20and%20CSS%20Things/icons.png"); background-position: -66px -88px;';
newSpoilerButton.addEventListener('click', evt => {
let bbe = document.querySelector('#bbtoolbar').parentNode.querySelector('textarea');
if (bbe) {
bbe.focus();
let start = bbe.selectionStart;
let end = bbe.selectionEnd;
bbe.value = bbe.value.slice(0, start) + '<div class="spoiler"><div>' + bbe.value.slice(start, end) + '</div></div>' + bbe.value.slice(end);
bbe.selectionStart = start + 26;
bbe.selectionEnd = end + 26;
}
});
flashButton.parentNode.insertBefore(newSpoilerButton, flashButton);
// Open preview in new tab with middle mouse
document.body.addEventListener('mouseup', evt => {
if (evt.toElement.value === "Preview" && evt.button === 1) {
evt.toElement.click();
return false;
}
});
}
else if (location.pathname === "/user/") {
let id = location.search.slice(3);
const statAdd = [];
// Button links
pageLoad(() => {
let msgButton = document.querySelector('#sendmsg');
if (msgButton) {
addLink(msgButton, '/my/messages/new/'); // note: doesn't input the desired user's id
addLink(document.querySelector('#favstories'), `/favs/${location.search}`);
return true;
}
});
// Add extra user stats
pageLoad(() => {
if (window.MSPFA) {
let stats = document.querySelector('#userinfo table');
let joinTr = stats.insertRow(1);
let joinTextTd = joinTr.insertCell(0);
joinTextTd.appendChild(document.createTextNode("Account created:"));
let joinDate = joinTr.insertCell(1);
let joinTime = document.createElement('b');
joinTime.textContent = "Loading...";
joinDate.appendChild(joinTime);
let advCountTr = stats.insertRow(2);
let advTextTd = advCountTr.insertCell(0);
advTextTd.appendChild(document.createTextNode("Adventures created:"));
let advCount = advCountTr.insertCell(1);
let advCountText = document.createElement('b');
advCountText.textContent = "Loading...";
advCount.appendChild(advCountText);
if (statAdd.indexOf('date') === -1) {
window.MSPFA.request(0, {
do: "user",
u: id
}, user => {
if (typeof user !== "undefined") {
statAdd.push('date');
let d = new Date(user.d).toString().split(' ').splice(1, 4).join(' ');
joinTime.textContent = d;
}
});
}
if (statAdd.indexOf('made') === -1) {
window.MSPFA.request(0, {
do: "editor",
u: id
}, s => {
if (typeof s !== "undefined") {
statAdd.push('made');
advCountText.textContent = s.length;
}
});
}
if (document.querySelector('#favstories').style.display !== 'none' && statAdd.indexOf('fav') === -1) {
statAdd.push('fav');
let favCountTr = stats.insertRow(3);
let favTextTd = favCountTr.insertCell(0);
favTextTd.appendChild(document.createTextNode("Adventures favorited:"));
let favCount = favCountTr.insertCell(1);
let favCountText = document.createElement('b');
favCountText.textContent = "Loading...";
window.MSPFA.request(0, {
do: "favs",
u: id
}, s => {
if (typeof s !== "undefined") {
favCountText.textContent = s.length;
}
});
favCount.appendChild(favCountText);
}
return true;
}
});
}
else if (location.pathname === "/favs/" && location.search) {
// Button links
pageLoad(() => {
let stories = document.querySelectorAll('#stories tr');
let favCount = 0;
if (stories.length > 0) {
stories.forEach(story => {
favCount++;
let id = story.querySelector('a').href.replace('https://mspfa.com/', '');
pageLoad(() => {
if (window.MSPFA.me.i) {
addLink(story.querySelector('.edit.major'), `/my/stories/info/${id}`);
return;
}
});
addLink(story.querySelector('.rss.major'), `/rss/${id}`);
});
// Fav count
let username = document.querySelector('#username');
username.parentNode.appendChild(document.createElement('br'));
username.parentNode.appendChild(document.createElement('br'));
username.parentNode.appendChild(document.createTextNode(`Favorited adventures: ${favCount}`));
return true;
}
});
}
else if (location.pathname === "/search/" && location.search) {
// Character and word statistics
let statTable = document.createElement('table');
let statTbody = document.createElement('tbody');
let statTr = statTbody.insertRow(0);
let charCount = statTr.insertCell(0);
let wordCount = statTr.insertCell(0);
let statParentTr = document.querySelector('#pages').parentNode.parentNode.insertRow(2);
let statParentTd = statParentTr.insertCell(0);
let statHeaderTr = statTbody.insertRow(0);
let statHeader = document.createElement('th');
statHeader.colSpan = '2';
statHeaderTr.appendChild(statHeader);
statHeader.textContent = 'Statistics may not be entirely accurate.';
statTable.style.width = "100%";
charCount.textContent = "Character count: loading...";
wordCount.textContent = "Word count: loading...";
statTable.appendChild(statTbody);
statParentTd.appendChild(statTable);
pageLoad(() => {
if (document.querySelector('#pages br')) {
let bbc = window.MSPFA.BBC.slice();
bbc.splice(0, 3);
window.MSPFA.request(0, {
do: "story",
s: location.search.replace('?s=', '')
}, story => {
if (typeof story !== "undefined") {
let pageContent = [];
story.p.forEach(p => {
pageContent.push(p.c);
pageContent.push(p.b);
});
let storyText = pageContent.join(' ')
.replace(/\n/g, ' ')
.replace(bbc[0][0], '$1')
.replace(bbc[1][0], '$1')
.replace(bbc[2][0], '$1')
.replace(bbc[3][0], '$1')
.replace(bbc[4][0], '$2')
.replace(bbc[5][0], '$3')
.replace(bbc[6][0], '$3')
.replace(bbc[7][0], '$3')
.replace(bbc[8][0], '$3')
.replace(bbc[9][0], '$3')
.replace(bbc[10][0], '$2')
.replace(bbc[11][0], '$1')
.replace(bbc[12][0], '$3')
.replace(bbc[13][0], '$3')
.replace(bbc[14][0], '')
.replace(bbc[16][0], '$1')
.replace(bbc[17][0], '$2 $4 $5')
.replace(bbc[18][0], '$2 $4 $5')
.replace(bbc[19][0], '')
.replace(bbc[20][0], '')
.replace(/<(.*?)>/g, '');
wordCount.textContent = `Word count: ${storyText.split(/ +/g).length}`;
charCount.textContent = `Character count: ${storyText.replace(/ +/g, '').length}`;
}
});
return true;
}
});
}
})();