PageSourceSearch

https://www.edenfantasys.com/Scripts/HeaderTabs/ProductStateManager.js

js edenfantasys.com collected 2026-09-24 18:14:17 UTC 13,827 bytes, 365 lines download raw bytes

1var ProductStateManager = new function () {
2
3    var customSaleShieldTargets = [];
4    var customSaleStateByPid = {};
5    var pendingProductStateRequests = {};
6    var productStateBatch = { current: null };
7    var fullProductStateByPid = {};
8    var pendingFullProductStateRequests = {};
9    var fullProductStateBatch = { current: null };
10
11    var uniqueValues = function (arr) {
12        var seen = {}, result = [];
13        for (var i = 0; i < arr.length; i++) {
14            var key = arr[i];
15            if (!seen.hasOwnProperty(key)) {
16                seen[key] = true;
17                result.push(key);
18            }
19        }
20        return result;
21    };
22
23    var escapeHtml = function (str) {
24        return str == null ? str : String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
25    };
26
27    var applyCustomSaleShield = function (priceWrap, insertAnchor, customSalePromo) {
28        var coupon = priceWrap.find('.prod-coupon[data-custom-sale]');
29
30        var discountLabel = priceWrap.find('.promo-disc-label');
31        if (!discountLabel.length) {
32            discountLabel = priceWrap.parent().find('.promo-disc-label');
33        }
34        discountLabel.toggleClass('hidden', !!customSalePromo);
35
36        if (!customSalePromo) {
37            coupon.remove();
38            return;
39        }
40
41        priceWrap.find('.prod-coupon').not(coupon).remove();
42
43        var amount = CurrencyUtils.formatAsCurrency(customSalePromo.promoPrice),
44            shieldText = escapeHtml(customSalePromo.shieldText);
45
46        if (!coupon.length) {
47            insertAnchor.after(
48                '<div class="prod-coupon" data-custom-sale="1"><div class="prod-coupon__inner">'
49                + '<div class="prod-coupon__amount">' + amount + '</div>'
50                + '<div class="prod-coupon__code"><span class="code-label">' + shieldText + '</span></div>'
51                + '</div></div>'
52            );
53            return;
54        }
55
56        coupon.find('.prod-coupon__amount').html(amount);
57        coupon.find('.prod-coupon__code').html('<span class="code-label">' + shieldText + '</span>');
58    };
59
60    var renderCustomSaleShields = function () {
61        $.each(customSaleShieldTargets, function (i, target) {
62            $(target.ctrlsSelector).each(function () {
63                var ctrl = $(this),
64                    pid = ctrl.data('pid');
65
66                if (!customSaleStateByPid.hasOwnProperty(pid)) {
67                    return;
68                }
69
70                var priceWrap = target.findPriceWrap(ctrl);
71                if (!priceWrap.length) {
72                    return;
73                }
74
75                var state = customSaleStateByPid[pid];
76                applyCustomSaleShield(priceWrap, target.findInsertAnchor(priceWrap), state && state.customSalePromo);
77            });
78        });
79    };
80
81    var processCustomSaleShieldTarget = function (target, container) {
82        var cont = container ? $(container) : target.defaultContainer();
83        var ctrls = cont.find(target.ctrlsSelector).filter(function () {
84            return !$(this).data('customSaleChecked');
85        });
86
87        if (!ctrls.length) {
88            return;
89        }
90
91        ctrls.data('customSaleChecked', true);
92
93        var pidsToFetch = ctrls.map(function () {
94            return $(this).data('pid');
95        }).get().filter(function (pid) {
96            return !customSaleStateByPid.hasOwnProperty(pid);
97        });
98
99        if (pidsToFetch.length) {
100            getProductsState(pidsToFetch, $.noop);
101        } else {
102            renderCustomSaleShields();
103        }
104    };
105
106    var rescanCustomSaleShieldTargets = function () {
107        $.each(customSaleShieldTargets, function (i, target) {
108            processCustomSaleShieldTarget(target);
109        });
110    };
111
112    var debouncedCustomSaleRescan = (function () {
113        var timer = null;
114        return function () {
115            clearTimeout(timer);
116            timer = setTimeout(rescanCustomSaleShieldTargets, 150);
117        };
118    })();
119
120    document.addEventListener('DOMContentLoaded', function () {
121        rescanCustomSaleShieldTargets();
122
123        if (window.MutationObserver) {
124            new MutationObserver(debouncedCustomSaleRescan).observe(document.body, { childList: true, subtree: true });
125        }
126    });
127
128    var getCartButtonsElements = function (cartButtons) {
129        var objitemEl = cartButtons.parent();
130        return {
131            objitem: objitemEl,
132            infoIcon: cartButtons.find('.prev_info'),
133            compareBtn: cartButtons.find('.cmpricon, .mc-icon_compare'),
134            cartStatusIcon: cartButtons.find('.carticon'),
135            wishlistBtn: cartButtons.find('.item_addtowishlist, .mc-icon_wishlist'),
136            shoppingCartSaleMarketingGroupId: objitemEl.find('.jsScsmkid').val()
137        };
138    }
139
140    var setProductButtonsState = function (cartButtons, productState) {
141        var btns = getCartButtonsElements(cartButtons);
142
143        if (productState.isInCompare) {
144            btns.compareBtn.removeClass('item_addtocompare').addClass('item_removefromcompare');
145        }
146
147        if (productState.isInWishList) {
148            btns.wishlistBtn.removeClass('add_wishlist').addClass('added_wishlist');
149        }
150
151        if (productState.status != "Normal") {
152            btns.cartStatusIcon.removeClass('item_addtocart').addClass('oos_status');
153            btns.objitem.addClass('product-item__oos');
154        } else if (productState.isInCart) {
155            btns.cartStatusIcon.removeClass('item_addtocart').addClass('item_removefromcart');
156        }
157
158        return btns;
159    };
160
161    var restoreButtonsState = function (cartButtons, productState) {
162
163        if (cartButtons.data('state-restored')) {
164            return null;
165        }
166
167        var objitem = cartButtons.parent(),
168            infoIcon = cartButtons.find('.prev_info'),
169            compareBtn = cartButtons.find('.cmpricon, .mc-icon_compare'),
170            cartStatusIcon = cartButtons.find('.carticon'),
171            wishlistBtn = cartButtons.find('.item_addtowishlist, .mc-icon_wishlist'),
172            shoppingCartSaleMarketingGroupId = objitem.find('.jsScsmkid').val();
173
174        if (productState.isInCompare) {
175            compareBtn.removeClass('item_addtocompare').addClass('item_removefromcompare');
176        }
177
178        if (productState.isInWishList) {
179            wishlistBtn.removeClass('add_wishlist').addClass('added_wishlist');
180        }
181
182        if (productState.status != "Normal") {
183            cartStatusIcon.removeClass('item_addtocart').addClass('oos_status');
184            objitem.addClass('product-item__oos');
185        } else if (productState.isInCart) {
186            cartStatusIcon.removeClass('item_addtocart').addClass('item_removefromcart');
187        }
188
189
190        if (productState.hasSpecialPrice && productState.status == "Normal") {
191            var productInfoBlock = cartButtons.parent().find(".product-item-visual");
192            productInfoBlock = productInfoBlock.length ? productInfoBlock : cartButtons.parents(".product-item-visual, .product-card-small");
193            if (productInfoBlock.length) {
194                if (productInfoBlock.find(".special-price").length === 0) {
195                    productInfoBlock.append(UTILS.getSpecialOfferLabelMarkup());
196                }
197            }
198        }
199
200        cartButtons.data('state-restored', true);
201        cartButtons.removeClass('dynamic-state');
202
203        return ProdCollection.add({
204            objitem: objitem,
205            pid: productState.pid,
206            infoIcon: infoIcon,
207            masterpid: productState.masterPid,
208            compareIcon: compareBtn,
209            cartPStatusIcon: cartStatusIcon,
210            wishListIcon: wishlistBtn,
211            stockStatus: productState.status,
212            shoppingCartPromotion: shoppingCartSaleMarketingGroupId ? { promotionMarketingGroupId: shoppingCartSaleMarketingGroupId } : null
213        });
214    };
215
216    var PID_BATCH_MAX_RETRIES = 1;
217    var PID_BATCH_WINDOW_MS = 20;
218
219    var sendPidBatchRequest = function (cache, pending, buildAjaxConfig, batch, retriesLeft) {
220        $.ajax($.extend(buildAjaxConfig(batch.pids), {
221            success: function (resp) {
222                $.each(resp || [], function (i, item) {
223                    cache[item.pid] = item;
224                });
225            },
226            complete: function (jqXHR) {
227                if (jqXHR.status !== 200 && retriesLeft > 0) {
228                    sendPidBatchRequest(cache, pending, buildAjaxConfig, batch, retriesLeft - 1);
229                    return;
230                }
231
232                $.each(batch.pids, function (i, pid) {
233                    if (pending[pid] === batch.deferred) {
234                        delete pending[pid];
235                    }
236                });
237                batch.deferred.resolve();
238            }
239        }));
240    };
241
242    var flushPidBatch = function (cache, pending, batchHolder, buildAjaxConfig) {
243        var batch = batchHolder.current;
244        batchHolder.current = null;
245
246        $.each(batch.pids, function (i, pid) {
247            pending[pid] = batch.deferred;
248        });
249
250        sendPidBatchRequest(cache, pending, buildAjaxConfig, batch, PID_BATCH_MAX_RETRIES);
251    };
252
253    var fetchPidsWithCoalescing = function (pids, cache, pending, batchHolder, buildAjaxConfig, callback, onSettled) {
254        var uniquePids = uniqueValues(pids),
255            waitFor = [];
256
257        $.each(uniquePids, function (i, pid) {
258            if (cache.hasOwnProperty(pid)) {
259                return;
260            }
261            if (pending.hasOwnProperty(pid)) {
262                waitFor.push(pending[pid]);
263                return;
264            }
265
266            if (!batchHolder.current) {
267                batchHolder.current = { pids: [], deferred: $.Deferred() };
268                setTimeout(function () {
269                    flushPidBatch(cache, pending, batchHolder, buildAjaxConfig);
270                }, PID_BATCH_WINDOW_MS);
271            }
272
273            if ($.inArray(pid, batchHolder.current.pids) === -1) {
274                batchHolder.current.pids.push(pid);
275            }
276
277            waitFor.push(batchHolder.current.deferred);
278        });
279
280        var onSettledInternal = function () {
281            onSettled && onSettled();
282
283            callback && callback(uniquePids.map(function (pid) {
284                return cache[pid];
285            }).filter(function (item) {
286                return !!item;
287            }));
288        };
289
290        return $.when.apply($, waitFor).done(onSettledInternal).fail(onSettledInternal);
291    };
292
293    var getProductsState = function (pids, callback) {
294        return fetchPidsWithCoalescing(pids, customSaleStateByPid, pendingProductStateRequests, productStateBatch, function (pidsToRequest) {
295            return {
296                url: '/ajax/product/ProductState.aspx',
297                data: { pids: pidsToRequest.join(',') },
298                cache: false
299            };
300        }, callback, renderCustomSaleShields);
301    };
302
303    var getFullProductVariations = function (pids, callback) {
304        return fetchPidsWithCoalescing(pids, fullProductStateByPid, pendingFullProductStateRequests, fullProductStateBatch, function (pidsToRequest) {
305            return {
306                url: '/ajax/htabs/exec.aspx',
307                dataType: 'json',
308                data: {
309                    op: 'vsd',
310                    pids: pidsToRequest.join(','),
311                    soosp: false
312                }
313            };
314        }, callback);
315    };
316
317    return {
318        syncCartButtonsState: function (context, onCompleteCallback, pidSrc) {
319            var pidsList = [],
320                pidSrcSelector = pidSrc || '.cart_btns.dynamic-state';
321
322            $(pidSrcSelector, context || document).each(function () {
323                var el = $(this);
324                if (el.data('state-restored')) {
325                    return true;
326                }
327                pidsList.push(el.data('pid'));
328            });
329
330            if (!pidsList.length) {
331                return;
332            }
333            getProductsState(pidsList, function (productStateList) {
334                for (var i = 0, len = productStateList.length; i < len; i++) {
335                    var prodState = productStateList[i],
336                        cartButtonsCont = $('div[data-pid="' + prodState.pid + '"]');
337
338                    cartButtonsCont.each(function() {
339                        restoreButtonsState($(this), prodState);
340                    });
341                    
342                }
343                onCompleteCallback && onCompleteCallback();
344            });
345        },
346        getState: function (pid, callback) {
347            var pidsParam = [];
348            pidsParam = Object.prototype.toString.call(pid) == '[object Array]' ? pid : [pid];
349            return getProductsState(pidsParam, callback);
350        },
351        getFullProductState: function(pids, callback) {
352            return getFullProductVariations(pids, callback);
353        },
354        setProductButtonsState: function (cartButtons, productState) {
355            setProductButtonsState(cartButtons, productState);
356        },
357        registerCustomSaleShieldTarget: function (target) {
358            customSaleShieldTargets.push(target);
359            processCustomSaleShieldTarget(target);
360        },
361        processCustomSaleShieldTarget: function (target, container) {
362            processCustomSaleShieldTarget(target, container);
363        }
364    };
365};

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.