MSPFA extras

Adds custom features to MSPFA.

当前为 2020-06-13 提交的版本,查看 最新版本

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