- // ==UserScript==
- // @name JVC_ImageViewer
- // @namespace http://tampermonkey.net/
- // @version 1.42.5
- // @description Naviguer entre les images d'un post sous forme de slideshow en cliquant sur une image sans ouvrir NoelShack.
- // @author HulkDu92
- // @match https://*.jeuxvideo.com/forums/*
- // @match https://*.jeuxvideo.com/profil/*
- // @match https://*.jeuxvideo.com/messages-prives/*
- // @match https://jvarchive.com/*
- // @grant GM_download
- // @grant GM.xmlHttpRequest
- // @connect image.noelshack.com
- // @run-at document-end
- // @license MIT
- // @icon https://image.noelshack.com/fichiers/2024/41/3/1728506420-image-viewer-icon.png
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- class ImageViewer {
- constructor() {
- if (ImageViewer.instance) {
- return ImageViewer.instance;
- }
-
- this.images = [];
- this.currentIndex = 0;
- this.overlay = null;
- this.imgElement = null;
- this.spinner = null;
- this.prevButton = null;
- this.nextButton = null;
- this.closeButton = null;
- this.infoText = null;
- this.downloadButton = null;
- this.searchButton = null;
- this.optionButton = null;
- this.indicatorsContainer = null;
- this.indicators = [];
- this.zoomLevel = 1;
- this.isDragging = false;
- this.isPinchZooming = false;
- this.startX = 0;
- this.startY = 0;
- this.offsetX = 0;
- this.offsetY = 0;
- this.xDown = null;
- this.yDown = null;
- this.initialDistance = null;
- this.startTouches = [];
- this.isSwiping = false;
- this.isScaling = false;
- this.imageElementScale = 1;
- this.start = {};
- this.isMouseDown = false;
- this.isTouchDragging = false;
- this.dragTimeout = null;
- this.mouseDownX = 0;
- this.mouseDownY = 0;
- this.initialScale = 1;
- this.isViewerOpen = false;
- this.thumbnailPanel = null;
- this.previousThumbnail = null;
- this.touchSensitivityFactor = 0.5; // Pour les appareils tactiles
- this.mouseSensitivityFactor = 0.4; // Pour les mouvements de souris
- this.defaultImageWidth = Math.min(window.innerWidth, 1200);
-
- ImageViewer.instance = this;
-
- this.handlePopState = this.handlePopState.bind(this);
-
- this.createOverlay();
- this.updateImage();
- }
-
- // Crée et configure les éléments du visualiseur d'images (overlay, boutons, texte d'information, etc.)
- createOverlay() {
- this.overlay = this.createElement('div', {
- position: 'fixed',
- top: 0,
- left: 0,
- width: '100%',
- height: '100%',
- backgroundColor: 'rgba(0, 0, 0, 0.8)',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- zIndex: 10000
- });
- this.overlay.classList.add('https://www.jeuxvideo.com/forums/42-51-74793440-1-0-1-0-officiel-jeux-paralympiques-de-paris-2024-du-28-aout-au-8-septembre.htm');
-
-
- this.imgElement = this.createElement('img', {
- maxWidth: '90%',
- maxHeight: '80%',
- objectFit: 'contain',
- transition: 'opacity 0.3s',
- opacity: 0,
- cursor: 'pointer',
- overflow: 'auto',
- });
-
- this.spinner = this.createSpinner();
- this.prevButton = this.createButton('<', 'left');
- this.nextButton = this.createButton('>', 'right');
- this.closeButton = this.createCloseButton();
- this.infoText = this.createInfoText();
-
- this.downloadButton = this.createDownloadButton();
- this.searchButton = this.createSearchButton();
- this.optionButton = this.createOptionButton();
-
- this.thumbnailPanel = this.createThumbnailPannel();
- this.optionsMenu = this.createOptionsMenu(); // must be last
-
- this.indicatorsContainer = this.createElement('div', {
- display: 'flex',
- justifyContent: 'center',
- marginBottom: '10px 0',
- position: 'absolute',
- bottom: '40px',
- });
-
- // Ajouter ici la logique de placement en bas à droite
- const buttonContainer = this.createElement('div', {
- position: 'absolute',
- bottom: '30px',
- right: '10px',
- display: 'flex',
- flexDirection: 'column',
- gap: '5px',
- zIndex: 10001,
- });
-
- // Ajouter les boutons dans ce conteneur
- buttonContainer.append(this.searchButton, this.downloadButton, this.optionButton);
- this.overlay.append(
- this.imgElement,
- this.spinner,
- this.infoText,
- this.prevButton,
- this.nextButton,
- this.closeButton,
- buttonContainer, // Ajouter le conteneur de boutons à l'overlay
- this.indicatorsContainer
- );
-
- // Positionner le menu d'options à gauche de buttonContainer
- this.overlay.append(this.optionsMenu);
-
- // Événements associés aux boutons et à l'overlay
- this.resetHideButtons();
- this.addEventListeners();
- this.addInteractionListeners();
-
- document.body.appendChild(this.overlay);
- }
-
- // Crée un élément HTML avec des styles
- createElement(tag, styles = {}) {
- const element = document.createElement(tag);
- Object.assign(element.style, styles);
- return element;
- }
-
- // Crée le bouton précédent ou suivant
- createButton(text, position) {
-
- const isMobileDevice = isMobile();
-
- const button = this.createElement('button', {
- position: 'absolute',
- [position]: '5px',
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
- color: 'white',
- fontSize: isMobileDevice ? '18px' : '22px',//'22px',
- border: 'none',
- borderRadius: '50%',
- width: isMobileDevice ? '35px' : '40px',//'40px',
- height: isMobileDevice ? '35px' : '40px',//'40px',
- cursor: 'pointer',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.6)',
- transition: 'background-color 0.3s, transform 0.3s'
- });
-
-
- //button.textContent = text;*
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('viewBox', '0 0 24 24');
- svg.setAttribute('width', '24');
- svg.setAttribute('height', '24');
- svg.setAttribute('fill', 'white');
-
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
- path.setAttribute('d', position === 'left'
- ? 'M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6z' // Icône flèche gauche
- : 'M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z'); // Icône flèche droite
- svg.appendChild(path);
- button.appendChild(svg);
-
- this.addButtonEffects(button);
-
- return button;
- }
-
- createDownloadButton() {
- const isMobileDevice = isMobile();
-
- const button = this.createElement('button', {
- position: 'relative',
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
- color: 'white',
- fontSize: isMobileDevice ? '12px' : '10px',
- border: '1px solid rgba(255, 255, 255, 0.3)',
- borderRadius: '50%',
- padding: '0',
- cursor: 'pointer',
- zIndex: 10001,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- width: isMobileDevice ? '37px' : '45px',
- height: isMobileDevice ? '37px' : '45px',
- boxShadow: '0 2px 4px rgba(0, 0, 0, 0.5)',
- transition: 'transform 0.3s ease, background-color 0.3s ease',
- });
- button.setAttribute('title', 'Enregistrer l\'image');
-
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
- svg.setAttribute('viewBox', '0 0 24 24');
- svg.setAttribute('width', isMobileDevice ? '18' : '22');
- svg.setAttribute('height', isMobileDevice ? '18' : '22');
- svg.setAttribute('fill', 'none');
- svg.setAttribute('stroke', 'currentColor');
- svg.setAttribute('stroke-linecap', 'round');
- svg.setAttribute('stroke-linejoin', 'round');
- svg.setAttribute('stroke-width', '2');
-
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
- path.setAttribute('d', 'M6 21h12M12 3v14m0 0l5-5m-5 5l-5-5');
-
- svg.appendChild(path);
- button.appendChild(svg);
- this.addButtonEffects(button);
-
- return button;
- }
-
- createSearchButton() {
- const isMobileDevice = isMobile();
-
- const button = this.createElement('button', {
- position: 'relative',
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
- color: 'white',
- fontSize: isMobileDevice ? '12px' : '10px',
- border: '1px solid rgba(255, 255, 255, 0.3)',
- borderRadius: '50%',
- padding: '0',
- cursor: 'pointer',
- zIndex: 10001,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- width: isMobileDevice ? '37px' : '45px',
- height: isMobileDevice ? '37px' : '45px',
- boxShadow: '0 2px 4px rgba(0, 0, 0, 0.5)',
- transition: 'transform 0.3s ease, background-color 0.3s ease',
- });
- button.setAttribute('title', 'Rechercher par image');
-
-
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
- svg.setAttribute('width', isMobileDevice ? '18' : '22');
- svg.setAttribute('height', isMobileDevice ? '18' : '22');
- svg.setAttribute('viewBox', '0 0 24 24');
- svg.setAttribute('fill', 'currentColor');
-
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
- path.setAttribute('fill', 'currentColor');
- path.setAttribute('d', 'M18 13v7H4V6h5.02c.05-.71.22-1.38.48-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-5l-2-2zm-1.5 5h-11l2.75-3.53l1.96 2.36l2.75-3.54zm2.8-9.11c.44-.7.7-1.51.7-2.39C20 4.01 17.99 2 15.5 2S11 4.01 11 6.5s2.01 4.5 4.49 4.5c.88 0 1.7-.26 2.39-.7L21 13.42L22.42 12L19.3 8.89zM15.5 9a2.5 2.5 0 0 1 0-5a2.5 2.5 0 0 1 0 5z');
-
- svg.appendChild(path);
- button.appendChild(svg);
-
- button.addEventListener('click', () => this.searchImageOnGoogle());
-
- this.addButtonEffects(button);
-
- return button;
- }
-
-
- createOptionButton() {
- const isMobileDevice = isMobile();
-
- const button = this.createElement('button', {
- position: 'relative',
- backgroundColor: 'rgba(0, 0, 0, 0.6)',
- color: 'white',
- fontSize: isMobileDevice ? '12px' : '10px',
- border: '1px solid rgba(255, 255, 255, 0.3)',
- borderRadius: '50%',
- padding: '0',
- cursor: 'pointer',
- zIndex: 10001,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- width: isMobileDevice ? '37px' : '45px',
- height: isMobileDevice ? '37px' : '45px',
- boxShadow: '0 2px 4px rgba(0, 0, 0, 0.5)',
- transition: 'transform 0.3s ease, background-color 0.3s ease',
- });
- button.setAttribute('title', 'Personnaliser');
-
- // Création du SVG avec trois points alignés verticalement
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('viewBox', '0 0 24 24');
- svg.setAttribute('width', isMobileDevice ? '18' : '22');
- svg.setAttribute('height', isMobileDevice ? '18' : '22');
- svg.setAttribute('fill', 'currentColor');
-
- // Création des trois cercles pour les trois points
- const circle1 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
- circle1.setAttribute('cx', '12');
- circle1.setAttribute('cy', '5');
- circle1.setAttribute('r', '2');
-
- const circle2 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
- circle2.setAttribute('cx', '12');
- circle2.setAttribute('cy', '12');
- circle2.setAttribute('r', '2');
-
- const circle3 = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
- circle3.setAttribute('cx', '12');
- circle3.setAttribute('cy', '19');
- circle3.setAttribute('r', '2');
-
- // Ajout des cercles dans le SVG
- svg.appendChild(circle1);
- svg.appendChild(circle2);
- svg.appendChild(circle3);
-
- // Ajout du SVG dans le bouton
- button.appendChild(svg);
-
- // Créer le menu d'options
- //this.optionsMenu = this.createOptionsMenu();
-
- this.addButtonEffects(button);
-
- return button;
- }
-
- // Crée le bouton de fermeture
- createCloseButton() {
- const isMobileDevice = isMobile();
-
- const button = this.createElement('button', {
- position: 'absolute',
- top: '80px',
- right: '10px',
- backgroundColor: 'rgba(0, 0, 0, 0.8)',
- color: 'white',
- fontSize: isMobileDevice ? '18px' : '16px',
- //border: 'none',
- border: '1px solid rgba(255, 255, 255, 0.3)',
- borderRadius: '50%',
- width: isMobileDevice ? '40px' : '35px',
- height: isMobileDevice ? '40px' : '35px',
- cursor: 'pointer',
- zIndex: 99999999
- });
-
- button.textContent = '✕';
- this.addButtonEffects(button);
-
- return button;
- }
-
- // Crée la zone d'affichage du texte d'information (numéro d'image)
- createInfoText() {
- return this.createElement('div', {
- position: 'absolute',
- //top: '80px',
- bottom: '0px',
- //left: '15px',
- right: '10px',
- color: 'white',
- fontSize: '12px',
- backgroundColor: 'rgba(5, 5, 5, 0.5)',
- padding: '5px',
- borderRadius: '5px',
- zIndex: 10001
- });
- }
-
- // Crée un spinner pour indiquer le chargement de l'image
- createSpinner() {
- const spinner = this.createElement('div', {
- position: 'absolute',
- border: '8px solid #f3f3f3',
- borderTop: '8px solid #3498db',
- borderRadius: '50%',
- width: '50px',
- height: '50px',
- animation: 'spin 1s linear infinite',
- zIndex: 10001
- });
- return spinner;
- }
-
- createOptionsMenu() {
- const optionsMenu = this.createElement('div', {
- position: 'relative',
- backgroundColor: 'rgba(5, 5, 5, 0.8)',
- color: 'white',
- padding: '10px',
- borderRadius: '5px',
- zIndex: 10001,
- display: 'none', // Caché par défaut
- flexDirection: 'column',
- });
- optionsMenu.style.position = 'absolute';
- optionsMenu.style.bottom = '20px';
- optionsMenu.style.right = '60px';
-
- optionsMenu.appendChild(this.createCheckboxOption(
- 'Afficher le bouton de téléchargement',
- true,
- this.downloadButton,
- (checked) => {
- this.downloadButton.style.display = checked ? 'block' : 'none';
- checked ? this.downloadButton.removeAttribute('data-hidden-by-options') : this.downloadButton.setAttribute('data-hidden-by-options', 'true');
- }
- ));
-
- optionsMenu.appendChild(this.createCheckboxOption(
- 'Afficher les miniatures',
- true,
- this.thumbnailPanel,
- (checked) => {
- this.thumbnailPanel.style.display = checked ? 'block' : 'none';
- checked ? this.thumbnailPanel.removeAttribute('data-hidden-by-options') : this.thumbnailPanel.setAttribute('data-hidden-by-options', 'true');
- }
- ));
-
- optionsMenu.appendChild(this.createCheckboxOption(
- 'Afficher le bouton Google Lens',
- false,
- this.searchButton,
- (checked) => {
- this.searchButton.style.display = checked ? 'block' : 'none';
- checked ? this.searchButton.removeAttribute('data-hidden-by-options') : this.searchButton.setAttribute('data-hidden-by-options', 'true');
- }
- ));
-
- return optionsMenu;
- }
-
- // Fonction pour créer une option avec une case à cocher
- createCheckboxOption(labelText, isChecked = false, elementDepend, onChange) {
- const container = this.createElement('div', {
- display: 'flex',
- alignItems: 'center',
- margin: '5px 0',
- cursor: 'pointer',
- userSelect: 'none',
- });
-
- const checkboxId = `jvcimageviwer-checkbox-${labelText.replace(/\s+/g, '-').toLowerCase()}`;
-
- // Mettre un drapeau indiquant si l'element doit etre caché ou pas si il y a eu une réponse dans le localStorage
- const storedValue = localStorage.getItem(checkboxId);
- if (storedValue != null) {
- isChecked = (storedValue === "true");
- if (elementDepend) {
- isChecked ? elementDepend.removeAttribute('data-hidden-by-options') : elementDepend.setAttribute('data-hidden-by-options', 'true');
- elementDepend.style.display = isChecked ? 'block' : 'none';
- }
- } else if (!isChecked) {
- elementDepend.setAttribute('data-hidden-by-options', 'true');
- }
-
- const checkbox = this.createElement('input');
- checkbox.setAttribute('type', 'checkbox');
- checkbox.checked = isChecked;
-
- // Donne un ID unique à la case à cocher pour l'associer au label
- checkbox.setAttribute('id', checkboxId);
-
- // Écouteur d'événement pour changer la valeur
- checkbox.addEventListener('change', (e) => {
- onChange(e.target.checked);
- localStorage.setItem(checkboxId, e.target.checked);
- });
-
- const label = this.createElement('label');
- label.textContent = labelText;
- label.setAttribute('for', checkboxId);
- label.style.marginLeft = '10px';
-
- container.append(checkbox, label);
-
- // Ajout d'un écouteur d'événement sur le conteneur pour activer la case à cocher
- container.addEventListener('click', () => {
- if (event.target !== checkbox && event.target !== label) {
- checkbox.checked = !checkbox.checked;
- onChange(checkbox.checked);
- localStorage.setItem(checkboxId, checkbox.checked);
- }
- });
-
- return container;
- }
-
- createThumbnailPannel() {
- const thumbnailPanel = this.createElement('div', {
- position: 'fixed',
- bottom: '10px',
- left: '50%',
- transform: 'translateX(-50%)',
- border: '0px solid',
- padding: '0px',
- zIndex: '1010',
- maxHeight: '80px',
- maxWidth: '80%',
- overflowY: 'hidden',
- overflowX: 'auto',
- display: 'flex',
- alignItems: 'center',
- backgroundColor: 'transparent',
- });
- thumbnailPanel.classList.add('thumbnail-scroll-container');
-
- return thumbnailPanel;
- }
-
- addButtonEffects(button) {
- button.addEventListener('mouseenter', () => {
- button.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
- button.style.color = 'black';
- button.style.transform = 'scale(1.1)';
- });
-
- button.addEventListener('mouseleave', () => {
- button.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
- button.style.color = 'white';
- button.style.transform = 'scale(1)';
- });
-
- button.addEventListener('mousedown', () => {
- button.style.transform = 'scale(0.9)';
- });
-
- button.addEventListener('mouseup', () => {
- button.style.transform = 'scale(1.1)';
- });
- }
-
- toggleMenuOptions() {
- if (this.optionsMenu.style.display === 'none') {
- this.optionsMenu.style.display = 'flex';
- } else {
- this.optionsMenu.style.display = 'none';
- }
- }
-
- // Ajoute les événements aux différents éléments du visualiseur
- addEventListeners() {
- // Bouttons de controles du visualiseur
- this.prevButton.addEventListener('click', () => this.changeImage(-1));
- this.nextButton.addEventListener('click', () => this.changeImage(1));
- this.closeButton.addEventListener('click', () => this.closeViewer());
- this.downloadButton.addEventListener('click', () => this.startDownload());
- this.optionButton.addEventListener('click', () => this.toggleMenuOptions());
- this.overlay.addEventListener('click', (event) => {
- if (event.target === this.overlay) {
- this.closeViewer();
- }
- });
-
- // Zoom avec la molette de la souris
- this.imgElement.addEventListener('wheel', (event) => this.handleZoom(event));
-
-
- // Déplacement lors du zoom (drag)
- this.imgElement.addEventListener('mousedown', (event) => this.startDrag(event));
- this.imgElement.addEventListener('mousedown', this.handleMouseDown.bind(this));
- this.imgElement.addEventListener('mouseup', this.handleMouseUp.bind(this));
-
- // Touches avec les doigts
- this.imgElement.addEventListener('touchstart', (event) => this.handleTouchEvent(event));
- this.imgElement.addEventListener('touchmove', (event) => this.handleTouchEvent(event));
- this.imgElement.addEventListener('touchend', (event) => this.handleTouchEvent(event));
-
- // Ouvrir l'image dans une no6velle fenêtre
- this.imgElement.addEventListener('click', () => {
- if (!this.isDragging) {
- window.open(this.images[this.currentIndex].href, '_blank');
- }
- });
-
- // Touches du clavier
- document.addEventListener('keydown', (event) => this.handleKeyboardEvents(event));
- }
-
- // Fonctions pour gérer les touches du clavier
- handleKeyboardEvents(event) {
- switch (event.key) {
- case 'ArrowLeft':
- case 'ArrowUp':
- this.changeImage(-1);
- break;
- case 'ArrowRight':
- case 'ArrowDown':
- this.changeImage(1);
- break;
- case 'Escape':
- event.preventDefault();
- this.closeViewer();
- break;
- }
- }
-
- // Fonctions pour gérer les touches tactiles
- handleTouchEvent(event) {
- switch (event.type) {
- case 'touchstart':
- if (event.touches.length === 1) {
- if (this.imageElementScale > 1) {
- // Si l'image est zoomée, permettre le déplacement (drag)
- this.startDrag(event);
- } else {
- // Sinon, démarrer le swipe
- this.handleSwipeStart(event);
- }
- } else if (event.touches.length === 2) {
- // Démarrer le pinch zoom
- this.handlePinchStart(event);
- }
- break;
-
- case 'touchmove':
- if (event.touches.length === 1) {
- if (this.imageElementScale > 1) {
- // Si l'image est zoomée, permettre le déplacement (drag)
- this.onDrag(event);
- } else {
- this.handleSwipeMove(event);
- }
- } else if (event.touches.length === 2) {
- // Gérer le pinch zoom
- this.handlePinchMove(event);
- }
- break;
-
- case 'touchend':
- if (event.touches.length === 1) {
- if (this.imageElementScale > 1) {
- this.endDrag(event);
- }
- }
- else if (event.touches.length === 0) {
- if (this.isSwiping) {
- this.handleSwipeEnd(event);
- } else if (this.isPinchZooming) {
- this.handlePinchEnd(event);
- } else {
- this.endDrag(event);
- }
- }
- break;
- }
- }
-
-
- // Gestion du début de l'interaction tactile
- handleSwipeStart(event) {
- if (event.touches.length === 1) {
- if(this.isPinchZooming) {
- return;
- }
- // Commencer le swipe
- this.isSwiping = true;
- this.startX = event.touches[0].clientX;
- this.startY = event.touches[0].clientY;
- }
- }
-
- // Gestion du mouvement tactile pour swipe
- handleSwipeMove(event) {
- if (this.isSwiping && event.touches.length === 1) {
- this.currentX = event.touches[0].clientX;
- this.currentY = event.touches[0].clientY;
- }
- }
-
- // Gestion de la fin de l'interaction tactile
- handleSwipeEnd(event) {
- if (event.touches.length === 0) {
- this.initialDistance = null;
- }
- if (this.isSwiping) {
- this.isSwiping = false;
- const deltaX = this.currentX - this.startX;
- const deltaY = this.currentY - this.startY;
-
- // Si le mouvement est suffisamment grand, on change d'image
- if (Math.abs(deltaX) > 50) {
- if (deltaX > 0) {
- this.showPreviousImage();
- } else {
- this.showNextImage();
- }
- }
-
- // Si le mouvement est suffisamment grand verticalement, on ferme le visualiseur
- if (Math.abs(deltaY) > 50) {
- this.closeViewer();
- }
- }
-
- // l'image revient à sa taille d'origine, réinitialiser le zIndex
- this.imgElement.style.zIndex = '';
- }
-
- // Calculate distance between two fingers
- distance(event){
- return Math.hypot(event.touches[0].pageX - event.touches[1].pageX, event.touches[0].pageY - event.touches[1].pageY);
- }
-
- // Gestion du début de l'interaction tactile pour le pincement
- handlePinchStart(event) {
- if (event.touches.length === 2) {
- event.preventDefault(); // Prevent page scroll
- this.isPinchZooming = true;
-
- // Calculate where the fingers have started on the X and Y axis
- this.start.x = (event.touches[0].pageX + event.touches[1].pageX) / 2;
- this.start.y = (event.touches[0].pageY + event.touches[1].pageY) / 2;
- this.start.distance = this.distance(event);
-
- // Save the current scale to use it as a base for the new scale
- this.initialScale = this.imageElementScale || 1; // Use 1 if there's no previous zoom
- }
- }
-
- // Gestion du mouvement tactile pour le pincement (zoom)
- handlePinchMove(event) {
- if (event.touches.length === 2) {
- event.preventDefault(); // Prevent page scroll
-
- // Safari provides event.scale as two fingers move on the screen
- // For other browsers just calculate the scale manually
- let scale;
- if (event.scale) {
- scale = event.scale;
- } else {
- const deltaDistance = this.distance(event);
- scale = (deltaDistance / this.start.distance); //* this.touchSensitivityFactor;
- }
- // this.imageElementScale = Math.min(Math.max(1, scale), 4);
- // Multiply the new scale by the starting scale to retain the zoom level
- // this.imageElementScale = Math.min(Math.max(1, this.startScale * scale), 4);
- this.imageElementScale = Math.min(Math.max(1, this.initialScale * scale), 4);
-
-
- // Calculate how much the fingers have moved on the X and Y axis
- const deltaX = (((event.touches[0].pageX + event.touches[1].pageX) / 2) - this.start.x) * 2; // x2 for accelarated movement
- const deltaY = (((event.touches[0].pageY + event.touches[1].pageY) / 2) - this.start.y) * 2; // x2 for accelarated movement
-
- // Transform the image to make it grow and move with fingers
- const transform = `translate3d(${deltaX}px, ${deltaY}px, 0) scale(${this.imageElementScale})`;
- this.imgElement.style.transform = transform;
- this.imgElement.style.WebkitTransform = transform;
- this.imgElement.style.zIndex = "9999";
- this.closeButton.style.zIndex = "10003";
- }
- }
-
- // Gestion de la fin de l'interaction tactile pour le pincement
- handlePinchEnd(event) {
- if (event.touches.length < 2) {
- // Ajouter un délai pour réinitialiser le drag (empeche les conflits avec le clic)
- this.dragTimeout = setTimeout(() => {
- this.isPinchZooming = false;
- this.dragTimeout = null;
- }, 100);
-
- // Si l'image est revenue à sa taille d'origine, réinitialiser le zIndex
- if (this.imageElementScale <= 1) {
- this.imgElement.style.zIndex = '';
- }
- }
- }
-
- // Fonction pour calculer la distance entre deux points de contact
- getTouchDistance(touches) {
- const [touch1, touch2] = touches;
- const deltaX = touch2.clientX - touch1.clientX;
- const deltaY = touch2.clientY - touch1.clientY;
- return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
- }
-
-
- handleZoom(event) {
- event.preventDefault();
- const zoomIncrement = 0.07; // Sensibilité du zoom
-
- let previousZoomLevel = this.imageElementScale;
-
- // Calcul du nouveau niveau de zoom
- if (event.deltaY < 0) {
- this.imageElementScale = Math.min(4, this.imageElementScale + zoomIncrement); // Zoom max
- } else {
- this.imageElementScale = Math.max(1, this.imageElementScale - zoomIncrement); // Zoom min
- }
-
- // Calcul de la position relative du zoom pour recentrer autour du point de la molette
- let imgRect = this.imgElement.getBoundingClientRect();
- let offsetXRelative = (event.clientX - this.offsetX) / previousZoomLevel;
- let offsetYRelative = (event.clientY - this.offsetY) / previousZoomLevel;
-
- // Recalculer les offsets pour centrer l'image correctement après le zoom
- this.offsetX = event.clientX - offsetXRelative * this.imageElementScale;
- this.offsetY = event.clientY - offsetYRelative * this.imageElementScale;
-
- // Appliquer les limites de déplacement après zoom
- this.limitOffsets();
-
- // Appliquer la transformation avec le zoom et les offsets
- //this.imgElement.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(${this.imageElementScale})`;
-
- // Ajuster le z-index si zoomé
- this.applyTransform();
- }
-
- applyTransform() {
- this.imgElement.style.transition = 'transform 0.1s ease'; // Ajout d'une transition fluide
- this.imgElement.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(${this.imageElementScale})`;
-
- // Ajuster le z-index si zoomé
- this.imgElement.style.zIndex = this.imageElementScale > 1 ? 10002 : '';
- }
-
- limitOffsets() {
- let imgRect = this.imgElement.getBoundingClientRect();
- let windowWidth = window.innerWidth;
- let windowHeight = window.innerHeight;
-
- // Calculer les limites maximales
- let maxOffsetX = Math.max(0, (imgRect.width * this.imageElementScale) - windowWidth);
- let maxOffsetY = Math.max(0, (imgRect.height * this.imageElementScale) - windowHeight);
-
- // Limiter les déplacements
- this.offsetX = Math.min(Math.max(this.offsetX, -maxOffsetX), 0);
- this.offsetY = Math.min(Math.max(this.offsetY, -maxOffsetY), 0);
- }
-
- startDrag(event) {
- event.preventDefault(); // Empêche la sélection de l'image
-
- // Gestion tactile
- if (event.type === 'touchstart') {
- this.isTouchDragging = true;
- this.startX = event.touches[0].clientX;
- this.startY = event.touches[0].clientY;
- } else {
- // Gestion avec la souris
- this.isMouseDown = true;
- this.startX = event.clientX;
- this.startY = event.clientY;
- }
-
- this.isDragging = true;
- this.imgElement.style.cursor = 'grabbing';
- this.imgElement.style.userSelect = 'none';
-
- // Ajouter des listeners sur le document pour capturer les mouvements
- if (event.touches) {
- document.addEventListener('touchmove', this.onDragBound = this.onDrag.bind(this));
- document.addEventListener('touchend', this.endDragBound = this.endDrag.bind(this));
- } else {
- document.addEventListener('mousemove', this.onDragBound = this.onDrag.bind(this));
- document.addEventListener('mouseup', this.endDragBound = this.endDrag.bind(this));
- }
- }
-
- onDrag(event) {
- if (!this.isDragging) return;
-
- event.preventDefault();
-
- let deltaX, deltaY;
- if (event.type === 'touchmove') {
- // Gestion tactile
- deltaX = (event.touches[0].clientX - this.startX) * this.touchSensitivityFactor;
- deltaY = (event.touches[0].clientY - this.startY) * this.touchSensitivityFactor;
- } else {
- // Gestion avec la souris
- deltaX = (event.clientX - this.startX) * this.mouseSensitivityFactor;
- deltaY = (event.clientY - this.startY) * this.mouseSensitivityFactor;
- }
-
- // Calculer les nouveaux offsets
- let newOffsetX = this.offsetX + deltaX;
- let newOffsetY = this.offsetY + deltaY;
-
- // Limiter les nouveaux offsets
- this.offsetX = Math.min(Math.max(newOffsetX, -this.imgElement.width * (this.imageElementScale - 1)), 0);
- this.offsetY = Math.min(Math.max(newOffsetY, -this.imgElement.height * (this.imageElementScale - 1)), 0);
-
- // Appliquer la translation ajustée par le facteur de sensibilité
- this.offsetX += deltaX;
- this.offsetY += deltaY;
-
- // Appliquer la transformation avec le zoom et la translation
- this.applyTransform();
-
- // Mettre à jour les points de départ pour le prochain déplacement
- this.startX = event.type === 'touchmove' ? event.touches[0].clientX : event.clientX;
- this.startY = event.type === 'touchmove' ? event.touches[0].clientY : event.clientY;
- }
-
- endDrag(event) {
- this.imgElement.style.cursor = 'grab';
-
- // Retirer les listeners
- if (event.type === 'touchend') {
- this.isTouchDragging = false; // Réinitialise l'état tactile
- document.removeEventListener('touchmove', this.onDragBound);
- document.removeEventListener('touchend', this.endDragBound);
- } else {
- this.isMouseDown = false; // Réinitialise l'état de la souris
- document.removeEventListener('mousemove', this.onDragBound);
- document.removeEventListener('mouseup', this.endDragBound);
- }
-
- // Réinitialiser le drag après un délai pour éviter les conflits
- this.dragTimeout = setTimeout(() => {
- this.isDragging = false;
- this.imgElement.style.cursor = 'pointer';
- this.dragTimeout = null;
- }, 100);
- }
-
-
- handleMouseDown(event) {
- this.isMouseDown = true;
- this.mouseDownX = event.clientX;
- this.mouseDownY = event.clientY;
-
- // Démarrer le drag après un délai pour éviter le drag lors des clics courts
- this.dragTimeout = setTimeout(() => {
- if (this.isMouseDown) {
- this.startDrag(event);
- }
- }, 200);
- }
-
- handleMouseUp(event) {
- this.isMouseDown = false;
-
- // Si le délai pour démarrer le drag est encore en cours, le nettoyer
- if (this.dragTimeout) {
- clearTimeout(this.dragTimeout);
- this.dragTimeout = null;
- }
-
- // Vérifier si le mouvement est suffisant pour considérer que c'est un drag
- const movedX = Math.abs(event.clientX - this.mouseDownX);
- const movedY = Math.abs(event.clientY - this.mouseDownY);
-
- if (movedX < 5 && movedY < 5) {
- // handleImageClick(event); // Traiter le clic si le mouvement est minime
- }
- }
-
- // Réinitialiser le zoom de l'image
- resetZoom() {
- this.imgElement.style.transform = 'scale(1)';
- this.imgElement.style.transformOrigin = '0 0';
- this.imageElementScale = 1;
- this.offsetX = 0;
- this.offsetY = 0;
- }
-
- // Réinitialiser la position du drag de l'image
- resetDrag() {
- this.imgElement.style.left = '0px';
- this.imgElement.style.top = '0px';
- }
-
-
- // Met à jour l'image affichée dans le visualiseur
- updateImage() {
- if (this.currentIndex >= 0 && this.currentIndex < this.images.length) {
- const imageUrl = this.images[this.currentIndex].href;
-
- this.imgElement.src = imageUrl;
- this.infoText.textContent = `${this.currentIndex + 1} / ${this.images.length}`;
- this.spinner.style.display = 'block';
-
- this.toggleButtonState();
-
- this.imgElement.onload = () => {
- this.imgElement.style.opacity = 1;
- this.spinner.style.display = 'none';
-
- // Réinitialiser le zoom et la position du drag
- this.resetZoom();
- this.resetDrag();
-
- // Calcul de la position des boutons
- const imgRect = this.imgElement.getBoundingClientRect();
- const isMobileDevice = isMobile(); // Détection des mobiles
-
- if (imgRect.width > this.defaultImageWidth) {
- this.defaultImageWidth = imgRect.width;
- }
-
- if (isMobileDevice) {
- // pass
- } else {
- const margin = 30;
- // Calcul de la position des boutons
- let prevButtonLeft = (window.innerWidth - this.defaultImageWidth) / 2 - this.prevButton.offsetWidth - margin;
- let nextButtonRight = (window.innerWidth - this.defaultImageWidth) / 2 - this.nextButton.offsetWidth - margin;
-
- // Limite les boutons pour qu'ils ne sortent pas de l'écran à gauche ou à droite
- prevButtonLeft = Math.max(prevButtonLeft, margin); // Empêche de sortir à gauche
- nextButtonRight = Math.max(nextButtonRight, margin); // Empêche de sortir à droite
-
- // Appliquer les positions ajustées
- this.prevButton.style.left = `${prevButtonLeft}px`;
- this.nextButton.style.right = `${nextButtonRight}px`;
- }
-
- this.focusOnThumbnail();
- };
-
- this.imgElement.onerror = () => this.handleImageError();
- }
- }
-
- // Gestion des erreurs de chargement d'image
- handleImageError() {
- const miniUrl = this.images[this.currentIndex].querySelector('img').src;
- const fullUrl = this.images[this.currentIndex].href;
- const extensions = this.reorderExtensions(fullUrl);
- const baseUrl = miniUrl.replace('/minis/', '/fichiers/');
-
- const tryNextExtension = (index) => {
- if (index >= extensions.length) {
- // Si toutes les extensions échouent, tenter l'URL originale (mini)
- this.imgElement.src = miniUrl;
- const imgTestMini = new Image();
- imgTestMini.src = miniUrl;
-
- imgTestMini.onload = () => {
- this.imgElement.src = miniUrl;
- };
- imgTestMini.onerror = () => {
- this.setImageNotFound(this.imgElement);
- };
- return;
- }
-
- // Remplacer l'extension et mettre à jour l'URL
- const updatedUrl = baseUrl.replace(/\.(jpg|png|jpeg|webp|gif)$/, extensions[index]);
-
- // Tester l'URL avec un élément Image temporaire
- const imgTest = new Image();
- imgTest.src = updatedUrl;
-
- imgTest.onload = () => {
- this.imgElement.src = updatedUrl;
- };
-
- imgTest.onerror = () => {
- // console.log("Error loading: " + updatedUrl);
- tryNextExtension(index + 1);
- };
- };
-
- // Commencer les essais avec la première extension
- tryNextExtension(0);
- }
-
- setImageNotFound(imageElement) {
- const notFoundImageUrl = "https://upload.wikimedia.org/wikipedia/commons/a/ac/No_image_available.svg";
- imageElement.src = notFoundImageUrl;
- }
-
- // Réarranger la liste des extensions a tester pour mettre l'extension utilisée sur noelshack en premier
- reorderExtensions(currentImageUrl) {
- const extensions = ['.jpg', '.png', '.jpeg'];
- const currentExtension = getImageExtension(currentImageUrl);
- const newExtensions = [...extensions];
-
- if (currentExtension) {
- if (!newExtensions.includes(currentExtension)) {
- newExtensions.unshift(currentExtension);
- } else {
- const index = newExtensions.indexOf(currentExtension);
- if (index > -1) {
- newExtensions.splice(index, 1);
- newExtensions.unshift(currentExtension);
- }
- }
- }
- return newExtensions;
- }
-
- // Change d'image en fonction de la direction (suivant/précédent)
- changeImage(direction) {
- this.currentIndex = (this.currentIndex + direction + this.images.length) % this.images.length;
- this.imgElement.style.opacity = 0;
- this.spinner.style.display = 'block';
- this.updateImage();
- }
-
- showPreviousImage() {
- this.changeImage(-1);
- }
-
- showNextImage() {
- this.changeImage(1);
- }
-
- // Désactive ou active les boutons suivant/précédent en fonction de l'index actuel
- toggleButtonState() {
- if (this.currentIndex === 0) {
- // this.prevButton.disabled = true;
- this.prevButton.style.opacity = 0.5;
- this.prevButton.style.cursor = 'initial';
- } else {
- // this.prevButton.disabled = false;
- this.prevButton.style.opacity = 1;
- this.prevButton.style.cursor = 'pointer';
- }
-
- if (this.currentIndex === this.images.length - 1) {
- // this.nextButton.disabled = true;
- this.nextButton.style.opacity = 0.5;
- this.nextButton.style.cursor = 'initial';
- } else {
- // this.nextButton.disabled = false;
- this.nextButton.style.opacity = 1;
- this.nextButton.style.cursor = 'pointer';
- }
- }
-
- // Cacher temporairement le menu JVC
- toggleMenuVisibility(isVisible) {
- const menu = document.querySelector('.header__bottom');
- if (menu) {
- menu.style.display = isVisible ? '' : 'none';
- }
- }
-
- focusOnThumbnail() {
- const thumbnails = this.thumbnailPanel ? this.thumbnailPanel.querySelectorAll('img') : [];
- const currentThumbnail = thumbnails[this.currentIndex];
-
- // Réinitialiser les styles de la miniature précédente
- if (this.previousThumbnail) {
- this.previousThumbnail.style.border = 'none';
- this.previousThumbnail.style.transform = 'scale(1)';
- this.previousThumbnail.style.boxShadow = 'none';
- this.previousThumbnail.style.filter = 'none';
- this.previousThumbnail.style.zIndex = '1'; // Remettre le z-index à 1
- this.previousThumbnail.style.position = 'relative';
- this.previousThumbnail.style.borderRadius = '0px';
- }
-
- // Ajouter des effets modernes à la miniature actuelle
- if (currentThumbnail) {
- currentThumbnail.style.transition = 'transform 0.4s ease, box-shadow 0.4s ease, filter 0.4s ease';
- currentThumbnail.style.transform = 'scale(1.3)';
- currentThumbnail.style.boxShadow = '0 8px 15px rgba(0, 0, 0, 0.1), 0 0 20px rgba(254, 254, 254, 0.4)';
- currentThumbnail.style.filter = 'brightness(1.2) saturate(1.3)';
- currentThumbnail.style.zIndex = '10'; // Z-index élevé pour passer devant les autres
- currentThumbnail.style.position = 'relative';
- currentThumbnail.style.borderRadius = '2px';
- currentThumbnail.parentElement.scrollIntoView({ behavior: 'smooth', inline: 'center' });
- }
-
- // Mettre à jour la référence de la miniature précédente
- this.previousThumbnail = currentThumbnail;
- }
-
-
- // Fonction pour créer et afficher le panneau des miniatures
- toggleThumbnailPanel() {
- /*if (this.thumbnailPanel) {
- this.closeThumbnailPanel(); // Ferme le panneau si déjà ouvert
- return;
- }*/
-
- // Créer le panneau
- if(this.thumbnailPanel == null) {
- this.thumbnailPanel = this.createThumbnailPannel();
- }
-
- // Conteneur pour le défilement horizontal
- const scrollContainer = this.createElement('div', {
- display: 'flex',
- overflowX: 'auto',
- whiteSpace: 'nowrap',
- maxWidth: '100%',
- });
-
- scrollContainer.classList.add('thumbnail-scroll-container');
-
-
- // Ajout des images au conteneur
- this.images.forEach((image, index) => {
- const imgContainer = this.createElement('div', {
- display: 'inline-block',
- width: '50px',
- height: '50px',
- margin: '5px 2px',
- padding: '4px 0px',
- transition: 'transform 0.3s',
- });
-
- const imgElement = this.createElement('img');
- imgElement.src = image.querySelector('img') ? image.querySelector('img').src : image.href || image.thumbnail;
- imgElement.onerror = () => {
- this.setImageNotFound(imgElement);
- };
-
- imgElement.alt = `Image ${index + 1}`;
- imgElement.style.width = '50px';
- imgElement.style.height = '100%';
- imgElement.style.objectFit = 'cover';
- imgElement.style.cursor = 'pointer';
-
- imgElement.addEventListener('click', () => {
- this.images.forEach((_, i) => {
- const container = scrollContainer.children[i];
- container.querySelector('img').style.border = 'none';
- });
- //imgElement.style.border = '2px solid blue';
- this.currentIndex = index;
- this.updateImage();
- //imgContainer.scrollIntoView({ behavior: 'smooth', inline: 'center' });
- });
-
- imgContainer.appendChild(imgElement);
- scrollContainer.appendChild(imgContainer);
- });
-
- this.thumbnailPanel.appendChild(scrollContainer);
- this.overlay.appendChild(this.thumbnailPanel);
-
- this.focusOnThumbnail();
- }
-
- // Ecouteurs d'événements pour réinitialiser le timer
- addInteractionListeners() {
- this.overlay.addEventListener('mousemove', this.resetHideButtons.bind(this));
- this.overlay.addEventListener('click', this.resetHideButtons.bind(this));
- this.overlay.addEventListener('touchstart', this.resetHideButtons.bind(this));
- }
-
- // Réinitialisez le timer pour cacher les boutons
- resetHideButtons() {
- if (this.hideButtonsTimeout) {
- clearTimeout(this.hideButtonsTimeout);
- }
- this.toggleButtonsVisibility(true);
- this.hideButtonsTimeout = setTimeout(() => {
- this.toggleButtonsVisibility(false); // Cachez les boutons après 3 secondes
- }, 2500);
- }
-
- // Changez la visibilité des boutons
- toggleButtonsVisibility(visible) {
- const displayValue = visible ? 'flex' : 'none';
-
- const elements = [
- this.prevButton,
- this.nextButton,
- this.thumbnailPanel,
- this.infoText,
- this.downloadButton,
- this.searchButton,
- this.optionButton,
- ];
-
- elements.forEach(element => {
- // Vérifiez si l'élément a été masqué par le système d'options
- if (element && element.hasAttribute('data-hidden-by-options')) {
- // Si l'élément a été masqué par les options, ne pas le réafficher
- element.style.display = 'none';
- } else if (element){
- element.style.display = displayValue;
- }
- });
-
- /*if(!visible) {
- this.optionsMenu.style.display = displayValue;
- }*/
- }
-
- startDownload() {
- this.downloadButton.classList.add('downloading'); // Ajout de la classe pour l'animation
-
- this.downloadCurrentImage().then(() => {
- // Retirer la classe après le téléchargement
- this.downloadButton.classList.remove('downloading');
- }).catch((error) => {
- console.error('Download failed:', error);
- this.downloadButton.classList.remove('downloading');
- });
- }
-
- downloadCurrentImage() {
- return new Promise((resolve, reject) => {
- const imageElement = this.imgElement;
- if (!imageElement) {
- console.error('Image not found!');
- reject('Image not found');
- return;
- }
-
- const imageUrl = imageElement.src;
- const fileNameWithExtension = imageUrl.split('/').pop();
- const fileName = fileNameWithExtension.substring(0, fileNameWithExtension.lastIndexOf('.'));
-
- // Utilisation de GM.xmlHttpRequest pour contourner CORS
- GM.xmlHttpRequest({
- method: "GET",
- url: imageUrl,
- responseType: "blob",
- headers: {
- 'Accept': 'image/jpeg,image/png,image/gif,image/bmp,image/tiff,image/*;q=0.8'
- },
- onload: function(response) {
- if (response.status === 200) {
- const blob = response.response;
- const url = URL.createObjectURL(blob);
-
- // Téléchargement du fichier
- const a = document.createElement('a');
- a.href = url;
- a.download = fileName;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
-
- resolve(); // Indique que le téléchargement est terminé
- } else {
- reject('Error downloading image: ' + response.statusText);
- }
- },
- onerror: function(err) {
- reject('Request failed: ' + err);
- }
- });
- });
- }
-
- searchImageOnGoogle() {
- if (this.images.length > 0) {
- const imageUrl = this.imgElement.src;
- const googleImageSearchUrl = `https://lens.google.com/uploadbyurl?url=${encodeURIComponent(imageUrl)}`;
- // Ouvrir le lien dans un nouvel onglet
- window.open(googleImageSearchUrl, '_blank');
- } else {
- console.error('Aucune image disponible pour la recherche.');
- }
- }
-
- // Fonction pour fermer le panneau des miniatures
- closeThumbnailPanel(thumbnailPanel) {
- if (this.thumbnailPanel && this.overlay.contains(this.thumbnailPanel)) {
- this.overlay.removeChild(this.thumbnailPanel);
- this.thumbnailPanel = null;
- }
- }
-
- closeViewer() {
- if (this.overlay) {
- this.handleCloseViewer(); // Ferme le visualiseur
- history.back(); // Supprime l'état ajouté par pushState
- }
- }
-
-
- // Ferme le visualiseur d'images
- handleCloseViewer() {
- if (this.overlay) {
- document.body.removeChild(this.overlay);
-
- // Ferme le panneau des miniatures si ouvert
- if (this.thumbnailPanel) {
- this.closeThumbnailPanel(this.thumbnailPanel);
- }
-
- window.removeEventListener('popstate', this.handlePopState);
-
- this.overlay = null;
- this.isViewerOpen = false;
- ImageViewer.instance = null; // Réinitialise l'instance singleton
-
- this.toggleMenuVisibility(true);
- }
- }
-
- openViewer(images, currentIndex) {
- if (this.overlay) {
- this.images = images;
- this.currentIndex = currentIndex;
- this.updateImage();
- this.toggleThumbnailPanel();
- } else {
- new ImageViewer();
- this.images = images;
- this.currentIndex = currentIndex;
- this.createOverlay();
- this.updateImage();
- this.toggleThumbnailPanel();
- }
- this.isViewerOpen = true;
-
- this.addHistoryState()
- window.addEventListener('popstate', this.handlePopState); // Ecouter l'événement bouton back du navigateur
-
- this.toggleMenuVisibility(false);
- }
-
- handlePopState(event) {
- if (ImageViewer.instance) {
- event.preventDefault();
- this.handleCloseViewer();
- }
- }
-
- // Ajouter une entrée dans l'historique
- addHistoryState() {
- history.pushState({ viewerOpen: true }, '');
- }
- }
-
- function addSpinnerStyles() {
- const style = document.createElement('style');
- style.textContent = `
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- .spinner {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- border: 5px solid transparent;
- border-top: 5px solid rgba(0, 0, 0, 0.1);
- background: conic-gradient(from 0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0));
- animation: spin 1s linear infinite;
- }
- `;
- document.head.appendChild(style);
- }
-
- function addDownloadButtonStyles() {
- const style = document.createElement('style');
- style.textContent = `
- /* Animation de rotation */
- @keyframes rotate {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
-
- /* Classe active lors du téléchargement */
- .downloading {
- animation: rotate 1s linear infinite; /* Rotation continue */
- background-color: rgba(0, 0, 0, 0.8); /* Change légèrement le fond */
- border-color: rgba(255, 255, 255, 0.5); /* Bordure plus marquée */
- opacity: 0.7; /* Légère transparence pour indiquer une action */
- }
- `;
- document.head.appendChild(style); // Ajout de la balise style dans le <head> du document
- }
-
- function addScrollBarStyles() {
- const style = document.createElement('style');
- style.textContent = `
- .thumbnail-scroll-container::-webkit-scrollbar {
- //height: thin;
- height: 7px;
- background-color: transparent;
- }
-
- .thumbnail-scroll-container::-webkit-scrollbar-button {
- display: none; /* Pas de boutons */
- }
-
- /* Coin de la scrollbar */
- .thumbnail-scroll-container::-webkit-scrollbar-corner {
- background-color: transparent;
- }
-
- /* Thumb (la barre de défilement visible) */
- .thumbnail-scroll-container::-webkit-scrollbar-thumb {
- background-color: rgba(74, 77, 82, 0.7);
- border: 2px solid transparent;
- border-radius: 10px;
- }
-
- /* Thumb au survol (hover) */
- .thumbnail-scroll-container::-webkit-scrollbar-thumb:hover {
- background-color: rgb(90, 93, 98, 0.7);
- }
- `;
-
- document.head.appendChild(style);
- }
-
- function injectStyles(){
- const isMobileDevice = isMobile();
-
- addSpinnerStyles();
- addDownloadButtonStyles();
-
- if (!isMobileDevice) {
- addScrollBarStyles();
- }
- }
-
- const parentClasses = `
- .txt-msg,
- .message,
- .conteneur-message.mb-3,
- .bloc-editor-forum,
- .signature-msg,
- .previsu-editor,
- .bloc-description-desc.txt-enrichi-desc-profil,
- .bloc-signature-desc.txt-enrichi-desc-profil
- `.replace(/\s+/g, ''); // Supprimer les sauts de ligne et espaces inutiles
-
- const linkSelectors = parentClasses.split(',').map(cls => `${cls} a`);
-
- // Ajouter des écouteurs d'événements aux images sur la page
- function addListeners() {
- linkSelectors.forEach(selector => {
- document.querySelectorAll(selector).forEach(link => {
- link.addEventListener('click', handleImageClick, true);
- });
- });
- }
-
- function handleImageClick(event) {
- // Si Ctrl ou Cmd est enfoncé, ne pas ouvrir l'ImageViewer
- if (event.ctrlKey || event.metaKey) {
- return;
- }
-
- const imgElement = this.querySelector('img');
- if (imgElement) {
- event.preventDefault();
- const closestElement = this.closest(parentClasses);
- if (closestElement) {
- const images = Array.from(closestElement.querySelectorAll('a')).filter(imgLink => imgLink.querySelector('img'));
- const currentIndex = images.indexOf(this);
-
- const viewer = new ImageViewer();
- viewer.openViewer(images, currentIndex);
- }
- }
- }
-
- function isMobile() {
- return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
- }
-
- function getImageExtension(url) {
- const match = url.match(/\.(jpg|jpeg|png|gif|bmp|webp|tiff)$/i); // Regexp pour matcher les extensions d'images
- return match ? match[0].toLowerCase() : null;
- }
-
- // Observer les changements dans le DOM
- function observeDOMChanges() {
- const observer = new MutationObserver(() => addListeners());
- observer.observe(document, { childList: true, subtree: true });
- }
-
- // Détection des changements d'URL
- function observeURLChanges() {
- let lastUrl = window.location.href;
-
- const urlObserver = new MutationObserver(() => {
- if (lastUrl !== window.location.href) {
- lastUrl = window.location.href;
- addListeners();
- }
- });
- urlObserver.observe(document, { subtree: true, childList: true });
- }
-
- function main() {
- injectStyles();
- addListeners();
- observeDOMChanges();
- observeURLChanges();
- }
-
- main();
-
- })();