JVC_ImageViewer

Navigue6 entre les images d'un post sous forme de slideshow en cliquant sur une image sans ouvrir NoelShack.

当前为 2024-09-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name JVC_ImageViewer
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.37.13
  5. // @description Navigue6 entre les images d'un post sous forme de slideshow en cliquant sur une image sans ouvrir NoelShack.
  6. // @author HulkDu92
  7. // @match https://*.jeuxvideo.com/forums/*
  8. // @match https://jvarchive.com/forums/*
  9. // @grant none
  10. // @run-at document-end
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. class ImageViewer {
  18. constructor() {
  19. if (ImageViewer.instance) {
  20. return ImageViewer.instance;
  21. }
  22.  
  23. this.images = [];
  24. this.currentIndex = 0;
  25. this.overlay = null;
  26. this.imgElement = null;
  27. this.spinner = null;
  28. this.prevButton = null;
  29. this.nextButton = null;
  30. this.closeButton = null;
  31. this.infoText = null;
  32. this.indicatorsContainer = null;
  33. this.indicators = [];
  34. this.zoomLevel = 1;
  35. this.isDragging = false;
  36. this.startX = 0;
  37. this.startY = 0;
  38. this.offsetX = 0;
  39. this.offsetY = 0;
  40. this.xDown = null;
  41. this.yDown = null;
  42. this.initialDistance = null;
  43. this.startTouches = [];
  44. this.isSwiping = false;
  45. this.isScaling = false;
  46. this.imageElementScale = 1;
  47. this.start = {};
  48. this.isMouseDown = false; // Suivi de l'état du clic
  49. this.dragTimeout = null;
  50. this.mouseDownX = 0; // Pour la détection du drag
  51. this.mouseDownY = 0; // Pour la détection du drag
  52.  
  53. ImageViewer.instance = this;
  54.  
  55. this.createOverlay();
  56. this.updateImage();
  57. }
  58.  
  59. // Crée et configure les éléments du visualiseur d'images (overlay, boutons, texte d'information, etc.)
  60. createOverlay() {
  61. this.overlay = this.createElement('div', {
  62. position: 'fixed',
  63. top: 0,
  64. left: 0,
  65. width: '100%',
  66. height: '100%',
  67. backgroundColor: 'rgba(0, 0, 0, 0.8)',
  68. display: 'flex',
  69. alignItems: 'center',
  70. justifyContent: 'center',
  71. zIndex: 10000
  72. });
  73.  
  74. this.imgElement = this.createElement('img', {
  75. maxWidth: '90%',
  76. maxHeight: '80%',
  77. objectFit: 'contain',
  78. transition: 'opacity 0.3s',
  79. opacity: 0,
  80. cursor: 'pointer'
  81. });
  82.  
  83. this.spinner = this.createSpinner();
  84. this.prevButton = this.createButton('<', 'left');
  85. this.nextButton = this.createButton('>', 'right');
  86. this.closeButton = this.createCloseButton();
  87. this.infoText = this.createInfoText();
  88.  
  89. this.indicatorsContainer = this.createElement('div', {
  90. display: 'flex',
  91. justifyContent: 'center',
  92. marginBottom: '10px 0',
  93. position: 'absolute',
  94. bottom: '40px',
  95. });
  96.  
  97.  
  98. // Événements associés aux boutons et à l'overlay
  99. this.addEventListeners();
  100.  
  101. // Ajout des éléments au DOM
  102. this.overlay.append(this.imgElement, this.spinner, this.prevButton, this.nextButton, this.closeButton, this.indicatorsContainer, this.infoText);
  103. document.body.appendChild(this.overlay);
  104. }
  105.  
  106. // Crée un élément HTML avec des styles
  107. createElement(tag, styles = {}) {
  108. const element = document.createElement(tag);
  109. Object.assign(element.style, styles);
  110. return element;
  111. }
  112.  
  113. // Crée les indicateurs pour chaque image
  114. createIndicators() {
  115. this.indicators.forEach(indicator => {
  116. this.indicatorsContainer.removeChild(indicator);
  117. });
  118. this.indicators = [];
  119.  
  120. for (let i = 0; i < this.images.length; i++) {
  121. const dot = this.createElement('div', {
  122. width: '10px',
  123. height: '10px',
  124. borderRadius: '50%',
  125. backgroundColor: 'rgba(255, 255, 255, 0.5)',
  126. margin: '0 5px',
  127. cursor: 'pointer'
  128. });
  129.  
  130. dot.addEventListener('click', () => {
  131. this.currentIndex = i;
  132. this.updateImage();
  133. });
  134.  
  135. this.indicators.push(dot);
  136. this.indicatorsContainer.appendChild(dot);
  137. }
  138.  
  139. this.updateIndicators();
  140. }
  141.  
  142. // Met à jour les styles des indicateurs
  143. updateIndicators() {
  144. this.indicators.forEach((indicator, index) => {
  145. if (index === this.currentIndex) {
  146. indicator.style.backgroundColor = 'white'; // Point actif
  147. indicator.style.width = '12px'; // Point plus grand pour l'image active
  148. indicator.style.height = '12px';
  149. } else {
  150. indicator.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'; // Points inactifs
  151. indicator.style.width = '10px';
  152. indicator.style.height = '10px';
  153. }
  154. });
  155. }
  156.  
  157.  
  158. // Crée le bouton précédent ou suivant
  159. createButton(text, position) {
  160.  
  161. const button = this.createElement('button', {
  162. position: 'absolute',
  163. [position]: '10px',
  164. backgroundColor: 'rgba(0, 0, 0, 0.6)',
  165. color: 'white',
  166. fontSize: '22px',
  167. border: 'none',
  168. borderRadius: '50%',
  169. width: '40px',
  170. height: '40px',
  171. cursor: 'pointer',
  172. display: 'flex',
  173. alignItems: 'center',
  174. justifyContent: 'center',
  175. boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.6)',
  176. transition: 'background-color 0.3s, transform 0.3s'
  177. });
  178.  
  179.  
  180. button.textContent = text;
  181. this.addButtonEffects(button);
  182.  
  183. return button;
  184. }
  185.  
  186. // Crée le bouton de fermeture
  187. createCloseButton() {
  188. const button = this.createElement('button', {
  189. position: 'absolute',
  190. top: '80px',
  191. right: '10px',
  192. backgroundColor: 'rgba(0, 0, 0, 0.8)',
  193. color: 'white',
  194. fontSize: '14px',
  195. border: 'none',
  196. borderRadius: '50%',
  197. width: '35px',
  198. height: '35px',
  199. cursor: 'pointer',
  200. zIndex: 10001
  201. });
  202.  
  203. button.textContent = '✕';
  204. this.addButtonEffects(button);
  205.  
  206. return button;
  207. }
  208.  
  209. // Crée la zone d'affichage du texte d'information (numéro d'image)
  210. createInfoText() {
  211. return this.createElement('div', {
  212. position: 'absolute',
  213. bottom: '10px',
  214. right: '10px',
  215. color: 'white',
  216. fontSize: '16px',
  217. backgroundColor: 'rgba(0, 0, 0, 0.5)',
  218. padding: '5px',
  219. borderRadius: '5px',
  220. zIndex: 10001
  221. });
  222. }
  223.  
  224. // Crée un spinner pour indiquer le chargement de l'image
  225. createSpinner() {
  226. const spinner = this.createElement('div', {
  227. position: 'absolute',
  228. border: '8px solid #f3f3f3',
  229. borderTop: '8px solid #3498db',
  230. borderRadius: '50%',
  231. width: '50px',
  232. height: '50px',
  233. animation: 'spin 1s linear infinite',
  234. zIndex: 10001
  235. });
  236. return spinner;
  237. }
  238.  
  239. addButtonEffects(button) {
  240. button.addEventListener('mouseenter', () => {
  241. button.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
  242. button.style.color = 'black';
  243. button.style.transform = 'scale(1.1)';
  244. });
  245.  
  246. button.addEventListener('mouseleave', () => {
  247. button.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
  248. button.style.color = 'white';
  249. button.style.transform = 'scale(1)';
  250. });
  251.  
  252. button.addEventListener('mousedown', () => {
  253. button.style.transform = 'scale(0.9)';
  254. });
  255.  
  256. button.addEventListener('mouseup', () => {
  257. button.style.transform = 'scale(1.1)';
  258. });
  259. }
  260.  
  261. // Ajoute les événements aux différents éléments du visualiseur
  262. addEventListeners() {
  263. // Bouttons de controles du visualiseur
  264. this.prevButton.addEventListener('click', () => this.changeImage(-1));
  265. this.nextButton.addEventListener('click', () => this.changeImage(1));
  266. this.closeButton.addEventListener('click', () => this.closeViewer());
  267. this.overlay.addEventListener('click', (event) => {
  268. if (event.target === this.overlay) {
  269. this.closeViewer();
  270. }
  271. });
  272.  
  273. // Zoom avec la molette de la souris
  274. this.imgElement.addEventListener('wheel', (event) => this.handleZoom(event));
  275.  
  276.  
  277. // Déplacement lors du zoom (drag)
  278. this.imgElement.addEventListener('mousedown', (event) => this.startDrag(event));
  279. this.imgElement.addEventListener('mousedown', this.handleMouseDown.bind(this));
  280. this.imgElement.addEventListener('mouseup', this.handleMouseUp.bind(this));
  281.  
  282. // Glissement sur mobile (swipe)
  283. this.overlay.addEventListener('touchstart', (event) => this.handleTouchStart(event));
  284. this.overlay.addEventListener('touchmove', (event) => this.handleTouchMove(event));
  285. this.overlay.addEventListener('touchend', (event) => this.handleTouchEnd(event));
  286.  
  287. // Zoom sur mobile
  288. this.overlay.addEventListener('touchstart', (event) => this.handlePinchStart(event));
  289. this.overlay.addEventListener('touchmove', (event) => this.handlePinchMove(event));
  290. this.overlay.addEventListener('touchend', (event) => this.handlePinchEnd(event));
  291.  
  292. // Ouvrir l'image dans une no6velle fenêtre
  293. this.imgElement.addEventListener('click', () => {
  294. if (!this.isDragging) {
  295. window.open(this.images[this.currentIndex].href, '_blank');
  296. }
  297. });
  298.  
  299. // Touches du clavier
  300. document.addEventListener('keydown', (event) => this.handleKeyboardEvents(event));
  301. }
  302.  
  303. // Fonction pour gérer les touches du clavier
  304. handleKeyboardEvents(event) {
  305. switch (event.key) {
  306. case 'ArrowLeft':
  307. case 'ArrowUp':
  308. this.changeImage(-1);
  309. break;
  310. case 'ArrowRight':
  311. case 'ArrowDown':
  312. this.changeImage(1);
  313. break;
  314. case 'Escape':
  315. this.closeViewer();
  316. break;
  317. }
  318. }
  319.  
  320. // Gestion du début de l'interaction tactile
  321. handleTouchStart(event) {
  322. if (event.touches.length === 1) {
  323. // Commencer le swipe
  324. this.isSwiping = true;
  325. this.startX = event.touches[0].clientX;
  326. } /*else if (event.touches.length === 2) {
  327. // Commencer le zoom par pincement
  328. this.startTouches = [...event.touches];
  329. this.initialDistance = this.getTouchDistance(this.startTouches);
  330. }*/
  331. }
  332.  
  333. // Gestion du mouvement tactile pour swipe ou zoom
  334. handleTouchMove(event) {
  335. if (this.isSwiping && event.touches.length === 1) {
  336. // Swipe
  337. this.currentX = event.touches[0].clientX;
  338. } /*else if (event.touches.length === 2) {
  339. // Zoom par pincement
  340. this.handlePinchZoom(event);
  341. }*/
  342. }
  343.  
  344. // Gestion de la fin de l'interaction tactile
  345. handleTouchEnd(event) {
  346. if (event.touches.length < 2) {
  347. this.initialDistance = null;
  348. }
  349. if (this.isSwiping) {
  350. this.isSwiping = false;
  351. const deltaX = this.currentX - this.startX;
  352.  
  353. // Si le mouvement est suffisamment grand, on change d'image
  354. if (Math.abs(deltaX) > 50) {
  355. if (deltaX > 0) {
  356. this.showPreviousImage(); // Fonction pour afficher l'image précédente
  357. } else {
  358. this.showNextImage(); // Fonction pour afficher l'image suivante
  359. }
  360. }
  361. }
  362. }
  363.  
  364. // Calculate distance between two fingers
  365. distance(event){
  366. return Math.hypot(event.touches[0].pageX - event.touches[1].pageX, event.touches[0].pageY - event.touches[1].pageY);
  367. }
  368.  
  369. // Gestion du début de l'interaction tactile pour le pincement
  370. handlePinchStart(event) {
  371. if (event.touches.length === 2) {
  372. event.preventDefault(); // Prevent page scroll
  373.  
  374. // Calculate where the fingers have started on the X and Y axis
  375. this.start.x = (event.touches[0].pageX + event.touches[1].pageX) / 2;
  376. this.start.y = (event.touches[0].pageY + event.touches[1].pageY) / 2;
  377. this.start.distance = this.distance(event);
  378. // On commence à zoomer
  379. //this.initialDistance = this.getTouchDistance(event.touches);
  380. //this.isScaling = true;
  381. }
  382. }
  383.  
  384. // Gestion du mouvement tactile pour le pincement (zoom)
  385. handlePinchMove(event) {
  386. if (event.touches.length === 2) {
  387. event.preventDefault(); // Prevent page scroll
  388.  
  389. // Safari provides event.scale as two fingers move on the screen
  390. // For other browsers just calculate the scale manually
  391. let scale;
  392. if (event.scale) {
  393. scale = event.scale;
  394. } else {
  395. const deltaDistance = this.distance(event);
  396. scale = deltaDistance / this.start.distance;
  397. }
  398. this.imageElementScale = Math.min(Math.max(1, scale), 4);
  399.  
  400. // Calculate how much the fingers have moved on the X and Y axis
  401. const deltaX = (((event.touches[0].pageX + event.touches[1].pageX) / 2) - this.start.x) * 2; // x2 for accelarated movement
  402. const deltaY = (((event.touches[0].pageY + event.touches[1].pageY) / 2) - this.start.y) * 2; // x2 for accelarated movement
  403.  
  404. // Transform the image to make it grow and move with fingers
  405. const transform = `translate3d(${deltaX}px, ${deltaY}px, 0) scale(${this.imageElementScale})`;
  406. this.imgElement.style.transform = transform;
  407. this.imgElement.style.WebkitTransform = transform;
  408. this.imgElement.style.zIndex = "9999";
  409. }
  410. }
  411.  
  412. // Gestion de la fin de l'interaction tactile pour le pincement
  413. handlePinchEnd(event) {
  414. if (event.touches.length < 2) {
  415. this.imgElement.style.transform = "";
  416. this.imgElement.style.WebkitTransform = "";
  417. this.imgElement.style.zIndex = "";
  418. }
  419. }
  420.  
  421. // Fonction pour calculer la distance entre deux points de contact
  422. getTouchDistance(touches) {
  423. const [touch1, touch2] = touches;
  424. const deltaX = touch2.clientX - touch1.clientX;
  425. const deltaY = touch2.clientY - touch1.clientY;
  426. return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
  427. }
  428.  
  429. handleZoom(event) {
  430. event.preventDefault();
  431. const zoomIncrement = 0.1;
  432.  
  433. if (event.deltaY < 0) {
  434. this.zoomLevel += zoomIncrement; // Zoomer
  435. } else {
  436. this.zoomLevel = Math.max(1, this.zoomLevel - zoomIncrement); // Dézoomer, mais ne pas descendre sous 1
  437. }
  438.  
  439. this.imgElement.style.transform = `scale(${this.zoomLevel}) translate(${this.offsetX}px, ${this.offsetY}px)`;
  440. }
  441.  
  442.  
  443. startDrag(event) {
  444. event.preventDefault(); // Empêche la sélection de l'image
  445. if (!this.isMouseDown) return; // Ne pas démarrer le drag si le clic n'est pas maintenu
  446.  
  447. this.isDragging = true;
  448. this.startX = event.clientX - this.offsetX;
  449. this.startY = event.clientY - this.offsetY;
  450. this.imgElement.style.cursor = 'grabbing';
  451. this.imgElement.style.userSelect = 'none';
  452.  
  453. // Ajouter des listeners sur le document pour capturer les mouvements même si on sort de l'image
  454. document.addEventListener('mousemove', this.onDragBound = this.onDrag.bind(this));
  455. document.addEventListener('mouseup', this.endDragBound = this.endDrag.bind(this));
  456. }
  457.  
  458. onDrag(event) {
  459. if (!this.isDragging) return;
  460.  
  461.  
  462. event.preventDefault();
  463.  
  464. this.offsetX = event.clientX - this.startX;
  465. this.offsetY = event.clientY - this.startY;
  466.  
  467. // Appliquer la transformation avec le zoom actuel, en se déplaçant dans l'image
  468. this.imgElement.style.transform = `scale(${this.zoomLevel}) translate(${this.offsetX}px, ${this.offsetY}px)`;
  469. }
  470.  
  471. endDrag() {
  472. this.imgElement.style.cursor = 'grab';
  473.  
  474. // Retirer les listeners du document quand le drag est terminé
  475. document.removeEventListener('mousemove', this.onDragBound);
  476. document.removeEventListener('mouseup', this.endDragBound);
  477.  
  478.  
  479. // Ajouter un délai pour réinitialiser le drag (empeche les conflits avec le clic)
  480. this.dragTimeout = setTimeout(() => {
  481. this.isDragging = false;
  482. this.imgElement.style.cursor = 'pointer';
  483. this.dragTimeout = null;
  484. }, 100);
  485. }
  486.  
  487. handleMouseDown(event) {
  488. this.isMouseDown = true;
  489. this.mouseDownX = event.clientX;
  490. this.mouseDownY = event.clientY;
  491.  
  492. // Démarrer le drag après un délai pour éviter le drag lors des clics courts
  493. this.dragTimeout = setTimeout(() => {
  494. if (this.isMouseDown) {
  495. this.startDrag(event);
  496. }
  497. }, 200);
  498. }
  499.  
  500. handleMouseUp(event) {
  501. this.isMouseDown = false;
  502.  
  503. // Si le délai pour démarrer le drag est encore en cours, le nettoyer
  504. if (this.dragTimeout) {
  505. clearTimeout(this.dragTimeout);
  506. this.dragTimeout = null;
  507. }
  508.  
  509. // Vérifier si le mouvement est suffisant pour considérer que c'est un drag
  510. const movedX = Math.abs(event.clientX - this.mouseDownX);
  511. const movedY = Math.abs(event.clientY - this.mouseDownY);
  512.  
  513. if (movedX < 5 && movedY < 5) {
  514. this.handleImageClick(event); // Traiter le clic si le mouvement est minime
  515. }
  516. }
  517.  
  518.  
  519. startTouchDrag(event) {
  520. if (event.touches.length === 1) { // S'assurer qu'il y a un seul doigt
  521. this.isDragging = true;
  522. this.startX = event.touches[0].clientX - this.offsetX;
  523. this.startY = event.touches[0].clientY - this.offsetY;
  524. }
  525. }
  526.  
  527. onTouchDrag(event) {
  528. if (!this.isDragging || event.touches.length !== 1) return;
  529.  
  530. this.offsetX = event.touches[0].clientX - this.startX;
  531. this.offsetY = event.touches[0].clientY - this.startY;
  532.  
  533. this.imgElement.style.transform = `scale(${this.zoomLevel}) translate(${this.offsetX}px, ${this.offsetY}px)`;
  534. }
  535.  
  536.  
  537. // Gestion du zoom pour appareils tactiles (pincement)
  538. handlePinchZoom(event) {
  539. this.zoomLevel += event.scale - 1; // Ajuste le zoom en fonction de l'échelle du pincement
  540. this.imgElement.style.transform = `scale(${Math.max(1, this.zoomLevel)})`; // Ne pas descendre en dessous d'un zoom de 1
  541. }
  542.  
  543. // Réinitialiser le zoom de l'image
  544. resetZoom() {
  545. this.imgElement.style.transform = 'scale(1)';
  546. this.imgElement.style.transformOrigin = '0 0';
  547. }
  548.  
  549. // Réinitialiser la position du drag de l'image
  550. resetDrag() {
  551. this.imgElement.style.left = '0px';
  552. this.imgElement.style.top = '0px';
  553. }
  554.  
  555. // Met à jour l'image affichée dans le visualiseur
  556. updateImage() {
  557. if (this.currentIndex >= 0 && this.currentIndex < this.images.length) {
  558. const imageUrl = this.images[this.currentIndex].href;
  559. this.imgElement.src = imageUrl;
  560. this.infoText.textContent = `${this.currentIndex + 1} / ${this.images.length}`;
  561. this.spinner.style.display = 'block';
  562.  
  563. this.toggleButtonState();
  564.  
  565. this.imgElement.onload = () => {
  566. this.imgElement.style.opacity = 1;
  567. this.spinner.style.display = 'none';
  568.  
  569. // Réinitialiser le zoom et la position du drag
  570. this.resetZoom();
  571. this.resetDrag();
  572.  
  573.  
  574. // Calcul de la position des boutons
  575. const imgRect = this.imgElement.getBoundingClientRect();
  576. const isMobileDevice = isMobile(); // Détection des mobiles
  577.  
  578. if (isMobileDevice) {
  579. this.prevButton.style.width = '35px';
  580. this.prevButton.style.height = '35px';
  581. this.prevButton.style.fontSize = '18px';
  582. this.nextButton.style.width = '35px';
  583. this.nextButton.style.height = '35px';
  584. this.nextButton.style.fontSize = '18px';
  585.  
  586. } else {
  587. const margin = 30;
  588. this.prevButton.style.left = `${imgRect.left - this.prevButton.offsetWidth - margin}px`;
  589. this.nextButton.style.right = `${window.innerWidth - imgRect.right - this.nextButton.offsetWidth - margin}px`;
  590. }
  591.  
  592. // Mettre à jour les indicateurs
  593. this.createIndicators();
  594.  
  595. };
  596.  
  597. this.imgElement.onerror = () => this.handleImageError();
  598. }
  599. }
  600.  
  601. // Gestion des erreurs de chargement d'image
  602. handleImageError() {
  603. const miniUrl = this.images[this.currentIndex].querySelector('img').src;
  604. const extensions = ['.jpg', '.png', '.jpeg'];
  605. const baseUrl = miniUrl.replace('/minis/', '/fichiers/');
  606.  
  607. const tryNextExtension = (index) => {
  608. if (index >= extensions.length) {
  609. // Si toutes les extensions échouent, tenter l'URL originale
  610. this.imgElement.src = miniUrl;
  611. return;
  612. }
  613.  
  614. // Remplacer l'extension et mettre à jour l'URL
  615. const updatedUrl = baseUrl.replace(/\.(jpg|png|jpeg)$/, extensions[index]);
  616. this.imgElement.src = updatedUrl;
  617.  
  618. // Tester l'URL
  619. this.imgElement.onerror = () => tryNextExtension(index + 1);
  620. };
  621.  
  622. // Commencer les essais avec la première extension
  623. tryNextExtension(0);
  624. }
  625.  
  626. // Change d'image en fonction de la direction (suivant/précédent)
  627. changeImage(direction) {
  628. this.currentIndex = (this.currentIndex + direction + this.images.length) % this.images.length;
  629. this.imgElement.style.opacity = 0;
  630. this.spinner.style.display = 'block';
  631. this.updateImage();
  632. }
  633.  
  634. showPreviousImage() {
  635. this.changeImage(-1);
  636. }
  637.  
  638. showNextImage() {
  639. this.changeImage(1);
  640. }
  641.  
  642. // Ferme le visualiseur d'images
  643. closeViewer() {
  644. if (this.overlay) {
  645. document.body.removeChild(this.overlay);
  646. this.overlay = null;
  647. ImageViewer.instance = null; // Réinitialise l'instance singleton
  648. }
  649. }
  650.  
  651. // Désactive ou active les boutons suivant/précédent en fonction de l'index actuel
  652. toggleButtonState() {
  653. if (this.currentIndex === 0) {
  654. // this.prevButton.disabled = true;
  655. this.prevButton.style.opacity = 0.5;
  656. this.prevButton.style.cursor = 'initial';
  657. } else {
  658. // this.prevButton.disabled = false;
  659. this.prevButton.style.opacity = 1;
  660. this.prevButton.style.cursor = 'pointer';
  661. }
  662.  
  663. if (this.currentIndex === this.images.length - 1) {
  664. // this.nextButton.disabled = true;
  665. this.nextButton.style.opacity = 0.5;
  666. this.nextButton.style.cursor = 'initial';
  667. } else {
  668. // this.nextButton.disabled = false;
  669. this.nextButton.style.opacity = 1;
  670. this.nextButton.style.cursor = 'pointer';
  671. }
  672. }
  673.  
  674. openViewer(images, currentIndex) {
  675. if (this.overlay) {
  676. this.images = images;
  677. this.currentIndex = currentIndex;
  678. this.updateImage();
  679. //this.overlay.style.display = 'flex';
  680. } else {
  681. new ImageViewer();
  682. this.images = images;
  683. this.currentIndex = currentIndex;
  684. this.createOverlay();
  685. this.updateImage();
  686. //this.overlay.style.display = 'flex';
  687. }
  688. }
  689. }
  690.  
  691. function addSpinnerStyles() {
  692. const style = document.createElement('style');
  693. style.textContent = `
  694. @keyframes spin {
  695. 0% { transform: rotate(0deg); }
  696. 100% { transform: rotate(360deg); }
  697. }
  698. .spinner { /* Exemple de classe pour spinner */
  699. width: 50px;
  700. height: 50px;
  701. border: 5px solid rgba(0, 0, 0, 0.1);
  702. border-left-color: #000;
  703. border-radius: 50%;
  704. animation: spin 1s linear infinite;
  705. }
  706. `;
  707. document.head.appendChild(style);
  708. }
  709.  
  710. const parentClasses = '.txt-msg, .message, .conteneur-message.mb-3, .bloc-editor-forum, .signature-msg';
  711. const linkSelectors = parentClasses.split(', ').map(cls => `${cls} a`);
  712.  
  713. // Ajouter des écouteurs d'événements aux images sur la page
  714. function addListeners() {
  715. linkSelectors.forEach(selector => {
  716. document.querySelectorAll(selector).forEach(link => {
  717. link.addEventListener('click', handleImageClick, true);
  718. });
  719. });
  720. }
  721.  
  722. function handleImageClick(event) {
  723. // Si Ctrl ou Cmd est enfoncé, ne pas ouvrir l'ImageViewer
  724. if (event.ctrlKey || event.metaKey) {
  725. return;
  726. }
  727.  
  728. const imgElement = this.querySelector('img');
  729. if (imgElement) {
  730. event.preventDefault();
  731. const closestElement = this.closest(parentClasses);
  732. if (closestElement) {
  733. const images = Array.from(closestElement.querySelectorAll('a')).filter(imgLink => imgLink.querySelector('img'));
  734. const currentIndex = images.indexOf(this);
  735.  
  736. const viewer = new ImageViewer();
  737. viewer.openViewer(images, currentIndex);
  738. }
  739. }
  740. }
  741.  
  742.  
  743. function isMobile() {
  744. return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  745. }
  746.  
  747.  
  748. function main() {
  749. addSpinnerStyles();
  750. addListeners();
  751.  
  752. const observer = new MutationObserver(() => addListeners());
  753. observer.observe(document, { childList: true, subtree: true });
  754. }
  755.  
  756. main();
  757.  
  758.  
  759. })();