MSPFA extras

Adds custom features to MSPFA.

当前为 2020-07-21 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MSPFA extras
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.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
  32. * https://github.com/GrantGryczan/MSPFA/issues/62 - Buttonless spoilers
  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.  
  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.  
  88. //console.log(settings);
  89.  
  90. let styleOptions = ["Standard", "Low Contrast", "Light", "Dark", "Felt", "Trickster", "Custom"];
  91.  
  92. let myLink = document.querySelector('nav a[href="/my/"]');
  93. let dropDiv = document.createElement('div');
  94. dropDiv.className = 'dropdown';
  95. Object.assign(dropDiv.style, {
  96. position: 'relative',
  97. display: 'inline-block',
  98. backgroundColor: 'inherit'
  99. });
  100.  
  101. let dropContent = document.createElement('div');
  102. dropContent.className = 'dropdown-content';
  103. Object.assign(dropContent.style, {
  104. display: 'none',
  105. backgroundColor: 'inherit',
  106. position: 'absolute',
  107. textAlign: 'left',
  108. minWidth: '100px',
  109. marginLeft: '-5px',
  110. padding: '2px',
  111. zIndex: '1',
  112. borderRadius: '0 0 5px 5px'
  113. });
  114.  
  115. dropDiv.addEventListener('mouseenter', evt => {
  116. dropContent.style.display = 'block';
  117. dropContent.style.color = getComputedStyle(myLink).color;
  118. });
  119. dropDiv.addEventListener('mouseleave', evt => {
  120. dropContent.style.display = 'none';
  121. });
  122.  
  123. if (myLink) {
  124. myLink.parentNode.insertBefore(dropDiv, myLink);
  125. dropDiv.appendChild(myLink);
  126. dropDiv.appendChild(dropContent);
  127.  
  128. let dLinks = [];
  129. dLinks[0] = [ 'Messages', 'My Adventures', 'Settings' ];
  130. dLinks[1] = [ '/my/messages/', '/my/stories/', '/my/settings/' ];
  131.  
  132. for (let i = 0; i < dLinks[0].length; i++) {
  133. let newLink = document.createElement('a');
  134. newLink.textContent = dLinks[0][i];
  135. newLink.href = dLinks[1][i];
  136. dropContent.appendChild(newLink);
  137. }
  138. }
  139.  
  140. window.addEventListener("load", () => {
  141. // Reload the page if 502 CloudFlare error page appears
  142. if (settings.auto502 && document.querySelector('.cf-error-overview')) {
  143. window.location.reload();
  144. }
  145.  
  146. // Append "My Profile" to the dropdown list if you're signed in
  147. pageLoad(() => {
  148. if (window.MSPFA) {
  149. if (window.MSPFA.me.n) {
  150. let newLink = document.createElement('a');
  151. newLink.textContent = "My Profile";
  152. newLink.href = `/user/?u=${window.MSPFA.me.i}`;
  153. dropContent.appendChild(newLink);
  154. return true;
  155. }
  156. return true;
  157. }
  158. });
  159. });
  160.  
  161. let dropStyleText = '#notification { z-index: 2; } .dropdown-content a { color: inherit; padding: 2px; text-decoration: underline; display: block;}';
  162. let dropStyle = document.createElement('style');
  163. dropStyle.id = 'dropdown-style';
  164. dropStyle.textContent = dropStyleText;
  165. //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;}';
  166.  
  167. let theme = document.createElement('link');
  168. Object.assign(theme, { id: 'theme', type: 'text/css', rel: 'stylesheet' });
  169.  
  170. if (!document.querySelector('#theme') && !/^\/css\/|^\/js\//.test(location.pathname)) {
  171. document.querySelector('head').appendChild(theme);
  172. }
  173. if (!document.querySelector('#dropdown-style')) {
  174. document.querySelector('head').appendChild(dropStyle);
  175. }
  176.  
  177. const updateTheme = (src) => {
  178. theme.href = src;
  179. }
  180.  
  181. if (settings.night || settings.style === 3) {
  182. updateTheme('/css/?s=36237');
  183. } else {
  184. updateTheme(settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL);
  185. }
  186.  
  187. pageLoad(() => {
  188. if (window.MSPFA) {
  189. if (window.MSPFA.story && window.MSPFA.story.y && window.MSPFA.story.y.length > 0) {
  190. updateTheme("");
  191. }
  192. return true;
  193. }
  194. });
  195.  
  196. pageLoad(() => {
  197. if (document.querySelector('footer .mspfalogo')) {
  198. document.querySelector('footer .mspfalogo').addEventListener('dblclick', evt => {
  199. if (evt.button === 0) {
  200. settings.night = !settings.night;
  201. saveData(settings);
  202.  
  203. if (settings.night || settings.style === 3) {
  204. updateTheme('/css/?s=36237');
  205. } else {
  206. updateTheme(settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL);
  207. }
  208.  
  209. dropStyle.textContent = dropStyleText + '*{transition:1s}';
  210. setTimeout(() => {
  211. dropStyle.textContent = dropStyleText;
  212. }, 1000);
  213.  
  214. console.log(`Night mode turned ${settings.night ? 'on' : 'off'}.`);
  215. }
  216. });
  217. return true;
  218. }
  219. });
  220.  
  221. if (location.pathname === "/" || location.pathname === "/preview/") {
  222. if (settings.autospoiler) {
  223. window.MSPFA.slide.push((p) => {
  224. document.querySelectorAll('#slide .spoiler:not(.open) > div:first-child > input').forEach(sb => sb.click());
  225. });
  226. }
  227. if (location.search) {
  228. pageLoad(() => {
  229. if (document.querySelector('#infobox tr td:nth-child(2)')) {
  230. 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(' ')));
  231. return true;
  232. }
  233. });
  234. if (settings.textFix) {
  235. pageLoad(() => {
  236. if (window.MSPFA.story && window.MSPFA.story.p) {
  237. // russian/bulgarian is not possible =(
  238. let currentPage = parseInt(/^\?s(?:.*?)&p=([\d]*)$/.exec(location.search)[1]);
  239. let library = [
  240. ["&acirc;��", "'"],
  241. ["&Atilde;�", "Ñ"],
  242. ["&Atilde;&plusmn;", "ñ"],
  243. ["&Atilde;&sup3;", "ó"],
  244. ["&Atilde;&iexcl;", "á"],
  245. ["&Atilde;&shy;", "í"],
  246. ["&Atilde;&ordm;", "ú"],
  247. ["&Atilde;&copy;", "é"],
  248. ["&Acirc;&iexcl;", "¡"],
  249. ["&Acirc;&iquest;", "¿"],
  250. ["N&Acirc;&ordm;", "#"]
  251. ];
  252. // https://mspfa.com/?s=5280&p=51 -- unknown error
  253.  
  254. const replaceTerms = (p) => {
  255. library.forEach(term => {
  256. if (window.MSPFA.story.p[p]) {
  257. window.MSPFA.story.p[p].c = window.MSPFA.story.p[p].c.replace(new RegExp(term[0], 'g'), term[1]);
  258. window.MSPFA.story.p[p].b = window.MSPFA.story.p[p].b.replace(new RegExp(term[0], 'g'), term[1]);
  259. }
  260. });
  261. };
  262.  
  263. replaceTerms(currentPage-1);
  264.  
  265. window.MSPFA.slide.push(p => {
  266. replaceTerms(p);
  267. replaceTerms(p-2);
  268. });
  269. window.MSPFA.page(currentPage);
  270. return true;
  271. }
  272. });
  273. }
  274. pageLoad(() => {
  275. let infoButton = document.querySelector('.edit.major');
  276. if (infoButton) {
  277. pageLoad(() => {
  278. if (window.MSPFA.me.i) {
  279. addLink(infoButton, `/my/stories/info/${location.search.split('&p=')[0]}`);
  280. return;
  281. }
  282. });
  283. addLink(document.querySelector('.rss.major'), `/rss/${location.search.split('&p=')[0]}`);
  284. return true;
  285. }
  286. });
  287. /*
  288. pageLoad(() => {
  289. let infoButton = document.querySelector('.edit.major');
  290. if (infoButton) {
  291. let editPages = document.createElement('button');
  292. Object.assign(editPages, { className: 'editpages major edit', title: 'Edit pages'});
  293. //infoButton.parentNode.insertBefore(editPages, infoButton);
  294. return true;
  295. }
  296. });/**/
  297. }
  298. }
  299. else if (location.pathname === "/my/settings/") { // Custom settings
  300. let saveBtn = document.querySelector('#savesettings');
  301.  
  302. let table = document.querySelector("#editsettings tbody");
  303. let saveTr = table.querySelectorAll("tr");
  304. saveTr = saveTr[saveTr.length - 1];
  305.  
  306. let headerTr = document.createElement('tr');
  307. let header = document.createElement('th');
  308. header.textContent = "Extra Settings";
  309. headerTr.appendChild(header);
  310.  
  311. let moreTr = document.createElement('tr');
  312. let more = document.createElement('td');
  313. more.textContent = "* This only applies to a select few older adventures that have had their text corrupted. Currently only some spanish/french is fixable. Russian/Bulgarian is not possible.";
  314. moreTr.appendChild(more);
  315.  
  316. let settingsTr = document.createElement('tr');
  317. let localMsg = document.createElement('span');
  318. let settingsTd = document.createElement('td');
  319. 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!";
  320. let plusTable = document.createElement('table');
  321. let plusTbody = document.createElement('tbody');
  322. plusTable.appendChild(plusTbody);
  323. settingsTd.appendChild(localMsg);
  324. settingsTd.appendChild(document.createElement('br'));
  325. settingsTd.appendChild(document.createElement('br'));
  326. settingsTd.appendChild(plusTable);
  327. settingsTr.appendChild(settingsTd);
  328.  
  329. let spoilerTr = plusTbody.insertRow(0);
  330. let spoilerTextTd = spoilerTr.insertCell(0);
  331. let spoilerInputTd = spoilerTr.insertCell(1);
  332. let spoilerInput = document.createElement('input');
  333. spoilerInputTd.appendChild(spoilerInput);
  334.  
  335. let errorTr = plusTbody.insertRow(1);
  336. let errorTextTd = errorTr.insertCell(0);
  337. let errorInputTd = errorTr.insertCell(1);
  338. let errorInput = document.createElement('input');
  339. errorInputTd.appendChild(errorInput);
  340.  
  341. let textFixTr = plusTbody.insertRow(2);
  342. let textFixTextTd = textFixTr.insertCell(0);
  343. let textFixInputTd = textFixTr.insertCell(1);
  344. let textFixInput = document.createElement('input');
  345. textFixInputTd.appendChild(textFixInput);
  346.  
  347. let cssTr = plusTbody.insertRow(3);
  348. let cssTextTd = cssTr.insertCell(0);
  349. let cssSelectTd = cssTr.insertCell(1);
  350. let cssSelect = document.createElement('select');
  351. cssSelectTd.appendChild(cssSelect);
  352.  
  353. let customTr = plusTbody.insertRow(4);
  354. let customTextTd = customTr.insertCell(0);
  355. let customCssTd = customTr.insertCell(1);
  356. let customCssInput = document.createElement('input');
  357. customCssTd.appendChild(customCssInput);
  358.  
  359. plusTable.style = "text-align: center;";
  360. spoilerTextTd.textContent = "Automatically open spoilers:";
  361. spoilerInput.name = "p1";
  362. spoilerInput.type = "checkbox";
  363. spoilerInput.checked = settings.autospoiler;
  364.  
  365. errorTextTd.textContent = "Automatically reload Cloudflare 502 error pages:";
  366. errorInput.name = "p2";
  367. errorInput.type = "checkbox";
  368. errorInput.checked = settings.auto502;
  369.  
  370. textFixTextTd.textContent = "Attempt to fix text errors (experimental)*:";
  371. textFixInput.name = "p2";
  372. textFixInput.type = "checkbox";
  373. textFixInput.checked = settings.textFix;
  374.  
  375. cssTextTd.textContent = "Change style:";
  376.  
  377. customTextTd.textContent = "Custom CSS URL:";
  378. customCssInput.style.width = "99px";
  379. customCssInput.value = settings.styleURL;
  380.  
  381. styleOptions.forEach(o => cssSelect.appendChild(new Option(o, o)));
  382.  
  383. // Enable the save button
  384. spoilerInput.addEventListener("change", () => {
  385. saveBtn.disabled = false;
  386. });
  387. errorInput.addEventListener("change", () => {
  388. saveBtn.disabled = false;
  389. });
  390. textFixInput.addEventListener("change", () => {
  391. saveBtn.disabled = false;
  392. });
  393. cssSelect.addEventListener("change", () => {
  394. saveBtn.disabled = false;
  395. });
  396. customCssInput.addEventListener("keydown", () => {
  397. saveBtn.disabled = false;
  398. });
  399.  
  400. saveTr.parentNode.insertBefore(headerTr, saveTr);
  401. saveTr.parentNode.insertBefore(settingsTr, saveTr);
  402. saveTr.parentNode.insertBefore(moreTr, saveTr);
  403. cssSelect.selectedIndex = settings.style;
  404.  
  405. saveBtn.addEventListener('mouseup', () => {
  406. settings.autospoiler = spoilerInput.checked;
  407. settings.style = cssSelect.selectedIndex;
  408. settings.styleURL = customCssInput.value;
  409. settings.auto502 = errorInput.checked;
  410. settings.textFix = textFixInput.checked;
  411. theme.href = settings.style < styleOptions.length - 1 ? '/css/theme' + settings.style + '.css' : settings.styleURL;
  412. settings.night = false;
  413. console.log(settings);
  414. saveData(settings);
  415.  
  416. dropStyle.textContent = dropStyleText + '*{transition:1s}';
  417. setTimeout(() => {
  418. dropStyle.textContent = dropStyleText;
  419. }, 1000);
  420. });
  421. }
  422. else if (location.pathname === "/my/messages/") { // New buttons
  423. let btnStyle = "margin: 10px 5px;";
  424.  
  425. // Select all read messages button.
  426. const selRead = document.createElement('input');
  427. selRead.style = btnStyle;
  428. selRead.value = "Select Read";
  429. selRead.id = "selectread";
  430. selRead.classList.add("major");
  431. selRead.type = "button";
  432.  
  433. // On click, select all messages with the style attribute indicating it as read.
  434. selRead.addEventListener('mouseup', () => {
  435. document.querySelectorAll('td[style="border-left: 8px solid rgb(221, 221, 221);"] > input').forEach((m) => m.click());
  436. });
  437.  
  438. // Select duplicate message (multiple update notifications).
  439. const selDupe = document.createElement('input');
  440. selDupe.style = btnStyle;
  441. selDupe.value = "Select Same";
  442. selDupe.id = "selectdupe";
  443. selDupe.classList.add("major");
  444. selDupe.type = "button";
  445.  
  446. selDupe.addEventListener('mouseup', evt => {
  447. let temp = document.querySelectorAll('#messages > tr');
  448. let msgs = [];
  449. for (let i = temp.length - 1; i >= 0; i--) {
  450. msgs.push(temp[i]);
  451. }
  452. let titles = [];
  453. msgs.forEach((msg) => {
  454. let title = msg.querySelector('a.major').textContent;
  455. if (/^New update: /.test(title)) { // Select only adventure updates
  456. if (titles.indexOf(title) === -1) {
  457. if (msg.querySelector('td').style.cssText !== "border-left: 8px solid rgb(221, 221, 221);") {
  458. titles.push(title);
  459. }
  460. } else {
  461. msg.querySelector('input').click();
  462. }
  463. }
  464. });
  465. });
  466.  
  467. // Add buttons to the page.
  468. let del = document.querySelector('#deletemsgs');
  469. del.parentNode.appendChild(document.createElement('br'));
  470. del.parentNode.appendChild(selRead);
  471. del.parentNode.appendChild(selDupe);
  472. }
  473. else if (location.pathname === "/my/stories/") {
  474. pageLoad(() => {
  475. let adventures = document.querySelectorAll('#stories tr');
  476. if (adventures.length > 0) {
  477. adventures.forEach(story => {
  478. let buttons = story.querySelectorAll('input.major');
  479. let id = story.querySelector('a').href.replace('https://mspfa.com/', '').replace('&p=1', '');
  480. addLink(buttons[0], `/my/stories/info/${id}`);
  481. addLink(buttons[1], `/my/stories/pages/${id}`);
  482. });
  483. return true;
  484. }
  485. });
  486.  
  487. let guides = ["MSPFA Etiquette", "Fanventure Guide for Dummies", "CSS Guide", "HTML and CSS Things"];
  488. let ids = ["27631", "29299", "21099", "23711"];
  489. let authors = ["Radical Dude 42", "nzar", "MadCreativity", "seymour schlong"];
  490.  
  491. let parentTd = document.querySelector('.container > tbody > tr:last-child > td');
  492. let unofficial = parentTd.querySelector('span');
  493. unofficial.textContent = "Unofficial Guides";
  494. let guideTable = document.createElement('table');
  495. let guideTbody = document.createElement('tbody');
  496. guideTable.style.width = "100%";
  497. guideTable.style.textAlign = "center";
  498.  
  499. guideTable.appendChild(guideTbody);
  500. parentTd.appendChild(guideTable);
  501.  
  502. for (let i = 0; i < guides.length; i++) {
  503. let guideTr = guideTbody.insertRow(i);
  504. let guideTd = guideTr.insertCell(0);
  505. let guideLink = document.createElement('a');
  506. guideLink.href = '/?s='+ids[i];
  507. guideLink.textContent = guides[i];
  508. guideLink.className = "major";
  509. guideTd.appendChild(guideLink);
  510. guideTd.appendChild(document.createElement('br'));
  511. guideTd.appendChild(document.createTextNode('by '+authors[i]));
  512. guideTd.appendChild(document.createElement('br'));
  513. guideTd.appendChild(document.createElement('br'));
  514. }
  515. }
  516. else if (location.pathname === "/my/stories/info/" && location.search) {
  517. addLink(document.querySelector('#userfavs'), `/readers/${location.search}`);
  518. addLink(document.querySelector('#editpages'), `/my/stories/pages/${location.search}`);
  519. }
  520. else if (location.pathname === "/my/stories/pages/" && location.search) {
  521. addLink(document.querySelector('#editinfo'), `/my/stories/info/${location.search}`);
  522. }
  523. else if (location.pathname === "/user/") {
  524. pageLoad(() => {
  525. let msgButton = document.querySelector('#sendmsg');
  526. if (msgButton) {
  527. addLink(msgButton, '/my/messages/new/'); // note: doesn't input the desired user's id
  528. addLink(document.querySelector('#favstories'), `/favs/${location.search}`);
  529. }
  530. });
  531.  
  532. pageLoad(() => {
  533. if (window.MSPFA) {
  534. window.MSPFA.request(0, {
  535. do: "user",
  536. u: location.search.slice(3)
  537. }, user => {
  538. if (typeof user !== "undefined") {
  539. let stats = document.querySelector('#userinfo table');
  540. let joinTr = stats.insertRow(1);
  541. let joinTextTd = joinTr.insertCell(0);
  542. joinTextTd.appendChild(document.createTextNode("Account created:"));
  543. let d = new Date(user.d).toString().split(' ').splice(1, 4).join(' ');
  544. let joinDate = joinTr.insertCell(1);
  545. let joinTime = document.createElement('b');
  546. joinTime.appendChild(document.createTextNode(d));
  547. joinDate.appendChild(joinTime);
  548. }
  549. }, status => {
  550. console.log(status);
  551. }, true);
  552. return true;
  553. }
  554. });
  555. }
  556. else if (location.pathname === "/favs/" && location.search) {
  557. pageLoad(() => {
  558. let stories = document.querySelectorAll('#stories tr');
  559.  
  560. if (stories.length > 0) {
  561. stories.forEach(story => {
  562. let id = story.querySelector('a').href.replace('https://mspfa.com/', '');
  563. pageLoad(() => {
  564. if (window.MSPFA.me.i) {
  565. addLink(story.querySelector('.edit.major'), `/my/stories/info/${id}`);
  566. return;
  567. }
  568. });
  569. addLink(story.querySelector('.rss.major'), `/rss/${id}`);
  570. });
  571. return true;
  572. }
  573. });
  574. }
  575. else if (location.pathname === "/search/" && location.search) {
  576. let pages = document.querySelector('#pages');
  577. let statTable = document.createElement('table');
  578. let statTbody = document.createElement('tbody');
  579. let statTr = statTbody.insertRow(0);
  580. let charCount = statTr.insertCell(0);
  581. let wordCount = statTr.insertCell(0);
  582. let statParentTr = pages.parentNode.parentNode.insertRow(2);
  583. let statParentTd = statParentTr.insertCell(0);
  584.  
  585. let statHeaderTr = statTbody.insertRow(0);
  586. let statHeader = document.createElement('th');
  587. statHeader.colSpan = '2';
  588.  
  589. statHeaderTr.appendChild(statHeader);
  590. statHeader.textContent = 'Statistics may not be entirely accurate.';
  591.  
  592. statTable.style.width = "100%";
  593.  
  594. charCount.textContent = "Character count: loading...";
  595. wordCount.textContent = "Word count: loading...";
  596.  
  597. statTable.appendChild(statTbody);
  598. statParentTd.appendChild(statTable);
  599.  
  600. pageLoad(() => {
  601. if (document.querySelector('#pages br')) {
  602. let bbc = window.MSPFA.BBC.slice();
  603. bbc.splice(0, 3);
  604.  
  605. window.MSPFA.request(0, {
  606. do: "story",
  607. s: location.search.replace('?s=', '')
  608. }, story => {
  609. if (typeof story !== "undefined") {
  610. let pageContent = [];
  611. story.p.forEach(p => {
  612. pageContent.push(p.c);
  613. pageContent.push(p.b);
  614. });
  615.  
  616. let storyText = pageContent.join(' ')
  617. .replace(/\n/g, ' ')
  618. .replace(bbc[0][0], '$1')
  619. .replace(bbc[1][0], '$1')
  620. .replace(bbc[2][0], '$1')
  621. .replace(bbc[3][0], '$1')
  622. .replace(bbc[4][0], '$2')
  623. .replace(bbc[5][0], '$3')
  624. .replace(bbc[6][0], '$3')
  625. .replace(bbc[7][0], '$3')
  626. .replace(bbc[8][0], '$3')
  627. .replace(bbc[9][0], '$3')
  628. .replace(bbc[10][0], '$2')
  629. .replace(bbc[11][0], '$1')
  630. .replace(bbc[12][0], '$3')
  631. .replace(bbc[13][0], '$3')
  632. .replace(bbc[14][0], '')
  633. .replace(bbc[16][0], '$1')
  634. .replace(bbc[17][0], '$2 $4 $5')
  635. .replace(bbc[18][0], '$2 $4 $5')
  636. .replace(bbc[19][0], '')
  637. .replace(bbc[20][0], '');
  638.  
  639. console.log(storyText);
  640.  
  641. wordCount.textContent = `Word count: ${storyText.split(/ +/g).length}`;
  642. charCount.textContent = `Character count: ${storyText.replace(/ +/g, '').length}`;
  643. }
  644. }, status => {
  645. console.log(status);
  646. }, true);
  647. return true;
  648. }
  649. });
  650. }
  651. })();