LemmyTools

A small suite of tools to make Lemmy easier.

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

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