Hover Zoom Minus --

image popup: zoom, pan, scroll, pin, scale

当前为 2024-04-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Hover Zoom Minus --
  3. // @namespace Hover Zoom Minus --
  4. // @version 1.0.9.9
  5. // @description image popup: zoom, pan, scroll, pin, scale
  6. // @author Ein, Copilot AI
  7. // @match *://*/*
  8. // @license MIT
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. // 01. to use hover over the image(container) to view a popup of the target image
  13. // 02. to zoom in/out use wheel up/down.
  14. // 03. click left mouse to lock popup this will make it move along with the mouse, click again to release (indicated by green border)
  15. // 04. while being locked"Y" the wheel up/down will act as scroll up/down
  16. // 05. double click will lock it on screen preventing it from being hidden
  17. // 06. hover below the image and click blue bar this will make a 3rd mode for wheel bottom, which will scroll next/previous image under an album
  18. // 07. while locked at screen (indicated by red outline) a single click with the blurred background will unblur it, only one popup per time, so the locked popup will prevent other popup to spawn
  19. // 08. double clicking on blurred background will de-spawn popup
  20. // 09. click on the corner to toggle scaling (blue) will retain current aspect ratio, double clicking will turn to 2nd mode for scaling (magenta) will not retain current aspect ratio
  21. // 10. return to original aspect ration via clicking on sides, if it's in an album to reset to aspect ratio on bottom side use double click
  22. // 11. you can now crop and save the image, just double click the top to initiate.
  23. // 12. to turn on/off hover at the bottom of the page
  24.  
  25. (function() {
  26. 'use strict';
  27.  
  28. // Configuration ----------------------------------------------------------
  29.  
  30. // Define regexp of web page you want HoverZoomMinus to run with,
  31. // 1st array value - default status at start of page: '1' for on, '0' for off
  32. // 2nd array value - spawn position for popup: 'center' for center of screen, '' for cursor position
  33. // 3rd array value - allowed interval for spawning popup; i.e. when exited on popup but immediately touches an img container thus making it "blink spawn blink spawn", experiment it with the right number
  34.  
  35. const siteConfig = {
  36. 'reddit.com': [1, 'center', '100'],
  37. '9gag.com': [1, 'center', '0'],
  38. 'feedly.com': [1, 'center', '200'],
  39. '4chan.org': [1, '', '400'],
  40. 'deviantart.com': [0, 'center', '300'],
  41. 'home': [1, 'center', '0'] /* for testing */
  42. };
  43.  
  44. // image container [hover box where popup triggers]
  45. const imgContainers = `
  46. /* ------- reddit */ ._3BxRNDoASi9FbGX01ewiLg, ._3Oa0THmZ3f5iZXAQ0hBJ0k > div, ._35oEP5zLnhKEbj5BlkTBUA, ._1ti9kvv_PMZEF2phzAjsGW > div,
  47. /* ------- reddit */ ._28TEYBuEdOuE3kN6UyoKMa div, ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx div, ._3m20hIKOhTTeMgPnfMbVNN, zoomable-img.fixed,
  48. /* --------- 9gag */ .post-container .post-view,
  49. /* ------- feedly */ .PinableImageContainer, .entryBody,
  50. /* -------- 4chan */ div.post div.file a,
  51. /* --- deviantart */ ._3_LJY, ._2e1g3, ._2SlAD, ._1R2x6
  52. `;
  53. // target img
  54. const imgElements = `
  55. /* ------- reddit */ ._2_tDEnGMLxpM6uOa2kaDB3, ._1dwExqTGJH2jnA-MYGkEL-, ._2_tDEnGMLxpM6uOa2kaDB3._1XWObl-3b9tPy64oaG6fax, zoomable-img.fixed img,
  56. /* --------- 9gag */ .post-container .post-view > picture > img,
  57. /* ------- feedly */ .pinable, .entryBody img,
  58. /* -------- 4chan */ div.post div.file img:nth-child(1), ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx img, div.post div.file .fileThumb img,
  59. /* --- deviantart */ ._3_LJY img, ._2e1g3 img, ._2SlAD img, ._1R2x6 img
  60. `;
  61. // excluded element
  62. const nopeElements = `
  63. /* ------- reddit */ ._2ED-O3JtIcOqp8iIL1G5cg
  64. `;
  65. // AlbumSelector take note that it will only load image that are already in the DOM tree
  66. // example reddit will not include all until you press navigator buttons so most of the time this will only load a few image
  67. // unless you update it via interacting with navigator button thus updating the DOM tree (added new function for navigation it can now update)
  68. let albumSelector = [
  69. /* ---reddit */ { imgElement: '._1dwExqTGJH2jnA-MYGkEL-', albumElements: '._1apobczT0TzIKMWpza0OhL' },
  70. /* ---feedly */ { imgElement: '.entryBody > div > img', albumElements: '.entryBody > div' },
  71. /* ---feedly */ { imgElement: 'div[id^="Article-"] > div > span > img', albumElements: 'div[id^="Article-"]' },
  72. ];
  73.  
  74. // specialElements were if targeted, will call it's paired function
  75. // for convenience an element className "specialElement" will be remove during mouseout or function hidepopup() is called you can use that with specialElements function for temporary elements
  76. const specialElements = [
  77. /* --- 4chan */ { selector: 'div.post div.file .fileThumb img', func: SP1 },
  78. /* -- reddit */ { selector: '._1dwExqTGJH2jnA-MYGkEL-', func: SP2 },
  79. /* -- reddit */ { selector: '._2_tDEnGMLxpM6uOa2kaDB3', func: SPunblurSpoiler }
  80. ];
  81. // special function triggered when clicking LeftBar/ wheel down in album mode
  82. const specialLeftBar = [
  83. /* -- reddit */ { selector: '._1dwExqTGJH2jnA-MYGkEL-', func: SPL }
  84. ];
  85. // special function triggered when clicking RightBar/ wheel up in album mode
  86. const specialRightBar = [
  87. /* -- reddit */ { selector: '._1dwExqTGJH2jnA-MYGkEL-', func: SPR }
  88. ];
  89. //-------------------------------------------------------------------------
  90. // Special Funtions -------------------------------------------------------
  91.  
  92.  
  93. // 4chan: replaces thumbnail so popup will use the larger version, or if its animated replace it with that
  94. function SP1(imageElement) {
  95. const parentElement = imageElement.parentElement;
  96. const href = parentElement.getAttribute('href');
  97. if (parentElement.tagName === 'a') {
  98. if (href.endsWith('.webm')) {
  99. const videoElement = document.createElement('video');
  100. videoElement.src = href;
  101. videoElement.controls = true;
  102. parentElement.replaceChild(videoElement, imageElement);
  103. } else {
  104. imageElement.setAttribute('src', href);
  105. }
  106. } else {
  107. imageElement.setAttribute('src', href);
  108. }
  109. }
  110.  
  111. // reddit: added the label/description of image along with the popup
  112. function SP2(imageElement) {
  113. if (enableP === 0) {
  114. return;
  115. }
  116. let closestElement = imageElement.closest('.m3aNC6yp8RrNM_-a0rrfa, .kcerW9lbT-se3SXd-wp2i');
  117. if (!closestElement) {
  118. return;
  119. }
  120. let descendantElement = closestElement.querySelector('._15nNdGlBIgryHV04IfAfpA');
  121. if (descendantElement) {
  122. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  123. let title = descendantElement.getAttribute('title');
  124. const specialContent = document.createElement('div');
  125. specialContent.className = 'specialElement';
  126. specialContent.style.cssText = 'position:fixed; bottom:80px; left:50%; transform:translateX(-50%); width:100vw; text-align:center; font-size:40px; color:white; text-shadow:0 0 10px rgba(255,255,255,0.7); z-index:99999;';
  127. specialContent.textContent = title;
  128. document.body.appendChild(specialContent);
  129.  
  130. var specialBackdrop = document.createElement('div');
  131. specialBackdrop.className = 'specialElement';
  132. specialBackdrop.style.cssText = 'position:fixed; bottom:60px; left:0; width:100vw; height:80px; background:rgba(0,0,0,0.2); backdrop-filter:blur(5px); z-index:99998;';
  133. document.body.appendChild(specialBackdrop);
  134.  
  135. } else {
  136. return;
  137. }
  138. }
  139. function SPL(imageElement) {
  140. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  141.  
  142. let closestElement = imageElement.closest('.kcerW9lbT-se3SXd-wp2i');
  143. var element = closestElement.querySelector('._1fSFPkxZ9pToLETLQT2dmc');
  144. if (element) element.click();
  145.  
  146. let descendantElement = closestElement.querySelector('._15nNdGlBIgryHV04IfAfpA');
  147. if (descendantElement) {
  148. let title = descendantElement.getAttribute('title');
  149. const specialContent = document.createElement('div');
  150. specialContent.className = 'specialElement';
  151. specialContent.style.cssText = 'position:fixed; bottom:80px; left:50%; transform:translateX(-50%); width:100vw; text-align:center; font-size:40px; color:white; text-shadow:0 0 10px rgba(255,255,255,0.7); z-index:99999;';
  152. specialContent.textContent = title;
  153. document.body.appendChild(specialContent);
  154.  
  155. var specialBackdrop = document.createElement('div');
  156. specialBackdrop.className = 'specialElement';
  157. specialBackdrop.style.cssText = 'position:fixed; bottom:60px; left:0; width:100vw; height:80px; background:rgba(0,0,0,0.2); backdrop-filter:blur(5px); z-index:99998;';
  158. document.body.appendChild(specialBackdrop);
  159. } else {
  160. return;
  161. }
  162. }
  163. function SPR(imageElement) {
  164. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  165.  
  166. let closestElement = imageElement.closest('.kcerW9lbT-se3SXd-wp2i');
  167. var element = closestElement.querySelector('._3-JCOd-nY76g29C7ZVX_kl:last-child');
  168. if (element) element.click();
  169.  
  170. let descendantElement = closestElement.querySelector('._15nNdGlBIgryHV04IfAfpA');
  171. if (descendantElement) {
  172. let title = descendantElement.getAttribute('title');
  173. const specialContent = document.createElement('div');
  174. specialContent.className = 'specialElement';
  175. specialContent.style.cssText = 'position:fixed; bottom:80px; left:50%; transform:translateX(-50%); width:100vw; text-align:center; font-size:40px; color:white; text-shadow:0 0 10px rgba(255,255,255,0.7); z-index:99999;';
  176. specialContent.textContent = title;
  177. document.body.appendChild(specialContent);
  178.  
  179. var specialBackdrop = document.createElement('div');
  180. specialBackdrop.className = 'specialElement';
  181. specialBackdrop.style.cssText = 'position:fixed; bottom:60px; left:0; width:100vw; height:80px; background:rgba(0,0,0,0.2); backdrop-filter:blur(5px); z-index:99998;';
  182. document.body.appendChild(specialBackdrop);
  183. } else {
  184. return;
  185. }
  186. }
  187.  
  188. // reddit: unblur spoiler, from https://greasyfork.org/en/scripts/479784-reddit-show-images-marked-with-spoiler-nsfw
  189. function SPunblurSpoiler(imageElement) {
  190. const regexIdImg = /https:\/\/.*\/(.*)\?/;
  191.  
  192. const imgs = imageElement.parentElement.querySelectorAll('._2_tDEnGMLxpM6uOa2kaDB3[src*="?blur="]');
  193. imgs.forEach(img => {
  194. img.src = `https://i.redd.it/${img.src.match(regexIdImg)[1]}`;
  195. img.classList.remove('_3oBPn1sFwq76ZAxXgwRhhn');
  196. });
  197.  
  198. let descendantElement = imageElement.closest('.m3aNC6yp8RrNM_-a0rrfa');
  199. if (descendantElement) {
  200. descendantElement.style.background = '#0000';
  201. let removeElement = descendantElement.querySelector('._2iaYXFpGyyEGq1rp02cl5w');
  202. if (removeElement) {
  203. removeElement.style.display = 'none';
  204. }
  205. }
  206. }
  207.  
  208. //-------------------------------------------------------------------------
  209.  
  210.  
  211. // Configuration variables
  212. const currentHref = window.location.href;
  213. let enableP, positionP, intervalP, URLmatched;
  214. Object.keys(siteConfig).some((config) => {
  215. const regex = new RegExp(config);
  216. if (currentHref.match(regex)) {
  217. [enableP, positionP, intervalP] = siteConfig[config];
  218. URLmatched = true;
  219. return true;
  220. }
  221. });
  222.  
  223.  
  224. // The HoverZoomMinus Function---------------------------------------------
  225. function HoverZoomMinus() {
  226. let isshowPopupEnabled = true;
  227. isshowPopupEnabled = true;
  228.  
  229. const style = document.createElement('style');
  230. style.type = 'text/css';
  231. style.innerHTML = `
  232. .popup-container { display:none; cursor: move; z-index:1001; }
  233. .popup-image { max-height:calc(90vh - 10px); display:none; }
  234. .popup-backdrop { position:fixed; top:0; left:0; width:100vw; height:100vh; display:none; z-index:1000; }
  235. .centerBox { top:40px; left:40px; position:absolute; height:calc(100% - 80px); width:calc(100% - 80px); z-index:9999; }
  236. .TopBar, .BottomBar, .LeftBar, .RightBar {
  237. opacity:0; box-sizing:border-box; background:#0000; position:absolute; z-index:9999;
  238. &::after { content:""; position:absolute; }
  239. }
  240. .TopBar, .BottomBar { height:40px; width:calc(100% - 80px); left:40px;
  241. &::after { display:none; width:25px; height:25px; transform:rotate(45deg) translateX(-50%) translateY(-50%); }
  242. }
  243. .TopBar { top:0; }
  244. .BottomBar { bottom:0; }
  245. .TopBar::after { left:calc(50% - 17px); top:calc(50% + 15px); border-top:5px solid #6f8e9e; border-left:5px solid #6f8e9e; }
  246. .BottomBar::after { left:calc(50% - 2px); bottom:-8px; border-bottom:5px solid #6f8e9e; border-right: 5px solid #6f8e9e; }
  247. .LeftBar, .RightBar {
  248. height:calc(100% - 80px); width:40px; top:40px;
  249. &::before { content:""; display:block; position:absolute; top:50%; width:40px; height:40px; transform:translateY(-50%); border-radius:9999px; background: #242526; }
  250. &::after { content:""; display:block; position:absolute; top:calc(50% + -2px); width:15px; height:15px; }
  251. &:hover::before { display:none; }
  252. &:hover::after { width:25px; height:25px; border-color:#6f8e9e; border-width:5px; }
  253. }
  254. .LeftBar { left:0; }
  255. .RightBar { right:0; }
  256. .LeftBar::before { left:5px; opacity:0 }
  257. .RightBar::before { right:5px; opacity:0 }
  258. .LeftBar:hover::after { left:3px; }
  259. .RightBar:hover::after { right:3px; }
  260. .LeftBar::after { transform:rotate(45deg) translateY(-50%); border-bottom:3px solid #777; border-left:3px solid #777; left:12px; opacity:0 }
  261. .RightBar::after { transform:rotate(-45deg) translateY(-50%); border-bottom: 3px solid #777; border-right:3px solid #777; right:12px; opacity:0 }
  262. .popup-container:hover { .LeftBar::before, .RightBar::before, .LeftBar::after, .RightBar::after, .CornerBox.TR { opacity:1 }}
  263. .CornerBox { box-sizing:border-box; height:40px; width:40px; background:#0000; position:absolute; z-index:9999; }
  264. .CornerBox.TR { top:0; right:0; font-size:11px; text-align:center; line-height:25px; color:white; text-shadow:0 0 4px rgba(255,255,255,0.7); opacity:0 }
  265. .CornerBox.TL { top:0; left:0; }
  266. .CornerBox.BR { bottom:0; right:0; }
  267. .CornerBox.BL { bottom:0; left:0;}
  268. `;
  269.  
  270. document.head.appendChild(style);
  271. const backdrop = document.createElement('div');
  272. backdrop.className = 'popup-backdrop';
  273. document.body.appendChild(backdrop);
  274. const popupContainer = document.createElement('div');
  275. popupContainer.className = 'popup-container';
  276. document.body.appendChild(popupContainer);
  277. const popup = document.createElement('img');
  278. popup.className = 'popup-image';
  279. popupContainer.appendChild(popup);
  280.  
  281. const BottomBar = document.createElement('div');
  282. BottomBar.className = 'BottomBar';
  283. popupContainer.appendChild(BottomBar);
  284. const TopBar = document.createElement('div');
  285. TopBar.className = 'TopBar';
  286. popupContainer.appendChild(TopBar);
  287. const RightBar = document.createElement('div');
  288. RightBar.className = 'RightBar';
  289. popupContainer.appendChild(RightBar);
  290. const LeftBar = document.createElement('div');
  291. LeftBar.className = 'LeftBar';
  292. popupContainer.appendChild(LeftBar);
  293.  
  294. const TR = document.createElement('div');
  295. TR.className = 'CornerBox TR';
  296. popupContainer.appendChild(TR);
  297. const TL = document.createElement('div');
  298. TL.className = 'CornerBox TL';
  299. popupContainer.appendChild(TL);
  300. const BR = document.createElement('div');
  301. BR.className = 'CornerBox BR';
  302. popupContainer.appendChild(BR);
  303. const BL = document.createElement('div');
  304. BL.className = 'CornerBox BL';
  305. popupContainer.appendChild(BL);
  306.  
  307. const centerBox = document.createElement('div');
  308. centerBox.className = 'popup centerBox';
  309. popupContainer.appendChild(centerBox);
  310.  
  311. const style2 = document.createElement('style');
  312. style2.type = 'text/css';
  313. document.head.appendChild(style2);
  314.  
  315.  
  316.  
  317. //-------------------------------------------------------------------------
  318.  
  319. // Variable
  320. const ZOOM_SPEED = 0.005;
  321. let pageDirection;
  322. let isLockedY = false;
  323. let isLockedX = false;
  324. let scale = 1;
  325. let clickTimeout;
  326. let popupTimer;
  327. let isScale, isScaleTR, isScaleBR, isScaleTL, isScaleBL;
  328. let rect, rectT, rectH, rectL, rectW;
  329. let rectIH, rectIW, rectIT, rectIL, rectIRatio;
  330. let rectzT, rectzH, rectzL, rectzW, rectzRatio;
  331. let scaleCurrentX, scaleCurrentY;
  332. let scaleFactorX, scaleFactorY;
  333. let offsetX, offsetY, offsetXR, offsetYT, offsetXL, offsetYB;
  334. let offsetRatioY, offsetRatioX;
  335. let ishidePopupEnabled = true;
  336. let ScaleMode2 = false;
  337. let imgElementsList = [];
  338. let currentZeroImgElement;
  339. let rectIzRatio;
  340.  
  341. function NoMode() {
  342. isLockedY = false;
  343. isLockedX = false;
  344. isScale = false;
  345. isScaleTR = false;
  346. isScaleTL = false;
  347. isScaleBL = false;
  348. isScaleBR = false;
  349. popupContainer.style.border = '';
  350. TR.style.border = '';
  351. BR.style.border = '';
  352. TL.style.border = '';
  353. BL.style.border = '';
  354. }
  355.  
  356. function LockedYMode() {
  357. NoMode();
  358. isLockedY = true;
  359. popupContainer.style.borderLeft = '6px solid #00ff00';
  360. popupContainer.style.borderRight = '6px solid #00ff00';
  361. LeftBar.style.opacity = '0';
  362. RightBar.style.opacity = '0';
  363. LeftBar.style.display = '';
  364. RightBar.style.display = '';
  365. }
  366. function LockedXMode() {
  367. NoMode();
  368. LockedScreen();
  369. isLockedX = true;
  370. popupContainer.style.borderTop = '6px solid #00ff00';
  371. popupContainer.style.borderBottom = '6px solid #00ff00';
  372. LeftBar.style.opacity = '1';
  373. RightBar.style.opacity = '1';
  374. style2.innerHTML = `.LeftBar::before, .LeftBar::after, .RightBar::before, .RightBar::after { display:block; }`;
  375. }
  376.  
  377. function toggleLockedScreen(event) {
  378. ishidePopupEnabled = !ishidePopupEnabled;
  379. popupContainer.style.outline = ishidePopupEnabled ? '' : '6px solid #ae0001';
  380. }
  381.  
  382. function LockedScreen() {
  383. ishidePopupEnabled = false;
  384. popupContainer.style.outline = '6px solid #ae0001';
  385. }
  386.  
  387. function ScalingMode1() {
  388. TL.style.borderTop = '6px solid #0000ff';
  389. TR.style.borderTop = '6px solid #0000ff';
  390. TL.style.borderLeft = '6px solid #0000ff';
  391. BL.style.borderLeft = '6px solid #0000ff';
  392. TR.style.borderRight = '6px solid #0000ff';
  393. BR.style.borderRight = '6px solid #0000ff';
  394. BR.style.borderBottom = '6px solid #0000ff';
  395. BL.style.borderBottom = '6px solid #0000ff';
  396. }
  397. function ScalingMode2() {
  398. ScalingMode1();
  399. TL.style.borderColor = '#ff00ff';
  400. TR.style.borderColor = '#ff00ff';
  401. BL.style.borderColor = '#ff00ff';
  402. BR.style.borderColor = '#ff00ff';
  403. }
  404. function ScalingMode0() {
  405. TL.style.border = '';
  406. TR.style.border = '';
  407. BL.style.border = '';
  408. BR.style.border = '';
  409. }
  410.  
  411. function ResetGeometry() {
  412. let rectF = popup.getBoundingClientRect();
  413. rectzH = rectF.height;
  414. rectzW = rectF.width;
  415. rectzT = rectF.top;
  416. rectzL = rectF.left;
  417. popup.style.maxHeight = rectzH + 'px';
  418. popup.style.height = rectzH + 'px';
  419. popup.style.width = rectzW + 'px';
  420. popupContainer.style.top = rectzT + (rectzH / 2) + 'px';
  421. popupContainer.style.left = rectzL + (rectzW / 2) + 'px';
  422. popupContainer.style.transformOrigin = '50% 50% 0px';
  423. popupContainer.style.transform = `translate(-50%, -50%) scaleX(1) scaleY(1)`;
  424. }
  425.  
  426. function BarClear() {
  427. TL.style.border = '';
  428. TR.style.border = '';
  429. BL.style.border = '';
  430. BR.style.border = '';
  431. TL.style.background = '';
  432. TR.style.background = '';
  433. BL.style.background = '';
  434. BR.style.background = '';
  435. TopBar.style.background = '';
  436. LeftBar.style.background = '';
  437. RightBar.style.background = '';
  438. BottomBar.style.background = '';
  439. popupContainer.style.border = '';
  440. LeftBar.style.opacity = '0';
  441. RightBar.style.opacity = '0';
  442. }
  443.  
  444.  
  445. const loadingIcon = document.createElement('div');
  446.  
  447. function NavigateAlbum() {
  448. let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
  449. if (pair) {
  450. let ancestorElement = currentZeroImgElement.closest(pair.albumElements);
  451. if (ancestorElement) {
  452. imgElementsList = Array.from(ancestorElement.querySelectorAll(pair.imgElement));
  453. let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);
  454. let direction = pageDirection;
  455. let newIndex = zeroIndex + direction;
  456. TR.textContent = `${newIndex + 1}/${imgElementsList.length}`;
  457.  
  458.  
  459. document.querySelectorAll('.loadingIcon').forEach(e => e.remove())
  460.  
  461.  
  462. if (newIndex <= 0) {
  463. LeftBar.style.display = 'none';
  464. TR.textContent = `1/${imgElementsList.length}`;
  465. } else {
  466. LeftBar.style.display = '';
  467. }
  468. if (newIndex >= imgElementsList.length - 1) {
  469. RightBar.style.display = 'none';
  470. TR.textContent = `${imgElementsList.length}/${imgElementsList.length}`;
  471. } else {
  472. RightBar.style.display = '';
  473. }
  474. if (newIndex < 0 || newIndex >= imgElementsList.length) {
  475. return;
  476. }
  477.  
  478. const loadingIcon = document.createElement('div');
  479. loadingIcon.className = 'loadingIcon'
  480. popupContainer.appendChild(loadingIcon);
  481. loadingIcon.style.display = 'none';
  482. loadingIcon.style.transition = 'transform 0.1s linear';
  483. loadingIcon.style.cssText = 'position:absolute; transform:translateZ(0); z-index:9999; font-size:55px; height:40px; width:40px; line-height:40px; top:calc(50% - 20px); left:calc(50% - 20px); background:url("chrome://global/skin/icons/reload.svg"); background-repeat:no-repeat; background-size:contain; filter:invert(.8);';
  484. loadingIcon.style.color = '#0000';;
  485. currentZeroImgElement = imgElementsList[newIndex];
  486. var img = new Image();
  487. function rotateLoadingIcon() {
  488. var angle = parseFloat(loadingIcon.getAttribute('data-angle')) || 0;
  489. angle += 27;
  490. loadingIcon.style.transform = 'rotate(' + angle + 'deg)';
  491. loadingIcon.setAttribute('data-angle', angle);
  492. loadingIcon.style.color = '#777';
  493. }
  494. var rotationInterval = setInterval(rotateLoadingIcon, 100);
  495.  
  496. img.onload = function() {
  497.  
  498. document.querySelectorAll('.loadingIcon').forEach(e => e.remove())
  499. clearInterval(rotationInterval);
  500.  
  501. let natHeight = img.naturalHeight;
  502. let vh = window.innerHeight / 100;
  503. let rect = popup.getBoundingClientRect();
  504. let rectH = rect.height;
  505. if ((natHeight > ((90 * vh) - 10)) || (rectH > ((90 * vh) - 10))) {
  506. popup.style.maxHeight = (90 * vh) - 10 + 'px';
  507. popup.style.width = '';
  508. popupContainer.style.top = '50%';
  509. } else {
  510. popup.style.height = '';
  511. popup.style.maxHeight = 'unset';
  512. }
  513. };
  514.  
  515. img.onerror = function() {
  516. loadingIcon.style.display = 'none';
  517. clearInterval(rotationInterval);
  518. };
  519. loadingIcon.style.display = 'block';
  520.  
  521. popup.src = currentZeroImgElement.src;
  522. img.src = popup.src;
  523. }
  524. }
  525. }
  526.  
  527. // Mouse Click: Center
  528. centerBox.addEventListener('click', function(event) {
  529. if (clickTimeout) clearTimeout(clickTimeout);
  530. clickTimeout = setTimeout(function() {
  531. ResetGeometry();
  532. if (!isScale) {
  533. isLockedY = !isLockedY;
  534. }
  535. if (isLockedY) {
  536. LockedYMode();
  537. let rect = popupContainer.getBoundingClientRect();
  538. offsetX = event.clientX - rect.left - (rect.width / 2);
  539. offsetY = event.clientY - rect.top - (rect.height / 2);
  540. } else {
  541. NoMode();
  542. }
  543. }, 300);
  544. });
  545.  
  546. centerBox.addEventListener('dblclick', function(event) {
  547. clearTimeout(clickTimeout);
  548. toggleLockedScreen();
  549. if (!ishidePopupEnabled) {
  550. backdrop.style.display = 'block';
  551. backdrop.style.zIndex = '999';
  552. backdrop.style.backdropFilter = 'blur(10px)';
  553. }
  554. });
  555. //-------------------------------------------------------------------------
  556.  
  557. // Mouse Click: Corners
  558. document.querySelectorAll('.CornerBox').forEach(element => {
  559. element.addEventListener('click', function(event) {
  560. ishidePopupEnabled = false;
  561. if (isScale) {
  562. BarClear();
  563. isLockedY = false;
  564. isLockedX = false;
  565. popup.style.border = '';
  566.  
  567. const clickedElement = event.target;
  568. const popupContainer = clickedElement.parentElement;
  569. const currentTransform = window.getComputedStyle(popupContainer).transform;
  570. const matrixMatch = currentTransform.match(/^matrix\(([^,]+), [^,]+, [^,]+, ([^,]+), [^,]+, [^,]+\)$/);
  571. if (matrixMatch) {
  572. scaleCurrentX = parseFloat(matrixMatch[1]);
  573. scaleCurrentY = parseFloat(matrixMatch[2]);
  574. }
  575.  
  576. let rect = popupContainer.getBoundingClientRect();
  577. offsetYT = event.clientY - rect.top;
  578. offsetXL = event.clientX - rect.left;
  579. offsetYB = rect.height + rect.top - event.clientY;
  580. offsetXR = rect.width + rect.left - event.clientX;
  581.  
  582. rectT = rect.top;
  583. rectL = rect.left;
  584. rectH = rect.height;
  585. rectW = rect.width;
  586.  
  587. } else {
  588. LockedScreen();
  589. ResetGeometry();
  590. }
  591.  
  592. });
  593. });
  594. document.querySelectorAll('.CornerBox').forEach(element => {
  595. element.addEventListener('click', function(event) {
  596. if (clickTimeout) clearTimeout(clickTimeout);
  597. isScale = !isScale;
  598. }, 300);
  599. });
  600. document.querySelectorAll('.CornerBox').forEach(element => {
  601. element.addEventListener('dblclick', function(event) {
  602. clearTimeout(clickTimeout);
  603. ScaleMode2 = !ScaleMode2;
  604. });
  605. });
  606.  
  607. TL.addEventListener('click', function(event) {
  608. if (clickTimeout) clearTimeout(clickTimeout);
  609. clickTimeout = setTimeout(function() {
  610. isScaleTL = !isScaleTL;
  611. isScaleTR = false;
  612. isScaleBL = false;
  613. isScaleBR = false;
  614. LeftBar.style.display = '';
  615. RightBar.style.display = '';
  616. }, 300);
  617. });
  618. TR.addEventListener('click', function(event) {
  619. if (clickTimeout) clearTimeout(clickTimeout);
  620. clickTimeout = setTimeout(function() {
  621. isScaleTL = false;
  622. isScaleTR = !isScaleTR;
  623. isScaleBL = false;
  624. isScaleBR = false;
  625. LeftBar.style.display = '';
  626. RightBar.style.display = '';
  627. }, 300);
  628. });
  629. BL.addEventListener('click', function(event) {
  630. if (clickTimeout) clearTimeout(clickTimeout);
  631. clickTimeout = setTimeout(function() {
  632. isScaleTL = false;
  633. isScaleTR = false;
  634. isScaleBL = !isScaleBL;
  635. isScaleBR = false;
  636. LeftBar.style.display = '';
  637. RightBar.style.display = '';
  638. }, 300);
  639. });
  640. BR.addEventListener('click', function(event) {
  641. if (clickTimeout) clearTimeout(clickTimeout);
  642. clickTimeout = setTimeout(function() {
  643. isScaleTL = false;
  644. isScaleTR = false;
  645. isScaleBL = false;
  646. isScaleBR = !isScaleBR;
  647. LeftBar.style.display = '';
  648. RightBar.style.display = '';
  649. }, 300);
  650. });
  651. //-------------------------------------------------------------------------
  652.  
  653.  
  654. // Mouse Move: Pan, Scale
  655. document.addEventListener('mousemove', function(event) {
  656. // Panning mode: popup locked and follows the mouse
  657. if (isLockedY) {
  658. popupContainer.style.left = (event.clientX - offsetX) + 'px';
  659. popupContainer.style.top = (event.clientY - offsetY) + 'px';
  660.  
  661. } else if (isScale) {
  662. ScalingMode1();
  663. if (ScaleMode2) {
  664. ScalingMode2();
  665. }
  666.  
  667. if (isScaleTL) {
  668. popupContainer.style.transformOrigin = '100% 100% 0px';
  669. scaleFactorY = scaleCurrentY * (1 + (rectT - event.clientY + offsetYT) / rectH);
  670. scaleFactorX = scaleCurrentX * (1 + (rectL - event.clientX + offsetXL) / rectW);
  671. } else if (isScaleTR) {
  672. popupContainer.style.transformOrigin = '0 100% 0px';
  673. scaleFactorY = scaleCurrentY * (1 + (rectT - event.clientY + offsetYT) / rectH);
  674. scaleFactorX = scaleCurrentX * ((event.clientX - rectL + offsetXR) / rectW);
  675. } else if (isScaleBL) {
  676. popupContainer.style.transformOrigin = '100% 0% 0px';
  677. scaleFactorY = scaleCurrentY * ((event.clientY - rectT + offsetYB) / rectH);
  678. scaleFactorX = scaleCurrentX * (1 + (rectL - event.clientX + offsetXL) / rectW);
  679. } else if (isScaleBR) {
  680. popupContainer.style.transformOrigin = '0% 0% 0px';
  681. scaleFactorY = scaleCurrentY * ((event.clientY - rectT + offsetYB) / rectH);
  682. scaleFactorX = scaleCurrentX * ((event.clientX - rectL + offsetXR) / rectW);
  683. }
  684. if (ScaleMode2) {
  685. popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scaleFactorX}) scaleY(${scaleFactorY})`;
  686.  
  687. } else {
  688. popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scaleFactorX}) scaleY(${scaleFactorX})`;
  689. }
  690. }
  691. });
  692.  
  693.  
  694. // Mouse Wheel: Zoom, Scroll, Navigate
  695. function ZoomOrScroll(event) {
  696. event.preventDefault();
  697. if (isLockedY) {
  698. let deltaY = event.deltaY * -ZOOM_SPEED;
  699. let newTop = parseInt(popupContainer.style.top) || 0;
  700. newTop += deltaY * 100;
  701. popupContainer.style.top = newTop + 'px';
  702. offsetY -= deltaY * 100;
  703.  
  704. } else if (isLockedX && currentZeroImgElement) {
  705. pageDirection = event.deltaY > 0 ? 1 : -1;
  706. NavigateAlbum();
  707.  
  708. if (pageDirection === 1) {
  709. const imageElement = currentContainer.querySelector(imgElements);
  710. specialRightBar.forEach(pair => {
  711. if (imageElement.matches(pair.selector)) {
  712. pair.func(imageElement);
  713. }
  714. });
  715. } else {
  716. const imageElement = currentContainer.querySelector(imgElements);
  717. specialLeftBar.forEach(pair => {
  718. if (imageElement.matches(pair.selector)) {
  719. pair.func(imageElement);
  720. }
  721. });
  722. }
  723.  
  724. } else {
  725. scale += event.deltaY * -ZOOM_SPEED;
  726. scale = Math.min(Math.max(0.125, scale), 10);
  727. popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scale}) scaleY(${scale})`;
  728. }
  729. }
  730. popupContainer.addEventListener('wheel', ZoomOrScroll);
  731. //-------------------------------------------------------------------------
  732.  
  733.  
  734. // Bottom Bar: Album
  735. BottomBar.addEventListener('mouseenter', function(e) {
  736.  
  737. if (isScale) {
  738. return;
  739. } else {
  740. BottomBar.style.opacity = '1';
  741.  
  742. let rect = popup.getBoundingClientRect();
  743. rectzRatio = rect.height / rect.width;
  744. rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
  745.  
  746. if (isAlbum) {
  747. if (!isScale) {
  748. BottomBar.style.background = 'linear-gradient(to right, rgba(0, 0, 255, 0) 0%, rgba(0, 0, 255, 0.5) 25%, rgba(0, 0, 255, 0.5) 75%, rgba(0, 0, 255, 0) 100%)';
  749. }
  750. } else {
  751. BottomBar.style.background = '';
  752. if (!( (rectIzRatio === 1) || isScale ) ) {
  753. style2.innerHTML = `
  754. .BottomBar::after {
  755. display: block;
  756. }
  757. `;
  758. BL.style.background = 'rgba(0, 0, 0, 0.5)';
  759. BR.style.background = 'rgba(0, 0, 0, 0.5)';
  760. BottomBar.style.background = 'rgba(0, 0, 0, 0.5)';
  761. } else {
  762. style2.innerHTML = `
  763. .BottomBar::after {
  764. display: none;
  765. }
  766. `;
  767. }
  768. }
  769. }
  770. });
  771.  
  772. BottomBar.addEventListener('mouseleave', function() {
  773. BottomBar.style.opacity = '0';
  774. if (!isLockedX) {
  775. BarClear();
  776. }
  777. });
  778.  
  779. BottomBar.addEventListener('click', function(event) {
  780. if (clickTimeout) clearTimeout(clickTimeout);
  781. clickTimeout = setTimeout(function() {
  782. if (!isScale) {
  783. LeftBar.style.opacity = '0';
  784. RightBar.style.opacity = '0';
  785. }
  786. if (isAlbum) {
  787. ResetGeometry();
  788. var img = new Image();
  789. img.onload = function() {
  790. natHeight = img.naturalHeight;
  791. natWidth = img.naturalWidth;
  792. };
  793. img.src = popup.src
  794. rectIRatio = natHeight / natWidth;
  795. isLockedX = !isLockedX;
  796. if (isLockedX) {
  797. LockedXMode();
  798. LeftBar.style.opacity = '1';
  799. RightBar.style.opacity = '1';
  800. } else {
  801. popupContainer.style.border = '';
  802. LeftBar.style.display = '';
  803. RightBar.style.display = '';
  804. }
  805. let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
  806. if (pair) {
  807. let ancestorElement = currentZeroImgElement.closest(pair.albumElements);
  808. if (ancestorElement) {
  809. imgElementsList = Array.from(ancestorElement.querySelectorAll(pair.imgElement));
  810. let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);
  811. if (zeroIndex <= 0) {
  812. LeftBar.style.display = 'none';
  813. } else {
  814. LeftBar.style.display = '';
  815. }
  816. if (zeroIndex >= imgElementsList.length - 1) {
  817. RightBar.style.display = 'none';
  818. } else {
  819. RightBar.style.display = '';
  820. }
  821. }
  822. }
  823. } else {
  824. ResetGeometry();
  825. if (isScale) {
  826. isScale = false;
  827. popup.style.maxHeight = rectzH + 'px';
  828. popup.style.height = rectzH + 'px';
  829. offsetRatioY = 0;
  830. } else {
  831. popup.style.maxHeight = 'unset';
  832. popup.style.height = '';
  833. offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
  834. }
  835. popupContainer.style.top = rectzT + (rectzH / 2) + offsetRatioY + 'px';
  836. }
  837. }, 300);
  838. });
  839.  
  840. BottomBar.addEventListener('dblclick', function(event) {
  841. clearTimeout(clickTimeout);
  842. if (isAlbum) {
  843. ResetGeometry();
  844. var img = new Image();
  845. img.onload = function() {
  846. natHeight = img.naturalHeight;
  847. natWidth = img.naturalWidth;
  848. };
  849. img.src = popup.src
  850. rectIRatio = natHeight / natWidth;
  851. if (isScale) {
  852. popup.style.maxHeight = rectzH + 'px';
  853. popup.style.height = rectzH + 'px';
  854. offsetRatioY = 0;
  855. } else {
  856. popup.style.maxHeight = 'unset';
  857. popup.style.height = '';
  858. offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
  859. }
  860. popupContainer.style.top = rectzT + (rectzH / 2) + offsetRatioY + 'px';
  861. BarClear();
  862. } else {
  863. return;
  864. }
  865. });
  866. //-------------------------------------------------------------------------
  867.  
  868.  
  869. // Indicators
  870. document.querySelectorAll('.CornerBox').forEach(element => {
  871. element.addEventListener('mouseenter', function(event) {
  872. ScalingMode1();
  873. if (ScaleMode2) {
  874. ScalingMode2();
  875. }
  876. });
  877. element.addEventListener('mouseleave', function(event) {
  878. if (!isScale) {
  879. ScalingMode0();
  880. }
  881. });
  882. });
  883.  
  884. // Re-scale/ Navigate
  885. TopBar.addEventListener('click', function(event) {
  886. if (clickTimeout) clearTimeout(clickTimeout);
  887. clickTimeout = setTimeout(function() {
  888. ResetGeometry();
  889. if (isAlbum) {
  890. var img = new Image();
  891. img.onload = function() {
  892. natHeight = img.naturalHeight;
  893. natWidth = img.naturalWidth;
  894. };
  895. img.src = popup.src
  896. rectIRatio = natHeight / natWidth;
  897. }
  898. if (isScale) {
  899. isScale = false;
  900. popup.style.maxHeight = rectzH + 'px';
  901. popup.style.height = rectzH + 'px';
  902. offsetRatioY = 0;
  903. } else {
  904. popup.style.maxHeight = 'unset';
  905. popup.style.height = '';
  906. offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
  907. }
  908. popupContainer.style.top = rectzT + (rectzH / 2) - offsetRatioY + 'px';
  909. BarClear();
  910.  
  911. }, 300);
  912. });
  913.  
  914. const styleCrop = document.createElement('style');
  915. styleCrop.type = 'text/css';
  916. styleCrop.innerHTML = `.cropArea1,.cropArea2,.cropArea3,.cropArea4 { display:none; }`;
  917. const CropCover = document.createElement('div');
  918. const cropArea1 = document.createElement('div');
  919. const cropArea2 = document.createElement('div');
  920. const cropArea3 = document.createElement('div');
  921. const cropArea4 = document.createElement('div');
  922.  
  923. let clickX1, clickY1, clickX2, clickY2;
  924. let clickCount;
  925. let cropAreaSet = false;
  926.  
  927. TopBar.addEventListener('dblclick', function(event) {
  928. clearTimeout(window.clickTimeout);
  929.  
  930. LockedScreen();
  931. clickCount = 0;
  932. ResetGeometry()
  933. cropAreaSet = false;
  934.  
  935. const CropCover = document.createElement('div');
  936. const cropArea1 = document.createElement('div');
  937. const cropArea2 = document.createElement('div');
  938. const cropArea3 = document.createElement('div');
  939. const cropArea4 = document.createElement('div');
  940. CropCover.className = 'CropCover'
  941. cropArea1.className = 'cropArea1';
  942. cropArea2.className = 'cropArea2';
  943. cropArea3.className = 'cropArea3';
  944. cropArea4.className = 'cropArea4';
  945. popupContainer.appendChild(CropCover);
  946. document.body.appendChild(cropArea1);
  947. document.body.appendChild(cropArea2);
  948. document.body.appendChild(cropArea3);
  949. document.body.appendChild(cropArea4);
  950. CropCover.style.cssText = 'position:absolute; transform:translateZ(0); top:0; left:0; box-sizing:border-box; height:100%; width:100%; z-index:99999; border:1px solid yellow;';
  951. cropArea1.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  952. cropArea2.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  953. cropArea3.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  954. cropArea4.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  955.  
  956. let rect = popup.getBoundingClientRect();
  957. rectzH = rect.height;
  958. rectzW = rect.width;
  959. rectzT = rect.top;
  960. rectzL = rect.left;
  961.  
  962. let clickTimeout;
  963.  
  964. CropCover.addEventListener('click', function(e) {
  965. if (clickCount === 0) {
  966. clickX1 = e.clientX;
  967. clickY1 = e.clientY;
  968. clickCount = 1;
  969. } else if (clickCount === 1) {
  970. clickCount = 2;
  971. clickX2 = e.clientX;
  972. clickY2 = e.clientY;
  973. cropAreaSet = true;
  974. if (clickCount === 2) {
  975. cropImage();
  976. }
  977. }
  978. });
  979.  
  980. document.addEventListener('mousemove', function(event) {
  981.  
  982. cropArea1.style.left = '0px';
  983. cropArea1.style.top = '0px';
  984. cropArea1.style.height = '100vh';
  985.  
  986. cropArea2.style.top = '0px';
  987.  
  988.  
  989. if (clickCount === 0) {
  990. cropArea1.style.width = event.clientX + 'px';
  991.  
  992. cropArea2.style.left = event.clientX + 'px';
  993. cropArea2.style.height = event.clientY + 'px';
  994. cropArea2.style.width = `calc(100vw - ${event.clientX}px)`;
  995.  
  996.  
  997. } else if (clickCount === 1) {
  998. cropArea1.style.width = clickX1 + 'px';
  999.  
  1000. cropArea2.style.left = clickX1 + 'px';
  1001. cropArea2.style.height = clickY1 + 'px';
  1002. cropArea2.style.width = `calc(100vw - ${clickX1}px)`;
  1003.  
  1004.  
  1005. }
  1006. if (!cropAreaSet && (clickCount === 1)) {
  1007. cropArea3.style.left = clickX1 + 'px';
  1008. cropArea3.style.top = event.clientY + 1 + 'px';
  1009. cropArea3.style.width = `calc(${event.clientX}px + 1px - ${clickX1}px)`;
  1010. cropArea3.style.height = `calc(100vh - 1px - ${event.clientY}px)`;
  1011.  
  1012. cropArea4.style.left = event.clientX + 1 + 'px';
  1013. cropArea4.style.top = clickY1 + 'px'
  1014. cropArea4.style.height = `calc(100vh - 1px - ${clickY1}px)`;
  1015. cropArea4.style.width = `calc(100vw - 1px - ${event.clientX}px)`;
  1016.  
  1017.  
  1018. }
  1019. });
  1020.  
  1021. });
  1022.  
  1023. function cropImage() {
  1024. var canvas = document.createElement('canvas');
  1025. canvas.id = 'cropCanvas';
  1026. var ctx = canvas.getContext('2d');
  1027. document.body.appendChild(canvas);
  1028. let cropX, cropY, cropW, cropH, scW, scH
  1029.  
  1030. scW = clickX2 - clickX1
  1031. scH = clickY2 - clickY1
  1032. cropX = natWidth * ((clickX1 - rectzL) / rectzW);
  1033. cropY = natHeight * ((clickY1 - rectzT) / rectzH);
  1034. cropW = natWidth * (scW / rectzW);
  1035. cropH = natHeight * (scH / rectzH);
  1036. canvas.width = scW;
  1037. canvas.height = scH;
  1038.  
  1039. var image = new Image();
  1040. image.crossOrigin = "anonymous";
  1041. image.src = popup.src
  1042. image.onload = function() {
  1043. ctx.drawImage(image, cropX, cropY, cropW, cropH, 0, 0, scW, scH);
  1044. saveImage(canvas);
  1045. }
  1046.  
  1047. document.querySelectorAll('.CropCover').forEach(e => e.remove())
  1048. }
  1049.  
  1050. function saveImage(canvas) {
  1051. if (confirm('Do you want to save the image?')) {
  1052. var croppedImageDataURL = canvas.toDataURL('image/png');
  1053. var originalFileName = popup.src.split('/').pop();
  1054. var truncatedFileName = originalFileName.length > 100 ? originalFileName.substring(0, 100) : originalFileName;
  1055. var downloadLink = document.createElement('a');
  1056. downloadLink.href = croppedImageDataURL;
  1057. downloadLink.download = truncatedFileName;
  1058. document.body.appendChild(downloadLink);
  1059. downloadLink.click();
  1060. }
  1061.  
  1062. clickCount = 0;
  1063. cropAreaSet = false;
  1064. document.querySelectorAll('.cropArea1').forEach(e => e.remove())
  1065. document.querySelectorAll('.cropArea2').forEach(e => e.remove())
  1066. document.querySelectorAll('.cropArea3').forEach(e => e.remove())
  1067. document.querySelectorAll('.cropArea4').forEach(e => e.remove())
  1068. document.body.removeChild(downloadLink);
  1069. var removecropCanvas = document.getElementById('cropCanvas');
  1070. removecropCanvas.remove();
  1071.  
  1072. }
  1073.  
  1074.  
  1075. let zeroIndex;
  1076. LeftBar.addEventListener('click', function(event) {
  1077. if (clickTimeout) clearTimeout(clickTimeout);
  1078. clickTimeout = setTimeout(function() {
  1079. ResetGeometry();
  1080. if (isAlbum) {
  1081. var img = new Image();
  1082. img.onload = function() {
  1083. natHeight = img.naturalHeight;
  1084. natWidth = img.naturalWidth;
  1085. };
  1086. img.src = popup.src
  1087. rectIRatio = natHeight / natWidth;
  1088. }
  1089. if (isScale) {
  1090. isScale = false;
  1091. popup.style.width = rectzW + 'px';
  1092. offsetRatioX = 0;
  1093. } else {
  1094. popup.style.width = '';
  1095. offsetRatioX = (rectzW - (rectzH / rectIRatio)) / 2 ;
  1096. }
  1097. if (isLockedX && currentZeroImgElement) {
  1098. pageDirection = -1;
  1099. NavigateAlbum();
  1100. TL.style.background = 'rgba(0, 0, 0, 0.5)';
  1101. BL.style.background = 'rgba(0, 0, 0, 0.5)';
  1102. LeftBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1103.  
  1104. const imageElement = currentContainer.querySelector(imgElements);
  1105. specialLeftBar.forEach(pair => {
  1106. if (imageElement.matches(pair.selector)) {
  1107. pair.func(imageElement);
  1108. }
  1109. });
  1110. } else {
  1111. popupContainer.style.left = rectzL + (rectzW / 2) - offsetRatioX + 'px';
  1112. }
  1113. }, 300);
  1114. });
  1115. RightBar.addEventListener('click', function(event) {
  1116. if (clickTimeout) clearTimeout(clickTimeout);
  1117. clickTimeout = setTimeout(function() {
  1118. ResetGeometry();
  1119. if (isAlbum) {
  1120. var img = new Image();
  1121. img.onload = function() {
  1122. natHeight = img.naturalHeight;
  1123. natWidth = img.naturalWidth;
  1124. };
  1125. img.src = popup.src
  1126. rectIRatio = natHeight / natWidth;
  1127. }
  1128. if (isScale) {
  1129. isScale = false;
  1130. popup.style.width = rectzW + 'px';
  1131. offsetRatioX = 0;
  1132. } else {
  1133. popup.style.width = '';
  1134. offsetRatioX = (rectzW - (rectzH / rectIRatio)) / 2 ;
  1135. }
  1136. if (isLockedX && currentZeroImgElement) {
  1137. pageDirection = 1;
  1138. NavigateAlbum();
  1139. TR.style.background = 'rgba(0, 0, 0, 0.5)';
  1140. BR.style.background = 'rgba(0, 0, 0, 0.5)';
  1141. RightBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1142.  
  1143. const imageElement = currentContainer.querySelector(imgElements);
  1144. specialRightBar.forEach(pair => {
  1145. if (imageElement.matches(pair.selector)) {
  1146. pair.func(imageElement);
  1147. }
  1148. });
  1149. } else {
  1150. popupContainer.style.left = rectzL + (rectzW / 2) + offsetRatioX + 'px';
  1151. }
  1152. }, 300);
  1153. });
  1154.  
  1155. TopBar.addEventListener('mouseenter', function() {
  1156.  
  1157. var img = new Image();
  1158. img.onload = function() {
  1159. natHeight = img.naturalHeight;
  1160. natWidth = img.naturalWidth;
  1161. };
  1162. img.src = popup.src
  1163. let rectZ = popup.getBoundingClientRect();
  1164. let rectZH = rectZ.height;
  1165. let rectZW = rectZ.width;
  1166. let rectZRatio = rectZH / rectZW;
  1167. let natRatio = natHeight / natWidth;
  1168. let rectNZRatio = Number(natRatio.toFixed(3)) / Number(rectZRatio.toFixed(3));
  1169. // not original Ratio and not scaling mode aplly with arrow indicator
  1170. if (!((rectNZRatio === 1) || isScale )) {
  1171.  
  1172. TopBar.style.opacity = '1';
  1173. TL.style.background = 'rgba(0, 0, 0, 0.5)';
  1174. TR.style.background = 'rgba(0, 0, 0, 0.5)';
  1175. TopBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1176.  
  1177. style2.innerHTML = `.TopBar::after { display: block; }`;
  1178.  
  1179. } else {
  1180. style2.innerHTML = `.TopBar::after { display: none; }`;
  1181. }
  1182. });
  1183. LeftBar.addEventListener('mouseenter', function() {
  1184. LeftBar.style.opacity = '1';
  1185. let rect = popup.getBoundingClientRect();
  1186. rectzRatio = rect.height / rect.width;
  1187. rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
  1188. if ( isLockedX || !( (rectIzRatio === 1) || isScale ) ) {
  1189. style2.innerHTML = `
  1190. .LeftBar::before, .LeftBar::after {
  1191. display: block;
  1192. }`;
  1193. TL.style.background = 'rgba(0, 0, 0, 0.5)';
  1194. BL.style.background = 'rgba(0, 0, 0, 0.5)';
  1195. LeftBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1196. } else {
  1197. style2.innerHTML = `
  1198. .LeftBar::before, .LeftBar::after {
  1199. display: none;
  1200. }`;
  1201. }
  1202. });
  1203. RightBar.addEventListener('mouseenter', function() {
  1204. RightBar.style.opacity = '1';
  1205. let rect = popup.getBoundingClientRect();
  1206. rectzRatio = rect.height / rect.width;
  1207. rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
  1208. if ( isLockedX || !( (rectIzRatio === 1) || isScale ) ) {
  1209. style2.innerHTML = `
  1210. .RightBar::before, .RightBar::after {
  1211. display: block;
  1212. }`;
  1213. TR.style.background = 'rgba(0, 0, 0, 0.5)';
  1214. BR.style.background = 'rgba(0, 0, 0, 0.5)';
  1215. RightBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1216. } else {
  1217. style2.innerHTML = `
  1218. .RightBar::before, .RightBar::after {
  1219. display: none;
  1220. }`;
  1221. }
  1222. });
  1223.  
  1224. TopBar.addEventListener('mouseleave', function() {
  1225. TopBar.style.opacity = '0';
  1226. TL.style.background = '';
  1227. TR.style.background = '';
  1228. });
  1229. LeftBar.addEventListener('mouseleave', function() {
  1230. if (isLockedX) {
  1231. LeftBar.style.opacity = '1';
  1232. } else {
  1233. LeftBar.style.opacity = '0';
  1234. }
  1235. TL.style.background = '';
  1236. BL.style.background = '';
  1237. LeftBar.style.background = '';
  1238. });
  1239. RightBar.addEventListener('mouseleave', function() {
  1240. if (isLockedX) {
  1241. RightBar.style.opacity = '1';
  1242. } else {
  1243. RightBar.style.opacity = '0';
  1244. }
  1245. TR.style.background = '';
  1246. BR.style.background = '';
  1247. RightBar.style.background = '';
  1248. });
  1249. //-------------------------------------------------------------------------
  1250.  
  1251. let natXratio, natYratio, natHeight, natWidth;
  1252.  
  1253. // show popup
  1254. function showPopup(src, mouseX, mouseY) {
  1255. console.log('isshowPopupEnabled:', isshowPopupEnabled);
  1256. if (!isshowPopupEnabled) return;
  1257. if (enableP === 0) return;
  1258.  
  1259. ishidePopupEnabled = true;
  1260.  
  1261. popup.src = src;
  1262. popup.style.display = 'block';
  1263. popupContainer.style.display = 'block';
  1264. popupContainer.style.position = 'fixed';
  1265. popupContainer.style.transform = 'translate(-50%, -50%) scaleX(1) scaleY(1)';
  1266. backdrop.style.display = 'block';
  1267. backdrop.style.zIndex = '999';
  1268. backdrop.style.backdropFilter = 'blur(10px)';
  1269.  
  1270. if (positionP === 'center') {
  1271. popupContainer.style.top = '50%';
  1272. popupContainer.style.left = '50%';
  1273. } else {
  1274. popupContainer.style.top = `${mouseY}px`;
  1275. popupContainer.style.left = `${mouseX}px`;
  1276. }
  1277.  
  1278. let rectI = popup.getBoundingClientRect();
  1279. rectIH = rectI.height;
  1280. rectIW = rectI.width;
  1281. rectIT = rectI.top;
  1282. rectIL = rectI.left;
  1283.  
  1284. var img = new Image();
  1285. img.onload = function() {
  1286. natHeight = img.naturalHeight;
  1287. natWidth = img.naturalWidth;
  1288. };
  1289. img.src = popup.src
  1290.  
  1291. natXratio = natWidth / rectIW;
  1292. natYratio = natHeight / rectIH;
  1293. rectIRatio = rectIH / rectIW;
  1294.  
  1295. if (positionP === '') {
  1296. if (mouseY < window.innerHeight * 0.33) {
  1297. popupContainer.style.top = mouseY + (rectIH / 2) - 40 + 'px';
  1298. } else if (mouseY >= window.innerHeight * 0.33 && mouseY < window.innerHeight * 0.67) {
  1299. popupContainer.style.top = mouseY + 'px';
  1300. } else if (mouseY >= window.innerHeight * 0.67) {
  1301. popupContainer.style.top = mouseY - (rectIH / 2) + 40 + 'px';
  1302. }
  1303. if (mouseX < window.innerWidth * 0.33) {
  1304. popupContainer.style.left = mouseX + (rectIW / 2) - 40 + 'px';
  1305. } else if (mouseX >= window.innerWidth * 0.33 && mouseX < window.innerWidth * 0.67) {
  1306. popupContainer.style.left = mouseX + 'px';
  1307. } else if (mouseX >= window.innerWidth * 0.67) {
  1308. popupContainer.style.left = mouseX - (rectIW / 2) + 40 + 'px';
  1309. }
  1310. }
  1311. if (isAlbum) {
  1312. LockedXMode();
  1313. }
  1314.  
  1315. }
  1316.  
  1317. let isAlbum;
  1318. let currentContainer;
  1319. document.addEventListener('mouseover', function(e) {
  1320. if (popupTimer) return;
  1321.  
  1322. let target = e.target.closest(imgContainers);
  1323. let relatedTarget = event.relatedTarget;
  1324.  
  1325. if (target.querySelector(nopeElements)) return;
  1326. currentContainer = target;
  1327. const imageElement = currentContainer.querySelector(imgElements);
  1328. specialElements.forEach(pair => {
  1329. if (imageElement.matches(pair.selector)) {
  1330. pair.func(imageElement);
  1331. }
  1332. });
  1333. if (imageElement) {
  1334. currentZeroImgElement = imageElement;
  1335. if (currentZeroImgElement) {
  1336. let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
  1337. if (pair) {
  1338. let closestAlbumElement = currentZeroImgElement.closest(pair.albumElements);
  1339. isAlbum = closestAlbumElement && closestAlbumElement.querySelectorAll(pair.imgElement).length > 1;
  1340. if (isAlbum) {
  1341. imgElementsList = Array.from(closestAlbumElement.querySelectorAll(pair.imgElement));
  1342. let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);
  1343. TR.textContent = `${zeroIndex + 1}/${imgElementsList.length}`;
  1344. if (zeroIndex === 0) {
  1345. LeftBar.style.display = 'none';
  1346. } else {
  1347. LeftBar.style.display = '';
  1348. }
  1349. if (zeroIndex === imgElementsList.length - 1) {
  1350. RightBar.style.display = 'none';
  1351. } else {
  1352. RightBar.style.display = '';
  1353. }
  1354. }
  1355. } else {
  1356. isAlbum = false;
  1357. TR.textContent = '';
  1358. LeftBar.style.display = '';
  1359. RightBar.style.display = '';
  1360. }
  1361. }
  1362. if (intervalP === '') {
  1363. showPopup(imageElement.src, e.clientX, e.clientY);
  1364. } else {
  1365. popupTimer = setTimeout(() => {
  1366. showPopup(imageElement.src, e.clientX, e.clientY);
  1367. popupTimer = null;
  1368. }, parseInt(intervalP));
  1369. }
  1370. }
  1371. });
  1372. //-------------------------------------------------------------------------
  1373.  
  1374. // hide popup
  1375. function hidePopup() {
  1376.  
  1377. isshowPopupEnabled = true;
  1378. if (!ishidePopupEnabled) return;
  1379. imgElementsList = [];
  1380. if (popupTimer) {
  1381. clearTimeout(popupTimer);
  1382. }
  1383. popup.style.display = 'none';
  1384. popupContainer.style.display = 'none';
  1385.  
  1386. NoMode();
  1387. popup.style.maxHeight = 'calc(90vh - 10px)';
  1388. popup.style.width = '';
  1389. popup.style.height = '';
  1390. popupContainer.style.left = '50%';
  1391. popupContainer.style.top = '50%';
  1392. popupContainer.style.position = 'fixed';
  1393. popupContainer.style.transform = 'translate(-50%, -50%) scaleX(1) scaleY(1)';
  1394. popupContainer.style.transformOrigin = '';
  1395. popupContainer.style.outline = '';
  1396. backdrop.style.zIndex = '';
  1397. backdrop.style.display = 'none';
  1398. backdrop.style.backdropFilter = '';
  1399. LeftBar.style.opacity = '0';
  1400. RightBar.style.opacity = '0';
  1401. style2.innerHTML = `
  1402. .LeftBar::before,
  1403. .LeftBar::after,
  1404. .RighttBar::before,
  1405. .RighttBar::after {
  1406. display: none;
  1407. }`;
  1408. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  1409.  
  1410. clickCount = 0;
  1411. cropAreaSet = false;
  1412. CropCover.style.zIndex = '1';
  1413. CropCover.style.border = '';
  1414. document.querySelectorAll('.cropArea1').forEach(e => e.remove())
  1415. document.querySelectorAll('.cropArea2').forEach(e => e.remove())
  1416. document.querySelectorAll('.cropArea3').forEach(e => e.remove())
  1417. document.querySelectorAll('.cropArea4').forEach(e => e.remove())
  1418.  
  1419. var removecropCanvas = document.getElementById('cropCanvas');
  1420. removecropCanvas.remove();
  1421. CropCover.remove();
  1422.  
  1423. }
  1424.  
  1425. popupContainer.addEventListener('mouseout', function(event) {
  1426. let relatedTarget = event.relatedTarget;
  1427. if (relatedTarget && (popupContainer.contains(relatedTarget) || relatedTarget.matches('.imgContainers'))) {
  1428. return;
  1429. }
  1430.  
  1431. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  1432. hidePopup();
  1433.  
  1434. if (intervalP !== '') {
  1435. popupTimer = setTimeout(() => {
  1436. popupTimer = null;
  1437. }, parseInt(intervalP));
  1438. }
  1439. });
  1440.  
  1441. //-------------------------------------------------------------------------
  1442.  
  1443.  
  1444. // lock popup in screen
  1445. backdrop.addEventListener('dblclick', function(event) {
  1446. clearTimeout(clickTimeout);
  1447. ishidePopupEnabled = true;
  1448. hidePopup();
  1449. });
  1450. backdrop.addEventListener('click', function(event) {
  1451. if (clickTimeout) clearTimeout(clickTimeout);
  1452. clickTimeout = setTimeout(function() {
  1453. ResetGeometry();
  1454. if (isScale) {
  1455. isScale = false;
  1456. isScaleTL = false;
  1457. isScaleTR = false;
  1458. isScaleBL = false;
  1459. isScaleBR = false;
  1460. ScalingMode0();
  1461.  
  1462. } else if (isLockedY) {
  1463. isLockedY = false;
  1464. popupContainer.style.border = '';
  1465. } else {
  1466. backdrop.style.zIndex = '';
  1467. backdrop.style.display = 'none';
  1468. backdrop.style.backdropFilter = '';
  1469. isshowPopupEnabled = false;
  1470. }
  1471. }, 300);
  1472. });
  1473.  
  1474. }
  1475. //-------------------------------------------------------------------------
  1476.  
  1477.  
  1478. // Is to be run -----------------------------------------------------------
  1479. if (URLmatched) {
  1480. const indicatorBar = document.createElement('div');
  1481. indicatorBar.style.cssText = `
  1482. position: fixed;
  1483. bottom: 0;
  1484. left: 50%;
  1485. transform: translateX(-50%);
  1486. z-index: 9999;
  1487. height: 30px;
  1488. width: 50vw;
  1489. background: #0000;`;
  1490. document.body.appendChild(indicatorBar);
  1491.  
  1492. function toggleIndicator() {
  1493. enableP = 1 - enableP;
  1494.  
  1495. indicatorBar.style.background = enableP ? 'linear-gradient(to right, rgba(50, 190, 152, 0) 0%, rgba(50, 190, 152, 0.5) 25%, rgba(50, 190, 152, 0.5) 75%, rgba(50, 190, 152, 0) 100%)' : 'linear-gradient(to right, rgba(174, 0, 1, 0) 0%, rgba(174, 0, 1, 0.5) 25%, rgba(174, 0, 1, 0.5) 75%, rgba(174, 0, 1, 0) 100%)';
  1496. setTimeout(() => {
  1497. indicatorBar.style.background = '#0000';
  1498. }, 1000);
  1499. if (enableP === 1) {
  1500. HoverZoomMinus();
  1501. } else {
  1502. document.querySelectorAll('.popup-container').forEach(e => e.remove());
  1503. document.querySelectorAll('.popup-backdrop').forEach(e => e.remove());
  1504. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  1505. }
  1506. }
  1507. let hoverTimeout;
  1508. indicatorBar.addEventListener('mouseenter', () => {
  1509. hoverTimeout = setTimeout(toggleIndicator, 500);
  1510. });
  1511. indicatorBar.addEventListener('mouseleave', () => {
  1512. clearTimeout(hoverTimeout);
  1513. indicatorBar.style.background = '#0000';
  1514. });
  1515. if (enableP === 1) {
  1516. HoverZoomMinus();
  1517. }
  1518. } else {
  1519. return;
  1520. }
  1521.  
  1522. })();