lemmy-keyboard-navigation

Easily navigate Lemmy with your keyboard

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

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