MSPFA extras

Adds custom features to MSPFA.

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

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