PageSourceSearch

https://cdn.publio.ca/js/Widget/publications.js?v=20260923201501

js publio.ca collected 2026-09-24 11:00:06 UTC 14,199 bytes, 461 lines download raw bytes

1(function () {
2    const RESIZE_MESSAGE_TYPE = "resize";
3    const parentOrigin = getParentOrigin();
4
5    function getParentOrigin() {
6        const value = new URLSearchParams(window.location.search).get("parentOrigin");
7        if (!value) {
8            return "*";
9        }
10
11        try {
12            return new URL(value).origin;
13        } catch (error) {
14            return "*";
15        }
16    }
17
18    function postWidgetResize() {
19        if (window.parent === window) {
20            return;
21        }
22
23        const documentHeight = Math.max(
24            document.documentElement.scrollHeight,
25            document.documentElement.getBoundingClientRect().height
26        );
27
28        window.parent.postMessage({
29            type: RESIZE_MESSAGE_TYPE,
30            height: documentHeight
31        }, parentOrigin);
32    }
33
34    function debounce(fn, delay) {
35        let timer = 0;
36        return function () {
37            window.clearTimeout(timer);
38            timer = window.setTimeout(fn, delay);
39        };
40    }
41
42    function sendTracking(url, id) {
43        if (!url || !id) {
44            return;
45        }
46
47        const beaconUrl = url.indexOf("?") === -1
48            ? url + "?id=" + encodeURIComponent(id)
49            : url + "&id=" + encodeURIComponent(id);
50
51        if (navigator.sendBeacon) {
52            navigator.sendBeacon(beaconUrl);
53            return;
54        }
55
56        fetch(url, {
57            method: "POST",
58            headers: {
59                "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
60            },
61            body: "id=" + encodeURIComponent(id),
62            keepalive: true
63        }).catch(function () { });
64    }
65
66    function applySingleImageBackground(card) {
67        if (!card
68            || !card.classList.contains("is-single-image-contained")
69            || card.dataset.singleImageBackgroundState) {
70            return;
71        }
72
73        const image = card.querySelector(".publio-card__single-image");
74        const src = image ? (image.currentSrc || image.src) : "";
75
76        card.dataset.singleImageBackgroundState = "ready";
77        if (src) {
78            card.style.setProperty("--publio-single-image-source", "url(" + JSON.stringify(src) + ")");
79        }
80    }
81
82    function attachVideoSource(videoEl) {
83        if (!videoEl || videoEl.dataset.videoReady === "1") {
84            return;
85        }
86
87        const src = videoEl.getAttribute("data-hls-src");
88        if (!src) {
89            return;
90        }
91
92        if (videoEl.canPlayType("application/vnd.apple.mpegURL") || videoEl.canPlayType("application/x-mpegURL")) {
93            videoEl.src = src;
94        } else if (window.Hls) {
95            const hls = new Hls({ autoStartLoad: true });
96            hls.loadSource(src);
97            hls.attachMedia(videoEl);
98            videoEl.publioHlsInstance = hls;
99        } else {
100            videoEl.src = src;
101        }
102
103        videoEl.dataset.videoReady = "1";
104    }
105
106    function stopVideo(card) {
107        if (!card) {
108            return;
109        }
110
111        const videoEl = card.querySelector(".publio-card__video");
112        if (!videoEl) {
113            return;
114        }
115
116        videoEl.pause();
117        videoEl.currentTime = 0;
118        card.classList.remove("is-video-playing");
119    }
120
121    function playVideo(card, root) {
122        if (!card) {
123            return;
124        }
125
126        const videoEl = card.querySelector(".publio-card__video");
127        if (!videoEl) {
128            return;
129        }
130
131        root.querySelectorAll(".publio-card.is-video-post").forEach(function (otherCard) {
132            if (otherCard !== card) {
133                stopVideo(otherCard);
134            }
135        });
136
137        attachVideoSource(videoEl);
138        videoEl.muted = true;
139        videoEl.playsInline = true;
140
141        const playPromise = videoEl.play();
142        if (playPromise && typeof playPromise.catch === "function") {
143            playPromise.catch(function () { });
144        }
145
146        card.classList.add("is-video-playing");
147    }
148
149    function applySingleImageLayout(card) {
150        if (!card || card.dataset.ratioApplied === "1" || card.classList.contains("is-single-panel")) {
151            return;
152        }
153
154        const image = card.querySelector(".publio-card__image");
155        if (!image) {
156            return;
157        }
158
159        function updateLayout() {
160            const width = image.naturalWidth;
161            const height = image.naturalHeight;
162
163            if (!width || !height) {
164                return;
165            }
166
167            const ratio = width / height;
168            card.classList.remove("is-ratio-pending", "is-single-cover", "is-single-wide");
169            card.classList.add(ratio > 1.45 ? "is-single-wide" : "is-single-cover");
170            card.dataset.ratioApplied = "1";
171        }
172
173        if (image.complete) {
174            updateLayout();
175        } else {
176            image.addEventListener("load", updateLayout, { once: true });
177        }
178    }
179
180    function getCarouselCards(carousel) {
181        return Array.from(carousel.querySelectorAll(".publio-card-track"));
182    }
183
184    function getNearestCardIndex(carousel) {
185        const cards = getCarouselCards(carousel);
186        if (!cards.length) {
187            return 0;
188        }
189
190        let closestIndex = 0;
191        let closestDistance = Number.POSITIVE_INFINITY;
192
193        cards.forEach(function (card, index) {
194            const distance = Math.abs(card.offsetLeft - carousel.scrollLeft);
195            if (distance < closestDistance) {
196                closestDistance = distance;
197                closestIndex = index;
198            }
199        });
200
201        return closestIndex;
202    }
203
204    function getFirstVisibleCardIndex(carousel) {
205        const cards = getCarouselCards(carousel);
206        if (!cards.length) {
207            return 0;
208        }
209
210        const currentLeft = carousel.scrollLeft + 1;
211        const firstVisibleIndex = cards.findIndex(function (card) {
212            return card.offsetLeft + card.offsetWidth > currentLeft;
213        });
214
215        return firstVisibleIndex === -1 ? Math.max(0, cards.length - 1) : firstVisibleIndex;
216    }
217
218    function getCardsPerView(carousel) {
219        const cards = getCarouselCards(carousel);
220        if (!cards.length) {
221            return 1;
222        }
223
224        const cardWidth = cards[0].offsetWidth;
225        if (!cardWidth) {
226            return 1;
227        }
228
229        const gap = cards.length > 1 ? Math.max(0, cards[1].offsetLeft - cards[0].offsetLeft - cardWidth) : 0;
230        const fullWidth = cardWidth + gap;
231        if (!fullWidth) {
232            return 1;
233        }
234
235        return Math.max(1, Math.floor((carousel.clientWidth + gap) / fullWidth));
236    }
237
238    function alignCarouselToCard(carousel, index, behavior) {
239        const cards = getCarouselCards(carousel);
240        if (!cards.length) {
241            return;
242        }
243
244        const cardsPerView = getCardsPerView(carousel);
245        const maxStartIndex = Math.max(0, cards.length - cardsPerView);
246        const safeIndex = Math.max(0, Math.min(index, maxStartIndex));
247        carousel.scrollTo({
248            left: cards[safeIndex].offsetLeft,
249            behavior: behavior || "smooth"
250        });
251    }
252
253    function bindCards(widget) {
254        const mode = widget.dataset.mode || "internal";
255        const clickUrl = widget.dataset.clickUrl || "";
256        const viewUrl = widget.dataset.viewUrl || "";
257        const carousel = widget.querySelector(".publio-carousel");
258        if (!carousel) {
259            return;
260        }
261
262        const observer = new IntersectionObserver(function (entries) {
263            entries.forEach(function (entry) {
264                const card = entry.target;
265
266                if (entry.isIntersecting) {
267                    applySingleImageBackground(card);
268                }
269
270                if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
271                    if (card.dataset.viewRegistered === "1") {
272                        return;
273                    }
274
275                    card.dataset.viewRegistered = "1";
276                    sendTracking(viewUrl, card.dataset.adid);
277                } else if (!entry.isIntersecting) {
278                    card.dataset.viewRegistered = "0";
279                }
280            });
281        }, {
282            root: carousel,
283            threshold: [0, 0.5]
284        });
285
286        widget.querySelectorAll(".publio-card-track").forEach(function (card) {
287            if (card.dataset.bound === "1") {
288                return;
289            }
290
291            card.dataset.bound = "1";
292            observer.observe(card);
293            applySingleImageLayout(card);
294
295            if (card.classList.contains("is-video-post")) {
296                card.addEventListener("mouseenter", function () {
297                    playVideo(card, widget);
298                });
299                card.addEventListener("mouseleave", function () {
300                    stopVideo(card);
301                });
302            }
303
304            const mainLink = card.querySelector(".publio-card__main-link");
305            if (mainLink) {
306                mainLink.addEventListener("click", function (event) {
307                    sendTracking(clickUrl, card.dataset.adid);
308
309                    if (mode === "external") {
310                        event.preventDefault();
311                        window.open(mainLink.href, "_blank", "noopener");
312                    }
313                });
314            }
315        });
316    }
317
318    function initWidget(widget) {
319        if (!widget || widget.dataset.widgetReady === "1") {
320            return;
321        }
322
323        widget.dataset.widgetReady = "1";
324
325        const carousel = widget.querySelector(".publio-carousel");
326        if (!carousel) {
327            return;
328        }
329
330        const prevButton = widget.querySelector('.publio-widget__nav[data-direction="prev"]');
331        const nextButton = widget.querySelector('.publio-widget__nav[data-direction="next"]');
332        const fetchUrl = widget.dataset.fetchUrl || "";
333        const idAdvertiser = widget.dataset.idAdvertiser || "";
334        const regionId = widget.dataset.regionId || "";
335        const useUserCityPreferences = widget.dataset.useUserCityPreferences === "true";
336        const prioritize = widget.dataset.prioritizeSelfAdvertiser === "true";
337        const usePersonalFeed = widget.dataset.usePersonalFeed !== "fal
337se";
338        const urlOrigin = widget.dataset.urlOrigin || "";
339        let loading = false;
340        let hasReachedEnd = false;
341        let firstLoad = true;
342        let queueInitialPreload = false;
343
344        function scrollByDirection(direction) {
345            const currentIndex = getFirstVisibleCardIndex(carousel);
346            const targetIndex = currentIndex + (direction * 3);
347            alignCarouselToCard(carousel, targetIndex, "smooth");
348        }
349
350        if (prevButton) {
351            prevButton.addEventListener("click", function () {
352                scrollByDirection(-1);
353            });
354        }
355
356        if (nextButton) {
357            nextButton.addEventListener("click", function () {
358                scrollByDirection(1);
359            });
360        }
361
362        async function loadMore() {
363            if (loading || hasReachedEnd || !fetchUrl) {
364                return;
365            }
366
367            const cards = carousel.querySelectorAll(".publio-card-track");
368            const lastCard = cards[cards.length - 1];
369            if (!lastCard) {
370                return;
371            }
372
373            loading = true;
374
375            try {
376                const params = new URLSearchParams({
377                    lastDateStart: lastCard.dataset.unixtimestamp || "0",
378                    lastId: lastCard.dataset.id || "0",
379                    size: "6"
380                });
381
382                if (idAdvertiser) {
383                    params.set("idAdvertiser", idAdvertiser);
384                }
385
386                if (regionId) {
387                    params.set("regionId", regionId);
388                }
389
390                if (useUserCityPreferences) {
391                    params.set("useUserCityPreferences", "true");
392                }
393
394                if (prioritize) {
395                    params.set("prioritizeSelfAdvertiser", "true");
396                }
397
398                params.set("usePersonalFeed", usePersonalFeed ? "true" : "false");
399
400                if (urlOrigin) {
401                    params.set("urlOrigin", urlOrigin);
402                }
403
404                const response = await fetch(fetchUrl + "?" + params.toString(), {
405                    credentials: "same-origin"
406                });
407
408                if (!response.ok) {
409                    throw new Error("Impossible de charger plus de publications.");
410                }
411
412                const html = await response.text();
413                if (!html.trim()) {
414                    hasReachedEnd = true;
415                    return;
416                }
417
418                const temp = document.createElement("div");
419                temp.innerHTML = html;
420                while (temp.firstChild) {
421                    carousel.appendChild(temp.firstChild);
422                }
423
424                bindCards(widget);
425                postWidgetResize();
426
427                if (firstLoad) {
428                    firstLoad = false;
429                    queueInitialPreload = true;
430                }
431            } catch (error) {
432                hasReachedEnd = true;
433            } finally {
434                loading = false;
435                if (queueInitialPreload && !hasReachedEnd) {
436                    queueInitialPreload = false;
437                    loadMore();
438                }
439            }
440        }
441
442        carousel.addEventListener("scroll", function () {
443            if (carousel.scrollLeft + carousel.clientWidth >= carousel.scrollWidth - 200) {
444                loadMore();
445            }
446        });
447
448        bindCards(widget);
449        window.setTimeout(function () {
450            postWidgetResize();
451            loadMore();
452        }, 0);
453    }
454
455    const debouncedResize = debounce(postWidgetResize, 120);
456    window.addEventListener("resize", debouncedResize);
457    document.addEventListener("DOMContentLoaded", function () {
458        document.querySelectorAll('[data-publio-widget="general"]').forEach(initWidget);
459        postWidgetResize();
460    });
461})();

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.