您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Easily navigate Lemmy with your keyboard
当前为
// ==UserScript== // @name lemmy-keyboard-navigation // @match https://*/* // @grant none // @version 2.1 // @author vmavromatis // @author [email protected] // @author InfinibyteF4 // @author aglidden // @license GPL3 // @icon https://raw.githubusercontent.com/vmavromatis/Lemmy-keyboard-navigation/main/icon.png?inline=true // @homepageURL https://github.com/vmavromatis/Lemmy-keyboard-navigation // @namespace https://github.com/vmavromatis/Lemmy-keyboard-navigation // @description Easily navigate Lemmy with your keyboard // @run-at document-end // ==/UserScript== /*global window,console,localStorage,sessionStorage,document,GM_addStyle,PRO_addStyle,addStyle,MutationObserver,location*/ //isLemmySite if (document.querySelectorAll('.lemmy-site').length >= 1) { // DEBUGGING (ignore me!) //localStorage.clear(); //sessionStorage.clear(); //settings page (this is from lemmyTools) const optionsKey = "lemmy-keyboard-navigation-Options"; function getSettingsFromLocalStorage() { try { return JSON.parse(localStorage.getItem(optionsKey) || "{}"); } catch (_) { return {}; } } function checkedIfTrue(val) { return val ? "checked" : ""; } function options(open) { const odiv = document.getElementById("lkOptions"); let userOptions = {}; if (open === "open") { odiv.style.display = "block"; } else if (open === "set") { //First run set defaults or pull from localstorage. userOptions = Object.assign({}, { pageOffset: 5, vimKeyNavigation: true, smoothScroll: false, scrollPosition: "middle", backgroundHex: "#373737", kb_expand: "KeyX", kb_comments: "KeyC", kb_openLink: "Enter", kb_parent: "KeyP", kb_upvote: "KeyA", kb_downvote: "KeyZ", kb_replyComm: "KeyR", kb_save: "KeyS", kb_context: "KeyQ", kb_smallerImg: "Minus", kb_largerImg: "Equal", kb_user: "KeyU", kb_edit: "KeyE", kb_top: "KeyT", m_dialog: "KeyG", m_first: "Digit1", m_second: "Digit2", m_third: "Digit3", m_fourth: "Digit4", m_fifth: "Digit5", m_frontpage: "KeyF", m_saved: "KeyS", m_userpage: "KeyU", m_inbox: "KeyI", m_options: "KeyO" }, getSettingsFromLocalStorage() ); localStorage.setItem(optionsKey, JSON.stringify(userOptions)); } else if (open === "save") { //save button odiv.style.display = "none"; //general userOptions.vimKeyNavigation = document.getElementById("option_vimKeyNavigation").checked; userOptions.smoothScroll = document.getElementById("option_smoothScroll").checked; let offset = parseFloat( document.getElementById("option_pageOffset").value ); if (isNaN(offset) || offset < 0 || offset > 100) { userOptions.pageOffset = 0; } else { userOptions.pageOffset = offset; } userOptions.scrollPosition = document.getElementById("option_scrollPosition").value; userOptions.backgroundHex = document.getElementById("option_backgroundHex").value; //keybinds userOptions.kb_expand = document.getElementById("option_kb_expand").value; userOptions.kb_comments = document.getElementById("option_kb_comments").value; userOptions.kb_openLink = document.getElementById("option_kb_openLink").value; userOptions.kb_parent = document.getElementById("option_kb_parent").value; userOptions.kb_upvote = document.getElementById("option_kb_upvote").value; userOptions.kb_downvote = document.getElementById("option_kb_downvote").value; userOptions.kb_replyComm = document.getElementById("option_kb_replyComm").value; userOptions.kb_save = document.getElementById("option_kb_save").value; userOptions.kb_context = document.getElementById("option_kb_context").value; userOptions.kb_smallerImg = document.getElementById("option_kb_smallerImg").value; userOptions.kb_largerImg = document.getElementById("option_kb_largerImg").value; userOptions.kb_user = document.getElementById("option_kb_user").value; userOptions.kb_edit = document.getElementById("option_kb_edit").value; userOptions.kb_top = document.getElementById("option_kb_top").value; //dialog keybinds userOptions.m_dialog = document.getElementById("option_m_dialog").value; userOptions.m_first = document.getElementById("option_m_first").value; userOptions.m_second = document.getElementById("option_m_second").value; userOptions.m_third = document.getElementById("option_m_third").value; userOptions.m_fourth = document.getElementById("option_m_fourth").value; userOptions.m_fifth = document.getElementById("option_m_fifth").value; userOptions.m_frontpage = document.getElementById("option_m_frontpage").value; userOptions.m_saved = document.getElementById("option_m_saved").value; userOptions.m_userpage = document.getElementById("option_m_userpage").value; userOptions.m_inbox = document.getElementById("option_m_inbox").value; userOptions.m_options = document.getElementById("option_m_options").value; localStorage.setItem(optionsKey, JSON.stringify(userOptions)); window.location.reload(); } userOptions = getSettingsFromLocalStorage(); return userOptions; } let settings = options("set"); let vimKeyNavigation = checkedIfTrue(settings.vimKeyNavigation); let smoothScroll = checkedIfTrue(settings.smoothScroll); let pageOffset = window.innerHeight * settings.pageOffset / 100; let scrollPosition = settings.scrollPosition; let backgroundHex = settings.backgroundHex; // Set selected entry colors const backgroundColor = `${backgroundHex}`; const textColor = 'white'; // Set navigation keys with keycodes here: https://www.toptal.com/developers/keycode let nextKey = 'ArrowDown'; let prevKey = 'ArrowUp'; let nextPageKey = 'ArrowRight'; let prevPageKey = 'ArrowLeft'; if (vimKeyNavigation) { nextKey = 'KeyJ'; prevKey = 'KeyK'; nextPageKey = 'KeyL'; prevPageKey = 'KeyH'; } const expandKey = `${settings.kb_expand}`; const openCommentsKey = `${settings.kb_comments}`; const openLinkAndCollapseKey = `${settings.kb_openLink}`; const parentCommentKey = `${settings.kb_parent}`; const upvoteKey = `${settings.kb_upvote}`; const downvoteKey = `${settings.kb_downvote}`; const replyCommKey = `${settings.kb_replyComm}`; const saveKey = `${settings.kb_save}`; const contextKey = `${settings.kb_context}`; const smallerImgKey = `${settings.kb_smallerImg}`; const biggerImgKey = `${settings.kb_largerImg}`; const userKey = `${settings.kb_user}`; const editKey = `${settings.kb_edit}`; const topKey = `${settings.kb_top}`; const linkOneKey = 'Digit1'; const linkTwoKey = 'Digit2'; const linkThreeKey = 'Digit3'; const linkFourKey = 'Digit4'; const linkFiveKey = 'Digit5'; const linkSixKey = 'Digit6'; const linkSevenKey = 'Digit7'; const linkEightKey = 'Digit8'; const linkNineKey = 'Digit9'; const linkZeroKey = 'Digit0'; const modalPopupKey = `${settings.m_dialog}`; const modalSortOneKey = `${settings.m_first}`; const modalSortTwoKey = `${settings.m_second}`; const modalSortThreeKey = `${settings.m_third}`; const modalSortFourKey = `${settings.m_fourth}`; const modalSortFiveKey = `${settings.m_fifth}`; const modalFrontpageKey = `${settings.m_frontpage}`; const modalSavedKey = `${settings.m_saved}`; const modalProfileKey = `${settings.m_userpage}`; const modalInboxKey = `${settings.m_inbox}`; const modalOptionsKey = `${settings.m_options}`; const escapeKey = 'Escape'; let modalMode = 0; console.log(`modalMode: ${modalMode}`); // Stop arrows from moving the page if not using Vim navigation window.addEventListener("keydown", function(e) { if (["ArrowUp", "ArrowDown"].indexOf(e.code) > -1 && !vimKeyNavigation) { e.preventDefault(); } }, false); // Remove scroll animations document.documentElement.style = "scroll-behavior: auto"; // Set CSS for selected entry const css = ` .selected { background-color: ${backgroundColor} !important; color: ${textColor}; }`; // dialog box let myDialog = document.createElement("dialog"); document.body.appendChild(myDialog); let para = document.createElement("p"); para.innerHTML = ` <h3><b>Sort by</b></h3> <p>${modalSortOneKey} = N/A</br> ${modalSortTwoKey} = N/A</br> ${modalSortThreeKey} = N/A</br> ${modalSortFourKey} = N/A</br> ${modalSortFiveKey} = N/A</p> <h3><b>Go To Page</b></h3> <p>${modalFrontpageKey} = Frontpage</br> ${modalSavedKey} = Saved</br> ${modalProfileKey} = User Profile Page</br> ${modalInboxKey} = Inbox</br></p> <h6>${modalOptionsKey} = Options Page</br></br></h6> `; myDialog.appendChild(para); let button = document.createElement("button"); button.classList.add('CLOSEBUTTON1'); button.innerHTML = `Press ESC or ${modalPopupKey} to Close`; myDialog.appendChild(button); //draw settings page const odiv = document.createElement("div"); odiv.setAttribute("id", "lkOptions"); odiv.classList.add("lkoptions", "border-secondary", "card"); odiv.innerHTML = ` <h4>Lemmy-keyboard-navigation Options</h4> </hr> <div class='table-responsive'> <table class='table'> <thead class='pointer'> <td><b>Save and close settings</b></td> <td><button id='LKsaveoptionsTop'>Save and Close</button></td> </tr> <tr> <th><h3><b>General</b></h3></th><td><td/> </thead> </tr> <tbody> <tr> <td><b>Use Vim key navigation</b><br/>Also known as HJKL navigation.<br/>Uncheck to use arrow keys instead.</td> <td><input type='checkbox' id='option_vimKeyNavigation' ${vimKeyNavigation} /></td> </tr> <tr> <td><b>Smooth scrolling</b><br/>Scroll smoothly to the current selection.</td> <td><input type='checkbox' id='option_smoothScroll' ${smoothScroll} /></td> </tr> <tr> <td><b>Page Offset</b><br/>Percent of page to offset selected entry when scrolling.<br/>0-20% recommended<br/>Default: 5</td> <td><textarea id='option_pageOffset'>${settings.pageOffset}</textarea>%</td> </tr> <tr> <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> <td><select id="option_scrollPosition"> <option value='${settings.scrollPosition}'>${settings.scrollPosition}</option> <option value='middle'>middle</option> <option value='top'>top</option> </select></td> </tr> <tr> <td><b>Selected Hex Code</b><br/>The background color of selected posts/comments.<br/>Default: #373737</td> <td><textarea id='option_backgroundHex'>${settings.backgroundHex}</textarea></td> </tr> <tr> <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/> </tr> <tr> <tr> <td><b>Expand/Collapse</b><br/>Expand/collapse both post and comment content.<br/>Default: KeyX</td> <td><textarea id='option_kb_expand'>${settings.kb_expand}</textarea></td> </tr> <tr> <td><b>Open Comments</b><br/>Go to the comments of a post.<br/>Default: KeyC</td> <td><textarea id='option_kb_comments'>${settings.kb_comments}</textarea></td> </tr> <tr> <td><b>Open Links</b><br/>Open Links on a post.<br/>(can also be used to collapse comments!)<br/>Default: Enter</td> <td><textarea id='option_kb_openLink'>${settings.kb_openLink}</textarea></td> </tr> <tr> <td><b>Go to Parent Comment</b><br/>Goes one level up the comment chain.<br/>Default: KeyP</td> <td><textarea id='option_kb_parent'>${settings.kb_parent}</textarea></td> </tr> <tr> <td><b>Upvote</b><br/>:\)<br/>Default: KeyA</td> <td><textarea id='option_kb_upvote'>${settings.kb_upvote}</textarea></td> </tr> <tr> <td><b>Downvote</b><br/>:\(<br/>Default: KeyZ</td> <td><textarea id='option_kb_downvote'>${settings.kb_downvote}</textarea></td> </tr> <tr> <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> <td><textarea id='option_kb_replyComm'>${settings.kb_replyComm}</textarea></td> </tr> <tr> <td><b>Save post/comment</b><br/>Saves the selected post/comment.<br/>Default: KeyS</td> <td><textarea id='option_kb_save'>${settings.kb_save}</textarea></td> </tr> <tr> <td><b>Get context of comment</b><br/>Goes to the context of the selected comment.<br/>Default: KeyQ</td> <td><textarea id='option_kb_context'>${settings.kb_context}</textarea></td> </tr> <tr> <td><b>Shrink expanded image</b><br/>Make an expanded image smaller.<br/>Default: Minus</td> <td><textarea id='option_kb_smallerImg'>${settings.kb_smallerImg}</textarea></td> </tr> <tr> <td><b>Grow expanded image</b><br/>Make an expanded image larger.<br/>Default: Equal</td> <td><textarea id='option_kb_largerImg'>${settings.kb_largerImg}</textarea></td> </tr> <tr> <td><b>Go to poster's profile</b><br/>Go to the profile of whoever posted the selected post/comment.<br/>Default: KeyU</td> <td><textarea id='option_kb_user'>${settings.kb_user}</textarea></td> </tr> <tr> <td><b>Edit the selected post/comment</b><br/>It only works on your own posts!<br/>Default: KeyE</td> <td><textarea id='option_kb_edit'>${settings.kb_edit}</textarea></td> </tr> <tr> <td><b>Scroll to top</b><br/>Scroll to the top of the page.<br/>Default: KeyT</td> <td><textarea id='option_kb_top'>${settings.kb_top}</textarea></td> </tr> <tr> <td><h3><b>Rebind Dialog Keys</b></h3></td><td><td/> </tr> <tr> <td><b>Open/Close Dialog</b><br/>Default: KeyG</td> <td><textarea id='option_m_dialog'>${settings.m_dialog}</textarea></td> </tr> <tr> <tr> <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/> </tr> <td><b>First Sort Button</b><br/>Default: Digit1</td> <td><textarea id='option_m_first'>${settings.m_first}</textarea></td> </tr> <tr> <td><b>Second Sort Button</b><br/>Default: Digit2</td> <td><textarea id='option_m_second'>${settings.m_second}</textarea></td> </tr> <tr> <td><b>Third Sort Button</b><br/>Default: Digit3</td> <td><textarea id='option_m_third'>${settings.m_third}</textarea></td> </tr> <tr> <td><b>Fourth Sort Button</b><br/>Default: Digit4</td> <td><textarea id='option_m_fourth'>${settings.m_fourth}</textarea></td> </tr> <tr> <td><b>Fifth Sort Button</b><br/>Default: Digit5</td> <td><textarea id='option_m_fifth'>${settings.m_fifth}</textarea></td> </tr> <tr> <td><h4><b>Go to page</b></h4></td><td><td/> </tr> <tr> <td><b>Go to Frontpage</b><br/>Default: KeyF</td> <td><textarea id='option_m_frontpage'>${settings.m_frontpage}</textarea></td> </tr> <tr> <td><b>Go to Saved posts/comments</b><br/>Default: KeyS</td> <td><textarea id='option_m_saved'>${settings.m_saved}</textarea></td> </tr> <tr> <td><b>Go to Current User's Profile</b><br/>Default: KeyU</td> <td><textarea id='option_m_userpage'>${settings.m_userpage}</textarea></td> </tr> <tr> <td><b>Go to Inbox</b><br/>Default: KeyI</td> <td><textarea id='option_m_inbox'>${settings.m_inbox}</textarea></td> </tr> <tr> <td><b>Open options page</b><br/>Default: KeyO</td> <td><textarea id='option_m_options'>${settings.m_options}</textarea></td> </tr> <tr> <td><b>Save and close settings</b></td> <td><button id='LKsaveoptions'>Save and Close</button></td> </tr> <tr> <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> </tr> <tr> <td><button id='LKresetoptions'>Reset All Settings</button></td><td></td> </tr> </tbody> </table> </div> <hr /> <p>lemmy-keyboard-navigation links:</p> <a href='https://github.com/vmavromatis/Lemmy-keyboard-navigation'>Github</a><br/><a href='https://greasyfork.org/en/scripts/470498-lemmy-keyboard-navigation'>GreasyFork</a><br/><a href='https://chrome.google.com/webstore/detail/lemmy-keyboard-navigator/lamoeoaekeeklbcekclbceaeafjkdhbi'>Chrome Extension</a><br/></p> <p>This settings page was taken from the <a href='https://github.com/howdy-tsc/LemmyTools'>LemmyTools</a> Userscript.</p> `; let styleString = ` .lkoptions { position: fixed; min-width: auto; min-height: auto; width: auto; height: 100%; top: 0; display: none; left: 0; overflow: scroll; z-index: 1000; padding: 0.5%; margin-top:35px; } `; document.head.appendChild(document.createElement("style")).innerHTML = styleString; document.body.appendChild(odiv); //options document.getElementById("LKsaveoptions").addEventListener("click", (e) => { e.preventDefault(); options("save"); }); document.getElementById("LKsaveoptionsTop").addEventListener("click", (e) => { e.preventDefault(); options("save"); }); document.getElementById("LKresetoptions").addEventListener("click", (e) => { e.preventDefault(); localStorage.clear(); window.location.reload(); }); // Global variables let currentEntry; let commentBlock; let entries = []; let previousUrl = ""; let expand = false; if (typeof GM_addStyle !== "undefined") { GM_addStyle(css); } else if (typeof PRO_addStyle !== "undefined") { PRO_addStyle(css); } else if (typeof addStyle !== "undefined") { addStyle(css); } else { let node = document.createElement("style"); node.type = "text/css"; node.appendChild(document.createTextNode(css)); let heads = document.getElementsByTagName("head"); if (heads.length > 0) { heads[0].appendChild(node); } else { // no head yet, stick it whereever document.documentElement.appendChild(node); } } const selectedClass = "selected"; const targetNode = document.documentElement; const config = { childList: true, subtree: true }; const observer = new MutationObserver(() => { entries = document.querySelectorAll(".post-listing, .comment-node"); if (entries.length > 0) { if (location.href !== previousUrl) { previousUrl = location.href; currentEntry = null; } init(); } }); observer.observe(targetNode, config); function init() { // If jumping to comments if (window.location.search.includes("scrollToComments=true") && entries.length > 1 && (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0) ) { selectEntry(entries[1], true); } // If jumping to comment from anchor link else if (window.location.pathname.includes("/comment/") && (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0) ) { const commentId = window.location.pathname.replace("/comment/", ""); const anchoredEntry = document.getElementById("comment-" + commentId); if (anchoredEntry) { selectEntry(anchoredEntry, true); } } // If no entries yet selected, default to last selected else if (!currentEntry || Array.from(entries).indexOf(currentEntry) < 0) { if (sessionStorage.getItem('currentselection') === null) { selectEntry(entries[0]); } else { sessionCurrentEntry("restore"); } } Array.from(entries).forEach(entry => { entry.removeEventListener("click", clickEntry, true); entry.addEventListener('click', clickEntry, true); }); document.removeEventListener("keydown", handleKeyPress, true); document.addEventListener("keydown", handleKeyPress, true); } function handleKeyPress(event) { if (["TEXTAREA", "INPUT"].indexOf(event.target.tagName) > -1 || event.metaKey) { return; } switch (modalMode) { case modalMode = 0: switch (event.code) { case nextKey: case prevKey: previousKey(event); break; case upvoteKey: upVote(); break; case downvoteKey: downVote(); break; case expandKey: toggleExpand(); expand = isExpanded() ? true : false; break; case smallerImgKey: imgResize("smaller"); break; case biggerImgKey: imgResize("larger"); break; case saveKey: save(); break; case editKey: edit(); break; case openCommentsKey: comments(event); break; case modalPopupKey: dialogUpdate(); goToDialog("open"); break; case contextKey: getContext(event); break; case replyCommKey: // allow refresh with Ctrl + R if (!event.ctrlKey) { if (window.location.pathname.includes("/post/")) { reply(event); } else { community(event); } } break; case userKey: visitUser(event); break; case openLinkAndCollapseKey: if (window.location.pathname.includes("/post/")) { toggleExpand(); } else { const linkElement = currentEntry.querySelector(".col.flex-grow-1>p>a"); if (linkElement) { if (event.shiftKey) { window.open(linkElement.href); } else { linkElement.click(); } } else { comments(event); } } break; case parentCommentKey: { let targetBlock; if (currentEntry.classList.contains("ms-1")) { targetBlock = getPrevEntry(currentEntry); } else if (currentEntry.parentElement.parentElement.parentElement.nodeName === "LI") { targetBlock = currentEntry.parentElement.parentElement.parentElement.getElementsByTagName("article")[0]; } if (targetBlock) { if (expand) { collapseEntry(); } selectEntry(targetBlock, true); if (expand) { expandEntry(); } } } break; case topKey: window.scrollTo(0, 0); sessionStorage.setItem('currentselection', 0); sessionCurrentEntry("restore"); break; case linkOneKey: clickLink(1); break; case linkTwoKey: clickLink(2); break; case linkThreeKey: clickLink(3); break; case linkFourKey: clickLink(4); break; case linkFiveKey: clickLink(5); break; case linkSixKey: clickLink(6); break; case linkSevenKey: clickLink(7); break; case linkEightKey: clickLink(8); break; case linkNineKey: clickLink(9); break; case linkZeroKey: clickLink(0); break; case nextPageKey: case prevPageKey: { const pageButtons = Array.from(document.querySelectorAll(".paginator>button")); if (pageButtons && (document.getElementsByClassName('paginator').length > 0)) { const buttonText = event.code === nextPageKey ? "Next" : "Prev"; pageButtons.find(btn => btn.innerHTML === buttonText).click(); } // Jump next block of comments if (event.code === nextPageKey) { commentBlock = getNextEntrySameLevel(currentEntry); } // Jump previous block of comments if (event.code === prevPageKey) { commentBlock = getPrevEntrySameLevel(currentEntry); } if (commentBlock) { if (expand) { collapseEntry(); } selectEntry(commentBlock, true); if (expand) { expandEntry(); } } } sessionStorage.setItem('currentselection', 0); //reset the selection back to the first post when switching pages } break; case modalMode = 1: switch (event.code) { case escapeKey: modalMode = 0; console.log(`modalMode: ${modalMode}`); break; case modalPopupKey: goToDialog("close"); break; case modalSavedKey: if (window.location.pathname.includes("/u/")) { let savedelement = document.getElementsByClassName("btn btn-outline-secondary pointer")[3]; if (savedelement) { savedelement.click(); goToDialog("close"); } } else { instanceAndUser("saved"); } break; case modalFrontpageKey: frontpage(); break; case modalProfileKey: let profileelement = document.getElementsByClassName("dropdown-item px-2")[0]; if (profileelement) { profileelement.click(); goToDialog("close"); } else { instanceAndUser("profile"); } break; case modalInboxKey: let notifelement = document.getElementsByClassName("nav-link d-inline-flex align-items-center d-md-inline-block")[2]; if (notifelement) { notifelement.click(); goToDialog("close"); } else { window.location.replace(window.location.origin + "/login"); } break; case modalSortOneKey: let firstbutton = document.getElementsByClassName("btn btn-outline-secondary")[0]; sessionStorage.setItem('currentselection', 0); //reset the selection to the first post when switching filters firstbutton.click(); goToDialog("close"); break; case modalSortTwoKey: let secondbutton = document.getElementsByClassName("btn btn-outline-secondary")[1]; sessionStorage.setItem('currentselection', 0); secondbutton.click(); goToDialog("close"); break; case modalSortThreeKey: let thirdbutton = document.getElementsByClassName("btn btn-outline-secondary")[2]; sessionStorage.setItem('currentselection', 0); thirdbutton.click(); goToDialog("close"); break; case modalSortFourKey: let fourthbutton = document.getElementsByClassName("btn btn-outline-secondary")[3]; sessionStorage.setItem('currentselection', 0); fourthbutton.click(); goToDialog("close"); break; case modalSortFiveKey: let fifthbutton = document.getElementsByClassName("btn btn-outline-secondary")[4]; sessionStorage.setItem('currentselection', 0); fifthbutton.click(); goToDialog("close"); break; case modalOptionsKey: options("open"); goToDialog("close"); break; } } } function getNextEntry(e) { const currentEntryIndex = Array.from(entries).indexOf(e); if (currentEntryIndex + 1 >= entries.length) { return e; } return entries[currentEntryIndex + 1]; } function getPrevEntry(e) { const currentEntryIndex = Array.from(entries).indexOf(e); if (currentEntryIndex - 1 < 0) { return e; } return entries[currentEntryIndex - 1]; } function getNextEntrySameLevel(e) { const nextSibling = e.parentElement.nextElementSibling; if (!nextSibling || nextSibling.getElementsByTagName("article").length < 1) { return getNextEntry(e); } return nextSibling.getElementsByTagName("article")[0]; } function getPrevEntrySameLevel(e) { const prevSibling = e.parentElement.previousElementSibling; if (!prevSibling || prevSibling.getElementsByTagName("article").length < 1) { return getPrevEntry(e); } return prevSibling.getElementsByTagName("article")[0]; } function dialogUpdate() { para.remove(); para = document.createElement("p"); para.innerHTML = ` <h3><b>Sort by</b></h3> <p>${modalSortOneKey} = ${dialogLocation(1)}</br> ${modalSortTwoKey} = ${dialogLocation(2)}</br> ${modalSortThreeKey} = ${dialogLocation(3)}</br> ${modalSortFourKey} = ${dialogLocation(4)}</br> ${modalSortFiveKey} = ${dialogLocation(5)}</p> <h3><b>Go To Page</b></h3> <p>${modalFrontpageKey} = Frontpage</br> ${modalSavedKey} = Saved</br> ${modalProfileKey} = User Profile Page</br> ${modalInboxKey} = Inbox</br></p> <h6>${modalOptionsKey} = Options Page</br></br></h6> `; myDialog.appendChild(para); myDialog.appendChild(button); } function dialogLocation(n) { var sort = document.getElementsByClassName("btn btn-outline-secondary"); if (n === 1 && 1 <= sort.length) { return sort[0].innerText; } if (n === 2 && 2 <= sort.length) { return sort[1].innerText; } if (n === 3 && 3 <= sort.length) { return sort[2].innerText; } if (n === 4 && 4 <= sort.length) { return sort[3].innerText; } if (n === 5 && 5 <= sort.length) { return sort[4].innerText; } return "N/A"; } function clickEntry(event) { const e = event.currentTarget; const target = event.target; // Deselect if already selected, also ignore if clicking on any link/button if (e === currentEntry && e.classList.contains(selectedClass) && !( target.tagName.toLowerCase() === "button" || target.tagName.toLowerCase() === "a" || target.parentElement.tagName.toLowerCase() === "button" || target.parentElement.tagName.toLowerCase() === "a" || target.parentElement.parentElement.tagName.toLowerCase() === "button" || target.parentElement.parentElement.tagName.toLowerCase() === "a" ) ) { e.classList.remove(selectedClass); } else { selectEntry(e); } } function selectEntry(e, scrollIntoView = false) { if (currentEntry) { currentEntry.classList.remove(selectedClass); let linkNumber = currentEntry.querySelectorAll(".linkNumber"); if (linkNumber) { for (const link of linkNumber) { link.remove(); } } } currentEntry = e; currentEntry.classList.add(selectedClass); sessionCurrentEntry("save"); let links = currentEntry.getElementsByClassName("md-div")[0]; if (links) { let alink = links.querySelectorAll('a'); if (alink.length > 0) { alink.forEach(function(value, i) { let linkNumber = document.createElement("span"); linkNumber.classList.add("linkNumber"); linkNumber.style.fontSize = "9px"; linkNumber.style.lineHeight = 0; linkNumber.style.verticalAlign = "super"; linkNumber.setAttribute("data-text", `[${i+1}]`); linkNumber.innerText = `[${i+1}]`; linkNumber.title = `Press ${i+1} to open link`; if (i <= 9) { value.appendChild(linkNumber); } }); } } if (scrollIntoView) { scrollIntoViewWithOffset(e, pageOffset); } } function sessionCurrentEntry(n) { const sessionEntry = sessionStorage.getItem('currentselection'); const currentEntryIndex = Array.from(entries).indexOf(currentEntry); if (n === "save") { if (document.querySelector(".home")) { sessionStorage.setItem('currentselection', currentEntryIndex); } } else if (n === "restore") { selectEntry(entries[sessionEntry]); console.log(`Set to entry ${sessionEntry}`); } } function clickLink(n) { let links = currentEntry.getElementsByClassName("md-div")[0]; let alink = links.querySelectorAll('a'); if (n === 1) { window.open( alink[0].href ); } else if (n === 2) { window.open( alink[1].href ); } else if (n === 3) { window.open( alink[2].href ); } else if (n === 4) { window.open( alink[3].href ); } else if (n === 5) { window.open( alink[4].href ); } else if (n === 6) { window.open( alink[5].href ); } else if (n === 7) { window.open( alink[6].href ); } else if (n === 8) { window.open( alink[7].href ); } else if (n === 9) { window.open( alink[8].href ); } else if (n === 0) { window.open( alink[9].href ); } } function isExpanded() { if ( currentEntry.querySelector("a.d-inline-block:not(.thumbnail)") || currentEntry.querySelector("#postContent") || currentEntry.querySelector(".card-body") ) { return true; } return false; } function previousKey(event) { let selectedEntry; // Next button if (event.code === nextKey) { if (event.shiftKey && vimKeyNavigation) { selectedEntry = getNextEntrySameLevel(currentEntry); } else { selectedEntry = getNextEntry(currentEntry); } } // Previous button if (event.code === prevKey) { if (event.shiftKey && vimKeyNavigation) { selectedEntry = getPrevEntrySameLevel(currentEntry); } else { selectedEntry = getPrevEntry(currentEntry); } } if (selectedEntry) { if (expand) { collapseEntry(); } selectEntry(selectedEntry, true); if (expand) { expandEntry(); } } } function upVote() { identifyButtons(); if (upvoteButton) { upvoteButton.click(); } } function downVote() { identifyButtons(); if (downvoteButton) { downvoteButton.click(); } } function goToDialog(n) { const closeButton = document.getElementsByClassName("CLOSEBUTTON1")[0]; closeButton.addEventListener("click", () => { myDialog.close(); modalMode = 0; console.log(`modalMode: ${modalMode}`); }); if (n === "open") { myDialog.showModal(); modalMode = 1; console.log(`modalMode: ${modalMode}`); } if (n === "close") { myDialog.close(); modalMode = 0; console.log(`modalMode: ${modalMode}`); } } function instanceAndUser(n) { let currentInstance = window.location.origin; let username = document.getElementsByClassName("btn dropdown-toggle")[0].textContent; if (n === "profile") { if (username) { let userlink = currentInstance + "/u/" + username; window.location.replace(userlink); } else { window.location.replace(currentInstance + "/login"); } } if (n === "saved") { if (username) { let savedlink = currentInstance + "/u/" + username + "?page=1&sort=New&view=Saved"; window.location.replace(savedlink); } else { window.location.replace(currentInstance + "/login"); } } } var selectionType; function checkSelection() { let postSelection = document.getElementsByClassName("post-listing mt-2 selected")[0]; let username = '@' + document.getElementsByClassName("btn dropdown-toggle")[0].textContent; let posterusername = currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].innerText; if (postSelection) { selectionType = "post"; contextCheck = currentEntry.getElementsByClassName("btn btn-link btn-animate")[11]; // check for direct link button (rainbow star) if (contextCheck) { selectionType = `${selectionType}-fedi`; } if (window.location.pathname.includes("/post/")) { contextCheck = currentEntry.getElementsByClassName("btn btn-link btn-animate")[13]; // check for direct link button if (contextCheck) { selectionType = "post-page-fedi" } else { selectionType = "post-page" } } } else { selectionType = "comment"; let contextButton = currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].href; let contextButton2 = currentEntry.getElementsByClassName("btn btn-link btn-animate")[2].href; if (contextButton === contextButton2) { selectionType = `${selectionType}-context`; } if (window.location.pathname.includes("/inbox")) { selectionType = "comment-inbox"; } } if (username === posterusername) { selectionType = `my-${selectionType}`; } console.log(`current selection: ${selectionType}`); } var upvoteButton; var downvoteButton; var replyButton; var moreButton; var saveButton; var editButton; var commentButton; function identifyButtons() { checkSelection(); let getButton = currentEntry.getElementsByClassName("btn btn-link btn-animate"); if (selectionType === "post") { // posts on link pages upvoteButton = getButton[0]; downvoteButton = getButton[1]; saveButton = getButton[2]; commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1]; if (selectionType === "my-post") { // add edit button if the post is yours editButton = currentEntry.getElementsByClassName("btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item")[2]; } } else if (selectionType === "post-fedi" || selectionType === "post-page-fedi") { // federated posts on link pages and on the page upvoteButton = getButton[1]; downvoteButton = getButton[2]; saveButton = getButton[3]; commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1]; } else if (selectionType === "post-page" || selectionType === "my-post-page") { // on the page of the post upvoteButton = getButton[0]; downvoteButton = getButton[1]; saveButton = getButton[2]; commentButton = currentEntry.getElementsByClassName("btn btn-link btn-sm text-muted ps-0")[1]; if (selectionType === "my-post-page") { // add edit button if the post is yours editButton = currentEntry.getElementsByClassName("btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item")[2]; } // X - X numbers is the getButton array size depending on if moreButton was clicked or not } else if (selectionType === "comment" || selectionType === "my-comment") { // 6 - 10 comments upvoteButton = getButton[2]; downvoteButton = getButton[3]; replyButton = getButton[4]; moreButton = getButton[5]; if (selectionType === "my-comment") { // 6 - 9 add edit button if the comment is yours saveButton = getButton[5]; editButton = getButton[7]; } else { saveButton = getButton[8]; } } else if (selectionType === "comment-context" || selectionType === "my-comment-context") { // 8 - 12 comments with context buttons upvoteButton = getButton[4]; downvoteButton = getButton[5]; replyButton = getButton[6]; moreButton = getButton[7]; if (selectionType === "my-comment-context") { // 8 - 11 add edit button if the comment is yours saveButton = getButton[7]; editButton = getButton[9]; } else { saveButton = getButton[10]; } } else if (selectionType === "comment-inbox") { // 9 - 11 comments in your inbox upvoteButton = getButton[5]; downvoteButton = getButton[6]; replyButton = getButton[7]; moreButton = getButton[8]; saveButton = getButton[11]; } } function frontpage() { let homeElement = document.getElementsByClassName("d-flex align-items-center navbar-brand me-md-3 active")[0]; if (homeElement) { homeElement.click(); goToDialog("close"); } else { window.location.replace(window.location.origin); } } function reply(event) { identifyButtons(); if (replyButton) { event.preventDefault(); replyButton.click(); } } function community(event) { if (event.shiftKey) { window.open( currentEntry.querySelector("a.community-link").href ); } else { currentEntry.querySelector("a.community-link").click(); } } function visitUser(event) { if (event.shiftKey) { window.open( currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].href ); } else { currentEntry.getElementsByClassName("person-listing d-inline-flex align-items-baseline text-info")[0].click(); } } function comments(event) { identifyButtons(); if (event.shiftKey) { window.open( commentButton.href ); } else { commentButton.click(); } } function getContext(event) { if (event.shiftKey) { window.open( currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].href ); } else { currentEntry.getElementsByClassName("btn btn-link btn-animate text-muted btn-sm")[0].click(); } } let maxSize = 0; function imgResize(n) { let expandedImg = currentEntry.getElementsByClassName("overflow-hidden pictrs-image img-fluid img-expanded slight-radius")[0]; let expandedHeight = expandedImg.height; let expandedWidth = expandedImg.width; let expandedHeightbefore = expandedHeight; let expandedWidthbefore = expandedWidth; if (n === "smaller") { expandedHeight = expandedHeight / 1.15; expandedWidth = expandedWidth / 1.15; expandedImg.style.height = expandedHeight + 'px'; expandedImg.style.width = expandedWidth + 'px'; maxSize = 0; console.log(`maxSize: ${maxSize}`); } if (n === "larger") { expandedHeight = expandedHeight * 1.15; expandedWidth = expandedWidth * 1.15; expandedImg.style.width = expandedWidth + 'px'; expandedImg.style.height = expandedHeight + 'px'; if (maxSize === 1) { expandedImg.style.width = expandedWidthbefore + 'px'; expandedImg.style.height = expandedHeightbefore + 'px'; } if (expandedImg.width !== Math.round(expandedWidth) || expandedImg.height !== Math.round(expandedHeight)) { maxSize = 1; console.log(`maxSize: ${maxSize}`); } } } function save() { identifyButtons(); if (saveButton) { saveButton.click(); } else { moreButton.click(); } } function edit() { identifyButtons(); if (editButton) { editButton.click(); } else { moreButton.click(); } } function toggleExpand() { const expandButton = currentEntry.getElementsByClassName("thumbnail rounded overflow-hidden d-inline-block bg-transparent")[0]; const textExpandButton = currentEntry.querySelector(".post-title>button"); const commentExpandButton = currentEntry.querySelector(".ms-2>div>button"); const moreExpandButton = currentEntry.querySelector(".ms-1>button"); if (expandButton) { expandButton.click(); // Scroll into view if picture/text preview cut off const imgContainer = currentEntry.querySelector("a.d-inline-block"); if (imgContainer) { // Check container positions once image is loaded imgContainer.querySelector("img").addEventListener("load", function() { scrollIntoViewWithOffset(imgContainer, pageOffset); }, true); currentEntry.getElementsByClassName("offset-sm-3 my-2 d-none d-sm-block")[0].className = "my-2 d-none d-sm-block"; } } if (textExpandButton) { textExpandButton.click(); const textContainers = [currentEntry.querySelector("#postContent"), currentEntry.querySelector(".card-body")]; textContainers.forEach(container => { if (container) { scrollIntoViewWithOffset(container, pageOffset); } }); } if (commentExpandButton) { commentExpandButton.click(); } if (moreExpandButton) { moreExpandButton.click(); selectEntry(getPrevEntry(currentEntry), true); } } function expandEntry() { if (!isExpanded()) { toggleExpand(); } } function collapseEntry() { if (isExpanded()) { toggleExpand(); } } function scrollIntoViewWithOffset(e, offset) { const y = e.getBoundingClientRect().top + window.scrollY - offset; if (scrollPosition === "middle") { if (e.getBoundingClientRect().top < 0 || e.getBoundingClientRect().bottom > window.innerHeight ) { scrollPage(y); } } else if (scrollPosition === "top") { scrollPage(y); } } function scrollPage(y) { if (smoothScroll) { window.scrollTo({ top: y, behavior: "smooth" }); } else { window.scrollTo({ top: y }); } } }