Embedded Image Viewer

Embedds the clicked Image on the Current Site, so you can view it without loading the submission Page

当前为 2023-09-12 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Embedded Image Viewer
  3. // @namespace Violentmonkey Scripts
  4. // @match *://*.furaffinity.net/*
  5. // @require https://greasyfork.org/scripts/475041-furaffinity-custom-settings/code/Furaffinity-Custom-Settings.js
  6. // @grant none
  7. // @version 1.5.1
  8. // @author Midori Dragon
  9. // @description Embedds the clicked Image on the Current Site, so you can view it without loading the submission Page
  10. // @icon https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png?v2
  11. // @homepageURL https://greasyfork.org/de/scripts/458971-embedded-image-viewer
  12. // @supportURL https://greasyfork.org/de/scripts/458971-embedded-image-viewer/feedback
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. // jshint esversion: 8
  17.  
  18. const matchList = ['net/browse', 'net/gallery', 'net/search', 'net/favorites', 'net/scraps', 'net/controls/favorites', 'net/controls/submissions', 'net/msg/submissions', 'd.furaffinity.net' ];
  19.  
  20. const isDFuraffinity = window.location.toString().includes("d.furaffinity.net");
  21. const isDownloadImage = window.location.toString().includes("?eidownload");
  22.  
  23. if (isDFuraffinity) {
  24. if (isDownloadImage)
  25. downloadImage();
  26. return;
  27. }
  28.  
  29. CustomSettings.name = "Extension Settings";
  30. CustomSettings.provider = "Midori's Script Settings";
  31. CustomSettings.headerName = `${GM_info.script.name} Settings`;
  32. const preventInstantDownloadSetting = CustomSettings.newSetting("Prevent Instant Download", "Sets wether to instantly download the Image when the download Button is pressed.", SettingTypes.Boolean, "Prevent Instant Download", false);
  33. const loadingSpinSpeedSetting = CustomSettings.newSetting("Fav Loading Animation", "Sets the spinning speed of the loading animation in milliseconds.", SettingTypes.Number, "", 100);
  34. CustomSettings.loadSettings();
  35.  
  36. let color = "color: blue";
  37. if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
  38. color = "color: aqua";
  39. if (window.location.toString().includes("?extension")) {
  40. console.info(`%cSettings: ${GM_info.script.name} v${GM_info.script.version}`, color);
  41. return;
  42. }
  43.  
  44. if (!matchList.some(x => window.location.toString().includes(x)))
  45. return;
  46.  
  47. console.info(`%cRunning: ${GM_info.script.name} v${GM_info.script.version} ${CustomSettings.toString()}`, color);
  48.  
  49. let isShowing = false;
  50. let notClosingElemsArr = [];
  51.  
  52. document.addEventListener("click", function(event) {
  53. if (isShowing && !notClosingElemsArr.includes(event.target.id)) {
  54. event.preventDefault();
  55. document.getElementById("embeddedElem").remove();
  56. isShowing = false;
  57. }
  58. });
  59.  
  60. addEmbedded();
  61.  
  62. window.updateEmbedded = function() { addEmbedded(); };
  63.  
  64. async function addEmbedded() {
  65. for (const figure of document.querySelectorAll('figure:not([embedded])')) {
  66. figure.setAttribute('embedded', true);
  67. figure.addEventListener("click", function(event) {
  68. if (!event.ctrlKey && !event.target.id.includes("favbutton") && event.target.type != "checkbox") {
  69. if (event.target.href)
  70. return;
  71. else
  72. event.preventDefault();
  73. if (!isShowing)
  74. showImage(figure);
  75. }
  76. });
  77. }
  78. }
  79.  
  80. async function showImage(figure) {
  81. const ddmenu = document.getElementById("ddmenu");
  82. const imageID = figure.id.substring(figure.id.indexOf("-") + 1);
  83. const favdoc = await getHTML("https://www.furaffinity.net/view/" + imageID);
  84.  
  85. await createElements(ddmenu, favdoc, imageID, figure);
  86.  
  87. isShowing = true;
  88. }
  89.  
  90. async function createElements(ddmenu, favdoc, imageID, figure) {
  91. const margin = 20;
  92.  
  93. let embeddedElem = document.createElement("div");
  94. embeddedElem.id = "embeddedElem";
  95. embeddedElem.style.position = "fixed";
  96. embeddedElem.style.zIndex = "999999";
  97. embeddedElem.style.width = window.innerWidth + "px";
  98. embeddedElem.style.height = window.innerHeight + "px";
  99. embeddedElem.style.background = "rgba(30,33,38,.65)";
  100.  
  101. let backgroundElem = document.createElement("div");
  102. backgroundElem.id = "embeddedBackgroundElem";
  103. notClosingElemsArr.push(backgroundElem.id);
  104. backgroundElem.style.position = "fixed";
  105. backgroundElem.style.display = "flex";
  106. backgroundElem.style.flexDirection = "column";
  107. backgroundElem.style.left = "50%";
  108. backgroundElem.style.transform = "translate(-50%, 0%)";
  109. backgroundElem.style.marginTop = margin + "px";
  110. backgroundElem.style.padding = margin + "px";
  111. backgroundElem.style.background = "rgba(30,33,38,.90)";
  112. backgroundElem.style.borderRadius = "10px";
  113.  
  114. let submissionContainer = document.createElement("a");
  115. submissionContainer.id = "embeddedSubmissionContainer";
  116. notClosingElemsArr.push(submissionContainer.id);
  117. submissionContainer.href = favdoc.querySelector('meta[property="og:url"]').content;
  118.  
  119. let submissionImg = favdoc.getElementById("submissionImg");
  120. submissionImg.id = "embeddedSubmissionImg";
  121. notClosingElemsArr.push(submissionImg.id);
  122. submissionImg.removeAttribute("data-fullview-src");
  123. submissionImg.removeAttribute("data-preview-src");
  124. submissionImg.style.maxWidth = "inherit";
  125. submissionImg.style.maxHeight = "inherit";
  126. submissionImg.style.maxWidth = window.innerWidth - margin * 2 + "px";
  127. submissionImg.style.maxHeight = window.innerHeight - ddmenu.clientHeight - 38 * 2 - margin * 2 - 100 + "px";
  128. submissionImg.style.borderRadius = "10px";
  129. submissionContainer.appendChild(submissionImg);
  130.  
  131. backgroundElem.appendChild(submissionContainer);
  132.  
  133. let buttonsContainer = document.createElement("div");
  134. buttonsContainer.id = "embeddedButtonsContainer";
  135. notClosingElemsArr.push(buttonsContainer.id);
  136. buttonsContainer.style.marginTop = "20px";
  137. buttonsContainer.style.marginLeft = "20px";
  138. buttonsContainer.style.marginRight = "20px";
  139.  
  140. let embeddedFavButton = document.createElement("a");
  141. embeddedFavButton.id = "embeddedFavButton";
  142. notClosingElemsArr.push(embeddedFavButton.id);
  143. embeddedFavButton.type = "button";
  144. embeddedFavButton.className = "button standard mobile-fix";
  145.  
  146. let favlink;
  147. //Fast Favoriter 2 integration <start>
  148. const favButton = figure.querySelector('[type="button"][class="button standard mobile-fix"]');
  149. if (favButton) {
  150. favlink = favButton.getAttribute("favlink");
  151. console.log(favlink);
  152. embeddedFavButton.textContent = favButton.textContent;
  153. } else { //Fast Favoriter 2 integration <end>
  154. favlink = await getfavlink(favdoc);
  155. if (favlink.includes("unfav"))
  156. embeddedFavButton.textContent = "-Fav";
  157. else
  158. embeddedFavButton.textContent = "+Fav";
  159. }
  160. embeddedFavButton.style.marginLeft = "4px";
  161. embeddedFavButton.style.marginRight = "4px";
  162. embeddedFavButton.onclick = function() {
  163. let erotation = rotateText(embeddedFavButton);
  164. console.log(favlink)
  165. favImage(figure, favlink, '', erotation);
  166. };
  167. buttonsContainer.appendChild(embeddedFavButton);
  168.  
  169. let downloadButton = document.createElement("a");
  170. downloadButton.id = "embeddedDownloadButton";
  171. notClosingElemsArr.push(downloadButton.id);
  172. downloadButton.type = "button";
  173. downloadButton.className = "button standard mobile-fix";
  174. downloadButton.textContent = "Download";
  175. downloadButton.style.marginLeft = "4px";
  176. downloadButton.style.marginRight = "4px";
  177. const downloadLink = await getDownloadLink(favdoc);
  178. if (preventInstantDownloadSetting.value)
  179. downloadButton.href = downloadLink;
  180. else
  181. downloadButton.href = downloadLink + "?eidownload";
  182. downloadButton.target = "_blank";
  183. downloadButton.download = downloadLink.substring(downloadLink.lastIndexOf("/") + 1);
  184. buttonsContainer.appendChild(downloadButton);
  185.  
  186. let closeButton = document.createElement("a");
  187. closeButton.id = "embeddedCloseButton";
  188. notClosingElemsArr.push(closeButton.id);
  189. closeButton.type = "button";
  190. closeButton.className = "button standard mobile-fix";
  191. closeButton.textContent = "Close";
  192. closeButton.style.marginLeft = "4px";
  193. closeButton.style.marginRight = "4px";
  194. closeButton.onclick = function() {
  195. ddmenu.removeChild(embeddedElem);
  196. isShowing = false;
  197. };
  198. buttonsContainer.appendChild(closeButton);
  199.  
  200. backgroundElem.appendChild(buttonsContainer);
  201.  
  202. embeddedElem.appendChild(backgroundElem);
  203.  
  204. ddmenu.appendChild(embeddedElem);
  205. }
  206.  
  207. async function favImage(figure, favlink, rotation, erotation) {
  208. if (!figure)
  209. return;
  210.  
  211. let footer = document.getElementById("footer");
  212. let iframe = document.createElement("iframe");
  213. iframe.id = "favIFrame";
  214. iframe.src = favlink;
  215. iframe.style.display = "none";
  216. iframe.addEventListener("load", async function() {
  217. let favdoc = iframe.contentDocument;
  218. footer.removeChild(iframe);
  219. let imageLink = figure.childNodes[0].childNodes[0].childNodes[0].href;
  220. favlink = await getfavlink(favdoc);
  221. if (!favlink) {
  222. checkFavLinkMissingReason(figure, favdoc);
  223. return;
  224. }
  225. changeFavButtonLink(favlink, figure, rotation, erotation);
  226. });
  227. footer.appendChild(iframe);
  228. }
  229.  
  230. async function checkFavLinkMissingReason(figure, favdoc) {
  231. favOnError(figure);
  232. let blocked = favdoc.getElementById("standardpage").querySelector('div[class="redirect-message"]');
  233. if (blocked && blocked.textContent.includes("blocked"))
  234. alert(blocked.textContent);
  235. }
  236.  
  237. async function favOnError(figure) {
  238. let embeddedFavButton = document.getElementById("embeddedFavButton");
  239. if (embeddedFavButton)
  240. embeddedFavButton.textContent = "x";
  241.  
  242. //Fast Favoriter 2 integration <start>
  243. let favButton = figure.querySelector('[type="button"][class="button standard mobile-fix"]');
  244. if (favButton)
  245. favButton.textContent = "x";
  246. //Fast Favoriter 2 integration <end>
  247. }
  248.  
  249. async function changeFavButtonLink(favlink, figure, rotation, erotation) {
  250. let embeddedFavButton = document.getElementById("embeddedFavButton");
  251. if (embeddedFavButton) {
  252. erotation();
  253. if (favlink.includes("unfav"))
  254. embeddedFavButton.textContent = "-Fav";
  255. else
  256. embeddedFavButton.textContent = "+Fav";
  257. embeddedFavButton.onclick = function() {
  258. erotation = rotateText(embeddedFavButton);
  259. favImage(figure, favlink, rotation, erotation);
  260. };
  261. }
  262.  
  263. //Fast Favoriter 2 integration <start>
  264. let favButton = figure.querySelector('[type="button"][class="button standard mobile-fix"]');
  265. if (favButton) {
  266. if (rotation)
  267. rotation();
  268. if (favlink.includes("unfav"))
  269. favButton.textContent = "-Fav";
  270. else
  271. favButton.textContent = "+Fav";
  272. favButton.onclick = function() {
  273. rotation = rotateText(favButton);
  274. favImage(figure, favlink, rotation, erotation);
  275. };
  276. }
  277. //Fast Favoriter 2 integration <end>
  278. }
  279.  
  280. function rotateText(element) {
  281. let isRotating = true;
  282. const characters = [ "◜", "◠", "◝", "◞", "◡", "◟" ];
  283. let index = 0;
  284.  
  285. function update() {
  286. if (!isRotating) return;
  287. element.textContent = characters[index % characters.length];
  288. index++;
  289. setTimeout(update, loadingSpinSpeedSetting.value);
  290. }
  291. if (!isRotating) return;
  292. update();
  293.  
  294. return function stopRotation() {
  295. isRotating = false;
  296. };
  297. }
  298.  
  299. function downloadImage() {
  300. console.log("Embedded Image Viewer downloading Image...");
  301. let url = window.location.toString();
  302. if (url.includes("?")) {
  303. const parts = url.split('?');
  304. url = parts[0];
  305. }
  306. const download = document.createElement('a');
  307. download.href = url;
  308. download.download = url.substring(url.lastIndexOf("/") + 1);
  309. download.style.display = 'none';
  310. document.body.appendChild(download);
  311. download.click();
  312. document.body.removeChild(download);
  313.  
  314. window.close();
  315. }
  316.  
  317. async function getfavlink(subdoc) {
  318. let buttons = subdoc.querySelectorAll('a[class="button standard mobile-fix"]');
  319. for (const button of buttons)
  320. if (button.textContent.includes("Fav") && button.textContent.length <= 4)
  321. return button.href;
  322. }
  323.  
  324. async function getDownloadLink(subdoc) {
  325. let buttons = subdoc.querySelectorAll('a[class="button standard mobile-fix"]');
  326. for (const button of buttons)
  327. if (button.textContent.includes("Download"))
  328. return button.href;
  329. }
  330.  
  331. async function getHTML(url) {
  332. try {
  333. const response = await fetch(url);
  334. const html = await response.text();
  335. const parser = new DOMParser();
  336. const doc = parser.parseFromString(html, "text/html");
  337. return doc;
  338. } catch (error) {
  339. console.error(error);
  340. }
  341. }