PageSourceSearch

https://cdn.chemin-des-poulaillers.com/static/version1789455327/fr…/Contestio_Connect/js/contestio.min.js

js chemin-des-poulaillers.com collected 2026-09-29 16:37:24 UTC 21,672 bytes, 692 lines download raw bytes

1(function () {
2  'use strict';
3
4  if (window.__contestioMagento2ScriptActive) {
5    console.log('contestio.js already initialised, skipping duplicate execution');
6    return;
7  }
8  window.__contestioMagento2ScriptActive = true;
9
10  console.log('contestio.js loaded');
11
12  const logger = {
13    log: function (message, data) {
14      console.log('Contestio - ' + message, data ?? '');
15    },
16    warn: function (message, data) {
17      console.warn('Contestio - ' + message, data ?? '');
18    },
19    error: function (message, data) {
20      console.error('Contestio - ' + message, data ?? '');
21    }
22  };
23
24  let iframeLoaded = false;
25  let pendingNavigationActions = [];
26  let navigationFlushTimeout = null;
27  let awaitingAck = null;
28  let awaitingAckTimeout = null;
29  const ACK_TIMEOUT_MS = 2000;
30  let lastAckPath = null;
31  const allowedIframeOrigins = new Set();
32
33  if (typeof window !== "undefined" && window.location && window.location.origin) {
34    allowedIframeOrigins.add(window.location.origin);
35  }
36
37  window.__contestioAllowedOrigins = allowedIframeOrigins;
38
39  function scheduleNavigationFlush(delay = 50) {
40    if (navigationFlushTimeout) {
41      return;
42    }
43
44    navigationFlushTimeout = setTimeout(() => {
45      navigationFlushTimeout = null;
46      if (iframeLoaded) {
47        flushPendingNavigationActions();
48      } else if (pendingNavigationActions.length) {
49        scheduleNavigationFlush(Math.min(delay * 2, 500));
50      }
51    }, delay);
52  }
53
54  function normalizePathValue(pathname) {
55    if (!pathname || pathname === '/' || pathname === '') {
56      return '/';
57    }
58
59    return pathname.startsWith('/') ? pathname : '/' + pathname;
60  }
61
62  function normalizeOrigin(url) {
63    if (!url || url === 'about:blank' || url.startsWith('about:')) return null;
64    try {
65      const origin = new URL(url).origin;
66      if (origin && origin !== 'null') {
67        return origin;
68      }
69    } catch (error) {
70      logger.warn('contestio.js - unable to normalize origin from url', url, error);
71    }
72    return null;
73  }
74
75  function queueNavigationAction(action, options = {}) {
76    if (
77      !pendingNavigationActions.some(
78        (item) =>
79          item.action === action &&
80          JSON.stringify(item.options) === JSON.stringify(options)
81      )
82    ) {
83      pendingNavigationActions.push({ action, options });
84      logger.log('contestio.js - queue navigation action', {
85        action,
86        options,
87        queueLength: pendingNavigationActions.length,
88      });
89    }
90    scheduleNavigationFlush();
91  }
92
93  function flushPendingNavigationActions() {
94    if (!pendingNavigationActions.length) {
95      logger.log('contestio.js - flush skip (queue empty)');
96      return;
97    }
98
99    if (navigationFlushTimeout) {
100      clearTimeout(navigationFlushTimeout);
101      navigationFlushTimeout = null;
102    }
103
104    const actions = pendingNavigationActions.slice();
105    pendingNavigationActions = [];
106
107    actions.forEach(({ action, options }) => {
108      logger.log('contestio.js - flush action', { action, options });
109      sendNavigationUpdateToIframe(action, { ...options, force: true });
110    });
111  }
112
113  function clearAwaitingAckTimeout() {
114    if (awaitingAckTimeout) {
115      clearTimeout(awaitingAckTimeout);
116      awaitingAckTimeout = null;
117    }
118  }
119
120  function setAwaitingAck(pathname, action, notifyAfterAck) {
121    const normalized = normalizePathValue(pathname);
122    awaitingAck = {
123      path: normalized,
124      action,
125      notifyAfterAck: Boolean(notifyAfterAck),
126    };
127
128    // awaiting ack-path (verbose log removed)
129    clearAwaitingAckTimeout();
130
131    if (lastAckPath && lastAckPath === normalized) {
132      completeAwaitingAck();
133      return;
134    }
135
136    if (awaitingAck) {
137      awaitingAckTimeout = setTimeout(() => {
138        logger.warn('contestio.js - ack timeout, forcing completion', {
139          awaitingAck,
140        });
141        const pending = awaitingAck;
142        awaitingAck = null;
143        awaitingAckTimeout = null;
144        if (pending && pending.action) {
145          sendNavigationUpdateToIframe(pending.action, {
146            force: true,
147            skipAckTracking: true,
148          });
149        }
150      }, ACK_TIMEOUT_MS);
151    }
152  }
153
154  function completeAwaitingAck() {
155    if (!awaitingAck) {
156      return;
157    }
158
159    const pending = awaitingAck;
160    awaitingAck = null;
161    clearAwaitingAckTimeout();
162
163    // ack-path resolved (verbose log removed)
164
165    if (pending.notifyAfterAck) {
166      sendNavigationUpdateToIframe(pending.action || 'replace', {
167        force: true,
168        skipAckTracking: true,
169      });
170    }
171  }
172
173  function handleAckFromIframe(pathname) {
174    const normalized = normalizePathValue(pathname);
175    lastAckPath = normalized;
176    // handle ack-path (verbose log removed)
177
178    if (awaitingAck && awaitingAck.path === normalized) {
179      completeAwaitingAck();
180    }
181  }
182
183  class KeyboardManager {
184    constructor(iframe) {
185      this.iframe = iframe;
186      this.lastHeight = window.visualViewport?.height || window.innerHeight;
187
188      if (window.visualViewport) {
189        window.visualViewport.addEventListener('resize', this.handleViewportResize.bind(this));
190      }
191    }
192
193    handleViewportResize(event) {
194      const currentHeight = window.visualViewport.height;
195      const heightDiff = Math.abs(this.lastHeight - currentHeight);
196
197      // Scroll to the top if the height change
198      if (heightDiff > 20 && currentHeight > this.lastHeight) {
199        window.scrollTo({
200          top: 0,
201          behavior: 'smooth'
202        });
203      }
204
205      this.lastHeight = currentHeight;
206    }
207  }
208
209  function getContestioIframe() {
210    return document.querySelector('.contestio-iframe');
211  }
212
213  function getIframeOrigin(iframe) {
214    if (!iframe) return null;
215
216    return normalizeOrigin(iframe.src);
217  }
218
219  function getParentPathname() {
220    try {
221      const urlParams = new URLSearchParams(window.location.search);
222      const l = urlParams.get('l');
223
224      if (!l || l === '/' || l === '') {
225        return '/';
226      }
227
228      return l.startsWith('/') ? l : '/' + l;
229    } catch (error) {
230      logger.warn('contestio.js - unable to read parent pathname', error);
231      return '/';
232    }
233  }
234
235  function sendNavigationUpdateToIframe(action = 'replace', options = {}) {
236    const {
237      force = false,
238      trackAck = false,
239      notifyAfterAck = false,
240      skipAckTracking = false,
241    } = options;
242
243    const iframe = getContestioIframe();
244
245    if (!iframe || !iframe.contentWindow) {
246      logger.warn('contestio.js - iframe not ready for navigation sync');
247      queueNavigationAction(action, {
248        trackAck: trackAck && !skipAckTracking,
249        notifyAfterAck,
250        skipAckTracking,
251      });
252      return;
253    }
254
255    if (!iframeLoaded && !force) {
256      queueNavigationAction(action, {
257        trackAck: trackAck && !skipAckTracking,
258        notifyAfterAck,
259        skipAckTracking,
260      });
261      return;
262    }
263
264    const iframeOrigin = getIframeOrigin(iframe);
265    if (!iframeOrigin) return;
266    allowedIframeOrigins.add(iframeOrigin);
267
268    const pathname = getParentPathname();
269
270    const message = {
271      type: 'parent-navigation',
272      action,
273      pathname,
274      title: document.title,
275      timestamp: Date.now()
276    };
277
278    try {
279      // send navigation update (verbose log removed)
280      iframe.contentWindow.postMessage(message, iframeOrigin);
281      iframeLoaded = true;
282      iframe.dataset.contestioIframeLoaded = 'true';
283      if (trackAck && !skipAckTracking) {
284        setAwaitingAck(pathname, action, notifyAfterAck);
285      }
286    } catch (error) {
287      logger.error('Error sending navigation update to iframe:', error);
288      queueNavigationAction(action, {
289        trackAck: trackAck && !skipAckTracking,
290        notifyAfterAck,
291        skipAckTracking,
292      });
293      scheduleNavigationFlush(100);
294    }
295  }
296
297  function handleParentPopstate() {
298    sendNavigationUpdateToIframe('popstate', {
299      trackAck: true,
300      notifyAfterAck: false,
301    });
302  }
303
304  function init() {
305    const startTime = Date.now();
306    const isSafari = /Safari/.test(navigator.userAgent) && /iPhone|iPad/.test(navigator.userAgent);
307    
308    logger.log(`init starting [${Date.now() - startTime}ms] - Safari iOS: ${isSafari}`);
309    
310    // Attendre que l'iframe soit prête
311    const waitForIframe = () => {
312      const contestioIframe = document.getElementById('contestio-iframe');
313      if (!contestioIframe) {
314        logger.warn('contestio.js - no iframe element found, aborting initialisation loop');
315        return;
316      }
317
318      if (!window.contestioGlobal || !window.contestioGlobal.iframeReady) {
319        logger.log(`waiting for iframe to be ready [${Date.now() - startTime}ms]`);
320        
321        // Ajouter à la queue des callbacks
322        if (window.contestioGlobal) {
323          window.contestioGlobal.callbacks.push(initContestioFeatures);
324        }
325        
326        // Fallback: réessayer dans 500ms pour IOS 
327        setTimeout(waitForIframe, 500);
328        return;
329      }
330      
331      initContestioFeatures();
332    };
333
334    const initContestioFeatures = () => {
335      if (window.contestioInitialized) {
336        logger.log(`already initialized [${Date.now() - startTime}ms]`);
337        return;
338      }
339      
340      window.contestioInitialized = true;
341      logger.log(`initializing features [${Date.now() - startTime}ms]`);
342
343      const container = document.querySelector('.contestio-container');
344      const iframe = document.querySelector('.contestio-iframe');
345
346      if (!container || !iframe) {
347        logger.warn(`container or iframe not found [${Date.now() - startTime}ms]`);
348        return;
349      }
350
351      logger.log(`Found iframe and container, initializing features [${Date.now() - startTime}ms]`);
352
353      if (iframe.dataset.contestioIframeLoaded === 'true') {
354        iframeLoaded = true;
355      }
356
357      if (!iframe.dataset.contestioLoadListenerAttached) {
358        iframe.addEventListener('load', () => {
359          iframeLoaded = true;
360          iframe.dataset.contestioIframeLoaded = 'true';
361          logger.log('contestio.js - iframe load detected, flushing queue');
362          flushPendingNavigationActions();
363        });
364        iframe.dataset.contestioLoadListenerAttached = 'true';
365      }
366
367      if (!iframeLoaded) {
368        try {
369          const href = iframe.contentWindow && iframe.contentWindow.location && iframe.contentWindow.location.href;
370          if (href && href !== 'about:blank') {
371            // Same-origin blank document, waiting for remote load.
372          }
373        } catch (error) {
374          iframeLoaded = true;
375          iframe.dataset.contestioIframeLoaded = 'true';
376          logger.log('contestio.js - iframe already loaded, flushing queue');
377          flushPendingNavigationActions();
378        }
379      }
380
381      sendNavigationUpdateToIframe('init', {
382        trackAck: true,
383        notifyAfterAck: false,
384      });
385
386      // Initialize keyboard manager
387      new KeyboardManager(iframe);
388
389      function adjustHeight() {
390        const mainContentElt = document.querySelector('#maincontent');
391        const containerElt = document.querySelector('.contestio-container');
392
393        if (!mainContentElt || !containerElt) {
394          logger.warn('mainContentElt or containerElt not found');
395          return;
396        }
397
398        let offset = mainContentElt.offsetTop || 0;
399        const windowHeight = window.innerHeight;
400        const newHeight = windowHeight - offset;
401
402        logger.log(`Adjusting height to: ${newHeight}px [${Date.now() - startTime}ms]`);
403        containerElt.style.height = `${newHeight}px`;
404      }
405
406      // Attendre un peu avant ajustement hauteur
407      setTimeout(() => {
408        logger.log(`Initial height adjustment [${Date.now() - startTime}ms]`);
409        adjustHeight();
410      }, 300);
411
412      window.addEventListener('resize', adjustHeight);
413
414      // Message listener setup
415      function createMessageListener() {
416        const messageHandler = async (event) => {
417          const iframeElt = document.querySelector('.contestio-iframe');
418          if (!iframeElt) {
419            logger.warn('Iframe element not found');
420            return;
421          }
422
423          const iframeOrigin = normalizeOrigin(iframeElt.src);
424          const baseOrigin = normalizeOrigin(iframeElt.dataset?.contestioBaseUrl);
425          if (baseOrigin) {
426            allowedIframeOrigins.add(baseOrigin);
427          }
428          if (iframeOrigin) {
429            allowedIframeOrigins.add(iframeOrigin);
430          }
431
432          if (!event.origin) {
433            return;
434          }
435
436          if (!allowedIframeOrigins.has(event.origin)) {
437            logger.log('Message received from unauthorized origin:', event.origin, {
438              allowed: Array.from(allowedIframeOrigins.values()),
439            });
440            return;
441          }
442
443          if (!event.data || typeof event.data !== 'object' || !event.data.type) {
444            return;
445          }
446
447          const replyOrigin =
448            event.origin ||
449            normalizeOrigin(iframeElt.src) ||
450            normalizeOrigin(iframeElt.dataset?.contestioBaseUrl);
451          if (!replyOrigin) {
452            logger.warn('contestio.js - unable to determine reply origin', event);
453            return;
454          }
455
456          const { type, loginCredentials, pathname, redirectUrl, clipboardText, cookie } = event.data;
457
458          logger.log(`Processing message type: ${type}`);
459
460          try {
461            switch (type) {
462              case 'login':
463                const url = window.location.href.includes('?')
464                  ? window.location.href.split('?')[0]
465                  : window.location.href;
466
467                const loginUrl = url.endsWith('/') ? url + 'login' : url + '/login';
468                logger.log('Attempting login to:', loginUrl);
469
470                const response = await fetch(loginUrl, {
471                  method: 'POST',
472                  headers: { 'Content-Type': 'application/json' },
473                  body: JSON.stringify({
474                    username: loginCredentials.username,
475                    password: loginCredentials.password
476                  }),
477                });
478
479                const data = await response.json();
480
481                if (data.success) {
482                  logger.log('Login successful, reloading page');
483                  window.location.reload();
484                } else {
485                  event.source.postMessage({
486                    loginResponse: {
487                      success: false,
488                      message: data.message,
489                      data: data
490                    }
491                  }, replyOrigin);
492                }
493                break;
494
495            case 'pathname':
496              const currentUrl = new URL(window.location.href);
497              currentUrl.searchParams.delete('l');
498              currentUrl.searchParams.delete('u');
499
500              if (pathname !== '' && pathname !== '/') {
501                currentUrl.searchParams.set('l', pathname);
502              } else {
503                currentUrl.searchParams.delete('l');
504              }
505
506              history.pushState(null, null, currentUrl.toString());
507              sendNavigationUpdateToIframe('push');
508              break;
509
510            case 'history-push':
511        const pushUrl = new URL(window.location.href);
512        pushUrl.searchParams.delete('l');
513        pushUrl.searchParams.delete('u');
514
515        if (pathname !== '' && pathname !== '/') {
516          pushUrl.searchParams.set('l', pathname);
517        } else {
518          pushUrl.searchParams.delete('l');
519        }
520
521        const newPushUrl = pushUrl.toString();
522
523        if (window.location.href !== newPushUrl) {
524          history.pushState({ title: event.data.title }, event.data.title || '', newPushUrl);
525          if (event.data.title) {
526            document.title = event.data.title;
527          }
528          sendNavigationUpdateToIframe('push');
529        }
530        break;
531
532      case 'history-replace':
533        const replaceUrl = new URL(window.location.href);
534        replaceUrl.searchParams.delete('l');
535        replaceUrl.searchParams.delete('u');
536
537        if (pathname !== '' && pathname !== '/') {
538          replaceUrl.searchParams.set('l', pathname);
539        } else {
540          replaceUrl.searchParams.delete('l');
541        }
542
543        const newReplaceUrl = replaceUrl.toString();
544        if (window.location.href !== newReplaceUrl) {
545          history.replaceState({ title: event.data.title }, event.data.title || '', newReplaceUrl);
546          if (event.data.title) {
547            document.title = event.data.title;
548          }
549          sendNavigationUpdateToIframe('replace');
550        }
551        break;
552
553            case 'history-back':
554              logger.log('History back');
555              if (window.history.length > 1) {
556                history.back();
557              }
558              break;
559
560            case 'request-parent-path':
561              logger.log('Iframe requested parent path sync');
562              sendNavigationUpdateToIframe('sync', {
563                trackAck: true,
564                notifyAfterAck: false,
565              });
566              break;
567
568            case 'ack-path':
569              {
570                const ackPath = pathname || event.data.fullPath || '/';
571                // ack-path message received (verbose log removed)
572                handleAckFromIframe(ackPath);
573              }
574              break;
575
576            case 'redirect':
577              logger.log('Redirect to:', redirectUrl);
578              window.location.href = redirectUrl;
579              break;
580
581              case 'clipboard':
582                logger.log('Copy to clipboard:', clipboardText);
583                await navigator.clipboard.writeText(clipboardText);
584                break;
585
586              case 'createCookie':
587                const isHttps = window.location.protocol === 'https:';
588                let cookieStr;
589                if (isHttps) {
590                  cookieStr = `${cookie.name}=${cookie.value}; expires=${cookie.expires};
590 path=${cookie.path || '/'}; SameSite=None; Secure`;
591                } else {
592                  cookieStr = `${cookie.name}=${cookie.value}; expires=${cookie.expires}; path=${cookie.path || '/'}; SameSite=Lax`;
593                }
594                document.cookie = cookieStr;
595                logger.log('Create cookie:', cookie, cookieStr);
596                break;
597
598              case 'getCookie':
599                const cookieValue = document.cookie.split('; ').find(row => row.startsWith(`${cookie.name}=`))?.split('=')[1];
600                logger.log('Get cookie:', cookie, cookieValue);
601                event.source.postMessage({
602                  getCookieResponse: {
603                    success: cookieValue ? true : false,
604                    cookieName: cookie.name,
605                    cookieValue: cookieValue
606                  }
607                }, replyOrigin);
608                break;
609
610              case 'deleteCookie':
611                const isHttpsDelete = window.location.protocol === 'https:';
612                let deleteCookieStr;
613                if (isHttpsDelete) {
614                  deleteCookieStr = `${cookie.name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${cookie.path || '/'}; SameSite=None; Secure`;
615                } else {
616                  deleteCookieStr = `${cookie.name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${cookie.path || '/'}; SameSite=Lax`;
617                }
618                document.cookie = deleteCookieStr;
619                logger.log('Delete cookie:', cookie, deleteCookieStr);
620                break;
621
622              default:
623                logger.warn('Unknown message type:', type);
624            }
625          } catch (error) {
626            logger.error('Error processing message:', error);
627
628            if (type === 'login') {
629              event.source.postMessage({
630                loginResponse: {
631                  success: false,
632                  message: "Erreur lors du traitement de la requête",
633                  error: error.message
634                }
635              }, replyOrigin);
636            }
637          }
638        };
639
640        window.addEventListener('message', messageHandler);
641
642        return () => {
643          logger.log('Cleaning up message listener');
644          window.removeEventListener('message', messageHandler);
645          window.__contestioMessageListenerActive = false;
646        };
647      }
648
649      let cleanup = window.__contestioMessageListenerCleanup || null;
650      window.__contestioMessageListenerActive = window.__contestioMessageListenerActive || false;
651
652      function setupListener() {
653        if (window.__contestioMessageListenerActive) {
654          logger.log('contestio.js - message listener already active, skipping setup');
655          return;
656        }
657        logger.log(`Setting up message listener [${Date.now() - startTime}ms]`);
658        if (cleanup) {
659          cleanup();
660        }
661        cleanup = createMessageListener();
662        window.__contestioMessageListenerCleanup = cleanup;
663        window.__contestioMessageListenerActive = true;
664      }
665
666      setupListener();
667
668      document.addEventListener('visibilitychange', () => {
669        if (document.visibilityState === 'visible') {
670          logger.log('Visibility changed to visible, reconfiguring listener');
671          setupListener();
672        }
673      });
674    };
675
676    // Démarrer l'attente
677    waitForIframe();
678  }
679
680  logger.log(`Document readyState: ${document.readyState}`);
681
682  // Initialisation
683  if (document.readyState === 'loading') {
684    logger.log('Waiting for DOMContentLoaded');
685    document.addEventListener('DOMContentLoaded', init);
686  } else {
687    logger.log('DOM ready, executing immediately');
688    init();
689  }
690
691  window.addEventListener('popstate', handleParentPopstate);
692})();

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.