lemmy-keyboard-navigation

Easily navigate Lemmy with your keyboard

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

  1. // ==UserScript==
  2. // @name lemmy-keyboard-navigation
  3. // @match https://*/*
  4. // @grant none
  5. // @version 2.2
  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. document.getElementById("navTitle").addEventListener("click", () => {
  523. sessionStorage.setItem('currentselection', 0);
  524. });
  525.  
  526. // Global variables
  527. let currentEntry;
  528. let commentBlock;
  529. let entries = [];
  530. let previousUrl = "";
  531. let expand = false;
  532.  
  533. if (typeof GM_addStyle !== "undefined") {
  534. GM_addStyle(css);
  535. } else if (typeof PRO_addStyle !== "undefined") {
  536. PRO_addStyle(css);
  537. } else if (typeof addStyle !== "undefined") {
  538. addStyle(css);
  539. } else {
  540. let node = document.createElement("style");
  541. node.type = "text/css";
  542. node.appendChild(document.createTextNode(css));
  543. let heads = document.getElementsByTagName("head");
  544. if (heads.length > 0) {
  545. heads[0].appendChild(node);
  546. } else {
  547. // no head yet, stick it whereever
  548. document.documentElement.appendChild(node);
  549. }
  550. }
  551.  
  552. const selectedClass = "selected";
  553.  
  554. const targetNode = document.documentElement;
  555. const config = {
  556. childList: true,
  557. subtree: true
  558. };
  559.  
  560. const observer = new MutationObserver(() => {
  561. entries = document.querySelectorAll(".post-listing, .comment-node");
  562.  
  563. if (entries.length > 0) {
  564. if (location.href !== previousUrl) {
  565. previousUrl = location.href;
  566. currentEntry = null;
  567. }
  568. init();
  569. }
  570. });
  571.  
  572. observer.observe(targetNode, config);
  573.  
  574. function init() {
  575. // If jumping to comments
  576. if (window.location.search.includes("scrollToComments=true") &&
  577. entries.length > 1 &&
  578. (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0)
  579. ) {
  580. selectEntry(entries[1], true);
  581. }
  582. // If jumping to comment from anchor link
  583. else if (window.location.pathname.includes("/comment/") &&
  584. (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0)
  585. ) {
  586. const commentId = window.location.pathname.replace("/comment/", "");
  587. const anchoredEntry = document.getElementById("comment-" + commentId);
  588.  
  589. if (anchoredEntry) {
  590. selectEntry(anchoredEntry, true);
  591. }
  592. }
  593. // If no entries yet selected, default to last selected
  594. else if (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0) {
  595. if (sessionStorage.getItem('currentselection') === null) {
  596. selectEntry(entries[0]);
  597. } else {
  598. sessionCurrentEntry("restore");
  599. }
  600. }
  601.  
  602. Array.from(entries).forEach(entry => {
  603. entry.removeEventListener("click", clickEntry, true);
  604. entry.addEventListener('click', clickEntry, true);
  605. });
  606.  
  607. document.removeEventListener("keydown", handleKeyPress, true);
  608. document.addEventListener("keydown", handleKeyPress, true);
  609. }
  610.  
  611. function handleKeyPress(event) {
  612. if (["TEXTAREA", "INPUT"].indexOf(event.target.tagName) > -1 || event.metaKey) {
  613. return;
  614. }
  615.  
  616. switch (modalMode) {
  617. case modalMode = 0:
  618. switch (event.code) {
  619. case nextKey:
  620. case prevKey:
  621. previousKey(event);
  622. break;
  623. case upvoteKey:
  624. upVote();
  625. break;
  626. case downvoteKey:
  627. downVote();
  628. break;
  629. case expandKey:
  630. if (event.shiftKey) {
  631. expandAll();
  632. } else {
  633. toggleExpand();
  634. expand = isExpanded() ? true : false;
  635. }
  636. break;
  637. case smallerImgKey:
  638. imgResize("smaller");
  639. break;
  640. case biggerImgKey:
  641. imgResize("larger");
  642. break;
  643. case saveKey:
  644. save();
  645. break;
  646. case editKey:
  647. edit();
  648. break;
  649. case openCommentsKey:
  650. comments(event);
  651. break;
  652. case modalPopupKey:
  653. dialogUpdate();
  654. goToDialog("open");
  655. break;
  656. case contextKey:
  657. getContext(event);
  658. break;
  659. case replyCommKey:
  660. // allow refresh with Ctrl + R
  661. if (!event.ctrlKey) {
  662. if (window.location.pathname.includes("/post/")) {
  663. reply(event);
  664. } else {
  665. community(event);
  666. }
  667. }
  668. break;
  669. case userKey:
  670. visitUser(event);
  671. break;
  672. case openLinkAndCollapseKey:
  673. if (window.location.pathname.includes("/post/")) {
  674. toggleExpand();
  675. } else {
  676. const linkElement = currentEntry.querySelector(".col.flex-grow-1>p>a");
  677. if (linkElement) {
  678. if (event.shiftKey) {
  679. window.open(linkElement.href);
  680. } else {
  681. linkElement.click();
  682. }
  683. } else {
  684. comments(event);
  685. }
  686. }
  687. break;
  688. case parentCommentKey: {
  689. let targetBlock;
  690. if (currentEntry.classList.contains("ms-1")) {
  691. targetBlock = getPrevEntry(currentEntry);
  692. } else if (currentEntry.parentElement.parentElement.parentElement.nodeName === "LI") {
  693. targetBlock = currentEntry.parentElement.parentElement.parentElement.getElementsByTagName("article")[0];
  694. }
  695. if (targetBlock) {
  696. if (expand) {
  697. collapseEntry();
  698. }
  699. selectEntry(targetBlock, true);
  700. if (expand) {
  701. expandEntry();
  702. }
  703. }
  704. }
  705. break;
  706. case topKey:
  707. goToTop();
  708. break;
  709. case linkOneKey:
  710. clickLink(1);
  711. break;
  712. case linkTwoKey:
  713. clickLink(2);
  714. break;
  715. case linkThreeKey:
  716. clickLink(3);
  717. break;
  718. case linkFourKey:
  719. clickLink(4);
  720. break;
  721. case linkFiveKey:
  722. clickLink(5);
  723. break;
  724. case linkSixKey:
  725. clickLink(6);
  726. break;
  727. case linkSevenKey:
  728. clickLink(7);
  729. break;
  730. case linkEightKey:
  731. clickLink(8);
  732. break;
  733. case linkNineKey:
  734. clickLink(9);
  735. break;
  736. case linkZeroKey:
  737. clickLink(0);
  738. break;
  739. case nextPageKey:
  740. case prevPageKey: {
  741. const pageButtons = Array.from(document.querySelectorAll(".paginator>button"));
  742.  
  743. if (pageButtons && (document.getElementsByClassName('paginator').length > 0)) {
  744. if (event.code === nextPageKey) {
  745. document.querySelectorAll(".paginator>.btn.btn-secondary")[1].click(); //next
  746. } else {
  747. document.querySelectorAll(".paginator>.btn.btn-secondary")[0].click(); //prev
  748. }
  749. }
  750. // Jump next block of comments
  751. if (event.code === nextPageKey) {
  752. commentBlock = getNextEntrySameLevel(currentEntry);
  753. }
  754. // Jump previous block of comments
  755. if (event.code === prevPageKey) {
  756. commentBlock = getPrevEntrySameLevel(currentEntry);
  757. }
  758. if (commentBlock) {
  759. if (expand) {
  760. collapseEntry();
  761. }
  762. selectEntry(commentBlock, true);
  763. if (expand) {
  764. expandEntry();
  765. }
  766. }
  767. }
  768. sessionStorage.setItem('currentselection', 0); //reset the selection back to the first post when switching pages
  769. }
  770. break;
  771. case modalMode = 1:
  772. switch (event.code) {
  773. case escapeKey:
  774. modalMode = 0;
  775. console.log(`modalMode: ${modalMode}`);
  776. break;
  777. case modalPopupKey:
  778. goToDialog("close");
  779. break;
  780. case modalSavedKey:
  781. if (window.location.pathname.includes("/u/")) {
  782. let savedelement = document.getElementsByClassName("btn btn-outline-secondary pointer")[3];
  783. if (savedelement) {
  784. savedelement.click();
  785. goToDialog("close");
  786. }
  787. } else {
  788. instanceAndUser("saved");
  789. }
  790. break;
  791. case modalFrontpageKey:
  792. frontpage();
  793. break;
  794. case modalProfileKey:
  795. let profileelement = document.getElementsByClassName("dropdown-item px-2")[0];
  796. if (profileelement) {
  797. profileelement.click();
  798. goToDialog("close");
  799. } else {
  800. instanceAndUser("profile");
  801. }
  802. break;
  803. case modalInboxKey:
  804. let notifelement = document.getElementsByClassName("nav-link d-inline-flex align-items-center d-md-inline-block")[2];
  805. if (notifelement) {
  806. notifelement.click();
  807. goToDialog("close");
  808. } else {
  809. window.location.replace(window.location.origin + "/login");
  810. }
  811. break;
  812. case modalSortOneKey:
  813. let firstbutton = document.getElementsByClassName("btn btn-outline-secondary")[0];
  814. sessionStorage.setItem('currentselection', 0); //reset the selection to the first post when switching filters
  815. firstbutton.click();
  816. goToDialog("close");
  817. break;
  818. case modalSortTwoKey:
  819. let secondbutton = document.getElementsByClassName("btn btn-outline-secondary")[1];
  820. sessionStorage.setItem('currentselection', 0);
  821. secondbutton.click();
  822. goToDialog("close");
  823. break;
  824. case modalSortThreeKey:
  825. let thirdbutton = document.getElementsByClassName("btn btn-outline-secondary")[2];
  826. sessionStorage.setItem('currentselection', 0);
  827. thirdbutton.click();
  828. goToDialog("close");
  829. break;
  830. case modalSortFourKey:
  831. let fourthbutton = document.getElementsByClassName("btn btn-outline-secondary")[3];
  832. sessionStorage.setItem('currentselection', 0);
  833. fourthbutton.click();
  834. goToDialog("close");
  835. break;
  836. case modalSortFiveKey:
  837. let fifthbutton = document.getElementsByClassName("btn btn-outline-secondary")[4];
  838. sessionStorage.setItem('currentselection', 0);
  839. fifthbutton.click();
  840. goToDialog("close");
  841. break;
  842. case modalOptionsKey:
  843. options("open");
  844. goToDialog("close");
  845. break;
  846. }
  847. }
  848. }
  849.  
  850. function getNextEntry(e) {
  851. const currentEntryIndex = Array.from(entries).indexOf(e);
  852.  
  853. if (currentEntryIndex + 1 >= entries.length) {
  854. return e;
  855. }
  856. return entries[currentEntryIndex + 1];
  857. }
  858.  
  859. function getPrevEntry(e) {
  860. const currentEntryIndex = Array.from(entries).indexOf(e);
  861.  
  862. if (currentEntryIndex - 1 < 0) {
  863. return e;
  864. }
  865. return entries[currentEntryIndex - 1];
  866. }
  867.  
  868. function getNextEntrySameLevel(e) {
  869. const nextSibling = e.parentElement.nextElementSibling;
  870.  
  871. if (!nextSibling || nextSibling.getElementsByTagName("article").length < 1) {
  872. return getNextEntry(e);
  873. }
  874.  
  875. return nextSibling.getElementsByTagName("article")[0];
  876. }
  877.  
  878. function getPrevEntrySameLevel(e) {
  879. const prevSibling = e.parentElement.previousElementSibling;
  880.  
  881. if (!prevSibling || prevSibling.getElementsByTagName("article").length < 1) {
  882. return getPrevEntry(e);
  883. }
  884.  
  885. return prevSibling.getElementsByTagName("article")[0];
  886. }
  887.  
  888. function dialogUpdate() {
  889. para.remove();
  890. para = document.createElement("p");
  891. para.innerHTML = `
  892. <h3><b>Sort by</b></h3>
  893. <p>${modalSortOneKey} = ${dialogLocation(1)}</br>
  894. ${modalSortTwoKey} = ${dialogLocation(2)}</br>
  895. ${modalSortThreeKey} = ${dialogLocation(3)}</br>
  896. ${modalSortFourKey} = ${dialogLocation(4)}</br>
  897. ${modalSortFiveKey} = ${dialogLocation(5)}</p>
  898. <h3><b>Go To Page</b></h3>
  899. <p>${modalFrontpageKey} = Frontpage</br>
  900. ${modalSavedKey} = Saved</br>
  901. ${modalProfileKey} = User Profile Page</br>
  902. ${modalInboxKey} = Inbox</br></p>
  903. <h6>${modalOptionsKey} = Options Page</br></br></h6>
  904. `;
  905. myDialog.appendChild(para);
  906. myDialog.appendChild(button);
  907. }
  908.  
  909. function dialogLocation(n) {
  910. var sort = document.getElementsByClassName("btn btn-outline-secondary");
  911.  
  912. if (n === 1 && 1 <= sort.length) {
  913. return sort[0].innerText;
  914. }
  915. if (n === 2 && 2 <= sort.length) {
  916. return sort[1].innerText;
  917. }
  918. if (n === 3 && 3 <= sort.length) {
  919. return sort[2].innerText;
  920. }
  921. if (n === 4 && 4 <= sort.length) {
  922. return sort[3].innerText;
  923. }
  924. if (n === 5 && 5 <= sort.length) {
  925. return sort[4].innerText;
  926. }
  927. return "N/A";
  928. }
  929.  
  930. function clickEntry(event) {
  931. const e = event.currentTarget;
  932. const target = event.target;
  933.  
  934. // Deselect if already selected, also ignore if clicking on any link/button
  935. if (e === currentEntry && e.classList.contains(selectedClass) &&
  936. !(
  937. target.tagName.toLowerCase() === "button" || target.tagName.toLowerCase() === "a" ||
  938. target.parentElement.tagName.toLowerCase() === "button" ||
  939. target.parentElement.tagName.toLowerCase() === "a" ||
  940. target.parentElement.parentElement.tagName.toLowerCase() === "button" ||
  941. target.parentElement.parentElement.tagName.toLowerCase() === "a"
  942. )
  943. ) {
  944. e.classList.remove(selectedClass);
  945. } else {
  946. selectEntry(e);
  947. }
  948. }
  949.  
  950. function selectEntry(e, scrollIntoView = false) {
  951. if (currentEntry) {
  952. currentEntry.classList.remove(selectedClass);
  953. let linkNumber = currentEntry.querySelectorAll(".linkNumber");
  954. if (linkNumber) {
  955. for (const link of linkNumber) {
  956. link.remove();
  957. }
  958. }
  959. }
  960. currentEntry = e;
  961. try {
  962. currentEntry.classList.add(selectedClass);
  963. } catch { // if currentEntry is undefined
  964. currentEntry = document.querySelectorAll(".post-listing, .comment-node")[0];
  965. currentEntry.classList.add(selectedClass);
  966. }
  967. sessionCurrentEntry("save");
  968. let links = currentEntry.getElementsByClassName("md-div")[0];
  969. if (links) {
  970. let alink = links.querySelectorAll('a');
  971. if (alink.length > 0) {
  972. alink.forEach(function(value, i) {
  973. let linkNumber = document.createElement("span");
  974. linkNumber.classList.add("linkNumber");
  975. linkNumber.style.fontSize = "9px";
  976. linkNumber.style.lineHeight = 0;
  977. linkNumber.style.verticalAlign = "super";
  978. linkNumber.setAttribute("data-text", `[${i+1}]`);
  979. linkNumber.innerText = `[${i+1}]`;
  980. linkNumber.title = `Press ${i+1} to open link`;
  981. if (i <= 9) {
  982. value.appendChild(linkNumber);
  983. }
  984. });
  985. }
  986. }
  987.  
  988. if (scrollIntoView) {
  989. scrollIntoViewWithOffset(e, pageOffset);
  990. }
  991. }
  992.  
  993. function sessionCurrentEntry(n) {
  994. const sessionEntry = sessionStorage.getItem('currentselection');
  995. const currentEntryIndex = Array.from(entries).indexOf(currentEntry);
  996.  
  997. if (n === "save") {
  998. if (document.querySelector(".home")) {
  999. sessionStorage.setItem('currentselection', currentEntryIndex);
  1000. }
  1001. } else if (n === "restore") {
  1002. selectEntry(entries[sessionEntry]);
  1003. console.log(`Set to entry ${sessionEntry}`);
  1004. }
  1005. }
  1006.  
  1007. function clickLink(n) {
  1008. let links = currentEntry.getElementsByClassName("md-div")[0];
  1009. let alink = links.querySelectorAll('a');
  1010. if (n === 1) {
  1011. window.open(
  1012. alink[0].href
  1013. );
  1014. } else if (n === 2) {
  1015. window.open(
  1016. alink[1].href
  1017. );
  1018. } else if (n === 3) {
  1019. window.open(
  1020. alink[2].href
  1021. );
  1022. } else if (n === 4) {
  1023. window.open(
  1024. alink[3].href
  1025. );
  1026. } else if (n === 5) {
  1027. window.open(
  1028. alink[4].href
  1029. );
  1030. } else if (n === 6) {
  1031. window.open(
  1032. alink[5].href
  1033. );
  1034. } else if (n === 7) {
  1035. window.open(
  1036. alink[6].href
  1037. );
  1038. } else if (n === 8) {
  1039. window.open(
  1040. alink[7].href
  1041. );
  1042. } else if (n === 9) {
  1043. window.open(
  1044. alink[8].href
  1045. );
  1046. } else if (n === 0) {
  1047. window.open(
  1048. alink[9].href
  1049. );
  1050. }
  1051. }
  1052.  
  1053. function isExpanded() {
  1054. if (
  1055. currentEntry.querySelector("a.d-inline-block:not(.thumbnail)") ||
  1056. currentEntry.querySelector("#postContent") ||
  1057. currentEntry.querySelector(".card-body")
  1058. ) {
  1059. return true;
  1060. }
  1061.  
  1062. return false;
  1063. }
  1064.  
  1065. function previousKey(event) {
  1066. let selectedEntry;
  1067. // Next button
  1068. if (event.code === nextKey) {
  1069. if (event.shiftKey && vimKeyNavigation) {
  1070. selectedEntry = getNextEntrySameLevel(currentEntry);
  1071.  
  1072. } else {
  1073. selectedEntry = getNextEntry(currentEntry);
  1074. }
  1075. }
  1076. // Previous button
  1077. if (event.code === prevKey) {
  1078. if (event.shiftKey && vimKeyNavigation) {
  1079. selectedEntry = getPrevEntrySameLevel(currentEntry);
  1080.  
  1081. } else {
  1082. selectedEntry = getPrevEntry(currentEntry);
  1083. }
  1084. }
  1085. if (selectedEntry) {
  1086. if (expand) {
  1087. collapseEntry();
  1088. }
  1089. selectEntry(selectedEntry, true);
  1090. if (expand) {
  1091. expandEntry();
  1092. }
  1093. }
  1094. }
  1095.  
  1096. function upVote() {
  1097. identifyButtons();
  1098.  
  1099. if (upvoteButton) {
  1100. upvoteButton.click();
  1101. }
  1102. }
  1103.  
  1104. function downVote() {
  1105. identifyButtons();
  1106.  
  1107. if (downvoteButton) {
  1108. downvoteButton.click();
  1109. }
  1110. }
  1111.  
  1112. function goToDialog(n) {
  1113.  
  1114. const closeButton = document.getElementsByClassName("CLOSEBUTTON1")[0];
  1115. closeButton.addEventListener("click", () => {
  1116. myDialog.close();
  1117. modalMode = 0;
  1118. console.log(`modalMode: ${modalMode}`);
  1119. });
  1120. if (n === "open") {
  1121. myDialog.showModal();
  1122. modalMode = 1;
  1123. console.log(`modalMode: ${modalMode}`);
  1124. }
  1125.  
  1126. if (n === "close") {
  1127. myDialog.close();
  1128. modalMode = 0;
  1129. console.log(`modalMode: ${modalMode}`);
  1130. }
  1131. }
  1132.  
  1133. function instanceAndUser(n) {
  1134. let currentInstance = window.location.origin;
  1135. let username = document.getElementsByClassName("btn dropdown-toggle")[0].textContent;
  1136.  
  1137. if (n === "profile") {
  1138. if (username) {
  1139. let userlink = currentInstance + "/u/" + username;
  1140. window.location.replace(userlink);
  1141. } else {
  1142. window.location.replace(currentInstance + "/login");
  1143. }
  1144. }
  1145. if (n === "saved") {
  1146. if (username) {
  1147. let savedlink = currentInstance + "/u/" + username + "?page=1&sort=New&view=Saved";
  1148. window.location.replace(savedlink);
  1149. } else {
  1150. window.location.replace(currentInstance + "/login");
  1151. }
  1152. }
  1153. }
  1154.  
  1155. var selectionType;
  1156. function checkSelection() {
  1157. let postSelection = document.getElementsByClassName("post-listing mt-2 selected")[0];
  1158. let username;
  1159. try {
  1160. username = '@' + document.getElementsByClassName("btn dropdown-toggle")[0].textContent;
  1161. } catch {
  1162. username = ''; // logged out
  1163. }
  1164. let posterusername = currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].innerText;
  1165. let contextCheck;
  1166.  
  1167. if (postSelection) {
  1168. selectionType = "post";
  1169. contextCheck = currentEntry.getElementsByClassName("btn btn-link btn-animate")[11]; // check for direct link button (rainbow star)
  1170. if (contextCheck) {
  1171. selectionType = `${selectionType}-fedi`;
  1172. }
  1173.  
  1174. if (window.location.pathname.includes("/post/")) {
  1175. contextCheck = currentEntry.getElementsByClassName("btn btn-link btn-animate")[8].href; // check for direct link button
  1176. if (contextCheck === `${window.location.origin}/create_post`) {
  1177. selectionType = "post-page"
  1178. } else {
  1179. selectionType = "post-page-fedi"
  1180. }
  1181. }
  1182. } else {
  1183. selectionType = "comment";
  1184. let contextButton = currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].href;
  1185. let contextButton2 = currentEntry.getElementsByClassName("btn btn-link btn-animate")[2].href;
  1186.  
  1187. if (contextButton === contextButton2) {
  1188. selectionType = `${selectionType}-context`;
  1189. }
  1190.  
  1191. if (window.location.pathname.includes("/inbox")) {
  1192. selectionType = "comment-inbox";
  1193. }
  1194. }
  1195. if (username === posterusername) {
  1196. selectionType = `my-${selectionType}`;
  1197. }
  1198. console.log(`current selection: ${selectionType}`);
  1199. }
  1200.  
  1201. var upvoteButton;
  1202. var downvoteButton;
  1203. var replyButton;
  1204. var moreButton;
  1205. var saveButton;
  1206. var editButton;
  1207. var commentButton;
  1208. function identifyButtons() {
  1209. checkSelection();
  1210. let getButton = currentEntry.getElementsByClassName("btn btn-link btn-animate");
  1211. if (selectionType === "post") { // posts on link pages
  1212. upvoteButton = getButton[0];
  1213. downvoteButton = getButton[1];
  1214. saveButton = getButton[2];
  1215. commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1];
  1216. if (selectionType === "my-post") { // add edit button if the post is yours
  1217. editButton = currentEntry.getElementsByClassName("btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item")[2];
  1218. }
  1219. } else if (selectionType === "post-fedi" || selectionType === "post-page-fedi") { // federated posts on link pages and on the page
  1220. upvoteButton = getButton[1];
  1221. downvoteButton = getButton[2];
  1222. saveButton = getButton[3];
  1223. commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1];
  1224. } else if (selectionType === "post-page" || selectionType === "my-post-page") { // on the page of the post
  1225. upvoteButton = getButton[0];
  1226. downvoteButton = getButton[1];
  1227. saveButton = getButton[2];
  1228. commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1];
  1229. if (selectionType === "my-post-page") { // add edit button if the post is yours
  1230. editButton = currentEntry.getElementsByClassName("btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item")[2];
  1231. }
  1232. // X - X numbers is the getButton array size depending on if moreButton was clicked or not
  1233. } else if (selectionType === "comment" || selectionType === "my-comment") { // 6 - 10 comments
  1234. upvoteButton = getButton[2];
  1235. downvoteButton = getButton[3];
  1236. replyButton = getButton[4];
  1237. moreButton = getButton[5];
  1238. if (selectionType === "my-comment") { // 6 - 9 add edit button if the comment is yours
  1239. saveButton = getButton[5];
  1240. editButton = getButton[7];
  1241. } else {
  1242. saveButton = getButton[8];
  1243. }
  1244. } else if (selectionType === "comment-context" || selectionType === "my-comment-context") { // 8 - 12 comments with context buttons
  1245. upvoteButton = getButton[4];
  1246. downvoteButton = getButton[5];
  1247. replyButton = getButton[6];
  1248. moreButton = getButton[7];
  1249. if (selectionType === "my-comment-context") { // 8 - 11 add edit button if the comment is yours
  1250. saveButton = getButton[7];
  1251. editButton = getButton[9];
  1252. } else {
  1253. saveButton = getButton[10];
  1254. }
  1255. } else if (selectionType === "comment-inbox") { // 9 - 13 comments in your inbox
  1256. upvoteButton = getButton[5];
  1257. downvoteButton = getButton[6];
  1258. replyButton = getButton[7];
  1259. moreButton = getButton[8];
  1260. saveButton = getButton[11];
  1261. }
  1262. }
  1263.  
  1264. function frontpage() {
  1265. let homeElement = document.getElementsByClassName("d-flex align-items-center navbar-brand me-md-3 active")[0];
  1266. if (homeElement) {
  1267. homeElement.click();
  1268. goToDialog("close");
  1269. } else {
  1270. window.location.replace(window.location.origin);
  1271. }
  1272. }
  1273.  
  1274. function reply(event) {
  1275. identifyButtons();
  1276.  
  1277. if (replyButton) {
  1278. event.preventDefault();
  1279. replyButton.click();
  1280. }
  1281. }
  1282.  
  1283. function community(event) {
  1284. if (event.shiftKey) {
  1285. window.open(
  1286. currentEntry.querySelector("a.community-link").href
  1287. );
  1288. } else {
  1289. currentEntry.querySelector("a.community-link").click();
  1290. }
  1291. }
  1292.  
  1293. function visitUser(event) {
  1294. if (event.shiftKey) {
  1295. window.open(
  1296. currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].href
  1297. );
  1298. } else {
  1299. currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].click();
  1300. }
  1301. }
  1302.  
  1303. function comments(event) {
  1304. identifyButtons();
  1305.  
  1306. if (event.shiftKey) {
  1307. window.open(
  1308. commentButton.href
  1309. );
  1310. } else {
  1311. commentButton.click();
  1312. }
  1313. }
  1314.  
  1315. function getContext(event) {
  1316. if (event.shiftKey) {
  1317. window.open(
  1318. currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].href
  1319. );
  1320. } else {
  1321. currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].click();
  1322. }
  1323. }
  1324.  
  1325. let maxSize = 0;
  1326.  
  1327. function imgResize(n) {
  1328. let expandedImg = currentEntry.getElementsByClassName("overflow-hidden pictrs-image img-fluid img-expanded slight-radius")[0];
  1329. let expandedHeight = expandedImg.height;
  1330. let expandedWidth = expandedImg.width;
  1331. let expandedHeightbefore = expandedHeight;
  1332. let expandedWidthbefore = expandedWidth;
  1333.  
  1334. if (n === "smaller") {
  1335. expandedHeight = expandedHeight / 1.15;
  1336. expandedWidth = expandedWidth / 1.15;
  1337. expandedImg.style.height = expandedHeight + 'px';
  1338. expandedImg.style.width = expandedWidth + 'px';
  1339. maxSize = 0;
  1340. console.log(`maxSize: ${maxSize}`);
  1341. }
  1342.  
  1343. if (n === "larger") {
  1344. expandedHeight = expandedHeight * 1.15;
  1345. expandedWidth = expandedWidth * 1.15;
  1346. expandedImg.style.width = expandedWidth + 'px';
  1347. expandedImg.style.height = expandedHeight + 'px';
  1348.  
  1349. if (maxSize === 1) {
  1350. expandedImg.style.width = expandedWidthbefore + 'px';
  1351. expandedImg.style.height = expandedHeightbefore + 'px';
  1352. }
  1353. if (expandedImg.width !== Math.round(expandedWidth) || expandedImg.height !== Math.round(expandedHeight)) {
  1354. maxSize = 1;
  1355. console.log(`maxSize: ${maxSize}`);
  1356. }
  1357. }
  1358. }
  1359.  
  1360. function save() {
  1361. identifyButtons();
  1362.  
  1363. if (saveButton) {
  1364. saveButton.click();
  1365. } else {
  1366. moreButton.click();
  1367. }
  1368. }
  1369.  
  1370. function edit() {
  1371. identifyButtons();
  1372.  
  1373. if (editButton) {
  1374. editButton.click();
  1375. } else {
  1376. moreButton.click();
  1377. }
  1378. }
  1379.  
  1380. function toggleExpand() {
  1381. const expandButton = currentEntry.getElementsByClassName("thumbnail rounded overflow-hidden d-inline-block bg-transparent")[0];
  1382. const textExpandButton = currentEntry.querySelector(".post-title>button");
  1383. const videoexpandButton = currentEntry.getElementsByClassName("thumbnail rounded bg-light d-flex justify-content-center")[0];
  1384. const commentExpandButton = currentEntry.querySelector(".ms-2>div>button");
  1385. const moreExpandButton = currentEntry.querySelector(".ms-1>button");
  1386.  
  1387. if (expandButton) {
  1388. expandButton.click();
  1389.  
  1390. // Scroll into view if picture/text preview cut off
  1391. const imgContainer = currentEntry.querySelector("a.d-inline-block");
  1392.  
  1393. if (imgContainer) {
  1394. // Check container positions once image is loaded
  1395. imgContainer.querySelector("img").addEventListener("load", function() {
  1396. scrollIntoViewWithOffset(imgContainer, pageOffset);
  1397. }, true);
  1398. currentEntry.getElementsByClassName("offset-sm-3 my-2 d-none d-sm-block")[0].className = "my-2 d-none d-sm-block";
  1399. }
  1400. }
  1401.  
  1402. if (textExpandButton) {
  1403. textExpandButton.click();
  1404.  
  1405. const textContainers = [currentEntry.querySelector("#postContent"), currentEntry.querySelector(".card-body")];
  1406. textContainers.forEach(container => {
  1407. if (container) {
  1408. scrollIntoViewWithOffset(container, pageOffset);
  1409. }
  1410. });
  1411. }
  1412.  
  1413. if (videoexpandButton) {
  1414. if (videoexpandButton.textContent === "play") { // check if it's a video and not a link or something // this works between languages :>
  1415. videoexpandButton.click();
  1416.  
  1417. // Scroll into view if video/text preview cut off
  1418. const vidContainer = currentEntry.querySelector("div.embed-responsive");
  1419.  
  1420. if (vidContainer) {
  1421. scrollIntoViewWithOffset(vidContainer, pageOffset);
  1422. }
  1423. }
  1424. }
  1425.  
  1426. if (commentExpandButton) {
  1427. commentExpandButton.click();
  1428. }
  1429.  
  1430. if (moreExpandButton) {
  1431. moreExpandButton.click();
  1432. selectEntry(getPrevEntry(currentEntry), true);
  1433. }
  1434. }
  1435.  
  1436. function expandEntry() {
  1437. if (!isExpanded()) {
  1438. toggleExpand();
  1439. }
  1440. }
  1441.  
  1442. function collapseEntry() {
  1443. if (isExpanded()) {
  1444. toggleExpand();
  1445. }
  1446. }
  1447.  
  1448. function expandAll() {
  1449. let imageExpand = document.getElementsByClassName("thumbnail rounded overflow-hidden d-inline-block bg-transparent");
  1450. let textExpand = document.getElementsByClassName("btn btn-sm btn-link link-dark link-opacity-75 link-opacity-100-hover align-baseline");
  1451.  
  1452. goToTop();
  1453. console.log(`Expanding`);
  1454. if (expandOption === "text" || expandOption === "both") {
  1455. for (let i = 1; i < textExpand.length; i += 2) {
  1456. try {
  1457. textExpand[i].click();
  1458. } catch {}
  1459. }
  1460. }
  1461. if (expandOption === "images" || expandOption === "both") {
  1462. for (let i = 0; i < imageExpand.length; i++) {
  1463. try {
  1464. imageExpand[i].click();
  1465. } catch {}
  1466. }
  1467. }
  1468. goToTop();
  1469. }
  1470.  
  1471. function goToTop () {
  1472. window.scrollTo(0, 0);
  1473. sessionStorage.setItem('currentselection', 0);
  1474. sessionCurrentEntry("restore");
  1475. }
  1476.  
  1477. function scrollIntoViewWithOffset(e, offset) {
  1478. const y = e.getBoundingClientRect().top + window.scrollY - offset;
  1479. if (scrollPosition === "middle") {
  1480. if (e.getBoundingClientRect().top < 0 ||
  1481. e.getBoundingClientRect().bottom > window.innerHeight
  1482. ) {
  1483. scrollPage(y);
  1484. }
  1485. } else if (scrollPosition === "top") {
  1486. scrollPage(y);
  1487. }
  1488. }
  1489.  
  1490. function scrollPage(y) {
  1491. if (smoothScroll) {
  1492. window.scrollTo({
  1493. top: y,
  1494. behavior: "smooth"
  1495. });
  1496. } else {
  1497. window.scrollTo({
  1498. top: y
  1499. });
  1500. }
  1501. }
  1502.  
  1503. }