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.