PageSourceSearch

https://e-rpd.ro/modules/unicreditepos/views/js/unicredit_calculator.js

js e-rpd.ro collected 2026-10-04 09:33:07 UTC 8,725 bytes, 216 lines download raw bytes

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.