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