PageSourceSearch

https://www.spokz.co.uk/static/version1790600689/frontend/Isev/Spo…/js/payments/minicart-bootstrap.js

js spokz.co.uk collected 2026-10-04 22:21:42 UTC 7,149 bytes, 191 lines download raw bytes

1/**
2 * Minicart express bootstrap.
3 *
4 * The minicart block is delivered inside Magento's customer-data
5 * "cart/extra_actions" section, which is fetched in a request of its own. A
6 * nonce minted during that request can never match the nonce in the page's CSP
7 * header, so this block must not emit inline script at all -- even correctly
8 * nonced markup is blocked. Everything the template used to run inline now
9 * lives in this file, which loads as an external script and needs no nonce.
10 *
11 * The markup arrives after page load, and is replaced on every cart change, so
12 * this watches for it rather than running once on DOM ready.
13 *
14 * @see https://github.com/ebizmarts/brippo-payments/issues/597
15 */
16(function () {
17    'use strict';
18
19    var ROOT_SELECTOR = '.scExpressMinicart[data-brippo-minicart]',
20        READY_ATTR = 'data-brippo-minicart-ready',
21        toggleBound = false,
22        subscriptionsBound = false,
23        observer = null;
24
25    /**
26     * Move the block into the open minicart, as the inline script used to.
27     */
28    function bindMinicartToggle(config) {
29        var toggle = document.querySelector('.minicart-wrapper > a');
30
31        if (!toggle || toggleBound) {
32            return;
33        }
34        toggleBound = true;
35
36        toggle.addEventListener('click', function () {
37            var selector = config.placementSelector,
38                target,
39                block;
40
41            if (!selector || !document.querySelector(selector)) {
42                selector = config.defaultPlacement;
43            }
44            target = document.querySelector(selector);
45            block = document.querySelector('.scExpressMinicart');
46
47            if (!target || !block) {
48                return;
49            }
50            target.appendChild(block);
51            block.style.display = 'block';
52        });
53    }
54
55    /**
56     * Agreement ids travel on the markup instead of being baked into a script.
57     */
58    function bindAgreements(root, scExpress, $) {
59        var items = root.querySelectorAll('[data-brippo-agreement-id]');
60
61        Array.prototype.forEach.call(items, function (item) {
62            var source = item.getAttribute('data-brippo-agreement-source'),
63                id = item.getAttribute('data-brippo-agreement-id');
64
65            $(item).find('label').on('click', function () {
66                scExpress.showAgreementsModal(source, id);
67            });
68        });
69    }
70
71    function bindCoupon(config, scExpress) {
72        var wrapper;
73
74        // The config travels as JSON, so this is a real boolean -- not the quoted
75        // escapeJs string the template used to interpolate. String(true) is "true",
76        // so any comparison against '1' silently disables the coupon UI.
77        if (!config.enabledCouponCode) {
78            return;
79        }
80        wrapper = document.querySelector('.scExpressMinicart .expressAddCoupon');
81
82        if (!wrapper) {
83            return;
84        }
85        wrapper.style.display = 'block';
86        scExpress.registerDiscountInput(
87            document.querySelector('.scExpressMinicart .expressAddCoupon .textLink'),
88            document.querySelector('.scExpressMinicart .expressAddCouponInputContainer'),
89            document.querySelector('.scExpressMinicart .expressAddCouponApply'),
90            document.querySelector('.scExpressMinicart .expressAddCouponRemove'),
91            document.querySelector('#brippoMinicartCouponCodeInput'),
92            { source: config.source, elementId: config.elementId },
93            document.querySelector('.scExpressMinicart .expressAddCouponSuccess'),
94            document.querySelector('.scExpressMinicart .expressAddCouponNote')
95        );
96    }
97
98    function startPaymentRequest(config, customerData, scExpress) {
99        var source = { source: config.source, elementId: config.elementId },
100            cart = customerData.get('cart'),
101            initAPI = function () {
102                scExpress.initPaymentRequest({
103                    pKey: config.pKey,
104                    isEnabled: config.isEnabled,
105                    accountId: config.accountId,
106                    buttonType: config.buttonType,
107                    buttonTheme: config.buttonTheme,
108                    buttonHeight: config.buttonHeight,
109                    enabledCouponCode: config.enabledCouponCode,
110                    thresholdMinimum: config.thresholdMinimum,
111                    thresholdMaximum: config.thresholdMaximum
112                }, source);
113            };
114
115        initAPI();
116
117        // Page-level, so they are bound once even if the root is ever re-rendered --
118        // otherwise every re-boot would stack another subscription on the same cart.
119        if (!subscriptionsBound) {
120            subscriptionsBound = true;
121
122            cart.subscribe(function () {
123                setTimeout(initAPI, 500);
124            });
125
126            window.addEventListener('brippo_shipping_estimate_success', function () {
127                initAPI();
128            });
129        }
130
131        bindCoupon(config, scExpress);
132    }
133
134    function boot(root, customerData, scExpress, $) {
135        var config;
136
137        if (root.getAttribute(READY_ATTR) === '1') {
138            return;
139        }
140        root.setAttribute(READY_ATTR, '1');
141
142        try {
143            config = JSON.parse(root.getAttribute('data-brippo-minicart') || '{}');
144        } catch (e) {
145            return;
146        }
147
148        bindMinicartToggle(config);
149        bindAgreements(root, scExpress, $);
150        startPaymentRequest(config, customerData, scExpress);
151    }
152
153    require([
154        'Magento_Customer/js/customer-data',
155        'brippo_payment_request_button',
156        'jquery'
157    ], function (customerData, scExpress, $) {
158        var scan = function () {
159            Array.prototype.forEach.call(document.querySelectorAll(ROOT_SELECTOR), function (root) {
160                boot(root, customerData, scExpress, $);
161            });
162        };
163
164        // The observer has to stay armed for the life of the page. Knockout tears down
165        // and rebuilds this subtree whenever the cart section re-renders -- summary_count
166        // flipping N->0->N, or any change to the extra_actions string -- and the
167        // replacement node carries data-brippo-minicart but not the ready flag. An
168        // earlier version disconnected after the first boot on the theory that
169        // cart.subscribe covered later renders; it does not. cart.subscribe re-runs
170        // initAPI only, so the agreement labels silently stopped opening their modal.
171        // Re-booting is cheap and idempotent: boot() returns early on any node already
172        // marked ready, and the page-level listeners are bound once via subscriptionsBound.
173        var watch = function () {
174            if (observer) {
175                return;
176            }
177            observer = new MutationObserver(scan);
178            observer.observe(
179                document.getElementById('minicart-content-wrapper') || document.body,
180                { childList: true, subtree: true }
181            );
182        };
183
184        scan();
185
186        $(function () {
187            scan();
188            watch();
189        });
190    });
191}());

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.