MSPFA extras

Adds custom features to MSPFA.

当前为 2020-02-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MSPFA extras
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Adds custom features to MSPFA.
  6. // @author seymour schlong
  7. // @match https://mspfa.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const saveData = (data) => {
  15. localStorage.mspfaextra = JSON.stringify(data);
  16. console.log("Saved cookies under mspfaextra.");
  17. };
  18.  
  19. let settings = {};
  20.  
  21. if (localStorage.mspfaextra) {
  22. settings = JSON.parse(localStorage.mspfaextra);
  23. } else {
  24. settings.autospoiler = false;
  25. settings.style = 0;
  26. settings.styleURL = "";
  27. saveData(settings);
  28. }
  29.  
  30. let styleOptions = ["Standard", "Low Contrast", "Light", "Dark", "Felt", "Trickster", "Custom"];
  31.  
  32. let myLink = document.querySelector('nav a[href="/my/"]');
  33. let dropdownDiv = document.createElement('div');
  34. dropdownDiv.className = 'dropdown';
  35. let dropdownContent = document.createElement('div');
  36. dropdownContent.className = 'dropdown-content';
  37.  
  38. if (myLink) {
  39. myLink.parentNode.insertBefore(dropdownDiv, myLink);
  40. dropdownDiv.appendChild(myLink);
  41. dropdownDiv.appendChild(dropdownContent);
  42.  
  43. let dLinks = [];
  44. dLinks[0] = [ 'Messages', 'My Adventures', 'Settings' ];
  45. dLinks[1] = [ '/my/messages/', '/my/stories/', '/my/settings/' ];
  46. for (let i = 0; i < dLinks[0].length; i++) {
  47. let newLink = document.createElement('a');
  48. newLink.textContent = dLinks[0][i];
  49. newLink.href = dLinks[1][i];
  50. dropdownContent.appendChild(newLink);
  51. }
  52. }
  53.  
  54.  
  55. let dropdownStyle = document.createElement('style');
  56. dropdownStyle.id = 'dropdown-style';
  57. dropdownStyle.textContent = '#notification { z-index: 2;}.dropdown:hover .dropdown-content { display: block;}.dropdown { position: relative; display: inline-block;}.dropdown-content { display: none; position: absolute; text-align: left; background-color: #000000; 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;}';
  58.  
  59. let theme = document.createElement('link');
  60. theme.id = 'theme';
  61. theme.type = "text/css";
  62. theme.rel = "stylesheet";
  63.  
  64. if (!document.querySelector('#theme') || !/^\/css\/|^\/js\//.test(location.pathname)) {
  65. document.querySelector('head').appendChild(theme);
  66. }
  67. if (!document.querySelector('#dropdown-style')) {
  68. document.querySelector('head').appendChild(dropdownStyle);
  69. }
  70.  
  71. theme.href = settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL;
  72.  
  73.  
  74. if (location.pathname === "/") { // Auto-open spoilers
  75. if (settings.autospoiler) {
  76. window.MSPFA.slide.push((p) => {
  77. document.querySelectorAll('#slide .spoiler:not(.open) > div:first-child > input').forEach(sb => sb.click());
  78. });
  79. }
  80. if (location.search) {
  81. let dateInterval = setInterval(() => {
  82. if (document.querySelector('#infobox tr td:nth-child(2)')) {
  83. 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(' ')));
  84. clearInterval(dateInterval);
  85. }
  86. }, 500);
  87. }
  88. }
  89. else if (location.pathname === "/my/settings/") { // Custom settings
  90. let saveBtn = document.querySelector('#savesettings');
  91.  
  92. let table = document.querySelector("#editsettings tbody");
  93. let saveTr = table.querySelectorAll("tr");
  94. saveTr = saveTr[saveTr.length - 1];
  95.  
  96. let headerTr = document.createElement('tr');
  97. let header = document.createElement('th');
  98. header.textContent = "Extra Settings";
  99. headerTr.appendChild(header);
  100.  
  101. let settingsTr = document.createElement('tr');
  102. let localMsg = document.createElement('span');
  103. let settingsTd = document.createElement('td');
  104. 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!";
  105. let plusTable = document.createElement('table');
  106. let plusTbody = document.createElement('tbody');
  107. plusTable.appendChild(plusTbody);
  108. settingsTd.appendChild(localMsg);
  109. settingsTd.appendChild(document.createElement('br'));
  110. settingsTd.appendChild(document.createElement('br'));
  111. settingsTd.appendChild(plusTable);
  112. settingsTr.appendChild(settingsTd);
  113.  
  114. let spoilerTr = plusTbody.insertRow(0);
  115. let spoilerTextTd = spoilerTr.insertCell(0);
  116. let spoilerInputTd = spoilerTr.insertCell(1);
  117. let spoilerInput = document.createElement('input');
  118. spoilerInputTd.appendChild(spoilerInput);
  119.  
  120. let cssTr = plusTbody.insertRow(1);
  121. let cssTextTd = cssTr.insertCell(0);
  122. let cssSelectTd = cssTr.insertCell(1);
  123. let cssSelect = document.createElement('select');
  124. cssSelectTd.appendChild(cssSelect);
  125.  
  126. let customTr = plusTbody.insertRow(2);
  127. let customTextTd = customTr.insertCell(0);
  128. let customCssTd = customTr.insertCell(1);
  129. let customCssInput = document.createElement('input');
  130. customCssTd.appendChild(customCssInput);
  131.  
  132. plusTable.style = "text-align: center;";
  133. spoilerTextTd.textContent = "Automatically open spoilers:";
  134. spoilerInput.name = "p1";
  135. spoilerInput.type = "checkbox";
  136. spoilerInput.checked = settings.autospoiler;
  137.  
  138. cssTextTd.textContent = "Change style:";
  139.  
  140. customTextTd.textContent = "Custom CSS URL:";
  141. customCssInput.style.width = "99px";
  142. customCssInput.value = settings.styleURL;
  143.  
  144. styleOptions.forEach(o => cssSelect.appendChild(new Option(o, o)));
  145.  
  146. // Enable the save button
  147. spoilerInput.addEventListener("mouseup", () => {
  148. saveBtn.disabled = false;
  149. });
  150. cssSelect.addEventListener("mouseup", () => {
  151. saveBtn.disabled = false;
  152. });
  153. customCssInput.addEventListener("mouseup", () => {
  154. saveBtn.disabled = false;
  155. });
  156.  
  157. saveTr.parentNode.insertBefore(headerTr, saveTr);
  158. saveTr.parentNode.insertBefore(settingsTr, saveTr);
  159. cssSelect.selectedIndex = settings.style;
  160.  
  161. saveBtn.addEventListener('mouseup', () => {
  162. settings.autospoiler = spoilerInput.checked;
  163. settings.style = cssSelect.selectedIndex;
  164. settings.styleURL = customCssInput.value;
  165. theme.href = settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL;
  166. console.log(settings);
  167. saveData(settings);
  168. });
  169. }
  170. else if (location.pathname === "/my/messages/") { // New buttons
  171. let btnStyle = "margin: 10px 5px;";
  172.  
  173. // Select all read messages button.
  174. const selRead = document.createElement('input');
  175. selRead.style = btnStyle;
  176. selRead.value = "Select Read";
  177. selRead.id = "selectread";
  178. selRead.classList.add("major");
  179. selRead.type = "button";
  180.  
  181. // On click, select all messages with the style attribute indicating it as read.
  182. selRead.addEventListener('mouseup', () => {
  183. document.querySelectorAll('td[style="border-left: 8px solid rgb(221, 221, 221);"] > input').forEach((m) => m.click());
  184. });
  185.  
  186. // Select duplicate message (multiple update notifications).
  187. const selDupe = document.createElement('input');
  188. selDupe.style = btnStyle;
  189. selDupe.value = "Select Same";
  190. selDupe.id = "selectdupe";
  191. selDupe.classList.add("major");
  192. selDupe.type = "button";
  193.  
  194. selDupe.addEventListener('mouseup', evt => {
  195. let temp = document.querySelectorAll('#messages > tr');
  196. let msgs = [];
  197. for (let i = temp.length - 1; i >= 0; i--) {
  198. msgs.push(temp[i]);
  199. }
  200. let titles = [];
  201. msgs.forEach((msg) => {
  202. let title = msg.querySelector('a.major').textContent;
  203. if (/^New update: /.test(title)) { // Select only adventure updates
  204. if (titles.indexOf(title) === -1) {
  205. if (msg.querySelector('td').style.cssText !== "border-left: 8px solid rgb(221, 221, 221);") {
  206. titles.push(title);
  207. }
  208. } else {
  209. msg.querySelector('input').click();
  210. }
  211. }
  212. });
  213. });
  214.  
  215. // Add buttons to the page.
  216. let del = document.querySelector('#deletemsgs');
  217. del.parentNode.appendChild(document.createElement('br'));
  218. del.parentNode.appendChild(selRead);
  219. del.parentNode.appendChild(selDupe);
  220. }
  221. else if (location.pathname === "/my/stories/") {
  222. let guides = ["MSPFA Etiquette", "Fanventure Guide for Dummies", "CSS Guide", "HTML and CSS Things"];
  223. let ids = ["27631", "29299", "21099", "23711"];
  224. let authors = ["Radical Dude 42", "nzar", "MadCreativity", "seymour schlong"];
  225.  
  226. let parentTd = document.querySelector('.container > tbody > tr:last-child > td');
  227. let unofficial = parentTd.querySelector('span');
  228. unofficial.textContent = "Unofficial Guides";
  229. let guideTable = document.createElement('table');
  230. let guideTbody = document.createElement('tbody');
  231. guideTable.style.width = "100%";
  232. guideTable.style.textAlign = "center";
  233.  
  234. guideTable.appendChild(guideTbody);
  235. parentTd.appendChild(guideTable);
  236.  
  237. for (let i = 0; i < guides.length; i++) {
  238. let guideTr = guideTbody.insertRow(i);
  239. let guideTd = guideTr.insertCell(0);
  240. let guideLink = document.createElement('a');
  241. guideLink.href = '/?s='+ids[i];
  242. guideLink.textContent = guides[i];
  243. guideLink.className = "major";
  244. guideTd.appendChild(guideLink);
  245. guideTd.appendChild(document.createElement('br'));
  246. guideTd.appendChild(document.createTextNode('by '+authors[i]));
  247. guideTd.appendChild(document.createElement('br'));
  248. guideTd.appendChild(document.createElement('br'));
  249. }
  250. }
  251. else if (location.pathname === "/user/") {
  252. let userInterval = setInterval(() => {
  253. if (window.MSPFA) {
  254. window.MSPFA.request(0, {
  255. do: "user",
  256. u: location.search.slice(3)
  257. }, user => {
  258. if (typeof user !== "undefined") {
  259. let stats = document.querySelector('#userinfo table');
  260. let joinTr = stats.insertRow(1);
  261. let joinTextTd = joinTr.insertCell(0);
  262. joinTextTd.appendChild(document.createTextNode("Account created:"));
  263. let d = new Date(user.d).toString().split(' ').splice(1, 4).join(' ');
  264. let joinDate = joinTr.insertCell(1);
  265. let joinTime = document.createElement('b');
  266. joinTime.appendChild(document.createTextNode(d));
  267. joinDate.appendChild(joinTime);
  268. }
  269. }, status => {
  270. console.log(status);
  271. }, true);
  272. clearInterval(userInterval);
  273. }
  274. }, 500);
  275. }
  276. })();