Hover Zoom Minus --

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

当前为 2024-03-26 提交的版本,查看 最新版本

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