1/** 2 * UniCredit ePOS â Product & Cart page installment calculator. 3 * 4 * Does NOT rely on DOMContentLoaded â the script runs immediately and uses 5 * event delegation on document.body so that dynamically-injected elements 6 * (e.g. from third-party checkout modules) are handled correctly. 7 * 8 * Listens to PrestaShop's "updatedCart" event to recalculate when the cart 9 * total changes. 10 */ 11(function () { 12 /* ââ helpers âââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 13 14 function toNumber(val) { 15 if (typeof val !== 'string') return Number(val) || 0; 16 val = val.trim().replace(',', '.').replace('%', ''); 17 var n = parseFloat(val); 18 return isFinite(n) ? n : 0; 19 } 20 21 function parseRanges(raw) { 22 try { 23 var arr = JSON.parse(raw || '[]'); 24 arr.sort(function (a, b) { return a.min_price - b.min_price; }); 25 return arr; 26 } catch (e) { 27 return []; 28 } 29 } 30 31 function pickRange(ranges, amt) { 32 for (var i = 0; i < ranges.length; i++) { 33 var r = ranges[i]; 34 if (amt >= r.min_price && amt <= r.max_price) return r; 35 } 36 if (!ranges.length) return null; 37 return (amt < ranges[0].min_price) ? ranges[0] : ranges[ranges.length - 1]; 38 } 39 40 function monthlyRate(C2, C3, C4, C7, C13) { 41 try { 42 var base = C2 + (C13 * C3 * C2); 43 var mr = C4 / 12.0; 44 var pow = Math.pow(1 + mr, C3 - 1); 45 var num = (-1 * base * (1 + C4 * 59 / 360) * (mr * pow) / (pow - 1)); 46 var den = (1 + (mr * pow) / (pow - 1)); 47 var val = (Math.round((num / den) * 100) / 100) - C7; 48 return (val * -1); 49 } catch (e) { 50 return 0; 51 } 52 } 53 54 function fmtCurrency(n) { 55 try { 56 return n.toLocaleString('ro-RO', {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' Lei'; 57 } catch (e) { 58 return (Math.round(n * 100) / 100).toFixed(2) + ' Lei'; 59 } 60 } 61 62 /* ââ main compute ââââââââââââââââââââââââââââââââââââââââââââââââââ */ 63 64 function compute(userChanged) { 65 var dataEl = document.getElementById('uc-ranges-raw'); 66 if (!dataEl) return; 67 68 var price = toNumber(dataEl.dataset.price || '0'); 69 var ranges = parseRanges(dataEl.dataset.ranges || ''); 70 71 var tenorSel = document.getElementById('uc-tenor'); 72 var resultEl = document.getElementById('uc-result'); 73 var simpleAmount = document.getElementById('uc-simple-amount'); 74 var simpleMonths = document.getElementById('uc-simple-months'); 75 76 if (!tenorSel || !resultEl) return; 77 78 var r = pickRange(ranges, price) || {}; 79 var minMonths = r.min_months ? parseInt(r.min_months, 10) : 6; 80 var maxMonths = r.max_months ? parseInt(r.max_months, 10) : 60; 81 82 // Rebuild options to only include months within the tier's allowed range 83 var allMonths = [60, 48, 36, 24, 18, 12, 9, 6]; 84 var currentVal = parseInt(tenorSel.value, 10) || 0; 85 var validMonths = allMonths.filter(function (m) { return m >= minMonths && m <= maxMonths; }); 86 if (!validMonths.length) validMonths = [maxMonths]; 87 88 // Only rebuild if the set of options changed 89 var existingVals = Array.prototype.map.call(tenorSel.options, function (o) { return o.value; }).join(','); 90 var newVals = validMonths.map(String).join(','); 91 if (existingVals !== newVals) { 92 tenorSel.innerHTML = ''; 93 validMonths.forEach(function (m) { 94 var opt = document.createElement('option'); 95 opt.value = String(m); 96 opt.textContent = m + ' luni'; 97 tenorSel.appendChild(opt); 98 }); 99 } 100 101 var bestValid = validMonths[0]; // largest valid (array is descending) 102 // Auto-select the largest valid tenor (unless user already chose a valid one) 103 if (!userChanged && !tenorSel.dataset.userChanged) {
104 tenorSel.value = String(bestValid); 105 } else if (currentVal < minMonths || currentVal > maxMonths) { 106 tenorSel.value = String(bestValid); 107 } 108 109 var months = parseInt(tenorSel.value, 10) || 6; 110 var C4 = (typeof r.c4 === 'number') ? r.c4 : 0.1799; 111 var C7 = (typeof r.c7 === 'number') ? r.c7 : 0; 112 var C13 = (typeof r.c13 === 'number') ? r.c13 : 0; 113 114 var rate = monthlyRate(price, months, C4, C7, C13) + 10; 115 resultEl.textContent = fmtCurrency(rate); 116 117 // Simple calculator display 118 if (simpleAmount) { 119 var rateSimple = monthlyRate(price, maxMonths, C4, C7, C13) + 10; 120 simpleAmount.textContent = rateSimple.toLocaleString('ro-RO', { 121 minimumFractionDigits: 2, 122 maximumFractionDigits: 2 123 }); 124 if (simpleMonths) { 125 simpleMonths.textContent = maxMonths; 126 } 127 } 128 } 129 130 /* ââ modal logic (event delegation) ââââââââââââââââââââââââââââââââ */ 131 132 document.body.addEventListener('click', function (e) { 133 // Open modal 134 var clickable = e.target.closest('.unicredit-clickable'); 135 if (clickable && clickable.id === 'unicredit-calculator') { 136 var overlay = document.getElementById('unicredit-modal-overlay'); 137 if (overlay) { 138 overlay.classList.add('unicredit-modal-active'); 139 document.body.classList.add('unicredit-modal-open'); 140 compute(false); 141 } 142 return; 143 } 144 145 // Close button 146 if (e.target.closest('#unicredit-modal-close')) { 147 var overlay = document.getElementById('unicredit-modal-overlay'); 148 if (overlay) { 149 overlay.classList.remove('unicredit-modal-active'); 150 document.body.classList.remove('unicredit-modal-open'); 151 } 152 return; 153 } 154 155 // Overlay backdrop 156 if (e.target.id === 'unicredit-modal-overlay') { 157 e.target.classList.remove('unicredit-modal-active'); 158 document.body.classList.remove('unicredit-modal-open'); 159 } 160 }); 161 162 /* ââ tenor dropdown â event delegation on body âââââââââââââââââââââ */ 163 164 document.body.addEventListener('change', function (e) { 165 if (e.target.matches('.unicredit-months-select')) { 166 e.target.dataset.userChanged = 'true'; 167 compute(true); 168 } 169 }); 170 171 /* ââ initial calculation (no DOMContentLoaded needed) ââââââââââââââ */ 172 173 compute(false); 174 175 /* ââ react to cart total changes (PrestaShop updatedCart event) ââââ */ 176 177 if (typeof prestashop !== 'undefined') { 178 prestashop.on('updatedCart', function (event) { 179 // Read the new cart total from prestashop.cart (set by core.js on updateCart) 180 var newTotal = null; 181 try { 182 var totals = prestashop.cart.totals; 183 if (totals && totals.total) { 184 newTotal = toNumber(totals.total.amount || totals.total.value || '0'); 185 } 186 } catch (e) {} 187 188 if (newTotal && newTotal > 0) { 189 var dataEl = document.getElementById('uc-ranges-raw'); 190 if (dataEl) { 191 dataEl.dataset.price = newTotal; 192 } 193 } 194 195 // Recompute immediately and also after a delay (DOM may be refreshed by AJAX) 196 compute(false); 197 setTimeout(function () { compute(false); }, 500); 198 }); 199 200 // Also listen for product page attribute changes (updatedProduct) 201 prestashop.on('updatedProduct', function (event) { 202 setTimeout(function () { 203 // Re-read price from the product page DOM if available 204 var priceEl = document.querySelector('[itemprop="price"]'); 205 var dataEl = document.getElementById('uc-ranges-raw'); 206 if (priceEl && dataEl) { 207 var newPrice = priceEl.getAttribute('content') || priceEl.textContent; 208 if (newPrice) { 209 dataEl.dataset.price = toNumber(newPrice); 210 } 211 } 212 compute(false); 213 }, 300); 214 }); 215 } 216})();
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.