Hover Zoom Minus --

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

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

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