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.2
  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. popup.src = currentZeroImgElement.src;
  459. ResetGeometry();
  460. popup.style.height = '';
  461. popup.style.maxHeight = 'unset';
  462.  
  463. let vh = window.innerHeight / 100;
  464. if (rectzH > ((90 * vh) - 10)) {
  465. popup.style.maxHeight = '90vh - 10px';
  466. popup.style.width = '';
  467. }
  468. }
  469. }
  470. }
  471.  
  472. // Mouse Click: Center
  473. centerBox.addEventListener('click', function(event) {
  474. if (clickTimeout) clearTimeout(clickTimeout);
  475. clickTimeout = setTimeout(function() {
  476. ResetGeometry();
  477. if (!isScale) {
  478. isLockedY = !isLockedY;
  479. }
  480. if (isLockedY) {
  481. LockedYMode();
  482. let rect = popupContainer.getBoundingClientRect();
  483. offsetX = event.clientX - rect.left - (rect.width / 2);
  484. offsetY = event.clientY - rect.top - (rect.height / 2);
  485. } else {
  486. NoMode();
  487. }
  488. }, 300);
  489. });
  490.  
  491. centerBox.addEventListener('dblclick', function(event) {
  492. clearTimeout(clickTimeout);
  493. toggleLockedScreen();
  494. if (!ishidePopupEnabled) {
  495. backdrop.style.display = 'block';
  496. backdrop.style.zIndex = '999';
  497. backdrop.style.backdropFilter = 'blur(10px)';
  498. }
  499. });
  500. //-------------------------------------------------------------------------
  501.  
  502. // Mouse Click: Corners
  503. document.querySelectorAll('.CornerBox').forEach(element => {
  504. element.addEventListener('click', function(event) {
  505. ishidePopupEnabled = false;
  506. if (isScale) {
  507. BarClear();
  508. isLockedY = false;
  509. isLockedX = false;
  510. popup.style.border = '';
  511.  
  512. const clickedElement = event.target;
  513. const popupContainer = clickedElement.parentElement;
  514. const currentTransform = window.getComputedStyle(popupContainer).transform;
  515. const matrixMatch = currentTransform.match(/^matrix\(([^,]+), [^,]+, [^,]+, ([^,]+), [^,]+, [^,]+\)$/);
  516. if (matrixMatch) {
  517. scaleCurrentX = parseFloat(matrixMatch[1]);
  518. scaleCurrentY = parseFloat(matrixMatch[2]);
  519. }
  520.  
  521. let rect = popupContainer.getBoundingClientRect();
  522. offsetYT = event.clientY - rect.top;
  523. offsetXL = event.clientX - rect.left;
  524. offsetYB = rect.height + rect.top - event.clientY;
  525. offsetXR = rect.width + rect.left - event.clientX;
  526.  
  527. rectT = rect.top;
  528. rectL = rect.left;
  529. rectH = rect.height;
  530. rectW = rect.width;
  531.  
  532. } else {
  533. LockedScreen();
  534. ResetGeometry();
  535. }
  536.  
  537. });
  538. });
  539.  
  540. TL.addEventListener('click', function(event) {
  541. if (clickTimeout) clearTimeout(clickTimeout);
  542. clickTimeout = setTimeout(function() {
  543. isScale = !isScale;
  544. isScaleTL = !isScaleTL;
  545. isScaleTR = false;
  546. isScaleBL = false;
  547. isScaleBR = false;
  548. LeftBar.style.display = '';
  549. RightBar.style.display = '';
  550. }, 300);
  551. });
  552. TR.addEventListener('click', function(event) {
  553. if (clickTimeout) clearTimeout(clickTimeout);
  554. clickTimeout = setTimeout(function() {
  555. isScale = !isScale;
  556. isScaleTL = false;
  557. isScaleTR = !isScaleTR;
  558. isScaleBL = false;
  559. isScaleBR = false;
  560. LeftBar.style.display = '';
  561. RightBar.style.display = '';
  562. }, 300);
  563. });
  564. BL.addEventListener('click', function(event) {
  565. if (clickTimeout) clearTimeout(clickTimeout);
  566. clickTimeout = setTimeout(function() {
  567. isScale = !isScale;
  568. isScaleTL = false;
  569. isScaleTR = false;
  570. isScaleBL = !isScaleBL;
  571. isScaleBR = false;
  572. LeftBar.style.display = '';
  573. RightBar.style.display = '';
  574. }, 300);
  575. });
  576. BR.addEventListener('click', function(event) {
  577. if (clickTimeout) clearTimeout(clickTimeout);
  578. clickTimeout = setTimeout(function() {
  579. isScale = !isScale;
  580. isScaleTL = false;
  581. isScaleTR = false;
  582. isScaleBL = false;
  583. isScaleBR = !isScaleBR;
  584. LeftBar.style.display = '';
  585. RightBar.style.display = '';
  586. }, 300);
  587. });
  588. TL.addEventListener('dblclick', function(event) {
  589. clearTimeout(window.clickTimeout);
  590. clearTimeout(clickTimeout);
  591. ScaleMode2 = !ScaleMode2;
  592.  
  593. });
  594. TR.addEventListener('dblclick', function(event) {
  595. clearTimeout(window.clickTimeout);
  596. clearTimeout(clickTimeout);
  597. ScaleMode2 = !ScaleMode2;
  598.  
  599. });
  600. BL.addEventListener('dblclick', function(event) {
  601. clearTimeout(window.clickTimeout);
  602. clearTimeout(clickTimeout);
  603. ScaleMode2 = !ScaleMode2;
  604.  
  605. });
  606. BR.addEventListener('dblclick', function(event) {
  607. clearTimeout(window.clickTimeout);
  608. clearTimeout(clickTimeout);
  609. ScaleMode2 = !ScaleMode2;
  610.  
  611. });
  612. //-------------------------------------------------------------------------
  613.  
  614.  
  615. // Mouse Move: Pan, Scale
  616. document.addEventListener('mousemove', function(event) {
  617. // Panning mode: popup locked and follows the mouse
  618. if (isLockedY) {
  619. popupContainer.style.left = (event.clientX - offsetX) + 'px';
  620. popupContainer.style.top = (event.clientY - offsetY) + 'px';
  621. } else if (isScale) {
  622. ScalingMode1();
  623. if (ScaleMode2) {
  624. ScalingMode2();
  625. }
  626. if (isScaleTL) {
  627. popupContainer.style.transformOrigin = '100% 100% 0px';
  628. scaleFactorY = scaleCurrentY * (1 + (rectT - event.clientY + offsetYT) / rectH);
  629. scaleFactorX = scaleCurrentX * (1 + (rectL - event.clientX + offsetXL) / rectW);
  630. } else if (isScaleTR) {
  631. popupContainer.style.transformOrigin = '0 100% 0px';
  632. scaleFactorY = scaleCurrentY * (1 + (rectT - event.clientY + offsetYT) / rectH);
  633. scaleFactorX = scaleCurrentX * ((event.clientX - rectL + offsetXR) / rectW);
  634. } else if (isScaleBL) {
  635. popupContainer.style.transformOrigin = '100% 0% 0px';
  636. scaleFactorY = scaleCurrentY * ((event.clientY - rectT + offsetYB) / rectH);
  637. scaleFactorX = scaleCurrentX * (1 + (rectL - event.clientX + offsetXL) / rectW);
  638. } else if (isScaleBR) {
  639. popupContainer.style.transformOrigin = '0% 0% 0px';
  640. scaleFactorY = scaleCurrentY * ((event.clientY - rectT + offsetYB) / rectH);
  641. scaleFactorX = scaleCurrentX * ((event.clientX - rectL + offsetXR) / rectW);
  642. }
  643. if (ScaleMode2) {
  644. popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scaleFactorX}) scaleY(${scaleFactorY})`;
  645.  
  646. } else {
  647. popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scaleFactorX}) scaleY(${scaleFactorX})`;
  648. }
  649. }
  650. });
  651.  
  652.  
  653. // Mouse Wheel: Zoom, Scroll, Navigate
  654. function ZoomOrScroll(event) {
  655. event.preventDefault();
  656. if (isLockedY) {
  657. let deltaY = event.deltaY * -ZOOM_SPEED;
  658. let newTop = parseInt(popupContainer.style.top) || 0;
  659. newTop += deltaY * 100;
  660. popupContainer.style.top = newTop + 'px';
  661. offsetY -= deltaY * 100;
  662.  
  663. } else if (isLockedX && currentZeroImgElement) {
  664. pageDirection = event.deltaY > 0 ? 1 : -1;
  665. NavigateAlbum();
  666.  
  667. if (pageDirection === 1) {
  668. const imageElement = currentContainer.querySelector(imgElements);
  669. specialRightBar.forEach(pair => {
  670. if (imageElement.matches(pair.selector)) {
  671. pair.func(imageElement);
  672. }
  673. });
  674. } else {
  675. const imageElement = currentContainer.querySelector(imgElements);
  676. specialLeftBar.forEach(pair => {
  677. if (imageElement.matches(pair.selector)) {
  678. pair.func(imageElement);
  679. }
  680. });
  681. }
  682.  
  683. } else {
  684. scale += event.deltaY * -ZOOM_SPEED;
  685. scale = Math.min(Math.max(0.125, scale), 10);
  686. popupContainer.style.transform = `translate(-50%, -50%) scaleX(${scale}) scaleY(${scale})`;
  687. }
  688. }
  689. popupContainer.addEventListener('wheel', ZoomOrScroll);
  690. //-------------------------------------------------------------------------
  691.  
  692.  
  693. // Bottom Bar: Album
  694. BottomBar.addEventListener('mouseenter', function(e) {
  695.  
  696. if (isScale) {
  697. return;
  698. } else {
  699. BottomBar.style.opacity = '1';
  700.  
  701. let rect = popup.getBoundingClientRect();
  702. rectzRatio = rect.height / rect.width;
  703. rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
  704.  
  705. if (isAlbum) {
  706. if (!isScale) {
  707. 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%)';
  708. }
  709. } else {
  710. BottomBar.style.background = '';
  711. if (!( (rectIzRatio === 1) || isScale ) ) {
  712. style2.innerHTML = `
  713. .BottomBar::after {
  714. display: block;
  715. }
  716. `;
  717. BL.style.background = 'rgba(0, 0, 0, 0.5)';
  718. BR.style.background = 'rgba(0, 0, 0, 0.5)';
  719. BottomBar.style.background = 'rgba(0, 0, 0, 0.5)';
  720. } else {
  721. style2.innerHTML = `
  722. .BottomBar::after {
  723. display: none;
  724. }
  725. `;
  726. }
  727. }
  728. }
  729. });
  730.  
  731. BottomBar.addEventListener('mouseleave', function() {
  732. BottomBar.style.opacity = '0';
  733. if (!isLockedX) {
  734. BarClear();
  735. }
  736. });
  737.  
  738. BottomBar.addEventListener('click', function(event) {
  739. if (clickTimeout) clearTimeout(clickTimeout);
  740. clickTimeout = setTimeout(function() {
  741. if (!isScale) {
  742. LeftBar.style.opacity = '0';
  743. RightBar.style.opacity = '0';
  744. }
  745. if (isAlbum) {
  746. ResetGeometry();
  747. var img = new Image();
  748. img.onload = function() {
  749. natHeight = img.naturalHeight;
  750. natWidth = img.naturalWidth;
  751. };
  752. img.src = popup.src
  753. rectIRatio = natHeight / natWidth;
  754. isLockedX = !isLockedX;
  755. if (isLockedX) {
  756. LockedXMode();
  757. LeftBar.style.opacity = '1';
  758. RightBar.style.opacity = '1';
  759. } else {
  760. popupContainer.style.border = '';
  761. LeftBar.style.display = '';
  762. RightBar.style.display = '';
  763. }
  764. } else {
  765. ResetGeometry();
  766. if (isScale) {
  767. isScale = false;
  768. popup.style.maxHeight = rectzH + 'px';
  769. popup.style.height = rectzH + 'px';
  770. offsetRatioY = 0;
  771. } else {
  772. popup.style.maxHeight = 'unset';
  773. popup.style.height = '';
  774. offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
  775. }
  776. popupContainer.style.top = rectzT + (rectzH / 2) + offsetRatioY + 'px';
  777. }
  778. }, 300);
  779. });
  780.  
  781. BottomBar.addEventListener('dblclick', function(event) {
  782. clearTimeout(clickTimeout);
  783. if (isAlbum) {
  784. ResetGeometry();
  785. var img = new Image();
  786. img.onload = function() {
  787. natHeight = img.naturalHeight;
  788. natWidth = img.naturalWidth;
  789. };
  790. img.src = popup.src
  791. rectIRatio = natHeight / natWidth;
  792. if (isScale) {
  793. popup.style.maxHeight = rectzH + 'px';
  794. popup.style.height = rectzH + 'px';
  795. offsetRatioY = 0;
  796. } else {
  797. popup.style.maxHeight = 'unset';
  798. popup.style.height = '';
  799. offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
  800. }
  801. popupContainer.style.top = rectzT + (rectzH / 2) + offsetRatioY + 'px';
  802. BarClear();
  803. } else {
  804. return;
  805. }
  806. });
  807. //-------------------------------------------------------------------------
  808.  
  809.  
  810. // Indicators
  811. document.querySelectorAll('.CornerBox').forEach(element => {
  812. element.addEventListener('mouseenter', function(event) {
  813. ScalingMode1();
  814. if (ScaleMode2) {
  815. ScalingMode2();
  816. }
  817. });
  818. element.addEventListener('mouseleave', function(event) {
  819. if (!isScale) {
  820. ScalingMode0();
  821. }
  822. });
  823. });
  824.  
  825. // Re-scale/ Navigate
  826. TopBar.addEventListener('click', function(event) {
  827. if (clickTimeout) clearTimeout(clickTimeout);
  828. clickTimeout = setTimeout(function() {
  829. ResetGeometry();
  830. if (isAlbum) {
  831. var img = new Image();
  832. img.onload = function() {
  833. natHeight = img.naturalHeight;
  834. natWidth = img.naturalWidth;
  835. };
  836. img.src = popup.src
  837. rectIRatio = natHeight / natWidth;
  838. }
  839. if (isScale) {
  840. isScale = false;
  841. popup.style.maxHeight = rectzH + 'px';
  842. popup.style.height = rectzH + 'px';
  843. offsetRatioY = 0;
  844. } else {
  845. popup.style.maxHeight = 'unset';
  846. popup.style.height = '';
  847. offsetRatioY = (rectzH - rectzW * rectIRatio) / 2 ;
  848. }
  849. popupContainer.style.top = rectzT + (rectzH / 2) - offsetRatioY + 'px';
  850. BarClear();
  851.  
  852. }, 300);
  853. });
  854.  
  855. const styleCrop = document.createElement('style');
  856. styleCrop.type = 'text/css';
  857. styleCrop.innerHTML = `.cropArea1,.cropArea2,.cropArea3,.cropArea4 { display:none; }`;
  858. const CropCover = document.createElement('div');
  859. const cropArea1 = document.createElement('div');
  860. const cropArea2 = document.createElement('div');
  861. const cropArea3 = document.createElement('div');
  862. const cropArea4 = document.createElement('div');
  863.  
  864. let clickX1, clickY1, clickX2, clickY2;
  865. let clickCount;
  866. let cropAreaSet = false;
  867.  
  868. TopBar.addEventListener('dblclick', function(event) {
  869. clearTimeout(window.clickTimeout);
  870.  
  871. LockedScreen();
  872. clickCount = 0;
  873. ResetGeometry()
  874. cropAreaSet = false;
  875.  
  876. const CropCover = document.createElement('div');
  877. const cropArea1 = document.createElement('div');
  878. const cropArea2 = document.createElement('div');
  879. const cropArea3 = document.createElement('div');
  880. const cropArea4 = document.createElement('div');
  881. CropCover.className = 'CropCover'
  882. cropArea1.className = 'cropArea1';
  883. cropArea2.className = 'cropArea2';
  884. cropArea3.className = 'cropArea3';
  885. cropArea4.className = 'cropArea4';
  886. popupContainer.appendChild(CropCover);
  887. document.body.appendChild(cropArea1);
  888. document.body.appendChild(cropArea2);
  889. document.body.appendChild(cropArea3);
  890. document.body.appendChild(cropArea4);
  891. 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;';
  892. cropArea1.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  893. cropArea2.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  894. cropArea3.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  895. cropArea4.style.cssText ='display: block; z-index: 9999; position: fixed; background: rgba(0, 0, 0, 0.4);';
  896.  
  897. let rect = popup.getBoundingClientRect();
  898. rectzH = rect.height;
  899. rectzW = rect.width;
  900. rectzT = rect.top;
  901. rectzL = rect.left;
  902.  
  903. let clickTimeout;
  904.  
  905. CropCover.addEventListener('click', function(e) {
  906. if (clickCount === 0) {
  907. clickX1 = e.clientX;
  908. clickY1 = e.clientY;
  909. clickCount = 1;
  910. } else if (clickCount === 1) {
  911. clickCount = 2;
  912. clickX2 = e.clientX;
  913. clickY2 = e.clientY;
  914. cropAreaSet = true;
  915. if (clickCount === 2) {
  916. cropImage();
  917. }
  918. }
  919. });
  920.  
  921. document.addEventListener('mousemove', function(event) {
  922.  
  923. cropArea1.style.left = '0px';
  924. cropArea1.style.top = '0px';
  925. cropArea1.style.height = '100vh';
  926.  
  927. cropArea2.style.top = '0px';
  928.  
  929.  
  930. if (clickCount === 0) {
  931. cropArea1.style.width = event.clientX + 'px';
  932.  
  933. cropArea2.style.left = event.clientX + 'px';
  934. cropArea2.style.height = event.clientY + 'px';
  935. cropArea2.style.width = `calc(100vw - ${event.clientX}px)`;
  936.  
  937.  
  938. } else if (clickCount === 1) {
  939. cropArea1.style.width = clickX1 + 'px';
  940.  
  941. cropArea2.style.left = clickX1 + 'px';
  942. cropArea2.style.height = clickY1 + 'px';
  943. cropArea2.style.width = `calc(100vw - ${clickX1}px)`;
  944.  
  945.  
  946. }
  947. if (!cropAreaSet && (clickCount === 1)) {
  948. cropArea3.style.left = clickX1 + 'px';
  949. cropArea3.style.top = event.clientY + 1 + 'px';
  950. cropArea3.style.width = `calc(${event.clientX}px + 1px - ${clickX1}px)`;
  951. cropArea3.style.height = `calc(100vh - 1px - ${event.clientY}px)`;
  952.  
  953. cropArea4.style.left = event.clientX + 1 + 'px';
  954. cropArea4.style.top = clickY1 + 'px'
  955. cropArea4.style.height = `calc(100vh - 1px - ${clickY1}px)`;
  956. cropArea4.style.width = `calc(100vw - 1px - ${event.clientX}px)`;
  957.  
  958.  
  959. }
  960. });
  961.  
  962. });
  963.  
  964.  
  965. function cropImage() {
  966. var canvas = document.createElement('canvas');
  967. canvas.id = 'cropCanvas';
  968. var ctx = canvas.getContext('2d');
  969. document.body.appendChild(canvas);
  970. let cropX, cropY, cropW, cropH, scW, scH
  971.  
  972. scW = clickX2 - clickX1
  973. scH = clickY2 - clickY1
  974. cropX = natWidth * ((clickX1 - rectzL) / rectzW);
  975. cropY = natHeight * ((clickY1 - rectzT) / rectzH);
  976. cropW = natWidth * (scW / rectzW);
  977. cropH = natHeight * (scH / rectzH);
  978. canvas.width = scW;
  979. canvas.height = scH;
  980.  
  981. var image = new Image();
  982. image.crossOrigin = "anonymous";
  983. image.src = popup.src
  984. image.onload = function() {
  985. ctx.drawImage(image, cropX, cropY, cropW, cropH, 0, 0, scW, scH);
  986. saveImage(canvas);
  987. }
  988.  
  989. document.querySelectorAll('.CropCover').forEach(e => e.remove())
  990. }
  991.  
  992.  
  993. function saveImage(canvas) {
  994. if (confirm('Do you want to save the image?')) {
  995. var croppedImageDataURL = canvas.toDataURL('image/png');
  996. var originalFileName = popup.src.split('/').pop();
  997. var truncatedFileName = originalFileName.length > 100 ? originalFileName.substring(0, 100) : originalFileName;
  998. var downloadLink = document.createElement('a');
  999. downloadLink.href = croppedImageDataURL;
  1000. downloadLink.download = truncatedFileName;
  1001. document.body.appendChild(downloadLink);
  1002. downloadLink.click();
  1003. }
  1004.  
  1005. clickCount = 0;
  1006. cropAreaSet = false;
  1007. document.querySelectorAll('.cropArea1').forEach(e => e.remove())
  1008. document.querySelectorAll('.cropArea2').forEach(e => e.remove())
  1009. document.querySelectorAll('.cropArea3').forEach(e => e.remove())
  1010. document.querySelectorAll('.cropArea4').forEach(e => e.remove())
  1011. document.body.removeChild(downloadLink);
  1012. var removecropCanvas = document.getElementById('cropCanvas');
  1013. removecropCanvas.remove();
  1014.  
  1015. }
  1016.  
  1017. let zeroIndex;
  1018. LeftBar.addEventListener('click', function(event) {
  1019. if (clickTimeout) clearTimeout(clickTimeout);
  1020. clickTimeout = setTimeout(function() {
  1021. ResetGeometry();
  1022. if (isAlbum) {
  1023. var img = new Image();
  1024. img.onload = function() {
  1025. natHeight = img.naturalHeight;
  1026. natWidth = img.naturalWidth;
  1027. };
  1028. img.src = popup.src
  1029. rectIRatio = natHeight / natWidth;
  1030. }
  1031. if (isScale) {
  1032. isScale = false;
  1033. popup.style.width = rectzW + 'px';
  1034. offsetRatioX = 0;
  1035. } else {
  1036. popup.style.width = '';
  1037. offsetRatioX = (rectzW - (rectzH / rectIRatio)) / 2 ;
  1038. }
  1039. if (isLockedX && currentZeroImgElement) {
  1040. pageDirection = -1;
  1041. NavigateAlbum();
  1042. TL.style.background = 'rgba(0, 0, 0, 0.5)';
  1043. BL.style.background = 'rgba(0, 0, 0, 0.5)';
  1044. LeftBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1045.  
  1046. const imageElement = currentContainer.querySelector(imgElements);
  1047. specialLeftBar.forEach(pair => {
  1048. if (imageElement.matches(pair.selector)) {
  1049. pair.func(imageElement);
  1050. }
  1051. });
  1052. } else {
  1053. popupContainer.style.left = rectzL + (rectzW / 2) - offsetRatioX + 'px';
  1054. }
  1055. }, 300);
  1056. });
  1057. RightBar.addEventListener('click', function(event) {
  1058. if (clickTimeout) clearTimeout(clickTimeout);
  1059. clickTimeout = setTimeout(function() {
  1060. ResetGeometry();
  1061. if (isAlbum) {
  1062. var img = new Image();
  1063. img.onload = function() {
  1064. natHeight = img.naturalHeight;
  1065. natWidth = img.naturalWidth;
  1066. };
  1067. img.src = popup.src
  1068. rectIRatio = natHeight / natWidth;
  1069. }
  1070. if (isScale) {
  1071. isScale = false;
  1072. popup.style.width = rectzW + 'px';
  1073. offsetRatioX = 0;
  1074. } else {
  1075. popup.style.width = '';
  1076. offsetRatioX = (rectzW - (rectzH / rectIRatio)) / 2 ;
  1077. }
  1078. if (isLockedX && currentZeroImgElement) {
  1079. pageDirection = 1;
  1080. NavigateAlbum();
  1081. TR.style.background = 'rgba(0, 0, 0, 0.5)';
  1082. BR.style.background = 'rgba(0, 0, 0, 0.5)';
  1083. RightBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1084.  
  1085. const imageElement = currentContainer.querySelector(imgElements);
  1086. specialRightBar.forEach(pair => {
  1087. if (imageElement.matches(pair.selector)) {
  1088. pair.func(imageElement);
  1089. }
  1090. });
  1091. } else {
  1092. popupContainer.style.left = rectzL + (rectzW / 2) + offsetRatioX + 'px';
  1093. }
  1094. }, 300);
  1095. });
  1096.  
  1097. TopBar.addEventListener('mouseenter', function() {
  1098.  
  1099. var img = new Image();
  1100. img.onload = function() {
  1101. natHeight = img.naturalHeight;
  1102. natWidth = img.naturalWidth;
  1103. };
  1104. img.src = popup.src
  1105. let rectZ = popup.getBoundingClientRect();
  1106. let rectZH = rectZ.height;
  1107. let rectZW = rectZ.width;
  1108. let rectZRatio = rectzH / rectzW;
  1109. let natRatio = natHeight / natWidth;
  1110. let rectNZRatio = Number(natRatio.toFixed(3)) / Number(rectZRatio.toFixed(3));
  1111. // not original Ratio and not scaling mode aplly with arrow indicator
  1112. if (!((rectNZRatio === 1) || isScale )) {
  1113.  
  1114. TopBar.style.opacity = '1';
  1115. TL.style.background = 'rgba(0, 0, 0, 0.5)';
  1116. TR.style.background = 'rgba(0, 0, 0, 0.5)';
  1117. TopBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1118.  
  1119. style2.innerHTML = `.TopBar::after { display: block; }`;
  1120.  
  1121. } else {
  1122. style2.innerHTML = `.TopBar::after { display: none; }`;
  1123. }
  1124. });
  1125. LeftBar.addEventListener('mouseenter', function() {
  1126. LeftBar.style.opacity = '1';
  1127. let rect = popup.getBoundingClientRect();
  1128. rectzRatio = rect.height / rect.width;
  1129. rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
  1130. if ( isLockedX || !( (rectIzRatio === 1) || isScale ) ) {
  1131. style2.innerHTML = `
  1132. .LeftBar::before, .LeftBar::after {
  1133. display: block;
  1134. }`;
  1135. TL.style.background = 'rgba(0, 0, 0, 0.5)';
  1136. BL.style.background = 'rgba(0, 0, 0, 0.5)';
  1137. LeftBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1138. } else {
  1139. style2.innerHTML = `
  1140. .LeftBar::before, .LeftBar::after {
  1141. display: none;
  1142. }`;
  1143. }
  1144. });
  1145. RightBar.addEventListener('mouseenter', function() {
  1146. RightBar.style.opacity = '1';
  1147. let rect = popup.getBoundingClientRect();
  1148. rectzRatio = rect.height / rect.width;
  1149. rectIzRatio = Number(rectIRatio.toFixed(3)) / Number(rectzRatio.toFixed(3));
  1150. if ( isLockedX || !( (rectIzRatio === 1) || isScale ) ) {
  1151. style2.innerHTML = `
  1152. .RightBar::before, .RightBar::after {
  1153. display: block;
  1154. }`;
  1155. TR.style.background = 'rgba(0, 0, 0, 0.5)';
  1156. BR.style.background = 'rgba(0, 0, 0, 0.5)';
  1157. RightBar.style.background = 'rgba(0, 0, 0, 0.5)';
  1158. } else {
  1159. style2.innerHTML = `
  1160. .RightBar::before, .RightBar::after {
  1161. display: none;
  1162. }`;
  1163. }
  1164. });
  1165.  
  1166. TopBar.addEventListener('mouseleave', function() {
  1167. TopBar.style.opacity = '0';
  1168. TL.style.background = '';
  1169. TR.style.background = '';
  1170. });
  1171. LeftBar.addEventListener('mouseleave', function() {
  1172. if (isLockedX) {
  1173. LeftBar.style.opacity = '1';
  1174. } else {
  1175. LeftBar.style.opacity = '0';
  1176. }
  1177. TL.style.background = '';
  1178. BL.style.background = '';
  1179. LeftBar.style.background = '';
  1180. });
  1181. RightBar.addEventListener('mouseleave', function() {
  1182. if (isLockedX) {
  1183. RightBar.style.opacity = '1';
  1184. } else {
  1185. RightBar.style.opacity = '0';
  1186. }
  1187. TR.style.background = '';
  1188. BR.style.background = '';
  1189. RightBar.style.background = '';
  1190. });
  1191. //-------------------------------------------------------------------------
  1192.  
  1193. let natXratio, natYratio, natHeight, natWidth;
  1194.  
  1195. // show popup
  1196. function showPopup(src, mouseX, mouseY) {
  1197. if (!isshowPopupEnabled) return;
  1198. ishidePopupEnabled = true;
  1199. if (enableP === 0) return;
  1200.  
  1201. popup.src = src;
  1202. popup.style.display = 'block';
  1203. popupContainer.style.display = 'block';
  1204. popupContainer.style.position = 'fixed';
  1205. popupContainer.style.transform = 'translate(-50%, -50%) scaleX(1) scaleY(1)';
  1206. backdrop.style.display = 'block';
  1207. backdrop.style.zIndex = '999';
  1208. backdrop.style.backdropFilter = 'blur(10px)';
  1209.  
  1210. if (positionP === 'center') {
  1211. popupContainer.style.top = '50%';
  1212. popupContainer.style.left = '50%';
  1213. } else {
  1214. popupContainer.style.top = `${mouseY}px`;
  1215. popupContainer.style.left = `${mouseX}px`;
  1216. }
  1217.  
  1218. let rectI = popup.getBoundingClientRect();
  1219. rectIH = rectI.height;
  1220. rectIW = rectI.width;
  1221. rectIT = rectI.top;
  1222. rectIL = rectI.left;
  1223.  
  1224. var img = new Image();
  1225. img.onload = function() {
  1226. natHeight = img.naturalHeight;
  1227. natWidth = img.naturalWidth;
  1228. };
  1229. img.src = popup.src
  1230.  
  1231. natXratio = natWidth / rectIW;
  1232. natYratio = natHeight / rectIH;
  1233. rectIRatio = rectIH / rectIW;
  1234.  
  1235. if (positionP === '') {
  1236. if (mouseY < window.innerHeight * 0.33) {
  1237. popupContainer.style.top = mouseY + (rectIH / 2) - 40 + 'px';
  1238. } else if (mouseY >= window.innerHeight * 0.33 && mouseY < window.innerHeight * 0.67) {
  1239. popupContainer.style.top = mouseY + 'px';
  1240. } else if (mouseY >= window.innerHeight * 0.67) {
  1241. popupContainer.style.top = mouseY - (rectIH / 2) + 40 + 'px';
  1242. }
  1243. if (mouseX < window.innerWidth * 0.33) {
  1244. popupContainer.style.left = mouseX + (rectIW / 2) - 40 + 'px';
  1245. } else if (mouseX >= window.innerWidth * 0.33 && mouseX < window.innerWidth * 0.67) {
  1246. popupContainer.style.left = mouseX + 'px';
  1247. } else if (mouseX >= window.innerWidth * 0.67) {
  1248. popupContainer.style.left = mouseX - (rectIW / 2) + 40 + 'px';
  1249. }
  1250. }
  1251. if (isAlbum) {
  1252. LockedXMode();
  1253. }
  1254.  
  1255. }
  1256.  
  1257. let isAlbum;
  1258. let currentContainer;
  1259. document.addEventListener('mouseover', function(e) {
  1260. if (popupTimer) return;
  1261. let target = e.target.closest(imgContainers);
  1262. if (!target) return;
  1263. if (target.querySelector(nopeElements)) return;
  1264. currentContainer = target;
  1265. const imageElement = currentContainer.querySelector(imgElements);
  1266. specialElements.forEach(pair => {
  1267. if (imageElement.matches(pair.selector)) {
  1268. pair.func(imageElement);
  1269. }
  1270. });
  1271. if (imageElement) {
  1272. currentZeroImgElement = imageElement;
  1273. if (currentZeroImgElement) {
  1274. let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
  1275. if (pair) {
  1276. let closestAlbumElement = currentZeroImgElement.closest(pair.albumElements);
  1277. isAlbum = closestAlbumElement && closestAlbumElement.querySelectorAll(pair.imgElement).length > 1;
  1278. if (isAlbum) {
  1279. imgElementsList = Array.from(closestAlbumElement.querySelectorAll(pair.imgElement));
  1280. let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);
  1281. TR.textContent = `${zeroIndex + 1}/${imgElementsList.length}`;
  1282. if (zeroIndex === 0) {
  1283. LeftBar.style.display = 'none';
  1284. } else {
  1285. LeftBar.style.display = '';
  1286. }
  1287. if (zeroIndex === imgElementsList.length - 1) {
  1288. RightBar.style.display = 'none';
  1289. } else {
  1290. RightBar.style.display = '';
  1291. }
  1292. }
  1293. } else {
  1294. isAlbum = false;
  1295. TR.textContent = '';
  1296. LeftBar.style.display = '';
  1297. RightBar.style.display = '';
  1298. }
  1299. }
  1300. if (intervalP === '') {
  1301. showPopup(imageElement.src, e.clientX, e.clientY);
  1302. } else {
  1303. popupTimer = setTimeout(() => {
  1304. showPopup(imageElement.src, e.clientX, e.clientY);
  1305. popupTimer = null;
  1306. }, parseInt(intervalP));
  1307. }
  1308. }
  1309. });
  1310. //-------------------------------------------------------------------------
  1311.  
  1312. // hide popup
  1313. function hidePopup() {
  1314. if (!ishidePopupEnabled) return;
  1315. imgElementsList = [];
  1316. if (popupTimer) {
  1317. clearTimeout(popupTimer);
  1318. }
  1319. popup.style.display = 'none';
  1320. popupContainer.style.display = 'none';
  1321.  
  1322. NoMode();
  1323. popup.style.maxHeight = 'calc(90vh - 10px)';
  1324. popup.style.width = '';
  1325. popup.style.height = '';
  1326. popupContainer.style.left = '50%';
  1327. popupContainer.style.top = '50%';
  1328. popupContainer.style.position = 'fixed';
  1329. popupContainer.style.transform = 'translate(-50%, -50%) scaleX(1) scaleY(1)';
  1330. popupContainer.style.transformOrigin = '';
  1331. popupContainer.style.outline = '';
  1332. backdrop.style.zIndex = '';
  1333. backdrop.style.display = 'none';
  1334. backdrop.style.backdropFilter = '';
  1335. LeftBar.style.opacity = '0';
  1336. RightBar.style.opacity = '0';
  1337. style2.innerHTML = `
  1338. .LeftBar::before,
  1339. .LeftBar::after,
  1340. .RighttBar::before,
  1341. .RighttBar::after {
  1342. display: none;
  1343. }`;
  1344. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  1345.  
  1346. clickCount = 0;
  1347. cropAreaSet = false;
  1348. CropCover.style.zIndex = '1';
  1349. CropCover.style.border = '';
  1350. document.querySelectorAll('.cropArea1').forEach(e => e.remove())
  1351. document.querySelectorAll('.cropArea2').forEach(e => e.remove())
  1352. document.querySelectorAll('.cropArea3').forEach(e => e.remove())
  1353. document.querySelectorAll('.cropArea4').forEach(e => e.remove())
  1354.  
  1355. var removecropCanvas = document.getElementById('cropCanvas');
  1356. removecropCanvas.remove();
  1357. CropCover.remove();
  1358.  
  1359. }
  1360. popupContainer.addEventListener('mouseout', function(event) {
  1361.  
  1362. let relatedTarget = event.relatedTarget;
  1363. if (relatedTarget && (popupContainer.contains(relatedTarget) || relatedTarget.matches('.imgContainers'))) {
  1364. return;
  1365. }
  1366.  
  1367. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  1368.  
  1369. hidePopup();
  1370. if (intervalP !== '') {
  1371. popupTimer = setTimeout(() => {
  1372. popupTimer = null;
  1373. }, parseInt(intervalP));
  1374. }
  1375. });
  1376. document.addEventListener('keydown', function(event) {
  1377. if (event.key === "Escape") {
  1378. event.preventDefault();
  1379. hidePopup();
  1380. }
  1381. });
  1382. //-------------------------------------------------------------------------
  1383.  
  1384.  
  1385. // lock popup in screen
  1386. backdrop.addEventListener('dblclick', function(event) {
  1387. clearTimeout(clickTimeout);
  1388. ishidePopupEnabled = true;
  1389. hidePopup();
  1390. });
  1391. backdrop.addEventListener('click', function(event) {
  1392. if (clickTimeout) clearTimeout(clickTimeout);
  1393. clickTimeout = setTimeout(function() {
  1394. ResetGeometry();
  1395. if (isScale) {
  1396. isScale = false;
  1397. isScaleTL = false;
  1398. isScaleTR = false;
  1399. isScaleBL = false;
  1400. isScaleBR = false;
  1401. ScalingMode0();
  1402.  
  1403. } else if (isLockedY) {
  1404. isLockedY = false;
  1405. popupContainer.style.border = '';
  1406. } else {
  1407. backdrop.style.zIndex = '';
  1408. backdrop.style.display = 'none';
  1409. backdrop.style.backdropFilter = '';
  1410. isshowPopupEnabled = false;
  1411. }
  1412. }, 300);
  1413. });
  1414.  
  1415. }
  1416. //-------------------------------------------------------------------------
  1417.  
  1418.  
  1419. // Is to be run -----------------------------------------------------------
  1420. if (URLmatched) {
  1421. const indicatorBar = document.createElement('div');
  1422. indicatorBar.style.cssText = `
  1423. position: fixed;
  1424. bottom: 0;
  1425. left: 50%;
  1426. transform: translateX(-50%);
  1427. z-index: 9999;
  1428. height: 30px;
  1429. width: 50vw;
  1430. background: #0000;`;
  1431. document.body.appendChild(indicatorBar);
  1432.  
  1433. function toggleIndicator() {
  1434. enableP = 1 - enableP;
  1435.  
  1436. 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%)';
  1437. setTimeout(() => {
  1438. indicatorBar.style.background = '#0000';
  1439. }, 1000);
  1440. if (enableP === 1) {
  1441. HoverZoomMinus();
  1442. } else {
  1443. document.querySelectorAll('.popup-container').forEach(e => e.remove());
  1444. document.querySelectorAll('.popup-backdrop').forEach(e => e.remove());
  1445. document.querySelectorAll('.specialElement').forEach(e => e.remove());
  1446. }
  1447. }
  1448. let hoverTimeout;
  1449. indicatorBar.addEventListener('mouseenter', () => {
  1450. hoverTimeout = setTimeout(toggleIndicator, 500);
  1451. });
  1452. indicatorBar.addEventListener('mouseleave', () => {
  1453. clearTimeout(hoverTimeout);
  1454. indicatorBar.style.background = '#0000';
  1455. });
  1456. if (enableP === 1) {
  1457. HoverZoomMinus();
  1458. }
  1459. } else {
  1460. return;
  1461. }
  1462.  
  1463. })();