PageSourceSearch

https://www.cbd.fr/modules/phyto_systempay/views/js/subscription_frequency_modal.js?v=1790061811

js cbd.fr collected 2026-09-26 15:48:52 UTC 5,164 bytes, 124 lines download raw bytes

1/**
2 * Phyto SystemPay - modal partagee de selection de frequence (slide-in depuis la droite).
3 * Composant sans etat serveur : construit son DOM au premier appel, reutilise ensuite.
4 * Consomme par la fiche produit (Task 3), le panier (Task 2) et le panneau de relance
5 * "Mes abonnements" (Task 10).
6 */
7(function () {
8    'use strict';
9
10    var root = null;
11    var currentOptions = null;
12    var closeTimer = null;
13
14    function frequencyId(value) {
15        return 'sp-fm-freq-' + value.replace(/[^a-z0-9]/gi, '');
16    }
17
18    function escapeHtml(str) {
19        if (typeof str !== 'string') { return ''; }
20        var div = document.createElement('div');
21        div.textContent = str;
22        return div.innerHTML;
23    }
24
25    function build() {
26        root = document.createElement('div');
27        root.id = 'phyto-sp-frequency-modal';
28        root.hidden = true;
29        root.innerHTML =
30            '<div class="sp-fm__overlay" data-sp-fm-dismiss></div>' +
31            '<div class="sp-fm__panel" role="dialog" aria-modal="true" aria-labelledby="sp-fm-title">' +
32                '<div class="sp-fm__header">' +
33                    '<h2 class="sp-fm__title" id="sp-fm-title">Personnaliser votre abonnement</h2>' +
34                    '<button type="button" class="sp-fm__close" data-sp-fm-dismiss aria-label="Fermer">&times;</button>' +
35                '</div>' +
36                '<div class="sp-fm__sep"></div>' +
37                '<div class="sp-fm__body">' +
38                    '<h3 class="sp-fm__subtitle">Fréquence de livraison</h3>' +
39                    '<div class="sp-fm__list"></div>' +
40                '</div>' +
41                '<div class="sp-fm__footer">' +
42                    '<button type="button" class="sp-fm__cta"></button>' +
43                    '<p class="sp-fm__note" hidden></p>' +
44                '</div>' +
45            '</div>';
46        document.body.appendChild(root);
47
48        root.addEventListener('click', function (e) {
49            if (e.target.hasAttribute('data-sp-fm-dismiss')) { close(); }
50        });
51        root.querySelector('.sp-fm__cta').addEventListener('click', function () {
52            if (!currentOptions) { return; }
53            var checked = root.querySelector('input[name="sp-fm-freq"]:checked');
54            var rrule = checked ? checked.value : currentOptions.currentRrule;
55            var onConfirm = currentOptions.onConfirm;
56            close();
57            if (typeof onConfirm === 'function') { onConfirm(rrule); }
58        });
59    }
60
61    function onKeydown(e) {
62        if (e.key === 'Escape') { close(); }
63    }
64
65    function renderList(options) {
66        var list = root.querySelector('.sp-fm__list');
67        list.innerHTML = '';
68        (options.frequencies || []).forEach(function (freq) {
69            var id = frequencyId(freq.value);
70            var escapedValue = escapeHtml(freq.value);
71            var escapedLabel = escapeHtml(freq.label);
72            var row = document.createElement('label');
73            row.className = 'sp-fm__row';
74            row.setAttribute('for', id);
75            row.innerHTML =
76                '<span class="sp-fm__radio"><input type="radio" name="sp-fm-freq" id="' + id + '" value="' + escapedValue + '"' +
77                (freq.value === options.currentRrule ? ' checked' : '') + '></span>' +
78                '<span class="sp-fm__freq-label">' + escapedLabel + '</span>' +
79                '<span class="sp-fm__freq-days">Choix de nos clients</span>';
80            list.appendChild(row);
81        });
82    }
83
84    function formatPrice(cents, sign) {
85        if (typeof cents !== 'number' || !sign) { return ''; }
86        return (cents / 100).toFixed(2).replace('.', ',') + ' ' + sign;
87    }
88
89    function open(options) {
90        if (!root) { build(); }
91        if (closeTimer) { window.clearTimeout(closeTimer); closeTimer = null; }
92        currentOptions = options || {};
93        renderList(currentOptions);
94
95        var cta = root.querySelector('.sp-fm__cta');
96        var priceLabel = formatPrice(currentOptions.priceCents, currentOptions.currencySign);
97        var ctaLabel = currentOptions.ctaLabel || 'Confirmer';
98        cta.textContent = priceLabel ? (priceLabel + ' – ' + ctaLabel) : ctaLabel;
99
100        var note = root.querySelector('.sp-fm__note');
101        note.textContent = currentOptions.note || '';
102        note.hidden = !currentOptions.note;
103
104        root.hidden = false;
105        // Force un reflow avant d'ajouter la classe d'ouverture, sinon la transition CSS ne joue pas.
106        void root.offsetWidth;
107        root.classList.add('is-open');
108        document.body.classList.add('sp-fm-open');
109        document.addEventListener('keydown', onKeydown);
110    }
111
112    function close() {
113        if (!root || root.hidden) { return; }
114        root.classList.remove('is-open');
115        document.body.classList.remove('sp-fm-open');
116        document.removeEventListener('keydown', onKeydown);
117        closeTimer = window.setTimeout(function () {
118            if (root && !root.classList.contains('is-open')) { root.hidden = true; }
119        }, 300);
120        currentOptions = null;
121    }
122
123    window.PhytoSpFrequencyModal = { open: open, close: close };
124})();

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.