Hover Zoom Minus --

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

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

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