PageSourceSearch

https://zuomod.com/wp-content/mu-plugins/widgets/modifiers/compone…/total-value-modifier.js?ver=5212ba42

js zuomod.com collected 2026-09-25 18:43:45 UTC 9,051 bytes, 233 lines download raw bytes

1(function () {
2    // Merge footerTotalConfig with defaults
3    const defaultConfig = {
4        currency: '$',
5        percentage: 0,
6        logicType: 'sum'
7    };
8
9    const config = typeof footerTotalConfig !== 'undefined'
10        ? { ...defaultConfig, ...footerTotalConfig }
11        : defaultConfig;
12
13    // Now extract directly - no need for fallbacks since config is guaranteed to have valid values
14    const currency = config.currency;
15    const percentage = parseFloat(config.percentage) || 0;  // Keep this for NaN protection
16    const logicType = String(config.logicType).toLowerCase();
17
18    // Re-query on each calculate — drawer HTML can be re-injected after PLP AJAX
19    function getTotalEl() {
20        return document.getElementById('footer-total-price');
21    }
22    function getQtyEl() {
23        return document.getElementById('footer-quantity');
24    }
25
26    if (!getTotalEl() || !getQtyEl()) return;
27
28    function parsePrice(str) {
29        if (!str) return 0;
30        const cleaned = String(str).replace(/[^0-9.\-]/g, '').replace(/,/g, '');
31        const n = parseFloat(cleaned);
32        return isNaN(n) ? 0 : n;
33    }
34
35    function getBasePrice() {
36        // Volume tier pricing via shared getVolumeTierPrice
37        if (window.__productDetailsData && window.__productDetailsData.pricing
38            && typeof window.getVolumeTierPrice === 'function') {
39            var pricing = window.__productDetailsData.pricing;
40            if (Array.isArray(pricing.volume_tiers) && pricing.volume_tiers.length > 0) {
41                var counterInput = document.getElementById('footer-counter-input');
42                var quantity = counterInput ? Math.max(1, parseInt(counterInput.value, 10) || 1) : Math.max(1, counterMultiplier);
43                return window.getVolumeTierPrice(pricing, quantity);
44            }
45        }
46
47        // Flat price fallbacks
48        if (window.__productDetailsData) {
49            if (window.__productDetailsData.price != null) {
50                return parseFloat(window.__productDetailsData.price) || 0;
51            }
52            if (window.__productDetailsData.pricing && window.__productDetailsData.pricing.price != null) {
53                return parseFloat(window.__productDetailsData.pricing.price) || 0;
54            }
55            if (window.__productDetailsData.catalog_list && window.__productDetailsData.catalog_list[0] && window.__productDetailsData.catalog_list[0].price != null) {
56                return parseFloat(window.__productDetailsData.catalog_list[0].price) || 0;
57            }
58        }
59
60        if (typeof product_details_data !== 'undefined') {
61            if (product_details_data.price != null) {
62                return parseFloat(product_details_data.price) || 0;
63            }
64            if (product_details_data.pricing && product_details_data.pricing.price != null) {
65                return parseFloat(product_details_data.pricing.price) || 0;
66            }
67        }
68
69        // 2. Try DOM selectors (for PDP pages)
70        const selectors = [
71            '.wiz-prodimgcmp-price span',
72            '.wiz-prodimgcmp-price',
73            '.product-price',
74            '.price',
75            '#product-price'
76        ];
77
78        for (let sel of selectors) {
79            const el = document.querySelector(sel);
80            if (el && el.textContent.trim()) {
81                const price = parsePrice(el.textContent);
82                if (price > 0) return price;
83            }
84        }
85
86        return 0;
87    }
88
89    // Store counter multiplier
90    let counterMultiplier = 1;
91
92    function calculate() {
93        let totals = [];
94
95        document.querySelectorAll('.cart-customization-table tr, .customizations-applied tr')
96            .forEach(row => {
97                const text = row.textContent || "";
98                const priceCell = row.querySelector('.cart-table-price, td:last-child');
99                if (priceCell) totals.push(parsePrice(priceCell.textContent));
100            });
101
102
103
104
105        let result = 0;
106        if (totals.length) {
107            switch (logicType) {
108                case 'max': result = Math.max(...totals); break;
109                case 'min': result = Math.min(...totals); break;
110                case 'average': result = totals.reduce((a, b) => a + b, 0) / totals.length; break;
111                default: result = totals.reduce((a, b) => a + b, 0);
112            }
113        }
114
115        result += getBasePrice();
116        if (percentage) result += (result * percentage) / 100;
117
118        const qtyEl = getQtyEl();
119        const totalEl = getTotalEl();
120        if (!totalEl) return;
121
122        const qty = Math.max(1, parseInt(qtyEl && qtyEl.value, 10) || 1);
123        result = result * qty;
124
125        // Apply counter multiplier to the final result
126        result = result * counterMultiplier;
127
128        totalEl.textContent = currency + (Number.isInteger(result) ? result : result.toFixed(2));
129
130
131    }  // calculate function end
132
133    //in case any delays fetching value
134    let pollAttempts = 0;
135    let pollHandle = null;
136    function ensureBasePriceAndCalc() {
137        const hasData = !!window.__productDetailsData || typeof product_details_data !== 'undefined';
138        if (hasData) {
139            calculate();
140            if (pollHandle) { clearInterval(pollHandle); pollHandle = null; }
141        } else {
142            pollAttempts++;
143            if (pollAttempts >= 20) {
144                calculate();
145                if (pollHandle) { clearInterval(pollHandle); pollHandle = null; }
146            }
147        }
148    }
149
150    function attachObservers() {
151        const tableTarget = document.querySelector('.cart-customization-table, .customizations-applied table, #modifier-table .cart-customization-table');
152        if (tableTarget && typeof MutationObserver !== 'undefined') {
153            const tableObs = new MutationObserver(function () {
154                clearTimeout(window.__footerTotalTimeout);
155                window.__footerTotalTimeout = setTimeout(calculate, 40);
156            });
157            tableObs.observe(tableTarget, { childList: true, subtree: true, characterData: true });
158        }
159
160        const baseEl = document.querySelector('.wiz-prodigmcmp-price, .wiz-prodigmcmp-price span');
161        if (baseEl && typeof MutationObserver !== 'undefined') {
162            const baseObs = new MutationObserver(function () {
163                clearTimeout(window.__footerTotalTimeout);
164                window.__footerTotalTimeout = setTimeout(calculate, 40);
165            });
166            baseObs.observe(baseEl, { childList: true, subtree: true, characterData: true });
167        }
168    }
169
170    calculate();
171    if (!pollHandle) pollHandle = setInterval(ensureBasePriceAndCalc, 200);
172
173    //automatically trigger calculate() whenever something changes in the DOM.
174    attachObservers();
175
176    document.addEventListener('input', function (e) {
177        if (e.target && e.target.id === 'footer-quantity') calculate();
178    });
179    document.addEventListener('change', function (e) {
180        if (e.target && e.target.id === 'footer-quantity') calculate();
181    });
182
183    // Listen for footer counter changes
184    document.addEventListener('footerCounterChanged', function (e) {
185        counterMultiplier = e.detail.value;
186        clearTimeout(window.__footerTotalTimeout);
187        window.__footerTotalTimeout = setTimeout(calculate, 40);
188    });
189
190    // Sync counterMultiplier from +/- on the live footer counter
191    document.addEventListener('input', function (e) {
192        if (!e.target || e.target.id !== 'footer-counter-input') return;
193        var val = parseInt(e.target.value, 10);
194        if (!isNaN(val) && val > 0) {
195            counterMultiplier = val;
196            clearTimeout(window.__footerTotalTimeout);
197            window.__footerTotalTimeout = setTimeout(calculate, 40);
198        }
199    });
200
201    document.addEventListener('click', function (e) {
202        const mt = document.getElementById('modifier-table');
203        if (mt && mt.contains(e.target)) {
204            clearTimeout(window.__footerTotalTimeout);
205            window.__footerTotalTimeout = setTimeout(calculate, 40);
206        }
207    }, true);
208
209    document.addEventListener('change', function (e) {
210        const mt = document.getElementById('modifier-table');
211        if (mt && mt.contains(e.target)) {
212            clearTimeout(window.__footerTotalTimeout);
213            window.__footerTotalTimeout = setTimeout(calculate, 30);
214        }
215    }, true);
216
217    // Listen for product details loaded (PLP support)
218    document.addEventListener('productDetails:loaded', function (e) {
219        // Force recalculation when product details (including price) are loaded
220        clearTimeout(window.__footerTotalTimeout);
221        window.__footerTotalTimeout = setTimeout(calculate, 50);
222    });
223
224    document.addEventListener('modifierRulesApplied', function () {
225        clearTimeout(window.__footerTotalTimeout);
226        window.__footerTotalTimeout = setTimeout(calculate, 50);
227    });
228
229    window.footerTotalForceUpdate = function () {
230        clearTimeout(window.__footerTotalTimeout);
231        return calculate();
232    };
233})();

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.