PageSourceSearch

https://www.sic-marking.com/wp-content/themes/pilopress-private-st…/hero-single-product.js?ver=1789744223

js sic-marking.com collected 2026-10-09 00:11:52 UTC 9,777 bytes, 301 lines download raw bytes

1/**
2 * Library 360: https://github.com/mladenilic/threesixty.js
3 * Generate your sprite here: https://jcthepants.github.io/Retina-CSS-Sprite-Generator/ (50 per row horizontal, save the retina version make two images) -> https://www.toptal.com/developers/css/sprite-generator (then assemble the two images with this)
4 */
5
6class SingleProductSandre {
7  constructor() {
8    // Ne pas exécuter dans le back-office WordPress
9    if (document.body.classList.contains("wp-admin")) {
10      return;
11    }
12
13    this.layout = document.querySelector(".hero-single-product");
14    if (this.layout) {
15      document.addEventListener("DOMContentLoaded", () => this.init());
16    }
17  }
18
19  init() {
20    // Initialize 3D viewer if enabled
21    const threeSixtyElement = document.getElementById("threesixty");
22    if (threeSixtyElement) {
23      const sprite = this.layout.getAttribute("data-sprite");
24      const imageSize = parseInt(this.layout.getAttribute("data-3d-image-size"), 10);
25      const numberOfImagesPerRow = parseInt(
26        this.layout.getAttribute("data-3d-nb-images-per-row"),
27        10,
28      );
29      const numberOfImagesUsed = parseInt(this.layout.getAttribute("data-3d-nb-images"), 10);
30
31      //https://github.com/mladenilic/threesixty.js
32      //Generate your sprite here: https://jcthepants.github.io/Retina-CSS-Sprite-Generator/ -> https://www.toptal.com/developers/css/sprite-generator
33      // A la génération on utilise backgroundSize = (100 × perRow) + "% " + (100 × Math.ceil(count / perRow)) + "%"
34      // Donc pour 50 images par ligne et 100 images au total : backgroundSize = "5000% 200%"
35      const threesixty = new ThreeSixty(threeSixtyElement, {
36        image: sprite,
37        width: imageSize,
38        height: imageSize,
39        count: numberOfImagesUsed,
40        perRow: numberOfImagesPerRow,
41        swipeTarget: document.getElementById("36-wrapper"),
42        prev: document.getElementById("36-prev"),
43        next: document.getElementById("36-next"),
44        speed: 50,
45      });
46
47      threesixty.play(false, 2);
48
49      // On hover stop animation
50      const wrapper36 = document.getElementById("36-wrapper");
51      wrapper36.addEventListener("mouseenter", () => threesixty.stop());
52      wrapper36.addEventListener("mouseleave", () => threesixty.play(false, 1));
53    }
54
55    // Initialize Splide slider
56    const splideWrapper = document.querySelector(".hero-single-product .splide");
57    if (!splideWrapper) {
58      return;
59    }
60
61    // Get attributes
62    const autoplay = splideWrapper.dataset.autoplay === "true";
63    const interval = parseInt(splideWrapper.dataset.interval, 10) || 3000;
64    const perPage = parseInt(splideWrapper.dataset.perPage, 10) || 4;
65
66    var main = new Splide(".hero-single-product .main-hero-single-product", {
67      type: "fade",
68      rewind: true,
69      pagination: false,
70      arrows: false,
71      breakpoints: {
72        600: {
73          pagination: true,
74        },
75      },
76    });
77
78    var thumbnails = new Splide(".hero-single-product .slider-hero-product", {
79      fixedWidth: 100,
80      fixedHeight: 60,
81      gap: 10,
82      rewind: true,
83      pagination: false,
84      arrows: false,
85      focus: "center",
86      isNavigation: true,
87      breakpoints: {
88        600: {
89          fixedWidth: 60,
90          fixedHeight: 44,
91        },
92      },
93    });
94    main.sync(thumbnails);
95    main.mount();
96    thumbnails.mount();
97
98    // Lightbox sur l'image active du slider principal (les vidéos sont exclues
99    // car elles ne sont pas wrappées dans un <a class="glightbox">).
100    if (typeof GLightbox === "function") {
101      GLightbox({
102        selector: ".hero-single-product .main-hero-single-product .glightbox",
103        touchNavigation: true,
104        loop: true,
105      });
106    }
107
108    // Function to handle video autoplay
109    const handleVideoAutoplay = () => {
110      // Stop all videos first
111      const allVideos = document.querySelectorAll(".hero-single-product lite-youtube");
112      allVideos.forEach((video) => {
113        // Remove iframe if exists to stop the video
114        const iframe = video.querySelector("iframe");
115        if (iframe) {
116          iframe.remove();
117          // Reset the lite-youtube component state
118          video.classList.remove("lyt-activated");
119        }
120      });
121
122      // Get the active slide
123      const activeSlide = document.querySelector(
124        ".hero-single-product .main-hero-single-product .splide__slide.is-active",
125      );
126
127      if (activeSlide) {
128        const liteYoutube = activeSlide.querySelector("lite-youtube");
129        if (liteYoutube) {
130          // Trigger the lite-youtube to load and play
131          setTimeout(() => {
132            const playButton = liteYoutube.querySelector(".lty-playbtn");
133            if (playButton) {
134              playButton.click();
135            }
136          }, 300); // Small delay to ensure slide transition is complete
137        }
138      }
139    };
140
141    // Auto-play video on slide change
142    main.on("moved", handleVideoAutoplay);
143
144    // Handle initial slide if it's a video
145    main.on("mounted", () => {
146      setTimeout(() => {
147        handleVideoAutoplay();
148      }, 100);
149    });
150
151    // Initialize modals
152    this.initModals();
153  }
154
155  initModals() {
156    // Get all modal triggers
157    const modalTriggers = document.querySelectorAll("[data-modal-target]");
158
159    modalTriggers.forEach((trigger) => {
160      trigger.addEventListener("click", (e) => {
161        const modalId = trigger.getAttribute("data-modal-target");
162        const tabId = trigger.getAttribute("data-tab");
163		const host = window.location.hostname;
164		const site = 'https://' + host + '/clic-download-' + tabId;
165		_paq.push(['trackLink', site, 'link']);
166        this.openModal(modalId, tabId);
167      });
168	      
169    });
170
171    // Get all modal close buttons
172    const modalCloseButtons = document.querySelectorAll("[data-modal-close]");
173
174    modalCloseButtons.forEach((button) => {
175      button.addEventListener("click", (e) => {
176        const modalId = button.getAttribute("data-modal-close");
177        this.closeModal(modalId);
178      });
179    });
180
181    // Get all tab buttons
182    const tabButtons = document.querySelectorAll("[data-tab-button]");
183
184    tabButtons.forEach((button) => {
185      button.addEventListener("click", (e) => {
186        const tabId = button.getAttribute("data-tab-button");
187        this.switchTab(tabId);
188      });
189    });
190
191    // Close modal on backdrop click
192    const modals = document.querySelectorAll('[id^="modal-"]');
193
194    modals.forEach((modal) => {
195      modal.addEventListener("click", (e) => {
196        if (e.target === modal) {
197          this.closeModal(modal.id);
198        }
199      });
200    });
201
202    // Close modal on Escape key
203    document.addEventListener("keydown", (e) => {
204      if (e.key === "Escape") {
205        const openModal = document.querySelector('[id^="modal-"]:not(.hidden)');
206        if (openModal) {
207          this.closeModal(openModal.id);
208        }
209      }
210    });
211  }
212
213  switchTab(tabId) {
214    const modal = document.querySelector('[id^="modal-"]:not(.hidden)');
215    if (!modal) return;
216
217    // Hide all tab contents and footers
218    const allContents = modal.querySelectorAll(".tab-content-modal");
219    const allFooters = modal.querySelectorAll(".tab-footer");
220    const allButtons = modal.querySelectorAll(".tab-button");
221
222    allContents.forEach((content) => content.classList.add("hidden"));
223    allFooters.forEach((footer) => footer.classList.add("hidden"));
224    allButtons.forEach((button) => {
225      button.classList.add("text-secondary-500");
226      button.classList.remove("bg-primary-500", "text-white");
227    });
228
229    // Show selected tab content and footer
230    const selectedContent = modal.querySelector(`[data-tab-content-modal="${tabId}"]`);
231    const selectedFooter = modal.querySelector(`[data-tab-footer="${tabId}"]`);
232    const selectedButton = modal.querySelector(`[data-tab-button="${tabId}"]`);
233
234    if (selectedContent) selectedContent.classList.remove("hidden");
235    if (selectedFooter) selectedFooter.classList.remove("hidden");
236    if (selectedButton) {
237      selectedButton.classList.add("bg-primary-500", "text-white");
238      selectedButton.classList.remove("text-secondary-500");
239    }
240  }
241
242  openModal(modalId, tabId = null) {
243    const modal = document.getElementById(modalId);
244    if (modal) {
245      const modalPanel = modal.querySelector("div");
246
247      // Show modal
248      modal.classList.remove("hidden");
249
250      // Trigger animation on next frame
251      requestAnimationFrame(() => {
252        modal.classList.remove("bg-opacity-0");
253        modal.classList.add("bg-opacity-50");
254
255        if (modalPanel) {
256          modalPanel.classList.remove("translate-x-full");
257          modalPanel.classList.add("translate-x-0");
258        }
259      });
260
261      // Switch to the specified tab or first tab
262      if (tabId) {
263        this.switchTab(tabId);
264      } else {
265        // Default to first tab
266        const firstTabButton = modal.querySelector("[data-tab-button]");
267        if (firstTabButton) {
268          const firstTabId = firstTabButton.getAttribute("data-tab-button");
269          this.switchTab(firstTabId);
270        }
271      }
272
273      document.body.style.overflow = "hidden";
274    }
275  }
276
277  closeModal(modalId) {
278    const modal = document.getElementById(modalId);
279    if (modal) {
280      const modalPanel = modal.querySelector("div");
281
282      // Animate out
283      modal.classList.add("bg-opacity-0");
284      modal.classList.remove("bg-opacity-50");
285
286      if (modalPanel) {
287        modalPanel.classList.add("translate-x-full");
288        modalPanel.classList.remove("translate-x-0");
289      }
290
291      // Hide after animation completes
292      setTimeout(() => {
293        modal.classList.add("hidden");
294      }, 300);
295
296      document.body.style.overflow = "";
297    }
298  }
299}
300
301new SingleProductSandre();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.