MSPFA extras

Adds custom features to MSPFA.

当前为 2020-08-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MSPFA extras
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3.3
  5. // @description Adds custom features to MSPFA.
  6. // @author seymour schlong
  7. // @match https://mspfa.com/
  8. // @match https://mspfa.com/*/
  9. // @match https://mspfa.com/*/?*
  10. // @match https://mspfa.com/?s=*
  11. // @match https://mspfa.com/my/*
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. /**
  20. * https://github.com/GrantGryczan/MSPFA/projects/1?fullscreen=true
  21. * Github to-do completion list
  22. *
  23. * https://github.com/GrantGryczan/MSPFA/issues/26 - Dropdown menu
  24. * https://github.com/GrantGryczan/MSPFA/issues/18 - MSPFA themes
  25. * https://github.com/GrantGryczan/MSPFA/issues/32 - Adventure creation dates
  26. * https://github.com/GrantGryczan/MSPFA/issues/32 - User creation dates
  27. * https://github.com/GrantGryczan/MSPFA/issues/40 - Turn certain buttons into links
  28. * https://github.com/GrantGryczan/MSPFA/issues/41 - Word and character count
  29. *
  30. * Extension to-do... maybe...
  31. * https://github.com/GrantGryczan/MSPFA/issues/57 - Default spoiler values (might be very difficult unless i can detect when any spoiler button on the bbtoolbar is clicked. EACH ONE. maybe when a dialog appears instead?)
  32. * https://github.com/GrantGryczan/MSPFA/issues/62 - Buttonless spoilers (may also be extremely tough, as you have to add a button to each toolbar (or add an checkbox in the regular spoiler?))
  33. */
  34.  
  35. // A general function that allows for waiting until a certain element appears on the page.
  36. const pageLoad = (fn) => {
  37. let interval = setInterval(() => {
  38. if (fn()) clearInterval(interval);
  39. }, 500);
  40. };
  41.  
  42. // Saves the options data for the script.
  43. const saveData = (data) => {
  44. localStorage.mspfaextra = JSON.stringify(data);
  45. //console.log("Saved cookies under mspfaextra.");
  46. };
  47.  
  48. // Encases an element within a link
  49. const addLink = (elm, url) => {
  50. let link = document.createElement('a');
  51. link.href = url;
  52. elm.parentNode.insertBefore(link, elm);
  53. link.appendChild(elm);
  54. };
  55.  
  56. let settings = {};
  57.  
  58. if (localStorage.mspfaextra) {
  59. settings = JSON.parse(localStorage.mspfaextra);
  60. } else {
  61. settings.autospoiler = false;
  62. settings.style = 0;
  63. settings.styleURL = "";
  64. settings.night = false;
  65. settings.auto502 = true;
  66. saveData(settings);
  67. }
  68. // If any settings are undefined, re-set to their default state. (For older users when new things get stored)
  69. if (typeof settings.autospoiler === "undefined") {
  70. settings.autospoiler = false;
  71. }
  72. if (typeof settings.style === "undefined") {
  73. settings.style = 0;
  74. }
  75. if (typeof settings.styleURL === "undefined") {
  76. settings.styleURL = "";
  77. }
  78. if (typeof settings.night === "undefined") {
  79. settings.night = false;
  80. }
  81. if (typeof settings.auto502 === "undefined") {
  82. settings.auto502 = true;
  83. }
  84. if (typeof settings.textFix === "undefined") {
  85. settings.textFix = false;
  86. }
  87. if (typeof settings.pixelFix === "undefined") {
  88. settings.pixelFix = false;
  89. }
  90.  
  91. //console.log(settings);
  92.  
  93. let styleOptions = ["Standard", "Low Contrast", "Light", "Dark", "Felt", "Trickster", "Custom"];
  94. let styleUrls = ['', '/css/theme1.css', '/css/theme2.css', '/css/?s=36237', '/css/theme4.css', '/css/theme5.css'];
  95.  
  96. let myLink = document.querySelector('nav a[href="/my/"]');
  97. let dropDiv = document.createElement('div');
  98. dropDiv.className = 'dropdown';
  99. Object.assign(dropDiv.style, {
  100. position: 'relative',
  101. display: 'inline-block',
  102. backgroundColor: 'inherit'
  103. });
  104.  
  105. let dropContent = document.createElement('div');
  106. dropContent.className = 'dropdown-content';
  107. Object.assign(dropContent.style, {
  108. display: 'none',
  109. backgroundColor: 'inherit',
  110. position: 'absolute',
  111. textAlign: 'left',
  112. minWidth: '100px',
  113. marginLeft: '-5px',
  114. padding: '2px',
  115. zIndex: '1',
  116. borderRadius: '0 0 5px 5px'
  117. });
  118.  
  119. dropDiv.addEventListener('mouseenter', evt => {
  120. dropContent.style.display = 'block';
  121. dropContent.style.color = getComputedStyle(myLink).color;
  122. });
  123. dropDiv.addEventListener('mouseleave', evt => {
  124. dropContent.style.display = 'none';
  125. });
  126.  
  127. if (myLink) {
  128. myLink.parentNode.insertBefore(dropDiv, myLink);
  129. dropDiv.appendChild(myLink);
  130. dropDiv.appendChild(dropContent);
  131.  
  132. let dLinks = [];
  133. dLinks[0] = [ 'Messages', 'My Adventures', 'Settings' ];
  134. dLinks[1] = [ '/my/messages/', '/my/stories/', '/my/settings/' ];
  135.  
  136. for (let i = 0; i < dLinks[0].length; i++) {
  137. let newLink = document.createElement('a');
  138. newLink.textContent = dLinks[0][i];
  139. newLink.href = dLinks[1][i];
  140. dropContent.appendChild(newLink);
  141. }
  142. }
  143.  
  144. window.addEventListener("load", () => {
  145. // Reload the page if 502 CloudFlare error page appears
  146. if (settings.auto502 && document.querySelector('.cf-error-overview')) {
  147. window.location.reload();
  148. }
  149.  
  150. if (document.body.textContent === "Your client is sending data to MSPFA too quickly. Wait a moment before continuing.") {
  151. setTimeout(() => {
  152. window.location.reload();
  153. }, 5000);
  154. }
  155.  
  156. // Append "My Profile" to the dropdown list if you're signed in
  157. pageLoad(() => {
  158. if (window.MSPFA) {
  159. if (window.MSPFA.me.n) {
  160. let newLink = document.createElement('a');
  161. newLink.textContent = "My Profile";
  162. newLink.href = `/user/?u=${window.MSPFA.me.i}`;
  163. dropContent.appendChild(newLink);
  164. return true;
  165. }
  166. return true;
  167. }
  168. });
  169. });
  170.  
  171. let pixelFixText = 'img, .mspfalogo, .major, .arrow, #flashytitle, .heart, .fav, .notify, .edit, .rss, input, #loading { image-rendering: pixelated !important; }'
  172. let dropStyleText = `#notification { z-index: 2; } .dropdown-content a { color: inherit; padding: 2px; text-decoration: underline; display: block;}`;
  173. let dropStyle = document.createElement('style');
  174. dropStyle.id = 'dropdown-style';
  175. dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '');
  176. //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;}';
  177.  
  178. let theme = document.createElement('link');
  179. Object.assign(theme, { id: 'theme', type: 'text/css', rel: 'stylesheet' });
  180.  
  181. if (!document.querySelector('#theme') && !/^\/css\/|^\/js\//.test(location.pathname)) {
  182. document.querySelector('head').appendChild(theme);
  183. }
  184. if (!document.querySelector('#dropdown-style')) {
  185. document.querySelector('head').appendChild(dropStyle);
  186. }
  187.  
  188. const updateTheme = (src) => {
  189. theme.href = src;
  190. }
  191.  
  192. if (settings.night) {
  193. updateTheme('/css/?s=36237');
  194. } else {
  195. updateTheme(settings.style == styleOptions.length - 1 ? settings.styleURL : styleUrls[settings.style]);
  196. }
  197.  
  198. pageLoad(() => {
  199. if (window.MSPFA) {
  200. if (window.MSPFA.story && window.MSPFA.story.y && window.MSPFA.story.y.length > 0) {
  201. updateTheme('');
  202. }
  203. return true;
  204. }
  205. });
  206.  
  207. pageLoad(() => {
  208. if (document.querySelector('footer .mspfalogo')) {
  209. document.querySelector('footer .mspfalogo').addEventListener('dblclick', evt => {
  210. if (evt.button === 0) {
  211. settings.night = !settings.night;
  212. saveData(settings);
  213.  
  214. if (settings.night) {
  215. updateTheme('/css/?s=36237');
  216. } else {
  217. updateTheme(settings.style == styleOptions.length - 1 ? settings.styleURL : styleUrls[settings.style]);
  218. }
  219.  
  220. dropStyle.textContent = dropStyleText + '';
  221. dropStyle.textContent = dropStyleText + '*{transition:1s}';
  222. setTimeout(() => {
  223. dropStyle.textContent = dropStyleText;
  224. }, 1000);
  225.  
  226. console.log(`Night mode turned ${settings.night ? 'on' : 'off'}.`);
  227. }
  228. });
  229. return true;
  230. }
  231. });
  232.  
  233. if (location.pathname === "/" || location.pathname === "/preview/") {
  234. if (settings.autospoiler) {
  235. window.MSPFA.slide.push((p) => {
  236. document.querySelectorAll('#slide .spoiler:not(.open) > div:first-child > input').forEach(sb => sb.click());
  237. });
  238. }
  239. if (location.search) {
  240. pageLoad(() => {
  241. if (document.querySelector('#infobox tr td:nth-child(2)')) {
  242. 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(' ')));
  243. return true;
  244. }
  245. });
  246. if (settings.textFix) {
  247. pageLoad(() => {
  248. if (window.MSPFA.story && window.MSPFA.story.p) {
  249. // russian/bulgarian is not possible =(
  250. let currentPage = parseInt(/^\?s(?:.*?)&p=([\d]*)$/.exec(location.search)[1]);
  251. let library = [
  252. ["&acirc;��", "'"],
  253. ["&Atilde;�", "Ñ"],
  254. ["&Atilde;&plusmn;", "ñ"],
  255. ["&Atilde;&sup3;", "ó"],
  256. ["&Atilde;&iexcl;", "á"],
  257. ["&Atilde;&shy;", "í"],
  258. ["&Atilde;&ordm;", "ú"],
  259. ["&Atilde;&copy;", "é"],
  260. ["&Acirc;&iexcl;", "¡"],
  261. ["&Acirc;&iquest;", "¿"],
  262. ["N&Acirc;&ordm;", "#"]
  263. ];
  264. // https://mspfa.com/?s=5280&p=51 -- unknown error
  265.  
  266. const replaceTerms = (p) => {
  267. library.forEach(term => {
  268. if (window.MSPFA.story.p[p]) {
  269. window.MSPFA.story.p[p].c = window.MSPFA.story.p[p].c.replace(new RegExp(term[0], 'g'), term[1]);
  270. window.MSPFA.story.p[p].b = window.MSPFA.story.p[p].b.replace(new RegExp(term[0], 'g'), term[1]);
  271. }
  272. });
  273. };
  274.  
  275. replaceTerms(currentPage-1);
  276.  
  277. window.MSPFA.slide.push(p => {
  278. replaceTerms(p);
  279. replaceTerms(p-2);
  280. });
  281. window.MSPFA.page(currentPage);
  282. return true;
  283. }
  284. });
  285. }
  286. pageLoad(() => {
  287. let infoButton = document.querySelector('.edit.major');
  288. if (infoButton) {
  289. pageLoad(() => {
  290. if (window.MSPFA.me.i) {
  291. addLink(infoButton, `/my/stories/info/${location.search.split('&p=')[0]}`);
  292. return;
  293. }
  294. });
  295. addLink(document.querySelector('.rss.major'), `/rss/${location.search.split('&p=')[0]}`);
  296. return true;
  297. }
  298. });
  299. /*
  300. pageLoad(() => {
  301. let infoButton = document.querySelector('.edit.major');
  302. if (infoButton) {
  303. let editPages = document.createElement('button');
  304. Object.assign(editPages, { className: 'editpages major edit', title: 'Edit pages'});
  305. //infoButton.parentNode.insertBefore(editPages, infoButton);
  306. return true;
  307. }
  308. });/**/
  309. }
  310. }
  311. else if (location.pathname === "/my/settings/") { // Custom settings
  312. let saveBtn = document.querySelector('#savesettings');
  313.  
  314. let table = document.querySelector("#editsettings tbody");
  315. let saveTr = table.querySelectorAll("tr");
  316. saveTr = saveTr[saveTr.length - 1];
  317.  
  318. let headerTr = document.createElement('tr');
  319. let header = document.createElement('th');
  320. header.textContent = "Extra Settings";
  321. headerTr.appendChild(header);
  322.  
  323. let moreTr = document.createElement('tr');
  324. let more = document.createElement('td');
  325. 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.";
  326. moreTr.appendChild(more);
  327.  
  328. let settingsTr = document.createElement('tr');
  329. let localMsg = document.createElement('span');
  330. let settingsTd = document.createElement('td');
  331. 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!";
  332. let plusTable = document.createElement('table');
  333. let plusTbody = document.createElement('tbody');
  334. plusTable.appendChild(plusTbody);
  335. settingsTd.appendChild(localMsg);
  336. settingsTd.appendChild(document.createElement('br'));
  337. settingsTd.appendChild(document.createElement('br'));
  338. settingsTd.appendChild(plusTable);
  339. settingsTr.appendChild(settingsTd);
  340.  
  341. let spoilerTr = plusTbody.insertRow(plusTbody.childNodes.length);
  342. let spoilerTextTd = spoilerTr.insertCell(0);
  343. let spoilerInputTd = spoilerTr.insertCell(1);
  344. let spoilerInput = document.createElement('input');
  345. spoilerInputTd.appendChild(spoilerInput);
  346.  
  347. let errorTr = plusTbody.insertRow(plusTbody.childNodes.length);
  348. let errorTextTd = errorTr.insertCell(0);
  349. let errorInputTd = errorTr.insertCell(1);
  350. let errorInput = document.createElement('input');
  351. errorInputTd.appendChild(errorInput);
  352.  
  353. let pixelFixTr = plusTbody.insertRow(plusTbody.childNodes.length);
  354. let pixelFixTextTd = pixelFixTr.insertCell(0);
  355. let pixelFixInputTd = pixelFixTr.insertCell(1);
  356. let pixelFixInput = document.createElement('input');
  357. pixelFixInputTd.appendChild(pixelFixInput);
  358.  
  359. let textFixTr = plusTbody.insertRow(plusTbody.childNodes.length);
  360. let textFixTextTd = textFixTr.insertCell(0);
  361. let textFixInputTd = textFixTr.insertCell(1);
  362. let textFixInput = document.createElement('input');
  363. textFixInputTd.appendChild(textFixInput);
  364.  
  365. let cssTr = plusTbody.insertRow(plusTbody.childNodes.length);
  366. let cssTextTd = cssTr.insertCell(0);
  367. let cssSelectTd = cssTr.insertCell(1);
  368. let cssSelect = document.createElement('select');
  369. cssSelectTd.appendChild(cssSelect);
  370.  
  371. let customTr = plusTbody.insertRow(plusTbody.childNodes.length);
  372. let customTextTd = customTr.insertCell(0);
  373. let customCssTd = customTr.insertCell(1);
  374. let customCssInput = document.createElement('input');
  375. customCssTd.appendChild(customCssInput);
  376.  
  377. plusTable.style = "text-align: center;";
  378. spoilerTextTd.textContent = "Automatically open spoilers:";
  379. spoilerInput.name = "p1";
  380. spoilerInput.type = "checkbox";
  381. spoilerInput.checked = settings.autospoiler;
  382.  
  383. errorTextTd.textContent = "Automatically reload Cloudflare 502 error pages:";
  384. errorInput.name = "p2";
  385. errorInput.type = "checkbox";
  386. errorInput.checked = settings.auto502;
  387.  
  388. pixelFixTextTd.textContent = "Change pixel scaling to nearest neighbour:";
  389. pixelFixInput.name = "p3";
  390. pixelFixInput.type = "checkbox";
  391. pixelFixInput.checked = settings.pixelFix;
  392.  
  393. textFixTextTd.textContent = "Attempt to fix text errors (experimental)*:";
  394. textFixInput.name = "p4";
  395. textFixInput.type = "checkbox";
  396. textFixInput.checked = settings.textFix;
  397.  
  398. cssTextTd.textContent = "Change style:";
  399.  
  400. customTextTd.textContent = "Custom CSS URL:";
  401. customCssInput.style.width = "99px";
  402. customCssInput.value = settings.styleURL;
  403.  
  404. styleOptions.forEach(o => cssSelect.appendChild(new Option(o, o)));
  405.  
  406. // Enable the save button
  407. spoilerInput.addEventListener("change", () => {
  408. saveBtn.disabled = false;
  409. });
  410. errorInput.addEventListener("change", () => {
  411. saveBtn.disabled = false;
  412. });
  413. pixelFixInput.addEventListener("change", () => {
  414. saveBtn.disabled = false;
  415. });
  416. textFixInput.addEventListener("change", () => {
  417. saveBtn.disabled = false;
  418. });
  419. cssSelect.addEventListener("change", () => {
  420. saveBtn.disabled = false;
  421. });
  422. customCssInput.addEventListener("keydown", () => {
  423. saveBtn.disabled = false;
  424. });
  425.  
  426. saveTr.parentNode.insertBefore(headerTr, saveTr);
  427. saveTr.parentNode.insertBefore(settingsTr, saveTr);
  428. saveTr.parentNode.insertBefore(moreTr, saveTr);
  429. cssSelect.selectedIndex = settings.style;
  430.  
  431. saveBtn.addEventListener('mouseup', () => {
  432. settings.autospoiler = spoilerInput.checked;
  433. settings.style = cssSelect.selectedIndex;
  434. settings.styleURL = customCssInput.value;
  435. settings.auto502 = errorInput.checked;
  436. settings.textFix = textFixInput.checked;
  437. settings.pixelFix = pixelFixInput.checked;
  438. settings.night = false;
  439. console.log(settings);
  440. saveData(settings);
  441.  
  442. updateTheme(settings.style == styleOptions.length - 1 ? settings.styleURL : styleUrls[settings.style]);
  443.  
  444. dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '');
  445.  
  446. dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '') + ' *{transition:1s}';
  447. setTimeout(() => {
  448. dropStyle.textContent = dropStyleText + (settings.pixelFix ? ' '+pixelFixText : '');
  449. }, 1000);
  450. });
  451. }
  452. else if (location.pathname === "/my/messages/") { // New buttons
  453. let btnStyle = "margin: 10px 5px;";
  454.  
  455. // Select all read messages button.
  456. const selRead = document.createElement('input');
  457. selRead.style = btnStyle;
  458. selRead.value = "Select Read";
  459. selRead.id = "selectread";
  460. selRead.classList.add("major");
  461. selRead.type = "button";
  462.  
  463. // On click, select all messages with the style attribute indicating it as read.
  464. selRead.addEventListener('mouseup', () => {
  465. document.querySelectorAll('td[style="border-left: 8px solid rgb(221, 221, 221);"] > input').forEach((m) => m.click());
  466. });
  467.  
  468. // Select duplicate message (multiple update notifications).
  469. const selDupe = document.createElement('input');
  470. selDupe.style = btnStyle;
  471. selDupe.value = "Select Same";
  472. selDupe.id = "selectdupe";
  473. selDupe.classList.add("major");
  474. selDupe.type = "button";
  475.  
  476. selDupe.addEventListener('mouseup', evt => {
  477. let temp = document.querySelectorAll('#messages > tr');
  478. let msgs = [];
  479. for (let i = temp.length - 1; i >= 0; i--) {
  480. msgs.push(temp[i]);
  481. }
  482. let titles = [];
  483. msgs.forEach((msg) => {
  484. let title = msg.querySelector('a.major').textContent;
  485. if (/^New update: /.test(title)) { // Select only adventure updates
  486. if (titles.indexOf(title) === -1) {
  487. if (msg.querySelector('td').style.cssText !== "border-left: 8px solid rgb(221, 221, 221);") {
  488. titles.push(title);
  489. }
  490. } else {
  491. msg.querySelector('input').click();
  492. }
  493. }
  494. });
  495. });
  496.  
  497. // Add buttons to the page.
  498. let del = document.querySelector('#deletemsgs');
  499. del.parentNode.appendChild(document.createElement('br'));
  500. del.parentNode.appendChild(selRead);
  501. del.parentNode.appendChild(selDupe);
  502. }
  503. else if (location.pathname === "/my/stories/") {
  504. pageLoad(() => {
  505. let adventures = document.querySelectorAll('#stories tr');
  506. if (adventures.length > 0) {
  507. adventures.forEach(story => {
  508. let buttons = story.querySelectorAll('input.major');
  509. let id = story.querySelector('a').href.replace('https://mspfa.com/', '').replace('&p=1', '');
  510. if (id) {
  511. addLink(buttons[0], `/my/stories/info/${id}`);
  512. addLink(buttons[1], `/my/stories/pages/${id}`);
  513. }
  514. });
  515. return true;
  516. }
  517. });
  518.  
  519. let guides = ["A Guide To Uploading Your Comic To MSPFA", "MSPFA Etiquette", "Fanventure Guide for Dummies", "CSS Guide", "HTML and CSS Things", ];
  520. let links = ["https://docs.google.com/document/d/17QI6Cv_BMbr8l06RrRzysoRjASJ-ruWioEtVZfzvBzU/edit?usp=sharing", "/?s=27631", "/?s=29299", "/?s=21099", "/?s=23711"];
  521. let authors = ["Farfrom Tile", "Radical Dude 42", "nzar", "MadCreativity", "seymour schlong"];
  522.  
  523. let parentTd = document.querySelector('.container > tbody > tr:last-child > td');
  524. let unofficial = parentTd.querySelector('span');
  525. unofficial.textContent = "Unofficial Guides";
  526. let guideTable = document.createElement('table');
  527. let guideTbody = document.createElement('tbody');
  528. guideTable.style.width = "100%";
  529. guideTable.style.textAlign = "center";
  530.  
  531. guideTable.appendChild(guideTbody);
  532. parentTd.appendChild(guideTable);
  533.  
  534. for (let i = 0; i < guides.length; i++) {
  535. let guideTr = guideTbody.insertRow(i);
  536. let guideTd = guideTr.insertCell(0);
  537. let guideLink = document.createElement('a');
  538. guideLink.href = links[i];
  539. guideLink.textContent = guides[i];
  540. guideLink.className = "major";
  541. guideTd.appendChild(guideLink);
  542. guideTd.appendChild(document.createElement('br'));
  543. guideTd.appendChild(document.createTextNode('by '+authors[i]));
  544. guideTd.appendChild(document.createElement('br'));
  545. guideTd.appendChild(document.createElement('br'));
  546. }
  547. }
  548. else if (location.pathname === "/my/stories/info/" && location.search) {
  549. addLink(document.querySelector('#userfavs'), `/readers/${location.search}`);
  550. addLink(document.querySelector('#editpages'), `/my/stories/pages/${location.search}`);
  551. }
  552. else if (location.pathname === "/my/stories/pages/" && location.search) {
  553. addLink(document.querySelector('#editinfo'), `/my/stories/info/${location.search}`);
  554. }
  555. else if (location.pathname === "/user/") {
  556. let id = location.search.slice(3);
  557. const statadd = [];
  558. pageLoad(() => {
  559. let msgButton = document.querySelector('#sendmsg');
  560. if (msgButton) {
  561. addLink(msgButton, '/my/messages/new/'); // note: doesn't input the desired user's id
  562. addLink(document.querySelector('#favstories'), `/favs/${location.search}`);
  563. return true;
  564. }
  565. });
  566.  
  567. pageLoad(() => {
  568. if (window.MSPFA) {
  569. let stats = document.querySelector('#userinfo table');
  570.  
  571. if (statadd.indexOf('date') === -1) {
  572. window.MSPFA.request(0, {
  573. do: "user",
  574. u: id
  575. }, user => {
  576. if (typeof user !== "undefined") {
  577. statadd.push('date');
  578. let joinTr = stats.insertRow(1);
  579. let joinTextTd = joinTr.insertCell(0);
  580. joinTextTd.appendChild(document.createTextNode("Account created:"));
  581. let d = new Date(user.d).toString().split(' ').splice(1, 4).join(' ');
  582. let joinDate = joinTr.insertCell(1);
  583. let joinTime = document.createElement('b');
  584. joinTime.appendChild(document.createTextNode(d));
  585. joinDate.appendChild(joinTime);
  586. }
  587. });
  588. }
  589.  
  590. if (statadd.indexOf('made') === -1) {
  591. let advCountTr = stats.insertRow(2);
  592. let advTextTd = advCountTr.insertCell(0);
  593. advTextTd.appendChild(document.createTextNode("Adventures created:"));
  594. let advCount = advCountTr.insertCell(1);
  595. let advCountText = document.createElement('b');
  596. window.MSPFA.request(0, {
  597. do: "editor",
  598. u: id
  599. }, s => {
  600. advCountText.appendChild(document.createTextNode(s.length));
  601. });
  602. advCount.appendChild(advCountText);
  603. }
  604.  
  605. if (document.querySelector('#favstories').style.display !== 'none' && statadd.indexOf('fav') === -1) {
  606. statadd.push('fav');
  607. let favCountTr = stats.insertRow(3);
  608. let favTextTd = favCountTr.insertCell(0);
  609. favTextTd.appendChild(document.createTextNode("Adventures favorited:"));
  610. let favCount = favCountTr.insertCell(1);
  611. let favCountText = document.createElement('b');
  612. window.MSPFA.request(0, {
  613. do: "favs",
  614. u: id
  615. }, s => {
  616. if (typeof s !== "undefined") {
  617. favCountText.appendChild(document.createTextNode(s.length));
  618. }
  619. });
  620. favCount.appendChild(favCountText);
  621. }
  622.  
  623. return true;
  624. }
  625. });
  626. }
  627. else if (location.pathname === "/favs/" && location.search) {
  628. // todo: add favourite counter
  629. pageLoad(() => {
  630. let stories = document.querySelectorAll('#stories tr');
  631.  
  632. if (stories.length > 0) {
  633. stories.forEach(story => {
  634. let id = story.querySelector('a').href.replace('https://mspfa.com/', '');
  635. pageLoad(() => {
  636. if (window.MSPFA.me.i) {
  637. addLink(story.querySelector('.edit.major'), `/my/stories/info/${id}`);
  638. return;
  639. }
  640. });
  641. addLink(story.querySelector('.rss.major'), `/rss/${id}`);
  642. });
  643. return true;
  644. }
  645. });
  646. }
  647. else if (location.pathname === "/search/" && location.search) {
  648. let pages = document.querySelector('#pages');
  649. let statTable = document.createElement('table');
  650. let statTbody = document.createElement('tbody');
  651. let statTr = statTbody.insertRow(0);
  652. let charCount = statTr.insertCell(0);
  653. let wordCount = statTr.insertCell(0);
  654. let statParentTr = pages.parentNode.parentNode.insertRow(2);
  655. let statParentTd = statParentTr.insertCell(0);
  656.  
  657. let statHeaderTr = statTbody.insertRow(0);
  658. let statHeader = document.createElement('th');
  659. statHeader.colSpan = '2';
  660.  
  661. statHeaderTr.appendChild(statHeader);
  662. statHeader.textContent = 'Statistics may not be entirely accurate.';
  663.  
  664. statTable.style.width = "100%";
  665.  
  666. charCount.textContent = "Character count: loading...";
  667. wordCount.textContent = "Word count: loading...";
  668.  
  669. statTable.appendChild(statTbody);
  670. statParentTd.appendChild(statTable);
  671.  
  672. pageLoad(() => {
  673. if (document.querySelector('#pages br')) {
  674. let bbc = window.MSPFA.BBC.slice();
  675. bbc.splice(0, 3);
  676.  
  677. window.MSPFA.request(0, {
  678. do: "story",
  679. s: location.search.replace('?s=', '')
  680. }, story => {
  681. if (typeof story !== "undefined") {
  682. let pageContent = [];
  683. story.p.forEach(p => {
  684. pageContent.push(p.c);
  685. pageContent.push(p.b);
  686. });
  687.  
  688. let storyText = pageContent.join(' ')
  689. .replace(/\n/g, ' ')
  690. .replace(bbc[0][0], '$1')
  691. .replace(bbc[1][0], '$1')
  692. .replace(bbc[2][0], '$1')
  693. .replace(bbc[3][0], '$1')
  694. .replace(bbc[4][0], '$2')
  695. .replace(bbc[5][0], '$3')
  696. .replace(bbc[6][0], '$3')
  697. .replace(bbc[7][0], '$3')
  698. .replace(bbc[8][0], '$3')
  699. .replace(bbc[9][0], '$3')
  700. .replace(bbc[10][0], '$2')
  701. .replace(bbc[11][0], '$1')
  702. .replace(bbc[12][0], '$3')
  703. .replace(bbc[13][0], '$3')
  704. .replace(bbc[14][0], '')
  705. .replace(bbc[16][0], '$1')
  706. .replace(bbc[17][0], '$2 $4 $5')
  707. .replace(bbc[18][0], '$2 $4 $5')
  708. .replace(bbc[19][0], '')
  709. .replace(bbc[20][0], '')
  710. .replace(/<(.*?)>/g, '');
  711.  
  712. wordCount.textContent = `Word count: ${storyText.split(/ +/g).length}`;
  713. charCount.textContent = `Character count: ${storyText.replace(/ +/g, '').length}`;
  714. }
  715. });
  716. return true;
  717. }
  718. });
  719. }
  720. })();