LemmyTools

A small suite of tools to make Lemmy easier.

当前为 2023-07-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name LemmyTools
  3. // @namespace https://thesimplecorner.org/c/lemmytools
  4. // @version 0.2.0.4
  5. // @description A small suite of tools to make Lemmy easier.
  6. // @author howdy@thesimplecorner.org
  7. // @author @cwagner@lemmy.cwagner.me
  8. // @grant none
  9. // @include https://*
  10. // ==/UserScript==
  11.  
  12. //If lemmy do
  13. (function () {
  14. "use strict";
  15. if (!isLemmySite()) {
  16. return;
  17. }
  18. const LogDebug = 0;
  19. const LogInformation = 1;
  20. const LogImportant = 2;
  21. // ------------ EDIT THIS VARIABLE ---------------------
  22. const homeInstance = "";
  23.  
  24. // Choose a log level for the console:
  25. const logLevel = LogDebug;
  26. // const logLevel = LogInformation;
  27. // const logLevel = LogImportant;
  28. // ------------ END EDIT AREA --------------------------
  29. // Fixes remote Instance home link. Example: const homeInstance = 'https://lemmy.world';
  30. //Nothing below needs editing.
  31. // -------------- VERSION -------------------
  32. const ltVer = "0.2.0.4";
  33. const ltTestedVer = "0.18.2";
  34. //--------------------------------------------
  35.  
  36. /* Globals */
  37.  
  38. const mobile = isltMobile();
  39. let remoteCommunityArray = [];
  40.  
  41. function isHomeInstanceSet(i2c) {
  42. return i2c.length > 3;
  43. }
  44.  
  45. function ltLog(msg, level) {
  46. level = level || LogImportant;
  47. if (level < logLevel) return;
  48. console.log("[LemmyTools]", msg);
  49. }
  50.  
  51. function isLemmySite() {
  52. return (
  53. document.querySelector('meta[name="Description"]').content === "Lemmy"
  54. );
  55. }
  56.  
  57. function notHomeAndInCommunity(url) {
  58. return (
  59. url.includes(settings.instance) === false &&
  60. (url.includes("/c/") ||
  61. url.includes("/post/") ||
  62. url.includes("/comment/") ||
  63. url.includes("/communities"))
  64. );
  65. }
  66.  
  67. function isltMobile() {
  68. if (/Android|iPhone/i.test(navigator.userAgent)) {
  69. ltLog("is mobile!");
  70. return true;
  71. } else {
  72. ltLog("is desktop!");
  73. return false;
  74. }
  75. }
  76.  
  77. //Remote Instance
  78. function update(comm, commName, subString) {
  79. try {
  80. if (comm) {
  81. const browsedComm = `<li><h5>${comm}</h5></li>
  82. <li>
  83. <a href='${homeInstance}/c/${commName}' target='_blank'><button type="button" class='ltbutton'>Browse/Sub on Home Instance</button></a>
  84. <br />
  85. <a href='${subString}' target='_blank'><button type="button" class='ltbutton'>Alternative Subscribe Method</button></a>
  86. </li>`;
  87. remoteCommunityArray.push(browsedComm);
  88. }
  89. } catch {}
  90.  
  91. return remoteCommunityArray;
  92. }
  93.  
  94. //Searches communityArray for results in LemmyTools Sidebar.
  95. function searchComms(query, full) {
  96. ltLog(`commsearch evt searchinput${query}${div}`, LogDebug);
  97. const url = window.location.href;
  98. query = query || "";
  99. query = query.toLowerCase();
  100.  
  101. if (query === "") {
  102. const commsCount = localStorage.getItem("commsCount");
  103. if (commsCount == null || full.length < 1) {
  104. div.innerHTML = `<hr /><b>Welcome to LemmyTools! Ver ${ltVer}</b><br /><br />
  105. If this is your first time running the script, set your lemmy homeinstance in the option page.
  106. [${ltVer}] - Manually enter your home lemmy instance in script for offsite home button functionality. (temporary)]. <br /><br />
  107. If you dont see your subscribed communities here, simply login to your lemmy instance and then click the LemmyTools home button above. `;
  108. } else {
  109. div.innerHTML = `Communities: ${commsCount}<hr />${full}`;
  110. }
  111. } else {
  112. //This searches the pushed communityArray with the query, saves it to a array, removes any duplicate values, sorts and then pushes to the commupdate function.
  113. div.innerHTML = full;
  114. ltLog(`Searching for:${query}`, LogDebug);
  115. const children = div.getElementsByTagName("li");
  116. ltLog(`Children found: ${children.length}`, LogDebug);
  117. let data = [""];
  118. let found;
  119. for (let i = 0; i < children.length; i++) {
  120. if (children[i].innerHTML.toLowerCase().indexOf(query) !== -1) {
  121. found = children[i].innerHTML + "<br />";
  122. ltLog(`Found: ${found}`, LogDebug);
  123. data.push(found);
  124. }
  125. }
  126. const resultSet = [...new Set(data)];
  127. resultSet.sort();
  128. commupdate(url, resultSet);
  129. }
  130. }
  131.  
  132. function commupdate(page, data) {
  133. ltLog("LTbar Update");
  134. let count = -1;
  135. data.forEach((_) => count++);
  136. data = data.join("");
  137. div.innerHTML = `Results: ${count}<hr /><br />${data}`;
  138. }
  139. const optionsKey = "LemmyToolsOptions";
  140.  
  141. function getSettingsFromLocalStorage() {
  142. try {
  143. return JSON.parse(localStorage.getItem(optionsKey) || "{}");
  144. } catch (_) {
  145. return {};
  146. }
  147. }
  148. function options(open) {
  149. const odiv = document.getElementById("ltOptions");
  150. ltLog(`Options Functions: ${open}`);
  151. let userOptions = {};
  152. if (open === 1) {
  153. odiv.style.display = "block";
  154. } else if (open === 2) {
  155. //First run set defaults or pull from localstorage.
  156. const mobile = isltMobile();
  157. userOptions = Object.assign(
  158. {},
  159. {
  160. commposSide: mobile ? "left" : "right",
  161. reverseSide: mobile ? "right" : "left",
  162. instance: homeInstance || window.location.origin,
  163. commposVertical: 0,
  164. expandImages: true,
  165. hideSideBar: false,
  166. expandImagesize: mobile ? 100 : 50,
  167. hoverCheck: false,
  168. unblurNSFW: false,
  169. alienSiteOld: !mobile,
  170. alienSiteOldReadWidth: 740,
  171. widthPixels: false,
  172. expandImageSpeed: 0.5,
  173. showAllImages: false,
  174. hideShowAllImagesButton: false,
  175. },
  176. getSettingsFromLocalStorage()
  177. );
  178. localStorage.setItem(optionsKey, JSON.stringify(userOptions));
  179. } else if (open === 3) {
  180. //save button
  181. odiv.style.display = "none";
  182.  
  183. userOptions.commposSide =
  184. document.getElementById("option_commposSide").value;
  185. userOptions.instance = document.getElementById(
  186. "option_homeInstance"
  187. ).value;
  188. userOptions.commposVertical = parseInt(
  189. document.getElementById("option_commposVertical").value
  190. );
  191. userOptions.expandImages = document.getElementById(
  192. "option_expandImages"
  193. ).checked;
  194. userOptions.expandImagesize = parseInt(
  195. document.getElementById("option_expandImagesize").value,
  196. 10
  197. );
  198. userOptions.expandImageSpeed = parseFloat(
  199. document.getElementById("option_expandImageSpeed").value
  200. );
  201. userOptions.hideSideBar =
  202. document.getElementById("option_hideSideBar").checked;
  203. userOptions.hoverCheck =
  204. document.getElementById("option_hoverCheck").checked;
  205. userOptions.unblurNSFW =
  206. document.getElementById("option_unblurNSFW").checked;
  207. userOptions.alienSiteOld = document.getElementById(
  208. "option_alienSiteOld"
  209. ).checked;
  210. userOptions.alienSiteOldReadWidth = parseInt(
  211. document.getElementById("option_alienSiteOldReadWidth").value
  212. );
  213. userOptions.widthPixels =
  214. document.getElementById("option_widthPixels").checked;
  215. userOptions.showAllImages = document.getElementById(
  216. "option_showAllImages"
  217. ).checked;
  218. userOptions.hideShowAllImagesButton = document.getElementById(
  219. "option_hideShowAllImagesButton"
  220. ).checked;
  221.  
  222. if (userOptions.commposVertical > 85) {
  223. userOptions.commposVertical = 85;
  224. } else if (userOptions.commposVertical <= -1) {
  225. userOptions.commposVertical = 0;
  226. }
  227.  
  228. if (userOptions.expandImageSpeed > 1) {
  229. userOptions.expandImageSpeed = 1;
  230. } else if (userOptions.expandImageSpeed < 0) {
  231. userOptions.expandImageSpeed = 0;
  232. }
  233.  
  234. if (userOptions.commposSide === "left") {
  235. userOptions.reverseSide = "right";
  236. } else {
  237. userOptions.reverseSide = "left";
  238. }
  239.  
  240. localStorage.setItem(optionsKey, JSON.stringify(userOptions));
  241. location.reload(true);
  242. }
  243.  
  244. userOptions = getSettingsFromLocalStorage();
  245. ltLog(`Settings ${JSON.stringify(userOptions)}`);
  246. return userOptions;
  247. }
  248.  
  249. //Used for offset removal
  250. function removeClassByWildcard(divClass) {
  251. // If the class ends with a "*", then it matches all classes that start with the given class name.
  252. if (divClass.endsWith("*")) {
  253. divClass = divClass.replace("*", "");
  254. // Get all elements with the given class name.
  255. const elements = document.getElementsByTagName("div");
  256. const re = new RegExp("(^|s)" + divClass + "(s|$)");
  257. const result = [];
  258. let className = "";
  259.  
  260. for (let i = 0; i < elements.length; i++) {
  261. if (re.test(elements[i].className)) {
  262. console.log("Match: " + elements[i]);
  263. result.push(elements[i]);
  264. for (let y = 0; y < elements[i].classList.length; y++) {
  265. if (elements[i].classList[y].indexOf(divClass) !== -1) {
  266. className = elements[i].classList[y];
  267. console.log(className);
  268. }
  269. }
  270. }
  271. }
  272. // Remove the class from all elements.
  273. for (let i = 0; i < result.length; i++) {
  274. result[i].classList.remove(className);
  275. }
  276. } else {
  277. // Otherwise, the class must match exactly.
  278. const elements = document.querySelectorAll("[class=" + divClass + "]");
  279.  
  280. // Remove the class from all elements.
  281. for (let i = 0; i < elements.length; i++) {
  282. elements[i].classList.remove(divClass);
  283. }
  284. }
  285. }
  286. //Expand all images on page
  287. function allImages(show) {
  288. let clickableImages = document.getElementsByClassName(
  289. "thumbnail rounded overflow-hidden d-inline-block position-relative p-0 border-0 bg-transparent"
  290. );
  291.  
  292. //ltLog(clickableImages.length, LogDebug);
  293. //ltLog(clickableImages, LogDebug);
  294. if (show) {
  295. for (let i = 0; i < clickableImages.length; i++) {
  296. try {
  297. console.log(clickableImages[i]);
  298.  
  299. clickableImages[i].click();
  300. } catch {}
  301. }
  302. } else {
  303. //lazy - need to figure out way to handle on dom iter
  304. location.reload(true);
  305. // for (let i = 0; i < clickableImages.length; i++) {
  306. // try {
  307. // clickableImages[i].click();
  308. // clickableImages[i].click();
  309. // } catch {}
  310. // }
  311. }
  312. }
  313.  
  314. // todo maybe something safer
  315. function alienSiteOldStyle_compact() {
  316. const xhr = new XMLHttpRequest();
  317. xhr.open(
  318. "GET",
  319. "https://cdn.jsdelivr.net/gh/soundjester/lemmy_monkey@main/old.reddit.compact.user.js"
  320. );
  321. xhr.onload = function () {
  322. document.head.appendChild(document.createElement("script")).innerHTML =
  323. xhr.responseText;
  324. };
  325. xhr.send();
  326. }
  327.  
  328. function linksInNewTab() {
  329. const links = document.getElementsByTagName("a");
  330. for (let i = 0; i < links.length; i++) {
  331. links[i].setAttribute("target", "_blank");
  332. links[i].setAttribute("rel", "noreferrer");
  333. }
  334. }
  335. // LemmyTools
  336.  
  337. //check if first run or load saved settings
  338. let settings = options(2);
  339.  
  340. /* The provided restyling was graciously used with permission from the developer(s) of Compact Lemmy to old.Reddit Re-format (Lemmy v0.18)
  341.  
  342. // @name Compact Lemmy to old.Reddit Re-format (Lemmy v0.18)
  343. // @namespace https://github.com/soundjester/lemmy_monkey
  344. // @description Reformat widescreen desktop to look more like Reddit
  345. // @author mershed_perderders, DarkwingDuck, dx1@lemmy.world, Djones4822, Jakylla
  346.  
  347. Thank you.
  348. */
  349.  
  350. //Add Compact AlienSiteOld Theme
  351. if (settings.alienSiteOld === true) {
  352. console.log("LemmyTools: Adding alienSiteOld");
  353. alienSiteOldStyle_compact();
  354. }
  355.  
  356. function checkedIfTrue(val) {
  357. return val ? "checked" : "";
  358. }
  359.  
  360. /* Script */
  361. let url = document.location.href;
  362. window.onload = () => {
  363. if (settings.showAllImages) allImages(true); // initial expansion
  364. const body = document.querySelector("body");
  365. const observer = new MutationObserver((_) => {
  366. if (url !== document.location.href) {
  367. url = document.location.href;
  368. if (settings.showAllImages) {
  369. // todo there has to be a better way to wait for the content to be loaded …
  370. var imagesTimer = setTimeout(allImages, 5000);
  371. clearTimeout(imagesTimer);
  372. //allImages(true);
  373. }
  374. }
  375. });
  376. observer.observe(body, { childList: true, subtree: true });
  377. };
  378. let count = 0;
  379. let eIcheck = checkedIfTrue(settings.expandImages);
  380. let hSBcheck = checkedIfTrue(settings.hideSideBar);
  381. let hoverCheck = checkedIfTrue(settings.hoverCheck);
  382. let unblurCheck = checkedIfTrue(settings.unblurNSFW);
  383. let aSOcheck = checkedIfTrue(settings.alienSiteOld);
  384. let widthPixelCheck = checkedIfTrue(settings.widthPixels);
  385. let widthPercentCheck = checkedIfTrue(!settings.widthPixels);
  386. let showAllImagesCheck = checkedIfTrue(settings.showAllImages);
  387. let hideShowAllImagesButtonCheck = checkedIfTrue(
  388. settings.hideShowAllImagesButton
  389. );
  390.  
  391. //Option Divs
  392. //Is HomeInstance Manually Set For WorkAround
  393.  
  394. var hIAlertString = isHomeInstanceSet(homeInstance)
  395. ? ""
  396. : "<b style='color: red;'>Your Home Instance has not been manually set in the UserScript.</b><br />";
  397.  
  398. //Create Lemmy Tools Elements ----------------------------------------
  399.  
  400. const odiv = document.createElement("div");
  401. odiv.setAttribute("id", "ltOptions");
  402. odiv.classList.add("ltoptions", "border-secondary", "card");
  403. odiv.innerHTML = `
  404. <h4>LemmyTools ${ltVer} Options</h4>
  405. </hr>
  406. <div class='table-responsive'>
  407. <table class='table'>
  408. <thead class='pointer'>
  409. <tr>
  410. <th>Option:</th>
  411. <th>Value:</th>
  412. </thead>
  413. </tr>
  414. <tbody>
  415. <tr>
  416. <td><b>LemmyTools Settings:</b></td>
  417. <td></td>
  418. </tr>
  419. <tr>
  420. <td><b>HomeInstance URL</b><br /> Make sure to edit the homeInstance variable of<br /> the UserScript for the
  421. remote instance Home button fix. (Temporary workaround).<br />(Ex:
  422. https://yourinstance.lemmy)<br />${hIAlertString}</td>
  423. <td><textarea id='option_homeInstance'>${settings.instance}</textarea></td>
  424. </tr>
  425. <tr>
  426. <td><b>LemmyTools bar window side</b><br /> - default: right</td>
  427. <td><select id="option_commposSide">
  428. <option value='${settings.commposSide}'>${settings.commposSide}</option>
  429. <option value='right'>right</option>
  430. <option value='left'>left</option>
  431. </select></td>
  432. </tr>
  433. <tr>
  434. <td><b>LemmyTools bar vertical position </b><br />% from top [0-85] - default: 0</td>
  435. <td><textarea id='option_commposVertical'>${settings.commposVertical}</textarea></td>
  436. </tr>
  437. <tr>
  438. <td><b>Keep LemmyTools Bar Open</b><br />Works best for widescreen desktops.</td>
  439. <td><input type='checkbox' id='option_hoverCheck' ${hoverCheck} /></td>
  440. </tr>
  441. <tr>
  442. <td><br /><br /></td>
  443. <td></td>
  444. </tr>
  445. <tr>
  446. <td><b>Site Style and Behaviors:</b></td>
  447. <td></td>
  448. </tr>
  449. <tr>
  450. <td><b>Compact Lemmy to old.Reddit Re-format (Lemmy v0.18) style</b><br />Like the old alien.site but lemmy!
  451. <br />Defaults - Desktop: On / Mobile: Off <br /><br /> Post width / comment width setting in pixels.
  452. Increase or Decrease to your reading preference while viewing posts. (Default 740) </td>
  453. <td><input type='checkbox' id='option_alienSiteOld' ${aSOcheck} /><br /><br /><br /><textarea
  454. id='option_alienSiteOldReadWidth'>${settings.alienSiteOldReadWidth}</textarea></td>
  455. </tr>
  456. <tr>
  457. <td><b>Hide Lemmy Sidebars</b><br /> (Trending, ServerInfo, Communities)<br /> More room for images on feed.
  458. </td>
  459. <td><input type='checkbox' id='option_hideSideBar' ${hSBcheck} /></td>
  460. </tr>
  461. <tr>
  462. <td><b>Expandable Images</b><br />Acts as an auto-expander and adds the ability to manually<br /> expand
  463. images by clicking and dragging.<br />Doubleclick to open full image.</td>
  464. <td><input type='checkbox' id='option_expandImages' ${eIcheck} /></td>
  465. </tr>
  466. <tr>
  467. <td><b>Auto Expand Size</b><br />Size of post image after opening a image post.<br /> Desktop Default: 50 /
  468. Mobile: 100</td>
  469. <td><textarea id='option_expandImagesize'>${settings.expandImagesize}</textarea>
  470. <br /> <label for="option_widthPixels">Pixels</label> <input type='radio' id='option_widthPixels' name="widthScaler" ${widthPixelCheck}/>
  471. <label for="option_widthPercent">Percent</label> <input type='radio' id='option_widthPercent' name="widthScaler" ${widthPercentCheck}/>
  472. </td>
  473. </tr>
  474. <tr>
  475. <td><b>Expand Image Speed</b><br />Speed multiplier for click&drag expanding images. If your images seem to
  476. expand<br /> too fast or slow, increase or decrease this value. [Values 0 to 1.0]<br /> Default: 0.50 </td>
  477. <td><textarea id='option_expandImageSpeed'>${settings.expandImageSpeed}</textarea></td>
  478. </tr>
  479. <tr>
  480. <td><b>Automatically open image posts</b><br /></td>
  481. <td><input type='checkbox' id='option_showAllImages'${showAllImagesCheck}/></td>
  482. </tr>
  483. <tr>
  484. <td><b>Hide the Show All Images button (when Auto open image posts is disabled)</b><br /></td>
  485. <td><input type='checkbox' id='option_hideShowAllImagesButton'${hideShowAllImagesButtonCheck}/></td>
  486. </tr>
  487. <tr>
  488. <td><b>Auto unblur NSFW images</b><br /></td>
  489. <td><input type='checkbox' id='option_unblurNSFW' ${unblurCheck} /></td>
  490. </tr>
  491. <tr>
  492. <td></td>
  493. <td><button id='LTsaveoptions'>Save / Close</button></td>
  494. </tr>
  495. </tbody>
  496. </table>
  497. </div>
  498. <p> Tested on Lemmy Version: ${ltTestedVer} on firefox. <br />
  499. <h5>LemmyTools Links</h5>
  500. <hr /><a href='https://thesimplecorner.org/c/lemmytools'>!lemmytools@thesimplecorner.org</a><br />Get it here: <a
  501. href='https://github.com/howdy-tsc/LemmyTools'>GitHub</a> or <a
  502. href='https://greasyfork.org/en/scripts/469169-lemmytools'>GreasyFork</a><br />Please submit issues to the GitHub
  503. for feature requests and problems: <a href='https://github.com/howdy-tsc/LemmyTools/issues'>GitHub LemmyTools
  504. Issues</a><br /></p>
  505.  
  506. <h5>Attributes/Credit: </h5>
  507. <li><b>@cwagner@lemmy.cwagner.me</b> - For coding, code cleanup, and mentoring.</li>
  508. <li><b>Charles Machalow - csm10495</b> - Coding contribution(s).</li>
  509. <li><b>jimmyhiggs337</b> - Coding contribution(s).</li>
  510. <li>The provided style pack option of 'Compact Lemmy to old.Reddit Re-format (Lemmy v0.18)' was graciously used with
  511. permission from the developer(s). <br />Please support their project here:<a
  512. href='https://github.com/soundjester/lemmy_monkey'> Compact Lemmy to old.Reddit Re-format (Lemmy v0.18)</a></li>
  513.  
  514. `;
  515.  
  516. //Adjust clickable area for mobile (add brandingString if desktop)
  517. let brandingString = "";
  518. if (mobile !== true) {
  519. brandingString =
  520. "<span id='brandingText' style='vertical-align: super !important; writing-mode: vertical-lr; text-orientation: mixed;'>LemmyTools</span>";
  521. }
  522.  
  523. //Comm divs
  524. const touchdiv = document.createElement("div");
  525. touchdiv.setAttribute("id", "touchdiv");
  526. const idiv = document.createElement("div");
  527. idiv.setAttribute("id", "searchdiv");
  528. idiv.classList.add("ltmenu", "border-secondary", "card");
  529. // todo on input
  530. idiv.innerHTML = `
  531. <div id='ltActiveSearchDiv' class='ltActiveSearchDiv'>
  532. <header id='ltBarHeader' class='card-header'>
  533. <h6><a href=${settings.instance}>Home</a> - <a href='https://lemmyverse.net/communities' target='_new'>Find
  534. Subs</a> - <img width=22 height=22 class='targetImg'
  535. src='data:image/webp;base64,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' />
  536. </h6>
  537. </header><input type='text' id='commsearch' name='commsearchinput'
  538. placeholder='Search your subscriptions (or visted subs)' />
  539. <div id='ltBarSubHeader' class='clickAble'>LemmyTools ${ltVer} - <a href='#' id='LToptions'>Options</a></div>
  540. <div style='clear:both;'></div>
  541. </div>
  542. <div id='ltPassiveSearchDiv' class='ltPassiveSearchDiv'><img width=30 height=30 class='targetImg'
  543. src='data:image/webp;base64,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' />${brandingString}
  544. </div><br />`;
  545.  
  546. const div = document.createElement("div");
  547. div.setAttribute("id", "myDiv");
  548. div.classList.add("ltcommsbar");
  549.  
  550. let styleString = `
  551. .ltmenu {
  552. position: fixed;
  553. top: ${settings.commposVertical}%;
  554. ${settings.commposSide}: 0;
  555. font-size: .75rem;
  556. display: block;
  557. height: 100%;
  558. min-height: auto;
  559. z-index: 999;
  560. overflow: scroll;
  561. border: thick double;
  562. border-right: none !important;
  563. outline: 1px solid grey !important;
  564. }
  565.  
  566. .ltActiveSearchDiv {
  567. font-size: 0.9rem;
  568. width: 100%;
  569. }
  570.  
  571. .ltmenu input {
  572. width: 100%;
  573. }
  574.  
  575. .ltPassiveSearchDiv {
  576. display: none;
  577. width: 100%;
  578. }
  579.  
  580. .post-listings .img-expanded {
  581. width: ${settings.expandImagesize}${settings.widthPixels ? "px" : "%"}
  582. }
  583.  
  584. #myDiv li {
  585. list-style-type: none;
  586. }
  587.  
  588. #myDiv hr {
  589. display: block;
  590. }
  591.  
  592. #searchdiv {
  593. ${settings.commposSide}: 0;
  594. position: fixed;
  595. height: 100%;
  596. min-height: auto;
  597. width: 240px;
  598. display: block;
  599. z-index: 999;
  600. overflow: scroll;
  601. transition-timing-function: ease;
  602. transition: ${settings.commposSide} .25s;
  603. transition-delay: 0, 0.25s;
  604. overflow: auto;
  605. -ms-overflow-style: none;
  606. scrollbar-width: none;
  607. }
  608.  
  609. .ltbutton {
  610. background-color: #ccffe5;
  611. }
  612.  
  613. .ltPassiveSearchDiv {
  614. display: none;
  615. }
  616.  
  617. .ltoptions {
  618. position: fixed;
  619. min-width: auto;
  620. min-height: auto;
  621. width: auto;
  622. height: 100%;
  623. top: 0;
  624. display: none;
  625. left: 0;
  626. overflow: scroll;
  627. z-index: 1000;
  628. padding: 0.5%;
  629. border: thick double;
  630. }
  631.  
  632. #myDiv::-webkit-scrollbar {
  633. display: none;
  634. }
  635.  
  636. #myDiv {
  637. -ms-overflow-style: none;
  638. scrollbar-width: none;
  639. }
  640.  
  641. #searchdiv::-webkit-scrollbar {
  642. display: none;
  643. }
  644. `;
  645.  
  646. if (settings.unblurNSFW) {
  647. styleString +=
  648. " .img-blur {filter: none !important; -webkit-filter: none !important; -moz-filter: none !important; -o-filter: none !important; -ms-filter: none !important;} ";
  649. } else {
  650. styleString +=
  651. " .img-blur {filter: blur !important; -webkit-filter: blur !important; -moz-filter: blur !important; -o-filter: blur !important; -ms-filter: blur !important;} ";
  652. }
  653.  
  654. if (settings.hideSideBar) {
  655. styleString +=
  656. ".container, .container-lg, .container-md, .container-sm, .container-xl { }" +
  657. ".col-md-8 {flex: 0 0 80% !important;max-width: 80%;}";
  658. } else {
  659. styleString +=
  660. ".container, .container-lg, .container-md, .container-sm, .container-xl {}";
  661. }
  662. if (!settings.hoverCheck) {
  663. styleString += `
  664. #myDiv:not(:hover) {
  665. animation: showNavOut 500ms ease-in-out both;
  666. display: none;
  667. height: 0;
  668. transition-timing-function: ease;
  669. transition: height;
  670. transition-duration: 1.0;
  671. transition-delay: 0.5s;
  672. }
  673.  
  674. .ltPassiveSearchDiv {
  675. display: block;
  676. float: ${settings.reverseSide};
  677. padding-${settings.commposSide}: 200px;
  678. }
  679.  
  680. #ltActiveSearchDiv {
  681. display: none;
  682. animation: showNav 500ms ease-in-out both;
  683. }
  684.  
  685. #sidebarSubscribed {
  686. display: none;
  687. }
  688.  
  689. #searchdiv {
  690. ${settings.commposSide}: -200px;
  691. position: fixed;
  692. height: 110px;
  693. min-height: auto;
  694. width: 240px;
  695. display: block;
  696. z-index: 999;
  697. overflow: auto;
  698. display: block;
  699. transition-timing-function: ease;
  700. transition: ${settings.commposSide},
  701. height;
  702. transition-duration: 0.25s, 0.25s;
  703. transition-delay: 0.25s, 0.25s;
  704. /*animation: showNavOut 250ms ease-in-out both;*/ /*Breaks things?*/
  705. }
  706.  
  707. #searchdiv:hover .ltActiveSearchDiv {
  708. display: block;
  709. }
  710.  
  711. #searchdiv:hover .ltPassiveSearchDiv {
  712. display: none;
  713. }
  714.  
  715. #searchdiv:hover {
  716. ${settings.commposSide}: 0;
  717. position: fixed;
  718. height: 100%;
  719. min-height: auto;
  720. width: 240px;
  721. display: block;
  722. z-index: 999;
  723. display: block;
  724. overflow: auto;
  725. }
  726.  
  727. #searchdiv:hover>#myDiv {
  728. ${settings.commposSide}: 0;
  729. word-wrap: break-word;
  730. overflow: auto;
  731. display: block;
  732. height: 100%;
  733. width: 240px;
  734. animation: showNav 500ms ease-in-out both;
  735. }
  736.  
  737. @keyframes showNav {
  738. from {
  739. opacity: 0;
  740. }
  741.  
  742. to {
  743. opacity: 1;
  744. }
  745. }
  746. @keyframes showNavOut { from {opacity: 1;} to {opacity: 0;}}`;
  747. } else {
  748. styleString +=
  749. " myDiv {visibility: visible; height: auto; width: auto; overflow:scroll !important;}";
  750. }
  751.  
  752. //For mobile layouts make the ltbar tab smaller
  753. styleString += `
  754. @media (max-width: 1199.98px) {
  755. #brandingText {
  756. display:none;
  757. }
  758. #searchdiv {
  759. height: 35px;
  760. }
  761. }`;
  762.  
  763. //Adjust Comment/Post width (for reading with compact old style)
  764. if (settings.alienSiteOld === true) {
  765. styleString += `
  766. #postContent,
  767. .md-div,
  768. .alert-warning {
  769. max-width: 740px !important;
  770. }
  771.  
  772. .mb-3.row {
  773. max-width: ${settings.alienSiteOldReadWidth}px !important;
  774. }
  775.  
  776. .comment {
  777. max-width: ${settings.alienSiteOldReadWidth}px !important;
  778. }`;
  779. }
  780.  
  781. // ADD MAIN CSS
  782. document.head.appendChild(document.createElement("style")).innerHTML =
  783. styleString;
  784.  
  785. //add lemmytools elements to page
  786. document.body.appendChild(odiv);
  787. document.body.appendChild(idiv);
  788. idiv.appendChild(div);
  789.  
  790. document.getElementById("LToptions").addEventListener("click", (e) => {
  791. e.preventDefault();
  792. options(1);
  793. });
  794.  
  795. const searchInput = document.getElementById("commsearch");
  796. searchInput.addEventListener("input", (e) => {
  797. e.preventDefault();
  798. searchComms(searchInput.value, communityArray);
  799. });
  800.  
  801. document.getElementById("LTsaveoptions").addEventListener("click", (e) => {
  802. e.preventDefault();
  803. options(3);
  804. });
  805.  
  806. // document.onreadystatechange = () => {
  807. // if (document.readyState === "interactive") {
  808. // // document ready
  809. // if (
  810. // showImagesButton.value == "Hide All Images" ||
  811. // settings.showAllImages
  812. // ) {
  813. // allImages(true);
  814. // }
  815. // }
  816. // };
  817.  
  818. //Easier Subscribe Buttons ---------------------------
  819. ltLog("url is " + url);
  820. let rCommunityArray = [];
  821. //Browsing remote instance
  822. setInterval(function () {
  823. const broken = url.split("/c/");
  824. const site = broken[0].replace("https://", "");
  825. let community = broken[1];
  826. let communityName = community;
  827. try {
  828. const broken2 = community.split("?");
  829. community = broken2[0];
  830. communityName =
  831. community.indexOf("@") > -1 ? community : community + "@" + site;
  832. } catch {}
  833.  
  834. const subString =
  835. `${settings.instance}/search?q=!${communityName}&type=All&listingType=All&page=1`.replace(
  836. "#",
  837. ""
  838. );
  839.  
  840. if (notHomeAndInCommunity(url)) {
  841. ltLog(`On remote instance community - DIRECT - Button to: ${subString}`);
  842. rCommunityArray = update(community, communityName, subString);
  843. rCommunityArray = [...new Set(rCommunityArray)];
  844. rCommunityArray = rCommunityArray.reverse();
  845. div.innerHTML = rCommunityArray;
  846. communityArray = rCommunityArray;
  847. }
  848. }, 1000);
  849.  
  850. // Update homeInstance Comms for bar to use
  851. let communityArray = [];
  852. if (url.includes(settings.instance)) {
  853. ltLog("home instance do bar");
  854. document
  855. .querySelectorAll('[class="list-inline-item d-inline-block"]')
  856. .forEach(function (el) {
  857. communityArray.push("<li>" + el.innerHTML + "</li>");
  858. });
  859. communityArray = [...new Set(communityArray)];
  860. if (count === 0 || count == null) {
  861. count = communityArray.length;
  862. }
  863.  
  864. communityArray = communityArray.join("");
  865.  
  866. div.innerHTML += communityArray;
  867. if (div.innerHTML.length >= 20) {
  868. ltLog("Got Results >= 20", LogDebug);
  869. ltLog("setting localcomms localstore", LogDebug);
  870. localStorage.setItem("localComms", communityArray);
  871. localStorage.setItem("commsCount", count.toString()); // todo why store the count? communityArray.length everywhere should be easier
  872. //force update the page
  873. searchComms("", communityArray);
  874. } else {
  875. ltLog("get localcomms from localstore", LogDebug);
  876. communityArray = localStorage.getItem("localComms");
  877.  
  878. div.innerHTML += communityArray;
  879. //force update the page
  880. searchComms("", communityArray);
  881. }
  882. } else {
  883. ltLog("On Remote Instance - Bar", LogDebug);
  884. }
  885.  
  886. //Expand Images----------------------------------------------
  887.  
  888. setInterval(function () {
  889. if (settings.expandImages === true) {
  890. let theImages = document.getElementsByClassName("img-expanded");
  891. for (let i = 0; i < theImages.length; i++) {
  892. theImages[i].addEventListener("mousedown", startDrag);
  893. }
  894.  
  895. let posX;
  896. let node;
  897.  
  898. function startDrag(e) {
  899. e.preventDefault();
  900.  
  901. node = e.currentTarget;
  902. node.style.cursor = "nwse-resize";
  903. try {
  904. node.closest("a").removeAttribute("href");
  905. node.target.closest("a").setAttribute("overflow", "auto;");
  906. node.preventDefault();
  907. } catch {}
  908.  
  909. posX = e.clientX;
  910. document.addEventListener("mousemove", resize);
  911. document.addEventListener("mouseup", stopDrag);
  912. }
  913.  
  914. function resize(e) {
  915. e.preventDefault();
  916. const nextPosX = e.pageX;
  917. node.style.width =
  918. node.offsetWidth +
  919. (nextPosX - posX) * settings.expandImageSpeed +
  920. "px";
  921. posX = nextPosX;
  922. }
  923.  
  924. function stopDrag(e) {
  925. e.preventDefault();
  926. node.style.cursor = "default";
  927. document.removeEventListener("mousemove", resize);
  928. document.removeEventListener("mouseup", stopDrag);
  929. }
  930. } // if expand images
  931.  
  932. //Removes the offset from images.
  933. try {
  934. if (settings.expandImages) {
  935. removeClassByWildcard("offset-*");
  936. }
  937. } catch {}
  938.  
  939. //sidebar settings do
  940. if (settings.hideSideBar === true) {
  941. try {
  942. const sidebarSubscribed = document.getElementById("sidebarContainer");
  943. sidebarSubscribed.style.display = "none";
  944. removeClassByWildcard("site-sideba*");
  945.  
  946. const serverInfo = document.getElementById("sidebarInfo");
  947. serverInfo.style.display = "none";
  948. } catch {}
  949. }
  950.  
  951. //Show All Images Functionality on button toggle.
  952. try {
  953. let addImageButtonArea = document.querySelector(".post-listings");
  954. let showImage = document.createElement("div");
  955. showImage.innerHTML =
  956. "<div class='col-auto'><input type='button' id='showAllImages' class='pointer btn btn-secondary text-bg-primary' value='Show All Images' /> </div>";
  957. if (
  958. addImageButtonArea.innerHTML.indexOf("showAllImages") === -1 &&
  959. !settings.showAllImages &&
  960. !settings.hideShowAllImagesButton
  961. ) {
  962. addImageButtonArea.prepend(showImage);
  963. const showImagesButton = document.getElementById("showAllImages");
  964. showImagesButton.addEventListener("click", function () {
  965. if (showImagesButton.value === "Show All Images") {
  966. showImagesButton.value = "Hide All Images";
  967. allImages(true);
  968. } else {
  969. showImagesButton.value = "Show All Images";
  970. allImages(false);
  971. }
  972. });
  973. }
  974. } catch {}
  975. //Links Open In New Tab
  976. linksInNewTab();
  977. }, 500);
  978. })();