lemmy-keyboard-navigation

Easily navigate Lemmy with your keyboard

当前为 2023-08-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name lemmy-keyboard-navigation
  3. // @match https://*/*
  4. // @grant none
  5. // @version 2.1
  6. // @author vmavromatis
  7. // @author howdy@thesimplecorner.org
  8. // @author InfinibyteF4
  9. // @author aglidden
  10. // @license GPL3
  11. // @icon https://raw.githubusercontent.com/vmavromatis/Lemmy-keyboard-navigation/main/icon.png?inline=true
  12. // @homepageURL https://github.com/vmavromatis/Lemmy-keyboard-navigation
  13. // @namespace https://github.com/vmavromatis/Lemmy-keyboard-navigation
  14. // @description Easily navigate Lemmy with your keyboard
  15. // @run-at document-end
  16. // ==/UserScript==
  17.  
  18. /*global window,console,localStorage,sessionStorage,document,GM_addStyle,PRO_addStyle,addStyle,MutationObserver,location*/
  19.  
  20. //isLemmySite
  21. if (document.querySelectorAll('.lemmy-site').length >= 1) {
  22.  
  23. // DEBUGGING (ignore me!)
  24. //localStorage.clear();
  25. //sessionStorage.clear();
  26.  
  27. //settings page (this is from lemmyTools)
  28. const optionsKey = "lemmy-keyboard-navigation-Options";
  29.  
  30. function getSettingsFromLocalStorage() {
  31. try {
  32. return JSON.parse(localStorage.getItem(optionsKey) || "{}");
  33. } catch (_) {
  34. return {};
  35. }
  36. }
  37.  
  38. function checkedIfTrue(val) {
  39. return val ? "checked" : "";
  40. }
  41.  
  42. function options(open) {
  43. const odiv = document.getElementById("lkOptions");
  44. let userOptions = {};
  45. if (open === "open") {
  46. odiv.style.display = "block";
  47. } else if (open === "set") {
  48. //First run set defaults or pull from localstorage.
  49. userOptions = Object.assign({}, {
  50. pageOffset: 5,
  51. vimKeyNavigation: true,
  52. smoothScroll: false,
  53. scrollPosition: "middle",
  54. expandOption: "both",
  55. backgroundHex: "#373737",
  56. kb_expand: "KeyX",
  57. kb_comments: "KeyC",
  58. kb_openLink: "Enter",
  59. kb_parent: "KeyP",
  60. kb_upvote: "KeyA",
  61. kb_downvote: "KeyZ",
  62. kb_replyComm: "KeyR",
  63. kb_save: "KeyS",
  64. kb_context: "KeyQ",
  65. kb_smallerImg: "Minus",
  66. kb_largerImg: "Equal",
  67. kb_user: "KeyU",
  68. kb_edit: "KeyE",
  69. kb_top: "KeyT",
  70. m_dialog: "KeyG",
  71. m_first: "Digit1",
  72. m_second: "Digit2",
  73. m_third: "Digit3",
  74. m_fourth: "Digit4",
  75. m_fifth: "Digit5",
  76. m_frontpage: "KeyF",
  77. m_saved: "KeyS",
  78. m_userpage: "KeyU",
  79. m_inbox: "KeyI",
  80. m_options: "KeyO"
  81. },
  82. getSettingsFromLocalStorage()
  83. );
  84. localStorage.setItem(optionsKey, JSON.stringify(userOptions));
  85. } else if (open === "save") {
  86. //save button
  87. odiv.style.display = "none";
  88. //general
  89. userOptions.vimKeyNavigation =
  90. document.getElementById("option_vimKeyNavigation").checked;
  91.  
  92. userOptions.smoothScroll =
  93. document.getElementById("option_smoothScroll").checked;
  94.  
  95. let offset = parseFloat(
  96. document.getElementById("option_pageOffset").value
  97. );
  98. if (isNaN(offset) || offset < 0 || offset > 100) {
  99. userOptions.pageOffset = 0;
  100. } else {
  101. userOptions.pageOffset = offset;
  102. }
  103.  
  104. userOptions.scrollPosition =
  105. document.getElementById("option_scrollPosition").value;
  106.  
  107. userOptions.expandOption =
  108. document.getElementById("option_expandOption").value;
  109.  
  110. userOptions.backgroundHex =
  111. document.getElementById("option_backgroundHex").value;
  112. //keybinds
  113. userOptions.kb_expand =
  114. document.getElementById("option_kb_expand").value;
  115.  
  116. userOptions.kb_comments =
  117. document.getElementById("option_kb_comments").value;
  118.  
  119. userOptions.kb_openLink =
  120. document.getElementById("option_kb_openLink").value;
  121.  
  122. userOptions.kb_parent =
  123. document.getElementById("option_kb_parent").value;
  124.  
  125. userOptions.kb_upvote =
  126. document.getElementById("option_kb_upvote").value;
  127.  
  128. userOptions.kb_downvote =
  129. document.getElementById("option_kb_downvote").value;
  130.  
  131. userOptions.kb_replyComm =
  132. document.getElementById("option_kb_replyComm").value;
  133.  
  134. userOptions.kb_save =
  135. document.getElementById("option_kb_save").value;
  136.  
  137. userOptions.kb_context =
  138. document.getElementById("option_kb_context").value;
  139.  
  140. userOptions.kb_smallerImg =
  141. document.getElementById("option_kb_smallerImg").value;
  142.  
  143. userOptions.kb_largerImg =
  144. document.getElementById("option_kb_largerImg").value;
  145.  
  146. userOptions.kb_user =
  147. document.getElementById("option_kb_user").value;
  148.  
  149. userOptions.kb_edit =
  150. document.getElementById("option_kb_edit").value;
  151.  
  152. userOptions.kb_top =
  153. document.getElementById("option_kb_top").value;
  154. //dialog keybinds
  155. userOptions.m_dialog =
  156. document.getElementById("option_m_dialog").value;
  157.  
  158. userOptions.m_first =
  159. document.getElementById("option_m_first").value;
  160.  
  161. userOptions.m_second =
  162. document.getElementById("option_m_second").value;
  163.  
  164. userOptions.m_third =
  165. document.getElementById("option_m_third").value;
  166.  
  167. userOptions.m_fourth =
  168. document.getElementById("option_m_fourth").value;
  169.  
  170. userOptions.m_fifth =
  171. document.getElementById("option_m_fifth").value;
  172.  
  173. userOptions.m_frontpage =
  174. document.getElementById("option_m_frontpage").value;
  175.  
  176. userOptions.m_saved =
  177. document.getElementById("option_m_saved").value;
  178.  
  179. userOptions.m_userpage =
  180. document.getElementById("option_m_userpage").value;
  181.  
  182. userOptions.m_inbox =
  183. document.getElementById("option_m_inbox").value;
  184.  
  185. userOptions.m_options =
  186. document.getElementById("option_m_options").value;
  187.  
  188. localStorage.setItem(optionsKey, JSON.stringify(userOptions));
  189. window.location.reload();
  190. }
  191.  
  192. userOptions = getSettingsFromLocalStorage();
  193. return userOptions;
  194. }
  195.  
  196. let settings = options("set");
  197. let vimKeyNavigation = checkedIfTrue(settings.vimKeyNavigation);
  198. let smoothScroll = checkedIfTrue(settings.smoothScroll);
  199. let pageOffset = window.innerHeight * settings.pageOffset / 100;
  200. let scrollPosition = settings.scrollPosition;
  201. let backgroundHex = settings.backgroundHex;
  202. let expandOption = settings.expandOption;
  203.  
  204. // Set selected entry colors
  205. const backgroundColor = `${backgroundHex}`;
  206. const textColor = 'white';
  207.  
  208. // Set navigation keys with keycodes here: https://www.toptal.com/developers/keycode
  209. let nextKey = 'ArrowDown';
  210. let prevKey = 'ArrowUp';
  211. let nextPageKey = 'ArrowRight';
  212. let prevPageKey = 'ArrowLeft';
  213.  
  214. if (vimKeyNavigation) {
  215. nextKey = 'KeyJ';
  216. prevKey = 'KeyK';
  217. nextPageKey = 'KeyL';
  218. prevPageKey = 'KeyH';
  219. }
  220.  
  221. const expandKey = `${settings.kb_expand}`;
  222. const openCommentsKey = `${settings.kb_comments}`;
  223. const openLinkAndCollapseKey = `${settings.kb_openLink}`;
  224. const parentCommentKey = `${settings.kb_parent}`;
  225. const upvoteKey = `${settings.kb_upvote}`;
  226. const downvoteKey = `${settings.kb_downvote}`;
  227. const replyCommKey = `${settings.kb_replyComm}`;
  228. const saveKey = `${settings.kb_save}`;
  229. const contextKey = `${settings.kb_context}`;
  230. const smallerImgKey = `${settings.kb_smallerImg}`;
  231. const biggerImgKey = `${settings.kb_largerImg}`;
  232. const userKey = `${settings.kb_user}`;
  233. const editKey = `${settings.kb_edit}`;
  234. const topKey = `${settings.kb_top}`;
  235. const linkOneKey = 'Digit1';
  236. const linkTwoKey = 'Digit2';
  237. const linkThreeKey = 'Digit3';
  238. const linkFourKey = 'Digit4';
  239. const linkFiveKey = 'Digit5';
  240. const linkSixKey = 'Digit6';
  241. const linkSevenKey = 'Digit7';
  242. const linkEightKey = 'Digit8';
  243. const linkNineKey = 'Digit9';
  244. const linkZeroKey = 'Digit0';
  245.  
  246. const modalPopupKey = `${settings.m_dialog}`;
  247. const modalSortOneKey = `${settings.m_first}`;
  248. const modalSortTwoKey = `${settings.m_second}`;
  249. const modalSortThreeKey = `${settings.m_third}`;
  250. const modalSortFourKey = `${settings.m_fourth}`;
  251. const modalSortFiveKey = `${settings.m_fifth}`;
  252. const modalFrontpageKey = `${settings.m_frontpage}`;
  253. const modalSavedKey = `${settings.m_saved}`;
  254. const modalProfileKey = `${settings.m_userpage}`;
  255. const modalInboxKey = `${settings.m_inbox}`;
  256. const modalOptionsKey = `${settings.m_options}`;
  257.  
  258.  
  259. const escapeKey = 'Escape';
  260. let modalMode = 0;
  261. console.log(`modalMode: ${modalMode}`);
  262.  
  263. // Stop arrows from moving the page if not using Vim navigation
  264. window.addEventListener("keydown", function(e) {
  265. if (["ArrowUp", "ArrowDown"].indexOf(e.code) > -1 && !vimKeyNavigation) {
  266. e.preventDefault();
  267. }
  268. }, false);
  269.  
  270. // Remove scroll animations
  271. document.documentElement.style = "scroll-behavior: auto";
  272.  
  273. // Set CSS for selected entry
  274. const css = `
  275. .selected {
  276. background-color: ${backgroundColor} !important;
  277. color: ${textColor};
  278. }`;
  279.  
  280. // dialog box
  281. let myDialog = document.createElement("dialog");
  282. document.body.appendChild(myDialog);
  283. let para = document.createElement("p");
  284. para.innerHTML = `
  285. <h3><b>Sort by</b></h3>
  286. <p>${modalSortOneKey} = N/A</br>
  287. ${modalSortTwoKey} = N/A</br>
  288. ${modalSortThreeKey} = N/A</br>
  289. ${modalSortFourKey} = N/A</br>
  290. ${modalSortFiveKey} = N/A</p>
  291. <h3><b>Go To Page</b></h3>
  292. <p>${modalFrontpageKey} = Frontpage</br>
  293. ${modalSavedKey} = Saved</br>
  294. ${modalProfileKey} = User Profile Page</br>
  295. ${modalInboxKey} = Inbox</br></p>
  296. <h6>${modalOptionsKey} = Options Page</br></br></h6>
  297. `;
  298. myDialog.appendChild(para);
  299. let button = document.createElement("button");
  300. button.classList.add('CLOSEBUTTON1');
  301. button.innerHTML = `Press ESC or ${modalPopupKey} to Close`;
  302. myDialog.appendChild(button);
  303.  
  304. //draw settings page
  305. const odiv = document.createElement("div");
  306. odiv.setAttribute("id", "lkOptions");
  307. odiv.classList.add("lkoptions", "border-secondary", "card");
  308. odiv.innerHTML = `
  309. <h4>Lemmy-keyboard-navigation Options</h4>
  310. </hr>
  311. <div class='table-responsive'>
  312. <table class='table'>
  313. <thead class='pointer'>
  314. <td><b>Save and close settings</b></td>
  315. <td><button id='LKsaveoptionsTop'>Save and Close</button></td>
  316. </tr>
  317. <tr>
  318. <th><h3><b>General</b></h3></th><td><td/>
  319. </thead>
  320. </tr>
  321. <tbody>
  322. <tr>
  323. <td><b>Use Vim key navigation</b><br/>Also known as HJKL navigation.<br/>Uncheck to use arrow keys instead.</td>
  324. <td><input type='checkbox' id='option_vimKeyNavigation' ${vimKeyNavigation} /></td>
  325. </tr>
  326. <tr>
  327. <td><b>Smooth scrolling</b><br/>Scroll smoothly to the current selection.</td>
  328. <td><input type='checkbox' id='option_smoothScroll' ${smoothScroll} /></td>
  329. </tr>
  330. <tr>
  331. <td><b>Page Offset</b><br/>Percent of page to offset selected entry when scrolling.<br/>0-20% recommended<br/>Default: 5</td>
  332. <td><textarea id='option_pageOffset'>${settings.pageOffset}</textarea>%</td>
  333. </tr>
  334. <tr>
  335. <td><b>Scrolling position</b><br/>middle: only scroll the page if selection is near the bottom.<br/>top: always scroll to keep the selection near the top.</td>
  336. <td><select id="option_scrollPosition">
  337. <option value='${settings.scrollPosition}'>${settings.scrollPosition}</option>
  338. <option value='middle'>middle</option>
  339. <option value='top'>top</option>
  340. </select></td>
  341. </tr>
  342. <tr>
  343. <td><b>Expand All Posts</b><br/>Pressing Shift + X will expand all posts.<br/>both: expand both text boxes and images.<br/>images: only expand images.<br/>text: only expand text boxes</td>
  344. <td><select id="option_expandOption">
  345. <option value='${settings.expandOption}'>${settings.expandOption}</option>
  346. <option value='both'>both</option>
  347. <option value='text'>text</option>
  348. <option value='images'>images</option>
  349. </select></td>
  350. </tr>
  351. <tr>
  352. <td><b>Selected Hex Code</b><br/>The background color of selected posts/comments.<br/>Default: #373737</td>
  353. <td><textarea id='option_backgroundHex'>${settings.backgroundHex}</textarea></td>
  354. </tr>
  355. <tr>
  356. <td><h3><b>Rebind Keys</b></h3>Set keybinds with keycodes here:<br/><a href='https://www.toptal.com/developers/keycode'>https://www.toptal.com/developers/keycode</a></td><td><td/>
  357. </tr>
  358. <tr>
  359. <tr>
  360. <td><b>Expand/Collapse</b><br/>Expand/collapse both post and comment content.<br/>Default: KeyX</td>
  361. <td><textarea id='option_kb_expand'>${settings.kb_expand}</textarea></td>
  362. </tr>
  363. <tr>
  364. <td><b>Open Comments</b><br/>Go to the comments of a post.<br/>Default: KeyC</td>
  365. <td><textarea id='option_kb_comments'>${settings.kb_comments}</textarea></td>
  366. </tr>
  367. <tr>
  368. <td><b>Open Links</b><br/>Open Links on a post.<br/>(can also be used to collapse comments!)<br/>Default: Enter</td>
  369. <td><textarea id='option_kb_openLink'>${settings.kb_openLink}</textarea></td>
  370. </tr>
  371. <tr>
  372. <td><b>Go to Parent Comment</b><br/>Goes one level up the comment chain.<br/>Default: KeyP</td>
  373. <td><textarea id='option_kb_parent'>${settings.kb_parent}</textarea></td>
  374. </tr>
  375. <tr>
  376. <td><b>Upvote</b><br/>:\)<br/>Default: KeyA</td>
  377. <td><textarea id='option_kb_upvote'>${settings.kb_upvote}</textarea></td>
  378. </tr>
  379. <tr>
  380. <td><b>Downvote</b><br/>:\(<br/>Default: KeyZ</td>
  381. <td><textarea id='option_kb_downvote'>${settings.kb_downvote}</textarea></td>
  382. </tr>
  383. <tr>
  384. <td><b>Reply/Go to community</b><br/>Posts: goes to the post's community<br/>Comments: replies to the selected comment<br/>Default: KeyR</td>
  385. <td><textarea id='option_kb_replyComm'>${settings.kb_replyComm}</textarea></td>
  386. </tr>
  387. <tr>
  388. <td><b>Save post/comment</b><br/>Saves the selected post/comment.<br/>Default: KeyS</td>
  389. <td><textarea id='option_kb_save'>${settings.kb_save}</textarea></td>
  390. </tr>
  391. <tr>
  392. <td><b>Get context of comment</b><br/>Goes to the context of the selected comment.<br/>Default: KeyQ</td>
  393. <td><textarea id='option_kb_context'>${settings.kb_context}</textarea></td>
  394. </tr>
  395. <tr>
  396. <td><b>Shrink expanded image</b><br/>Make an expanded image smaller.<br/>Default: Minus</td>
  397. <td><textarea id='option_kb_smallerImg'>${settings.kb_smallerImg}</textarea></td>
  398. </tr>
  399. <tr>
  400. <td><b>Grow expanded image</b><br/>Make an expanded image larger.<br/>Default: Equal</td>
  401. <td><textarea id='option_kb_largerImg'>${settings.kb_largerImg}</textarea></td>
  402. </tr>
  403. <tr>
  404. <td><b>Go to poster's profile</b><br/>Go to the profile of whoever posted the selected post/comment.<br/>Default: KeyU</td>
  405. <td><textarea id='option_kb_user'>${settings.kb_user}</textarea></td>
  406. </tr>
  407. <tr>
  408. <td><b>Edit the selected post/comment</b><br/>It only works on your own posts!<br/>Default: KeyE</td>
  409. <td><textarea id='option_kb_edit'>${settings.kb_edit}</textarea></td>
  410. </tr>
  411. <tr>
  412. <td><b>Scroll to top</b><br/>Scroll to the top of the page.<br/>Default: KeyT</td>
  413. <td><textarea id='option_kb_top'>${settings.kb_top}</textarea></td>
  414. </tr>
  415. <tr>
  416. <td><h3><b>Rebind Dialog Keys</b></h3></td><td><td/>
  417. </tr>
  418. <tr>
  419. <td><b>Open/Close Dialog</b><br/>Default: KeyG</td>
  420. <td><textarea id='option_m_dialog'>${settings.m_dialog}</textarea></td>
  421. </tr>
  422. <tr>
  423. <tr>
  424. <td><h4><b>Sort buttons</b></h4>For example: If you were to press G then 3 on the front page,<br/>it would sort by subscribed, but doing the same in a<br/>comment section would sort by new. The dialog<br/>text will change with what sort buttons are avaliable!</td><td><td/>
  425. </tr>
  426. <td><b>First Sort Button</b><br/>Default: Digit1</td>
  427. <td><textarea id='option_m_first'>${settings.m_first}</textarea></td>
  428. </tr>
  429. <tr>
  430. <td><b>Second Sort Button</b><br/>Default: Digit2</td>
  431. <td><textarea id='option_m_second'>${settings.m_second}</textarea></td>
  432. </tr>
  433. <tr>
  434. <td><b>Third Sort Button</b><br/>Default: Digit3</td>
  435. <td><textarea id='option_m_third'>${settings.m_third}</textarea></td>
  436. </tr>
  437. <tr>
  438. <td><b>Fourth Sort Button</b><br/>Default: Digit4</td>
  439. <td><textarea id='option_m_fourth'>${settings.m_fourth}</textarea></td>
  440. </tr>
  441. <tr>
  442. <td><b>Fifth Sort Button</b><br/>Default: Digit5</td>
  443. <td><textarea id='option_m_fifth'>${settings.m_fifth}</textarea></td>
  444. </tr>
  445. <tr>
  446. <td><h4><b>Go to page</b></h4></td><td><td/>
  447. </tr>
  448. <tr>
  449. <td><b>Go to Frontpage</b><br/>Default: KeyF</td>
  450. <td><textarea id='option_m_frontpage'>${settings.m_frontpage}</textarea></td>
  451. </tr>
  452. <tr>
  453. <td><b>Go to Saved posts/comments</b><br/>Default: KeyS</td>
  454. <td><textarea id='option_m_saved'>${settings.m_saved}</textarea></td>
  455. </tr>
  456. <tr>
  457. <td><b>Go to Current User's Profile</b><br/>Default: KeyU</td>
  458. <td><textarea id='option_m_userpage'>${settings.m_userpage}</textarea></td>
  459. </tr>
  460. <tr>
  461. <td><b>Go to Inbox</b><br/>Default: KeyI</td>
  462. <td><textarea id='option_m_inbox'>${settings.m_inbox}</textarea></td>
  463. </tr>
  464. <tr>
  465. <td><b>Open options page</b><br/>Default: KeyO</td>
  466. <td><textarea id='option_m_options'>${settings.m_options}</textarea></td>
  467. </tr>
  468. <tr>
  469. <td><b>Save and close settings</b></td>
  470. <td><button id='LKsaveoptions'>Save and Close</button></td>
  471. </tr>
  472. <tr>
  473. <td><b style='color:red;'>WARNING:<br/>The button below will reset all your settings to default.<br/>This cannot be undone.</b></td><td></td>
  474. </tr>
  475. <tr>
  476. <td><button id='LKresetoptions'>Reset All Settings</button></td><td></td>
  477. </tr>
  478. </tbody>
  479. </table>
  480. </div>
  481. <hr />
  482. <p>lemmy-keyboard-navigation links:</p>
  483. <a
  484. href='https://github.com/vmavromatis/Lemmy-keyboard-navigation'>Github</a><br/><a
  485. href='https://greasyfork.org/en/scripts/470498-lemmy-keyboard-navigation'>GreasyFork</a><br/><a
  486. href='https://chrome.google.com/webstore/detail/lemmy-keyboard-navigator/lamoeoaekeeklbcekclbceaeafjkdhbi'>Chrome Extension</a><br/></p>
  487. <p>This settings page was taken from the <a href='https://github.com/howdy-tsc/LemmyTools'>LemmyTools</a> Userscript.</p>
  488. `;
  489.  
  490. let styleString = `
  491. .lkoptions {
  492. position: fixed;
  493. min-width: auto;
  494. min-height: auto;
  495. width: auto;
  496. height: 100%;
  497. top: 0;
  498. display: none;
  499. left: 0;
  500. overflow: scroll;
  501. z-index: 1000;
  502. padding: 0.5%;
  503. margin-top:35px;
  504. }
  505. `;
  506. document.head.appendChild(document.createElement("style")).innerHTML = styleString;
  507. document.body.appendChild(odiv); //options
  508.  
  509. document.getElementById("LKsaveoptions").addEventListener("click", (e) => {
  510. e.preventDefault();
  511. options("save");
  512. });
  513. document.getElementById("LKsaveoptionsTop").addEventListener("click", (e) => {
  514. e.preventDefault();
  515. options("save");
  516. });
  517. document.getElementById("LKresetoptions").addEventListener("click", (e) => {
  518. e.preventDefault();
  519. localStorage.clear();
  520. window.location.reload();
  521. });
  522.  
  523. // Global variables
  524. let currentEntry;
  525. let commentBlock;
  526. let entries = [];
  527. let previousUrl = "";
  528. let expand = false;
  529.  
  530. if (typeof GM_addStyle !== "undefined") {
  531. GM_addStyle(css);
  532. } else if (typeof PRO_addStyle !== "undefined") {
  533. PRO_addStyle(css);
  534. } else if (typeof addStyle !== "undefined") {
  535. addStyle(css);
  536. } else {
  537. let node = document.createElement("style");
  538. node.type = "text/css";
  539. node.appendChild(document.createTextNode(css));
  540. let heads = document.getElementsByTagName("head");
  541. if (heads.length > 0) {
  542. heads[0].appendChild(node);
  543. } else {
  544. // no head yet, stick it whereever
  545. document.documentElement.appendChild(node);
  546. }
  547. }
  548.  
  549. const selectedClass = "selected";
  550.  
  551. const targetNode = document.documentElement;
  552. const config = {
  553. childList: true,
  554. subtree: true
  555. };
  556.  
  557. const observer = new MutationObserver(() => {
  558. entries = document.querySelectorAll(".post-listing, .comment-node");
  559.  
  560. if (entries.length > 0) {
  561. if (location.href !== previousUrl) {
  562. previousUrl = location.href;
  563. currentEntry = null;
  564. }
  565. init();
  566. }
  567. });
  568.  
  569. observer.observe(targetNode, config);
  570.  
  571. function init() {
  572. // If jumping to comments
  573. if (window.location.search.includes("scrollToComments=true") &&
  574. entries.length > 1 &&
  575. (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0)
  576. ) {
  577. selectEntry(entries[1], true);
  578. }
  579. // If jumping to comment from anchor link
  580. else if (window.location.pathname.includes("/comment/") &&
  581. (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0)
  582. ) {
  583. const commentId = window.location.pathname.replace("/comment/", "");
  584. const anchoredEntry = document.getElementById("comment-" + commentId);
  585.  
  586. if (anchoredEntry) {
  587. selectEntry(anchoredEntry, true);
  588. }
  589. }
  590. // If no entries yet selected, default to last selected
  591. else if (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0) {
  592. if (sessionStorage.getItem('currentselection') === null) {
  593. selectEntry(entries[0]);
  594. } else {
  595. sessionCurrentEntry("restore");
  596. }
  597. }
  598.  
  599. Array.from(entries).forEach(entry => {
  600. entry.removeEventListener("click", clickEntry, true);
  601. entry.addEventListener('click', clickEntry, true);
  602. });
  603.  
  604. document.removeEventListener("keydown", handleKeyPress, true);
  605. document.addEventListener("keydown", handleKeyPress, true);
  606. }
  607.  
  608. function handleKeyPress(event) {
  609. if (["TEXTAREA", "INPUT"].indexOf(event.target.tagName) > -1 || event.metaKey) {
  610. return;
  611. }
  612.  
  613. switch (modalMode) {
  614. case modalMode = 0:
  615. switch (event.code) {
  616. case nextKey:
  617. case prevKey:
  618. previousKey(event);
  619. break;
  620. case upvoteKey:
  621. upVote();
  622. break;
  623. case downvoteKey:
  624. downVote();
  625. break;
  626. case expandKey:
  627. if (event.shiftKey) {
  628. expandAll();
  629. } else {
  630. toggleExpand();
  631. expand = isExpanded() ? true : false;
  632. }
  633. break;
  634. case smallerImgKey:
  635. imgResize("smaller");
  636. break;
  637. case biggerImgKey:
  638. imgResize("larger");
  639. break;
  640. case saveKey:
  641. save();
  642. break;
  643. case editKey:
  644. edit();
  645. break;
  646. case openCommentsKey:
  647. comments(event);
  648. break;
  649. case modalPopupKey:
  650. dialogUpdate();
  651. goToDialog("open");
  652. break;
  653. case contextKey:
  654. getContext(event);
  655. break;
  656. case replyCommKey:
  657. // allow refresh with Ctrl + R
  658. if (!event.ctrlKey) {
  659. if (window.location.pathname.includes("/post/")) {
  660. reply(event);
  661. } else {
  662. community(event);
  663. }
  664. }
  665. break;
  666. case userKey:
  667. visitUser(event);
  668. break;
  669. case openLinkAndCollapseKey:
  670. if (window.location.pathname.includes("/post/")) {
  671. toggleExpand();
  672. } else {
  673. const linkElement = currentEntry.querySelector(".col.flex-grow-1>p>a");
  674. if (linkElement) {
  675. if (event.shiftKey) {
  676. window.open(linkElement.href);
  677. } else {
  678. linkElement.click();
  679. }
  680. } else {
  681. comments(event);
  682. }
  683. }
  684. break;
  685. case parentCommentKey: {
  686. let targetBlock;
  687. if (currentEntry.classList.contains("ms-1")) {
  688. targetBlock = getPrevEntry(currentEntry);
  689. } else if (currentEntry.parentElement.parentElement.parentElement.nodeName === "LI") {
  690. targetBlock = currentEntry.parentElement.parentElement.parentElement.getElementsByTagName("article")[0];
  691. }
  692. if (targetBlock) {
  693. if (expand) {
  694. collapseEntry();
  695. }
  696. selectEntry(targetBlock, true);
  697. if (expand) {
  698. expandEntry();
  699. }
  700. }
  701. }
  702. break;
  703. case topKey:
  704. goToTop();
  705. break;
  706. case linkOneKey:
  707. clickLink(1);
  708. break;
  709. case linkTwoKey:
  710. clickLink(2);
  711. break;
  712. case linkThreeKey:
  713. clickLink(3);
  714. break;
  715. case linkFourKey:
  716. clickLink(4);
  717. break;
  718. case linkFiveKey:
  719. clickLink(5);
  720. break;
  721. case linkSixKey:
  722. clickLink(6);
  723. break;
  724. case linkSevenKey:
  725. clickLink(7);
  726. break;
  727. case linkEightKey:
  728. clickLink(8);
  729. break;
  730. case linkNineKey:
  731. clickLink(9);
  732. break;
  733. case linkZeroKey:
  734. clickLink(0);
  735. break;
  736. case nextPageKey:
  737. case prevPageKey: {
  738. const pageButtons = Array.from(document.querySelectorAll(".paginator>button"));
  739.  
  740. if (pageButtons && (document.getElementsByClassName('paginator').length > 0)) {
  741. if (event.code === nextPageKey) {
  742. document.querySelectorAll(".paginator>.btn.btn-secondary")[1].click(); //next
  743. } else {
  744. document.querySelectorAll(".paginator>.btn.btn-secondary")[0].click(); //prev
  745. }
  746. }
  747. // Jump next block of comments
  748. if (event.code === nextPageKey) {
  749. commentBlock = getNextEntrySameLevel(currentEntry);
  750. }
  751. // Jump previous block of comments
  752. if (event.code === prevPageKey) {
  753. commentBlock = getPrevEntrySameLevel(currentEntry);
  754. }
  755. if (commentBlock) {
  756. if (expand) {
  757. collapseEntry();
  758. }
  759. selectEntry(commentBlock, true);
  760. if (expand) {
  761. expandEntry();
  762. }
  763. }
  764. }
  765. sessionStorage.setItem('currentselection', 0); //reset the selection back to the first post when switching pages
  766. }
  767. break;
  768. case modalMode = 1:
  769. switch (event.code) {
  770. case escapeKey:
  771. modalMode = 0;
  772. console.log(`modalMode: ${modalMode}`);
  773. break;
  774. case modalPopupKey:
  775. goToDialog("close");
  776. break;
  777. case modalSavedKey:
  778. if (window.location.pathname.includes("/u/")) {
  779. let savedelement = document.getElementsByClassName("btn btn-outline-secondary pointer")[3];
  780. if (savedelement) {
  781. savedelement.click();
  782. goToDialog("close");
  783. }
  784. } else {
  785. instanceAndUser("saved");
  786. }
  787. break;
  788. case modalFrontpageKey:
  789. frontpage();
  790. break;
  791. case modalProfileKey:
  792. let profileelement = document.getElementsByClassName("dropdown-item px-2")[0];
  793. if (profileelement) {
  794. profileelement.click();
  795. goToDialog("close");
  796. } else {
  797. instanceAndUser("profile");
  798. }
  799. break;
  800. case modalInboxKey:
  801. let notifelement = document.getElementsByClassName("nav-link d-inline-flex align-items-center d-md-inline-block")[2];
  802. if (notifelement) {
  803. notifelement.click();
  804. goToDialog("close");
  805. } else {
  806. window.location.replace(window.location.origin + "/login");
  807. }
  808. break;
  809. case modalSortOneKey:
  810. let firstbutton = document.getElementsByClassName("btn btn-outline-secondary")[0];
  811. sessionStorage.setItem('currentselection', 0); //reset the selection to the first post when switching filters
  812. firstbutton.click();
  813. goToDialog("close");
  814. break;
  815. case modalSortTwoKey:
  816. let secondbutton = document.getElementsByClassName("btn btn-outline-secondary")[1];
  817. sessionStorage.setItem('currentselection', 0);
  818. secondbutton.click();
  819. goToDialog("close");
  820. break;
  821. case modalSortThreeKey:
  822. let thirdbutton = document.getElementsByClassName("btn btn-outline-secondary")[2];
  823. sessionStorage.setItem('currentselection', 0);
  824. thirdbutton.click();
  825. goToDialog("close");
  826. break;
  827. case modalSortFourKey:
  828. let fourthbutton = document.getElementsByClassName("btn btn-outline-secondary")[3];
  829. sessionStorage.setItem('currentselection', 0);
  830. fourthbutton.click();
  831. goToDialog("close");
  832. break;
  833. case modalSortFiveKey:
  834. let fifthbutton = document.getElementsByClassName("btn btn-outline-secondary")[4];
  835. sessionStorage.setItem('currentselection', 0);
  836. fifthbutton.click();
  837. goToDialog("close");
  838. break;
  839. case modalOptionsKey:
  840. options("open");
  841. goToDialog("close");
  842. break;
  843. }
  844. }
  845. }
  846.  
  847. function getNextEntry(e) {
  848. const currentEntryIndex = Array.from(entries).indexOf(e);
  849.  
  850. if (currentEntryIndex + 1 >= entries.length) {
  851. return e;
  852. }
  853. return entries[currentEntryIndex + 1];
  854. }
  855.  
  856. function getPrevEntry(e) {
  857. const currentEntryIndex = Array.from(entries).indexOf(e);
  858.  
  859. if (currentEntryIndex - 1 < 0) {
  860. return e;
  861. }
  862. return entries[currentEntryIndex - 1];
  863. }
  864.  
  865. function getNextEntrySameLevel(e) {
  866. const nextSibling = e.parentElement.nextElementSibling;
  867.  
  868. if (!nextSibling || nextSibling.getElementsByTagName("article").length < 1) {
  869. return getNextEntry(e);
  870. }
  871.  
  872. return nextSibling.getElementsByTagName("article")[0];
  873. }
  874.  
  875. function getPrevEntrySameLevel(e) {
  876. const prevSibling = e.parentElement.previousElementSibling;
  877.  
  878. if (!prevSibling || prevSibling.getElementsByTagName("article").length < 1) {
  879. return getPrevEntry(e);
  880. }
  881.  
  882. return prevSibling.getElementsByTagName("article")[0];
  883. }
  884.  
  885. function dialogUpdate() {
  886. para.remove();
  887. para = document.createElement("p");
  888. para.innerHTML = `
  889. <h3><b>Sort by</b></h3>
  890. <p>${modalSortOneKey} = ${dialogLocation(1)}</br>
  891. ${modalSortTwoKey} = ${dialogLocation(2)}</br>
  892. ${modalSortThreeKey} = ${dialogLocation(3)}</br>
  893. ${modalSortFourKey} = ${dialogLocation(4)}</br>
  894. ${modalSortFiveKey} = ${dialogLocation(5)}</p>
  895. <h3><b>Go To Page</b></h3>
  896. <p>${modalFrontpageKey} = Frontpage</br>
  897. ${modalSavedKey} = Saved</br>
  898. ${modalProfileKey} = User Profile Page</br>
  899. ${modalInboxKey} = Inbox</br></p>
  900. <h6>${modalOptionsKey} = Options Page</br></br></h6>
  901. `;
  902. myDialog.appendChild(para);
  903. myDialog.appendChild(button);
  904. }
  905.  
  906. function dialogLocation(n) {
  907. var sort = document.getElementsByClassName("btn btn-outline-secondary");
  908.  
  909. if (n === 1 && 1 <= sort.length) {
  910. return sort[0].innerText;
  911. }
  912. if (n === 2 && 2 <= sort.length) {
  913. return sort[1].innerText;
  914. }
  915. if (n === 3 && 3 <= sort.length) {
  916. return sort[2].innerText;
  917. }
  918. if (n === 4 && 4 <= sort.length) {
  919. return sort[3].innerText;
  920. }
  921. if (n === 5 && 5 <= sort.length) {
  922. return sort[4].innerText;
  923. }
  924. return "N/A";
  925. }
  926.  
  927. function clickEntry(event) {
  928. const e = event.currentTarget;
  929. const target = event.target;
  930.  
  931. // Deselect if already selected, also ignore if clicking on any link/button
  932. if (e === currentEntry && e.classList.contains(selectedClass) &&
  933. !(
  934. target.tagName.toLowerCase() === "button" || target.tagName.toLowerCase() === "a" ||
  935. target.parentElement.tagName.toLowerCase() === "button" ||
  936. target.parentElement.tagName.toLowerCase() === "a" ||
  937. target.parentElement.parentElement.tagName.toLowerCase() === "button" ||
  938. target.parentElement.parentElement.tagName.toLowerCase() === "a"
  939. )
  940. ) {
  941. e.classList.remove(selectedClass);
  942. } else {
  943. selectEntry(e);
  944. }
  945. }
  946.  
  947. function selectEntry(e, scrollIntoView = false) {
  948. if (currentEntry) {
  949. currentEntry.classList.remove(selectedClass);
  950. let linkNumber = currentEntry.querySelectorAll(".linkNumber");
  951. if (linkNumber) {
  952. for (const link of linkNumber) {
  953. link.remove();
  954. }
  955. }
  956. }
  957. currentEntry = e;
  958. currentEntry.classList.add(selectedClass);
  959. sessionCurrentEntry("save");
  960. let links = currentEntry.getElementsByClassName("md-div")[0];
  961. if (links) {
  962. let alink = links.querySelectorAll('a');
  963. if (alink.length > 0) {
  964. alink.forEach(function(value, i) {
  965. let linkNumber = document.createElement("span");
  966. linkNumber.classList.add("linkNumber");
  967. linkNumber.style.fontSize = "9px";
  968. linkNumber.style.lineHeight = 0;
  969. linkNumber.style.verticalAlign = "super";
  970. linkNumber.setAttribute("data-text", `[${i+1}]`);
  971. linkNumber.innerText = `[${i+1}]`;
  972. linkNumber.title = `Press ${i+1} to open link`;
  973. if (i <= 9) {
  974. value.appendChild(linkNumber);
  975. }
  976. });
  977. }
  978. }
  979.  
  980. if (scrollIntoView) {
  981. scrollIntoViewWithOffset(e, pageOffset);
  982. }
  983. }
  984.  
  985. function sessionCurrentEntry(n) {
  986. const sessionEntry = sessionStorage.getItem('currentselection');
  987. const currentEntryIndex = Array.from(entries).indexOf(currentEntry);
  988.  
  989. if (n === "save") {
  990. if (document.querySelector(".home")) {
  991. sessionStorage.setItem('currentselection', currentEntryIndex);
  992. }
  993. } else if (n === "restore") {
  994. selectEntry(entries[sessionEntry]);
  995. console.log(`Set to entry ${sessionEntry}`);
  996. }
  997. }
  998.  
  999. function clickLink(n) {
  1000. let links = currentEntry.getElementsByClassName("md-div")[0];
  1001. let alink = links.querySelectorAll('a');
  1002. if (n === 1) {
  1003. window.open(
  1004. alink[0].href
  1005. );
  1006. } else if (n === 2) {
  1007. window.open(
  1008. alink[1].href
  1009. );
  1010. } else if (n === 3) {
  1011. window.open(
  1012. alink[2].href
  1013. );
  1014. } else if (n === 4) {
  1015. window.open(
  1016. alink[3].href
  1017. );
  1018. } else if (n === 5) {
  1019. window.open(
  1020. alink[4].href
  1021. );
  1022. } else if (n === 6) {
  1023. window.open(
  1024. alink[5].href
  1025. );
  1026. } else if (n === 7) {
  1027. window.open(
  1028. alink[6].href
  1029. );
  1030. } else if (n === 8) {
  1031. window.open(
  1032. alink[7].href
  1033. );
  1034. } else if (n === 9) {
  1035. window.open(
  1036. alink[8].href
  1037. );
  1038. } else if (n === 0) {
  1039. window.open(
  1040. alink[9].href
  1041. );
  1042. }
  1043. }
  1044.  
  1045. function isExpanded() {
  1046. if (
  1047. currentEntry.querySelector("a.d-inline-block:not(.thumbnail)") ||
  1048. currentEntry.querySelector("#postContent") ||
  1049. currentEntry.querySelector(".card-body")
  1050. ) {
  1051. return true;
  1052. }
  1053.  
  1054. return false;
  1055. }
  1056.  
  1057. function previousKey(event) {
  1058. let selectedEntry;
  1059. // Next button
  1060. if (event.code === nextKey) {
  1061. if (event.shiftKey && vimKeyNavigation) {
  1062. selectedEntry = getNextEntrySameLevel(currentEntry);
  1063.  
  1064. } else {
  1065. selectedEntry = getNextEntry(currentEntry);
  1066. }
  1067. }
  1068. // Previous button
  1069. if (event.code === prevKey) {
  1070. if (event.shiftKey && vimKeyNavigation) {
  1071. selectedEntry = getPrevEntrySameLevel(currentEntry);
  1072.  
  1073. } else {
  1074. selectedEntry = getPrevEntry(currentEntry);
  1075. }
  1076. }
  1077. if (selectedEntry) {
  1078. if (expand) {
  1079. collapseEntry();
  1080. }
  1081. selectEntry(selectedEntry, true);
  1082. if (expand) {
  1083. expandEntry();
  1084. }
  1085. }
  1086. }
  1087.  
  1088. function upVote() {
  1089. identifyButtons();
  1090.  
  1091. if (upvoteButton) {
  1092. upvoteButton.click();
  1093. }
  1094. }
  1095.  
  1096. function downVote() {
  1097. identifyButtons();
  1098.  
  1099. if (downvoteButton) {
  1100. downvoteButton.click();
  1101. }
  1102. }
  1103.  
  1104. function goToDialog(n) {
  1105.  
  1106. const closeButton = document.getElementsByClassName("CLOSEBUTTON1")[0];
  1107. closeButton.addEventListener("click", () => {
  1108. myDialog.close();
  1109. modalMode = 0;
  1110. console.log(`modalMode: ${modalMode}`);
  1111. });
  1112. if (n === "open") {
  1113. myDialog.showModal();
  1114. modalMode = 1;
  1115. console.log(`modalMode: ${modalMode}`);
  1116. }
  1117.  
  1118. if (n === "close") {
  1119. myDialog.close();
  1120. modalMode = 0;
  1121. console.log(`modalMode: ${modalMode}`);
  1122. }
  1123. }
  1124.  
  1125. function instanceAndUser(n) {
  1126. let currentInstance = window.location.origin;
  1127. let username = document.getElementsByClassName("btn dropdown-toggle")[0].textContent;
  1128.  
  1129. if (n === "profile") {
  1130. if (username) {
  1131. let userlink = currentInstance + "/u/" + username;
  1132. window.location.replace(userlink);
  1133. } else {
  1134. window.location.replace(currentInstance + "/login");
  1135. }
  1136. }
  1137. if (n === "saved") {
  1138. if (username) {
  1139. let savedlink = currentInstance + "/u/" + username + "?page=1&sort=New&view=Saved";
  1140. window.location.replace(savedlink);
  1141. } else {
  1142. window.location.replace(currentInstance + "/login");
  1143. }
  1144. }
  1145. }
  1146.  
  1147. var selectionType;
  1148. function checkSelection() {
  1149. let postSelection = document.getElementsByClassName("post-listing mt-2 selected")[0];
  1150. let username = '@' + document.getElementsByClassName("btn dropdown-toggle")[0].textContent;
  1151. let posterusername = currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].innerText;
  1152.  
  1153. if (postSelection) {
  1154. selectionType = "post";
  1155. contextCheck = currentEntry.getElementsByClassName("btn btn-link btn-animate")[11]; // check for direct link button (rainbow star)
  1156. if (contextCheck) {
  1157. selectionType = `${selectionType}-fedi`;
  1158. }
  1159.  
  1160. if (window.location.pathname.includes("/post/")) {
  1161. contextCheck = currentEntry.getElementsByClassName("btn btn-link btn-animate")[13]; // check for direct link button
  1162. if (contextCheck) {
  1163. selectionType = "post-page-fedi"
  1164. } else {
  1165. selectionType = "post-page"
  1166. }
  1167. }
  1168. } else {
  1169. selectionType = "comment";
  1170. let contextButton = currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].href;
  1171. let contextButton2 = currentEntry.getElementsByClassName("btn btn-link btn-animate")[2].href;
  1172.  
  1173. if (contextButton === contextButton2) {
  1174. selectionType = `${selectionType}-context`;
  1175. }
  1176.  
  1177. if (window.location.pathname.includes("/inbox")) {
  1178. selectionType = "comment-inbox";
  1179. }
  1180. }
  1181. if (username === posterusername) {
  1182. selectionType = `my-${selectionType}`;
  1183. }
  1184. console.log(`current selection: ${selectionType}`);
  1185. }
  1186.  
  1187. var upvoteButton;
  1188. var downvoteButton;
  1189. var replyButton;
  1190. var moreButton;
  1191. var saveButton;
  1192. var editButton;
  1193. var commentButton;
  1194. function identifyButtons() {
  1195. checkSelection();
  1196. let getButton = currentEntry.getElementsByClassName("btn btn-link btn-animate");
  1197. if (selectionType === "post") { // posts on link pages
  1198. upvoteButton = getButton[0];
  1199. downvoteButton = getButton[1];
  1200. saveButton = getButton[2];
  1201. commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1];
  1202. if (selectionType === "my-post") { // add edit button if the post is yours
  1203. editButton = currentEntry.getElementsByClassName("btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item")[2];
  1204. }
  1205. } else if (selectionType === "post-fedi" || selectionType === "post-page-fedi") { // federated posts on link pages and on the page
  1206. upvoteButton = getButton[1];
  1207. downvoteButton = getButton[2];
  1208. saveButton = getButton[3];
  1209. commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1];
  1210. } else if (selectionType === "post-page" || selectionType === "my-post-page") { // on the page of the post
  1211. upvoteButton = getButton[0];
  1212. downvoteButton = getButton[1];
  1213. saveButton = getButton[2];
  1214. commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1];
  1215. if (selectionType === "my-post-page") { // add edit button if the post is yours
  1216. editButton = currentEntry.getElementsByClassName("btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item")[2];
  1217. }
  1218. // X - X numbers is the getButton array size depending on if moreButton was clicked or not
  1219. } else if (selectionType === "comment" || selectionType === "my-comment") { // 6 - 10 comments
  1220. upvoteButton = getButton[2];
  1221. downvoteButton = getButton[3];
  1222. replyButton = getButton[4];
  1223. moreButton = getButton[5];
  1224. if (selectionType === "my-comment") { // 6 - 9 add edit button if the comment is yours
  1225. saveButton = getButton[5];
  1226. editButton = getButton[7];
  1227. } else {
  1228. saveButton = getButton[8];
  1229. }
  1230. } else if (selectionType === "comment-context" || selectionType === "my-comment-context") { // 8 - 12 comments with context buttons
  1231. upvoteButton = getButton[4];
  1232. downvoteButton = getButton[5];
  1233. replyButton = getButton[6];
  1234. moreButton = getButton[7];
  1235. if (selectionType === "my-comment-context") { // 8 - 11 add edit button if the comment is yours
  1236. saveButton = getButton[7];
  1237. editButton = getButton[9];
  1238. } else {
  1239. saveButton = getButton[10];
  1240. }
  1241. } else if (selectionType === "comment-inbox") { // 9 - 13 comments in your inbox
  1242. upvoteButton = getButton[5];
  1243. downvoteButton = getButton[6];
  1244. replyButton = getButton[7];
  1245. moreButton = getButton[8];
  1246. saveButton = getButton[11];
  1247. }
  1248. }
  1249.  
  1250. function frontpage() {
  1251. let homeElement = document.getElementsByClassName("d-flex align-items-center navbar-brand me-md-3 active")[0];
  1252. if (homeElement) {
  1253. homeElement.click();
  1254. goToDialog("close");
  1255. } else {
  1256. window.location.replace(window.location.origin);
  1257. }
  1258. }
  1259.  
  1260. function reply(event) {
  1261. identifyButtons();
  1262.  
  1263. if (replyButton) {
  1264. event.preventDefault();
  1265. replyButton.click();
  1266. }
  1267. }
  1268.  
  1269. function community(event) {
  1270. if (event.shiftKey) {
  1271. window.open(
  1272. currentEntry.querySelector("a.community-link").href
  1273. );
  1274. } else {
  1275. currentEntry.querySelector("a.community-link").click();
  1276. }
  1277. }
  1278.  
  1279. function visitUser(event) {
  1280. if (event.shiftKey) {
  1281. window.open(
  1282. currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].href
  1283. );
  1284. } else {
  1285. currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].click();
  1286. }
  1287. }
  1288.  
  1289. function comments(event) {
  1290. identifyButtons();
  1291.  
  1292. if (event.shiftKey) {
  1293. window.open(
  1294. commentButton.href
  1295. );
  1296. } else {
  1297. commentButton.click();
  1298. }
  1299. }
  1300.  
  1301. function getContext(event) {
  1302. if (event.shiftKey) {
  1303. window.open(
  1304. currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].href
  1305. );
  1306. } else {
  1307. currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].click();
  1308. }
  1309. }
  1310.  
  1311. let maxSize = 0;
  1312.  
  1313. function imgResize(n) {
  1314. let expandedImg = currentEntry.getElementsByClassName("overflow-hidden pictrs-image img-fluid img-expanded slight-radius")[0];
  1315. let expandedHeight = expandedImg.height;
  1316. let expandedWidth = expandedImg.width;
  1317. let expandedHeightbefore = expandedHeight;
  1318. let expandedWidthbefore = expandedWidth;
  1319.  
  1320. if (n === "smaller") {
  1321. expandedHeight = expandedHeight / 1.15;
  1322. expandedWidth = expandedWidth / 1.15;
  1323. expandedImg.style.height = expandedHeight + 'px';
  1324. expandedImg.style.width = expandedWidth + 'px';
  1325. maxSize = 0;
  1326. console.log(`maxSize: ${maxSize}`);
  1327. }
  1328.  
  1329. if (n === "larger") {
  1330. expandedHeight = expandedHeight * 1.15;
  1331. expandedWidth = expandedWidth * 1.15;
  1332. expandedImg.style.width = expandedWidth + 'px';
  1333. expandedImg.style.height = expandedHeight + 'px';
  1334.  
  1335. if (maxSize === 1) {
  1336. expandedImg.style.width = expandedWidthbefore + 'px';
  1337. expandedImg.style.height = expandedHeightbefore + 'px';
  1338. }
  1339. if (expandedImg.width !== Math.round(expandedWidth) || expandedImg.height !== Math.round(expandedHeight)) {
  1340. maxSize = 1;
  1341. console.log(`maxSize: ${maxSize}`);
  1342. }
  1343. }
  1344. }
  1345.  
  1346. function save() {
  1347. identifyButtons();
  1348.  
  1349. if (saveButton) {
  1350. saveButton.click();
  1351. } else {
  1352. moreButton.click();
  1353. }
  1354. }
  1355.  
  1356. function edit() {
  1357. identifyButtons();
  1358.  
  1359. if (editButton) {
  1360. editButton.click();
  1361. } else {
  1362. moreButton.click();
  1363. }
  1364. }
  1365.  
  1366. function toggleExpand() {
  1367. const expandButton = currentEntry.getElementsByClassName("thumbnail rounded overflow-hidden d-inline-block bg-transparent")[0];
  1368. const textExpandButton = currentEntry.querySelector(".post-title>button");
  1369. const commentExpandButton = currentEntry.querySelector(".ms-2>div>button");
  1370. const moreExpandButton = currentEntry.querySelector(".ms-1>button");
  1371.  
  1372. if (expandButton) {
  1373. expandButton.click();
  1374.  
  1375. // Scroll into view if picture/text preview cut off
  1376. const imgContainer = currentEntry.querySelector("a.d-inline-block");
  1377.  
  1378. if (imgContainer) {
  1379. // Check container positions once image is loaded
  1380. imgContainer.querySelector("img").addEventListener("load", function() {
  1381. scrollIntoViewWithOffset(imgContainer, pageOffset);
  1382. }, true);
  1383. currentEntry.getElementsByClassName("offset-sm-3 my-2 d-none d-sm-block")[0].className = "my-2 d-none d-sm-block";
  1384. }
  1385. }
  1386.  
  1387. if (textExpandButton) {
  1388. textExpandButton.click();
  1389.  
  1390. const textContainers = [currentEntry.querySelector("#postContent"), currentEntry.querySelector(".card-body")];
  1391. textContainers.forEach(container => {
  1392. if (container) {
  1393. scrollIntoViewWithOffset(container, pageOffset);
  1394. }
  1395. });
  1396. }
  1397.  
  1398. if (commentExpandButton) {
  1399. commentExpandButton.click();
  1400. }
  1401.  
  1402. if (moreExpandButton) {
  1403. moreExpandButton.click();
  1404. selectEntry(getPrevEntry(currentEntry), true);
  1405. }
  1406. }
  1407.  
  1408. function expandEntry() {
  1409. if (!isExpanded()) {
  1410. toggleExpand();
  1411. }
  1412. }
  1413.  
  1414. function collapseEntry() {
  1415. if (isExpanded()) {
  1416. toggleExpand();
  1417. }
  1418. }
  1419.  
  1420. function expandAll() {
  1421. let imageExpand = document.getElementsByClassName("thumbnail rounded overflow-hidden d-inline-block bg-transparent");
  1422. let textExpand = document.getElementsByClassName("btn btn-sm btn-link link-dark link-opacity-75 link-opacity-100-hover align-baseline");
  1423.  
  1424. goToTop();
  1425. console.log(`Expanding`);
  1426. if (expandOption === "text" || expandOption === "both") {
  1427. for (let i = 1; i < textExpand.length; i += 2) {
  1428. try {
  1429. textExpand[i].click();
  1430. } catch {}
  1431. }
  1432. }
  1433. if (expandOption === "images" || expandOption === "both") {
  1434. for (let i = 0; i < imageExpand.length; i++) {
  1435. try {
  1436. imageExpand[i].click();
  1437. } catch {}
  1438. }
  1439. }
  1440. goToTop();
  1441. }
  1442.  
  1443. function goToTop () {
  1444. window.scrollTo(0, 0);
  1445. sessionStorage.setItem('currentselection', 0);
  1446. sessionCurrentEntry("restore");
  1447. }
  1448.  
  1449. function scrollIntoViewWithOffset(e, offset) {
  1450. const y = e.getBoundingClientRect().top + window.scrollY - offset;
  1451. if (scrollPosition === "middle") {
  1452. if (e.getBoundingClientRect().top < 0 ||
  1453. e.getBoundingClientRect().bottom > window.innerHeight
  1454. ) {
  1455. scrollPage(y);
  1456. }
  1457. } else if (scrollPosition === "top") {
  1458. scrollPage(y);
  1459. }
  1460. }
  1461.  
  1462. function scrollPage(y) {
  1463. if (smoothScroll) {
  1464. window.scrollTo({
  1465. top: y,
  1466. behavior: "smooth"
  1467. });
  1468. } else {
  1469. window.scrollTo({
  1470. top: y
  1471. });
  1472. }
  1473. }
  1474.  
  1475. }