PageSourceSearch

https://www.scalabad.no/scripts/render-utils.js

js scalabad.no collected 2026-10-08 01:16:27 UTC 43,423 bytes, 1,023 lines download raw bytes

1 
2function reloadAllLinkedFieldsExtra(renderEngine) { //TODO: MOVE, move to separate publisher-only file
3    var otherFields = [];
4    jQuery("div.PubFieldMenu[data-include-linked-field-content='True']").each(function (index, item) {
5        var fieldIdFound = jQuery(item).data("fieldid");
6        otherFields.push(fieldIdFound);
7    });
8    renderEngine.reloadPageCallback(otherFields);
9};
10
11function reloadSingleElement(renderEngine, nodeId, activePlid, area, fieldId, element) {  //TODO: MOVE, move to separate publisher-only file
12    var item = jQuery("#Field_" + fieldId + " div.WebPubElement[data-elementid=" + element + "]");
13    if (item.length <= 0) {//implies this element has not been rendered in the field. Let us add a placholder at the end of the field, or if this field contains
14        //linked content, we must add it in front of all the dynamic content
15        var field = jQuery("div.ajax-field[data-field-id=" + fieldId + "]");
16        var placeholder = "<div id='plcholder" + fieldId + "' data-fieldid='" + fieldId + "' class='WebPubElement'/>";
17        if (field.length > 0) {
18            var linkedChildren = field.children(".WebPubElement.linked-element");
19            if (linkedChildren.length > 0) {
20                jQuery(linkedChildren[0]).before(placeholder);
21            }
22            else {
23                field.append(placeholder);
24
25            }
26            item = jQuery("#plcholder" + fieldId);
27        }
28    }
29    if (nodeId == null)
30        nodeId = jQuery('#hidden-nodeid').text();
31
32    var width = Math.round(jQuery(item).width());
33    if (item.length > 0) {
34        var params = {
35            NodeId: nodeId,
36            Width: width,
37            ActivePlid: activePlid,
38            AreaName: area,
39            FieldId: fieldId,
40            ElementId: element,
41            ForceNormalMode: false,
42            ForcedLayoutId: 0
43        };
44        renderEngine.fieldRenderService.PostAction("RenderSingleElement", params).then(function (result) {
45            if (result && result.Success) {
46                jQuery(item).replaceWith(result.Content);
47            }
48        });
49    }
50};
51
52function reloadDummyFields(renderEngine, deviceSize) {
53
54    //first, find the area that contains the dummy fields
55    var container = jQuery('.layout-picker').find('.load-later');
56    if (container.length > 0) {
57        var nodeId = jQuery('#hidden-nodeid').text();
58        var param = {
59            areaName: 'LayoutPicker',
60            nodeId: nodeId,
61            isEdm: false,
62            deviceSize: deviceSize
63        };
64        renderEngine.service.GetAction("RenderAreaFramework", param).then(
65            function (result) {
66                if (result != undefined && result.length > 0) {
67                    container.html(result);
68                }
69                renderEngine.updateFields(".ajax-field.not-loaded-dummy", "", false, false, true, 0);
70                scaleLayoutPicker();
71            });
72
73    }
74    //then fetch the new field framework as it might have been changed
75    //then update the content of the fields
76
77};
78
79function scaleLayoutPicker () {
80    var layoutPickerContainer = jQuery(".PublisherContainer.layout-picker");
81    if (layoutPickerContainer != null) {
82        var layoutSelectorFields = layoutPickerContainer.find('.web-pub-field.container.dummy');
83
84        layoutSelectorFields.each(function (index, element) {
85            var myField = jQuery(element)
86            var contentHeight = myField.height();
87            var contentWidth = myField.width();
88            var availableWidth = 170;
89            var availableHeight = 100;
90            var scale = Math.min(
91                availableWidth / contentWidth,
92                availableHeight / contentHeight
93            );
94            //todo: find out why the height isn't always correct
95            myField.css("position", "absolute");
96
97            var translateVertical = "0 0px";
98            myField.css("transform-origin", translateVertical);
99
100            myField.css("transform", "scale(" + scale + ")");
101            var layoutName = myField.data("layout-name");
102
103            if (myField.hasClass("selected")) {
104                jQuery(myField).wrap("<div class='layout-picker-wrapper selected'></div>");
105            } else {
106                jQuery(myField).wrap("<div class='layout-picker-wrapper'></div>");
107            }
108            jQuery(myField.parent()).before("<div class='layout-name'>" + layoutName + "</div>");
109
110        });
111    }
112};
113
114function replaceFieldExtra(renderEngine, fieldId, nodeId, isEdm) {//todo: move to publisher script
115    var areaContainer = jQuery("#Field_" + fieldId).closest("div.area-content-container");//[data-area-name='" +areaName + "']");
116    if (areaContainer.length > 0) {
117        if (nodeId == undefined || nodeId == 0)
118            nodeId = jQuery('#hidden-nodeid').text();
119        if (isEdm == undefined)
120            isEdm = false;
121
122        var width = Math.round(jQuery(areaContainer).width());
123        var param = {
124            FieldId: fieldId,
125            NodeId: nodeId,
126            IsEdm: isEdm,
127            Width: width,
128            ActivePlid: 0,
129            ActiveArticleId: 0,
130            AreaUniqueId: areaContainer[0].id
131        };
132
133        renderEngine.fieldRenderService.PostAction("RenderField", param).then(function (result) {
134            if (result && result.Response.length > 0) {
135                jQuery("#Field_" + fieldId).replaceWith(result.Response);
136                EnableLinksForArticlesWithBackground();
137            }
138        });
139
140    }
141};
142
143function addFieldToAreaExtra(renderEngine, areaName, fieldId, nodeId) {//todo: move to publisher script
144    var areaContainer = jQuery("div.area-content-container[data-area-name='" + areaName + "']");
145    if (areaContainer.length > 0) {
146        var width = Math.round(jQuery(areaContainer).width());
147        var param = {
148            AreaName: areaName,
149            FieldId: fieldId,
150            NodeId: nodeId,
151            Width: width,
152            ActivePlid: 0,
153            ActiveArticleId: 0,
154            AreaUniqueId: areaContainer[0].id
155        };
156
157        renderEngine.fieldRenderService.PostAction("RenderField", param).then(function (result) {
158            if (result && result.Response.length > 0) {
159                if (result.AddToEnd)
160                    jQuery(areaContainer).append(result.Response);
161                else {
162                    var existingFields = jQuery(areaContainer).find(".web-pub-field");
163                    if (existingFields.length > 0) {
164                        jQuery(existingFields[0]).before(result.Response);
165                    }
166                    else $(areaContainer).append(result.Response);
167                }
168                EnableLinksForArticlesWithBackground();
169            }
170        });
171
172    }
173};
174
175//todo: move to another file (getLibrary call from RenderFilter)
176function fetchAttributeValues(renderEngine, attributeId, target, targetId, nodeId, fieldId) {
177    var queryFilterParam = renderEngine.getParameterByName("Filter");
178    var queryMinParam = renderEngine.getParameterByName("Min");
179    var queryMaxParam = renderEngine.getParameterByName("Max");
180    var pageIndex = renderEngine.getParameterByName("pageID");
181    var fieldRequestFilter = {
182        NodeId: nodeId,
183        Url: document.location.pathname + document.location.search,
184        Filter: queryFilterParam,
185        MinPrice: queryMinParam,
186        MaxPrice: queryMaxParam,
187        SearchString: renderEngine.getParameterByName("q"),
188        ClientId: targetId,
189        PageIndex: pageIndex,
190        FilterCacheKey: ''
191    }
192
193    renderEngine.service.PostAction("RenderAttribute", { AttributeId: attributeId, FieldId: fieldId, RequestFilter: fieldRequestFilter }).then(
194        function (result) {
195            if (result != null) {
196                updateAttributeContent(result);
197            }
198        }
199    );
200};
201
202function updateAttributeContent (result) {
203    if (result.Success) {
204        $('#' + result.ClientId).html(result.Response);
205    }
206};
207function toggleGroupVariants(renderEngine, obj) {
208    try {
209        var data = { group: obj.checked };
210
211        jQuery("div.ajax-field[data-field-is-productlist=1]").data("grouped-variants", obj.checked);
212
213        var service = new WebApiService("AttributeFilter");
214        service.GetAction("ToggleGroupVariants", data).then(function (result) {
215            var newUrl = renderEngine.getCurrentFirstPage();
216            renderEngine.CurrentPageNumber = 1;
217            window.history.pushState(newUrl, '', newUrl);
218            renderEngine.SetAllowedUseOfProductCache(false);
219            renderEngine.reloadPageListingOnlyCallback(false);
220        });
221    } catch (e) {
222        alert("Exception: " + e);
223    }
224};
225
226function toggleStockMode (renderEngine, obj) {
227    try {
228        var data = { show: obj.checked };
229        var service = new WebApiService("AttributeFilter")
230        service.GetAction("ToggleShowOnlyStock", data).then(function (result) {
231            renderEngine.CurrentPageNumber = 1;
232            renderEngine.resetAutoScrolledPages();
233            renderEngine.SetAllowedUseOfProductCache(false);
234            var newUrl = renderEngine.getCurrentFirstPage();
235            renderEngine.CurrentPageNumber = 1;
236            window.history.pushState(newUrl, '', newUrl);
237            renderEngine.reloadPageListingOnlyCallback(false);
238
239        });
240    } catch (e) {
241        alert("Exception: " + e);
242    }
243};
244
245function toggleStockModeFjernlager (renderEngine, obj) {
246    try {
247        var data = { show: obj.checked };
248        var service = new WebApiService("AttributeFilter")
249        service.GetAction("ToggleShowOnlyStockFjernlager", data).then(function (result) {
250            renderEngine.CurrentPageNumber = 1;
251            renderEngine.resetAutoScrolledPages();
252            renderEngine.SetAllowedUseOfProductCache(false);
253            var newUrl = renderEngine.getCurrentFirstPage();
254            renderEngine.CurrentPageNumber = 1;
255            window.history.pushState(newUrl, '', newUrl);
256            renderEngine.reloadPageListingOnlyCallback(false);
257        });
258    } catch (e) {
259        alert("Exception: " + e);
260    }
261};
262
263
264function removeAttributeExtra(url, parsedFilter) {
265    var parsedUrl = decodeURIComponent(url);
266    var newLocation = parsedUrl.replace(parsedFilter, "");
267    if (newLocation.substring(newLocation.length - 1) === '|') //find pipe at the end (|)
268        newLocation = newLocation.substring(0, newLocation.length - 1);
269
270    var indexOfPipeAfterFilter = newLocation.indexOf('Filter=|'); //check if there is a remaining pipe at the beginning of the filter
271    if (indexOfPipeAfterFilter > 1)
272        newLocation = newLocation.replace('|', "");
273
274
275    if (newLocation.substring(newLocation.length - 7) === 'Filter=')
276        newLocation = newLocation.substring(0, newLocation.length - 7);
277
278    if (newLocation.substring(newLocation.length - 1) === '&')
279        newLocation = newLocation.substring(0, newLocation.length - 1);
280
281    if (newLocation.substring(newLocation.length - 1) === '?')
282        newLocation = newLocation.substring(0, newLocation.length - 1);
283
284    newLocation = newLocation.replace('||', '|'); //If we remove an attribute from the middle, we'll have an extra pipe which can be removed.
285
286    return newLocation;
287};
288
289function removeAttributeValueFromLink(attributeID, attributeValueID) {
290    var url = new String(document.location.href);
291    url = decodeURIComponent(url);
292    var attValRegexp = new RegExp(attributeValueID, 'ig');
293    var indexBeforeAttributeValue;
294    var m;
295    while (m = attValRegexp.exec(url)) { //Find all occurences of the string. It might be a substring of another, so we will check if it is an exact match
296
297        indexBeforeAttributeValue = m.index;
298        var endOfStringIndex = m.index + attributeValueID.length; //index of right after string...
299
300        if (endOfStringIndex >= url.length) { //we've reached end url, so it's OK
301            break;
302            //} else if (url.substr(indexBeforeAttributeValue + attributeValueID.length, 1).match(/(^|~)/i)) { //we've found the ^ character, so it's the end of the search string and thus OK
303        } else {
304            var substring = url.substr(indexBeforeAttributeValue + attributeValueID.length, 1);
305            if (substring === "~" || substring === "^")
306                break;
307
308        }
309
310    }
311
312    var newLink = '';
313    if (url.substr(indexBeforeAttributeValue - 1, 1) == '^' ||
314        url.substr(indexBeforeAttributeValue + attributeValueID.length, 1) == '^') {
315        if (url.substr(indexBeforeAttributeValue - 1, 1) == ':') {
316            newLink = url.replace(attributeValueID + '^', '');
317        } else {
318            if ((indexBeforeAttributeValue + attributeValueID.length) >= url.length) {
319                newLink = url.substr(0, indexBeforeAttributeValue);
320                if (newLink.substr(newLink.length - 1, 1) === "^")
321                    newLink = newLink.substr(0, newLink.length - 1);
322            }
323            else {
324                newLink = url.replace('^' + attributeValueID, '');
325            }
326
327        }
328        return newLink;
329    } else {
330        return removeAttributeExtra(url, attributeID + ':' + attributeValueID);
331    }
332};
333
334function addValueToQueryStringExtra(renderEngine, me, attributeId, attributeValueId) {
335
336    if (jQuery(me).hasClass("disabled"))
337        return;//shouldn't allow clicks when the disabled class i set. 
338
339    jQuery(me).addClass("attribute-spinner");
340    var loc = document.location.href;
341    var searchLoc = decodeURIComponent(renderEngine.getParameterByName('Filter'));
342    var newLocation = "";
343    var attributeValueIdExisted = false;
344    if (searchLoc.length > 0) {
345        var currentAttributes = searchLoc.split('|');
346        for (var i = 0; i < currentAttributes.length; i++) {
347            var attributeAndValues = currentAttributes[i].split(':');
348            if (attributeAndValues.length >= 2) {
349                //first entry in array should be the attributeId and the second a list of attributeValues
350                if (attributeAndValues[0] === attributeId) {
351                    var attributeValues = attributeAndValues[1].split('^');
352                    for (var j = 0; j < attributeValues.length; j++) {
353                        var currentAttributeValueId = attributeValues[j];
354                        if (currentAttributeValueId === attributeValueId)
355                            attributeValueIdExisted = true;
356                    }
357                }
358            }
359        }
360
361
362    }
363    scrollViewToTopOfPage(jQuery(me).parents("[data-scrolltime]").data("scrolltime"));
364    if (attributeValueIdExisted) {
365        newLocation = removeAttributeValueFromLink(attributeId, attributeValueId);
366        RemoveAttributeValueLinkFromDom(attributeValueId);
367
368    } else {
369        newLocation = encodeURI(CreateUrl(loc, attributeId + ':' + attributeValueId));
370        var titleOfRemoved = me.text;
371        AddDivForRemovingAttributeValue(attributeValueId, attributeId, titleOfRemoved);
372    }
373
374    newLocation = RemovePageNumberFromLink(newLocation);
375
376    window.history.pushState(attributeId + ':' + attributeValueId, document.title, newLocation);
377
378    var searchLocAfterFiltering = decodeURIComponent(renderEngine.getParameterByName('Filter'));
379
380    informShopOfFilterActivation(renderEngine, searchLocAfterFiltering);
381
382    renderEngine.CurrentPageNumber = 1;
383    setInternalListOfOpenFilters(renderEngine);
384    renderEngine.updateSelectedAttributeListVisibility();
385    renderEngine.updateFields(".ajax-field", "#AttributeListBox", true, false, false, 0);
386};
387
388function informShopOfFilterActivation(renderEngine, currentFilter) {
389    var data = { clientHasFilter: currentFilter !== null && currentFilter !== undefined && currentFilter.length > 0 };
390    renderEngine.filterService.GetAction("SetFilterActive", data).then(function (result) {
391        jQuery("#cbxGroupVariants").prop("checked", result);
392    });
393};
394
395function setInternalListOfOpenFilters(renderEngine) {
396    var openAttributes = jQuery("#AttributeListBox div.AttributeBox.show-extra-values");
397    var openAttributesList = [];
398    openAttributes.each(function (index, item) {
399        openAttributesList.push(jQuery(item).data("attribute-id"));
400    });
401    renderEngine.openAttributesList = openAttributesList;
402
403};
404
405//Removes filter and keep parameters from the url.
406function RemoveFilter() {
407    var url = new String(document.location.href);
408    url = url.replace(keepFilterOpen, "");
409    var startIndex = url.indexOf("&Filter=", 0);
410    var skipone = startIndex >= 0 ? 1 : 0;
411
412    if (startIndex < 0)
413        startIndex = url.indexOf("Filter=", 0);
414
415    var endIndex = url.indexOf("&", startIndex + skipone);
416
417
418    if (startIndex >= 0) {
419        var newUrl = url.substring(0, startIndex);
420
421        if (endIndex > 0)
422            newUrl = newUrl + url.substring(endIndex, url.length - 1);
423
424        newUrl = RemovePageNumberFromLink(newUrl);
425
426        RemoveAllAttributeValueLinkFromDom();
427
428        mcWeb.ajaxRenderEngine.CurrentPageNumber = 1;
429        window.history.pushState(newUrl, '', newUrl);
430        mcWeb.ajaxRenderEngine.updateSelectedAttributeListVisibility();
431        mcWeb.ajaxRenderEngine.reloadPageListingOnlyCallback(false);//todo: sørg for at denne ber om å rendre filteret på nytt når den kalles her i fra.
432        //reloadPageCallback();
433        //document.location = newUrl;
434    } else
435        location.reload(true);
436}
437
438function RemoveAttributeValueLinkFromDom(attributeValueId) {
439    jQuery("div.SelectedAttribute[data-attribute-value-id='" + attributeValueId + "']").remove();
440}
441
442function RemoveAllAttributeValueLinkFromDom() {
443    jQuery("div.SelectedAttribute").remove();
444    jQuery("div.SearchFilterAttributes .attribute-value a.selected").removeClass("selected");
445}
446
447
448function AddDivForRemovingAttributeValue(attributeValueId, attributeId, title) {
449    jQuery("div.SelectedAttributeBox .filter-selection").append("<div class=\"SelectedAttribute\" data-attribute-value-id=\"" +
450        attributeValueId + "\"> <a onclick=\"mcWeb.ajaxRenderEngine.addValueToQueryString(this,'" +
451        attributeId + "','" + attributeValueId + "');\" title=\"" + title + "\">" + title + "</a></div>");
452}
453
454this.fetchAttributeValuesExtra = function (renderEngine, attributeId, targetId, nodeId, fieldId) {
455    var queryFilterParam = renderEngine.getParameterByName("Filter");
456    var queryMinParam = renderEngine.getParameterByName("Min");
457    var queryMaxParam = renderEngine.getParameterByName("Max");
458    var pageIndex = renderEngine.getParameterByName("pageID");
459    var fieldRequestFilter = {
460        NodeId: nodeId,
461        Url: document.location.pathname + document.location.search,
462        Filter: queryFilterParam,
463        MinPrice: queryMinParam,
464        MaxPrice: queryMaxParam,
465        SearchString: renderEngine.getParameterByName("q"),
466        ClientId: targetId,
467        PageIndex: pageIndex,
468        FilterCacheKey: ''
469    }
470
471    renderEngine.service.PostAction("RenderAttribute", { AttributeId: attributeId, FieldId: fieldId, RequestFilter: fieldRequestFilter }).then(
472        function (result) {
473            if (result != null) {
474                updateAttributeContent(result);
475            }
476        }
477    );
478};
479
480
481function CreateUrl(currentUrl, valToAdd) {
482    var url = decodeURIComponent(currentUrl);
483
484    url = RemovePageNumberFromLink(url);
485
486    var filterIndex = url.indexOf('Filter=');
487    if (filterIndex >= 0) {
488        filterIndex += 7;
489        var indexOfColonInNewValue = valToAdd.indexOf(':');
490
491        var existingAttributeID = valToAdd.substr(0, indexOfColonInNewValue);
492
493        var startOfExistingAttributeIDInUrl = McIndexOf(url, existingAttributeID + ':');
494        if (startOfExistingAttributeIDInUrl >= 0) {
495            var startOfUrl = url.substr(0, startOfExistingAttributeIDInUrl + existingAttributeID.length + 1);
496            var endOfUrl = url.substr(startOfExistingAttributeIDInUrl + existingAttributeID.length + 1, url.length);
497            var attributeValueID = valToAdd.substr(indexOfColonInNewValue + 1, valToAdd.length);
498            return startOfUrl + attributeValueID + '^' + endOfUrl;
499
500        } else {
501            return url.substring(0, filterIndex) + valToAdd + '|' + url.substring(filterIndex, url.length);
502        }
503    } else {
504        if (url.indexOf('?') < 0) {
505            url += '?';
506        } else {
507            if (url.substr(url.length - 1, 1) != '&' && url.substr(url.length - 1, 1) != '?')
508                url += '&';
509        }
510
511        url += 'Filter=' + valToAdd;
512        //url += 'Filter=' + valToAdd;
513        return url;
514    }
515}
516
517function replaceAll(find, replace, str) {
518    return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
519};
520
521function escapeRegExp(str) {
522    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
523};
524
525function RemovePageNumberFromLink(url) {
526    var index = url.indexOf('pageID=');
527    if (index >= 0) {
528        var urlPartWithPageID = url.substring(index, url.length);
529        var urlPartWithoutPageID = urlPartWithPageID.substring(7, urlPartWithPageID.length);
530        var indexOfFirstAmpersand = urlPartWithoutPageID.indexOf('&', 1);
531        if (indexOfFirstAmpersand > 0) {
532            var tmp = urlPartWithoutPageID.substring(indexOfFirstAmpersand, urlPartWithoutPageID.length);
533            return tmp;
534        } else {
535            return replaceAll(urlPartWithPageID, '', url);
536        }
537    } else
538        return url;
539}
540
541function scrollViewToTopOfPage(scrolltime) {
542    if (scrolltime == null || scrolltime < 0)
543        return;
544
545    var scrollTo = jQuery(jQuery(".FieldSorter.CenterContentDynamicAdList")[0]);
546    if (scrollTo.length <= 0)
547        scrollTo = jQuery(jQuery(".FieldSorter.CenterContentDynamicProdListAlternate")[0]);
548    if (scrollTo.length === 0)
549        return;
550
551
552    var position = scrollTo.offset().top - 250;
553    jQuery([document.documentElement, document.body]).animate({
554        scrollTop: position
555    }, scrolltime);
556
557
558};
559
560function scrollToNextPageExtra(renderEngine, element) {
561    try {
562        var url = element.getAttribute("data-next-page-url");
563        var nextPageIndex = parseInt(element.getAttribute("data-next-page-index"));
564        if (nextPageIndex > renderEngine.CurrentPageNumber) {
565            //CurrentPageNumber may be off when we started on page > 1. We must reset currentpage here since we know which is to be the next page
566            if (nextPageIndex > 1)
567                renderEngine.CurrentPageNumber = nextPageIndex - 1;
568
569            var title = element.getAttribute("title");
570            window.history.pushState(url, document.title + ' - ' + title, url);
571            renderEngine.updateFields(".ajax-field[data-field-is-productlist=1]",
572                "#AttributeListBox",
573                true,
574                true,
575                false,
576                0,
577                nextPageIndex); //todo: check that we're actually only changing page, because otherwise we need to set the param doNotRenderFilter to false
578            updateScroller(renderEngine, element);
579        }
580        return false; //indicate navgiation is complete
581    } catch (e) {
582        return true; //must navigate using the link
583    }
584};
585
586function updateScroller(renderEngine, element) {
587
588    renderEngine.service.GetAction("GetNextPageUrl", { currentUrl: jQuery(element).data("next-page-url"), currentIndex: renderEngine.CurrentPageNumber }).then(
589        function (result) {
590            if (result != null) {
591                renderEngine.CurrentPageNumber = renderEngine.CurrentPageNumber + 1;
592                if (renderEngine.CurrentPageNumber > renderEngine.HighestPageNumberSeen)
593                    renderEngine.HighestPageNumberSeen = renderEngine.CurrentPageNumber;
594
595                jQuery(element).data("next-page-url", result);
596                var nextIndex = parseInt(jQuery(element).data("next-page-index"));
597                jQuery(element).data("next-page-index", nextIndex + 1);
598
599            }
600        }
601    );
602};
603
604function handleScrollExtra(renderEngine, mcUtils) {
605    if (renderEngine.HasMorePages && !renderEngine.LoadingFromScroll && !renderEngine.fieldUpdateInProgress && (renderEngine.PagingMode == 3 || renderEngine.AutoScrolledPages < 3)) {
606        doScrollHandling(renderEngine, mcUtils);
607    }
608    else if (renderEngine.HasMorePages && !renderEngine.LoadingFromScroll && renderEngine.fieldUpdateInProgress && (renderEngine.PagingMode == 3 || renderEngine.AutoScrolledPages < 3)) {
609        //need to delay the scroll handling and try again, in case we complete the ongoing field update and it subsequently needs to handle the scroll
610        setTimeout(function () {
611            if (!renderEngine.HasMorePages || renderEngine.LoadingFromScroll || renderEngine.fieldUpdateInProgress || renderEngine.PagingMode < 3 || renderEngine.AutoScrolledPages >= 3)
612                return;
613
614            doScrollHandling(renderEngine, mcUtils);
615        }, 500);
616    }
617};
618
619function doScrollHandling(renderEngine, mcUtils) {
620    var scrollerContainer = jQuery(".field-paging-container");
621    if (scrollerContainer.length > 0 && mcUtils.isElementInView(scrollerContainer)) {
622        var scrollerTag = jQuery(".field-paging-next:visible");
623        if (scrollerTag != null && scrollerTag.length > 0 && mcUtils.isElementInView(scrollerTag)) {
624            renderEngine.LoadingFromScroll = true; //pause event-checking on scrolling
625            renderEngine.AutoScrolledPages = renderEngine.AutoScrolledPages + 1;
626            scrollToNextPageExtra(renderEngine, scrollerTag[0]);
627        }
628    }
629};
630
631
632function startSaleCountDown($field) {
633    var timer = setInterval(function () {
634
635        var now = new Date().getTime();
636        $field.find(".WebPubElement .sale-countdown").each(function (index) {
637            var salesCountdown = jQuery(this);
638            var endDate = new Date(salesCountdown.data("bid-expires")).getTime();
639            var t = endDate - now;
640
641            if (t >= 0) {
642                var days = Math.floor(t / (1000 * 60 * 60 * 24));
643                var hours = Math.floor((t % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
644                var mins = Math.floor((t % (1000 * 60 * 60)) / (1000 * 60));
645                var secs = Math.floor((t % (1000 * 60)) / 1000);
646
647                jQuery(salesCountdown).find(".timer-days").html(days +
648                    "<span class='label'>DAY(S)</span>");
649
650                jQuery(salesCountdown).find(".timer-hours").html(("0" + hours).slice(-2) +
651                    "<span class='label'>HR(S)</span>");
652
653                jQuery(salesCountdown).find(".timer-mins").html(("0" + mins).slice(-2) +
654                    "<span class='label'>MIN(S)</span>");
655
656                jQuery(salesCountdown).find(".timer-secs").html(("0" + secs).slice(-2) +
657                    "<span class='label'>SEC(S)</span>");
658            } else {
659                //the price for this product should now be invalid
660                salesCountdown.removeClass("sale-countdown");
661                salesCountdown.data("bid-expires", "");
662                salesCountdown.html('');
663            }
664        });
665
666    }, 1000);
667}
668
669function fillLightboxWithContentExtra(renderEngine, fieldData, lightboxContainer) {
670    for (var i = 0; i < fieldData.Data.length; i++) {
671        var data = fieldData.Data[i];
672        if (data.Response.length <= 0) {
673            continue;
674        }
675        data.targetField = jQuery('#' + data.ClientId);
676        if (data.targetField != null && data.targetField != undefined) {
677            //add a placeholder where the field was found, so we can put it back when the lightbox is closed
678            data.targetField.hide();
679            jQuery(data.targetField).html(data.Response);
680            var fields = jQuery(data.targetField).closest(".web-pub-field.container, .web-pub-field.container-fluid");
681
682            fields.after("<div style='display:none;' data-placeholder-id='" + fields[0].id + "'/>");
683            var fieldPopupHtml = jQuery(fields).detach();
684            lightboxContainer.append(fieldPopupHtml);
685            data.targetField.show();
686            if (data.LoadFilter) {
687                updateFilterContentExtra(renderEngine,data);
688
689            }
690            renderEngine.refineInsertedContent(data.targetField, data);
691            if (fieldData.InitializeRoyalSlider)
692                InitializeRoyalSliderInAds("#" + data.ClientId + " .variant-slider");
693        }
694
695        renderEngine.initialiseAdLinks();
696    }
697};
698
699function searchAtttributesExtra (target) {
700    jQuery(target).siblings().removeClass('search-hidden');
701
702    const searchQuery = target.value.toLowerCase();
703
704    if (searchQuery.length < 1) {
705        jQuery(target).parent().removeClass('search-show-extra-values');
706        jQuery(target).siblings('a').removeClass('toggle-hidden');
707        jQuery(target).siblings('a').show();
708        return;
709    }
710
711    jQuery(target).parent().addClass('search-show-extra-values');
712    jQuery(target).siblings('a').addClass('toggle-hidden');
713
714    const elements = jQuery(target).siblings('.attribute-value').find('a');
715
716
717    elements.filter(function (index) {
718        const title = jQuery(this).attr('title').toLowerCase();
719        return !title.includes(searchQuery) && !jQuery(this).hasClass('selected');
720    }).parent().addClass('search-hidden');
721};
722
723function updateVariantMatrixField(data) {
724    var listingField = jQuery('.matrix-popup .ajax-field[data-field-products-set-clientside="1"]');
725    if (listingField != null && listingField.length > 0) {
726        if (data != undefined && data != null && data.variantMatrix != undefined && data.variantMatrix != null
727            && data.variantMatrix.VariantChilds != undefined && data.variantMatrix.VariantChilds != null) {
728            var children = "";
729
730            for (var i = 0; i < data.variantMatrix.VariantChilds.length; i++) {
731                children += data.variantMatrix.VariantChilds[i].ProduktLagerId + ",";
732            }
733            listingField.attr('data-Plids', children);
734            listingField.attr('data-variant-sorting', 'true');
735        }
736    }
737};
738
739function loadLightboxFields (renderEngine) {
740    if (jQuery('body.listtype-grid').length > 0) {
741        renderEngine.ListingTypeGrid = true;
742    }
743
744    var pageParam = renderEngine.getParameterByName('pageID');
745    if (pageParam.length > 0) {
746        renderEngine.InitialPageNumber = parseInt(pageParam);
747        renderEngine.CurrentPageNumber = renderEngine.InitialPageNumber;
748        renderEngine.HighestPageNumberSeen = renderEngine.InitialPageNumber;
749        renderEngine.LowestPageNumberSeen = renderEngine.InitialPageNumber;
750
751    }
752    var articleIdentifier = jQuery("div#current-article-id");
753    if (articleIdentifier.length > 0) {
754        renderEngine.currentArticleId = jQuery(articleIdentifier[0]).data("current-article");
755    }
756
757    renderEngine.CheckStatistics = (
758        window.D4AiActive != undefined &&
759        window.D4AiActive &&
760        window.D4AiRegisterStats != undefined &&
761        window.D4AiRegisterStats);
762
763    var loadFirstParams = {
764        targetKey: "div.modal div .ajax-field.not-loaded",
765        filterTargetKey: "#AttributeListBox",
766        onlyUpdateProductList: false,
767        doNotRenderFilter: false,
768        useDummyData: false,
769        popupFilter: 0,
770        nextPageNumber: 0
771    };
772
773    renderEngine.fieldsToLoad = [];
774    renderEngine.fieldsToLoad.push(loadFirstParams);
775
776    renderEngine.runUpdateFields();
777};
778function shouldDelayLoadingFilter(attributeFilterParent) {
779    if (attributeFilterParent == undefined || attributeFilterParent == null || attributeFilterParent.length <= 0 || attributeFilterParent[0].id !== "AttributeListBox") {
780        return false;
781    }
782    return jQuery("#filter-toggle").length > 0 && jQuery("#filter-toggle").data("expandlevel").length > 0 && !jQuery("#filter-content").is(":visible");
783};
784
785function storeFilterLoadCall (renderEngine, parent, manufacturerId) {
786    renderEngine.delayLoadFilterParent = parent;
787    renderEngine.delayLoadFilterManufacturerId = manufacturerId;
788    PubSub.subscribe("AjaxRenderingEngine.onFilterLoadRequested", function (message, data) {
789        if (renderEngine.delayLoadFilterLoaded)
790            return;//loaded already
791
792        loadAttributeFilterNotYetLoaded(renderEngine, renderEngine.delayLoadFilterParent, renderEngine.delayLoadFilterManufacturerId, false);
793    });
794};
795
796//todo: move to secondary file (getLibrary call - "filterutils")
797function loadAttributeFilterNotYetLoaded(renderEngine, attributeFilterParent, manufacturerid, allowDelay) {
798    if (allowDelay && shouldDelayLoadingFilter(attributeFilterParent)) {
799        // delay loading filter until the user clicks the show filter button
800        renderEngine.delayLoadFilterLoaded = false;//allow filter loading
801        storeFilterLoadCall(renderEngine, attributeFilterParent, manufacturerid);
802        return;
803    }
804    renderEngine.delayLoadFilterLoaded = true;//filter loading now requested, prevent additional calls
805
806    jQuery(attributeFilterParent).addClass("attribute-spinner");
807    var queryFilterParam = renderEngine.getParameterByName("Filter");
808    var queryMinParam = renderEngine.getParameterByName("Min");
809    var queryMaxParam = renderEngine.getParameterByName("Max");
810    var pageIndex = renderEngine.getParameterByName("pageID");
811    var listingFields = jQuery(".ajax-field[data-field-is-productlist='1']");
812    var cacheKey = '';
813    if (renderEngine.urlParamsFilterIsValidFor === null || renderEngine.urlParamsFilterIsValidFor === queryFilterParam) {
814        renderEngine.urlParamsFilterIsValidFor = queryFilterParam;
815        if (listingFields.length > 0) {
816            cacheKey = jQuery(listingFields[0]).attr("data-cachekey");
817        }
818    }
819
820
821    var fieldRequestFilter = {
822        NodeId: jQuery('#hidden-nodeid').text(),
823        Url: document.location.pathname + document.location.search,
824        Filter: queryFilterParam,
825        MinPrice: queryMinParam,
826        MaxPrice: queryMaxParam,
827        SearchString: renderEngine.getParameterByName("q"),
828        ClientId: '',
829        PageIndex: pageIndex,
830        FilterCacheKey: cacheKey,
831        ManufacturerId: manufacturerid
832    }
833
834
835    var notProcessedFields = attributeFilterParent.find(".not-processed");
836
837    if (notProcessedFields.length > 0) {
838        var cachingparams = {
839            RequestFilter: fieldRequestFilter,
840            FieldId: jQuery(notProcessedFields[0]).data("fieldid")
841        };
842
843        renderEngine.service.PostAction("CacheProductFilter", cachingparams).then(function (result) {
844            if (result.Success)
845                fieldRequestFilter.FilterCacheKey = result.CacheKey;
846
847            checkAttributeVisibility(renderEngine, attributeFilterParent, fieldRequestFilter);
848        });
849    } else {
850        jQuery(attributeFilterParent).find('.is-processed .hidden.AttributeBox').removeClass('hidden');
851        if (allowDelay) {
852            //no filters to show, only the menu, must show it immediately
853            attributeFilterParent.css("height", "");
854            renderEngine.unhideProductMenu();
855        }
856        jQuery(attributeFilterParent).removeClass("attribute-spinner");
857    }
858
859};
860
861function checkAttributeVisibility(renderEngine, attributeFilterParent, fieldRequestFilter) {
862    var attributesToProcess = attributeFilterParent.find(".not-processed");
863    var itemsToProcess = attributesToProcess.length;
864    var itemsProcessed = 0;
865    var notProcessedAttributeFilters = attributeFilterParent.find(".not-processed");
866
867    if (notProcessedAttributeFilters.length === 0) {
868        attributeFilterParent.find(".is-processed .hidden").removeClass("hidden");
869    }
870    else {
871        var filterItems = [];
872        notProcessedAttributeFilters.each(function (index, item) {
873            const $item = jQuery(item);
874            var itemParam = {
875                AttributeId: $item.data("aid"),
876                NodeId: $item.data("nodeid"),
877                FieldId: $item.data("fieldid"),
878                Identifier: item.id
879            }
880            filterItems.push(itemParam);
881        });
882        renderEngine.service.PostAction("CheckAttributeVisibility", { FilterItems: filterItems, RequestFilter: fieldRequestFilter, OnlyFirstItemWhenNotCached:true  }).then(function (result) {
883            if (!!result && result.Success) {
884                for (var i = 0; i < result.Items.length; i++) {
885                    var item = result.Items[i];
886                    const $itemDiv = jQuery('#' + item.Identifier);
887                    $itemDiv.prevAll(".is-processed").find(".hidden.AttributeBox").removeClass("hidden");
888                    if (item.Visible) {
889                        $itemDiv.replaceWith(item.RenderedData);
890                    }
891                    else {
892                        $itemDiv.replaceWith('');
893                    }
894
895                    var nextItem = $itemDiv.next();
896                    if (nextItem.length > 0 && !nextItem.hasClass("not-process")) {
897                        nextItem.find("div.AttributeBox").removeClass("hidden");
898                    }
899                    itemsProcessed++;
900                }
901            }
902            jQuery(attributeFilterParent).removeClass("attribute-spinner");
903            
904            if (itemsProcessed >= itemsToProcess) { //we're done, let's show the menu
905                attributeFilterParent.css("height", "");
906                renderEngine.unhideProductMenu();
907            }
908
909            if (!result.HasCachedResponse && itemsProcessed < itemsToProcess)
910                checkAttributeVisibilityRemainingitems(renderEngine, attributeFilterParent, fieldRequestFilter);
911
912        });
913        
914    }
915};
916
917
918function checkAttributeVisibilityRemainingitems(renderEngine, attributeFilterParent, fieldRequestFilter) {
919    var attributesToProcess = attributeFilterParent.find(".not-processed");
920    var itemsToProcess = attributesToProcess.length;
921    var itemsProcessed = 0;
922    var notProcessedAttributeFilters = attributeFilterParent.find(".not-processed");
923
924    if (notProcessedAttributeFilters.length === 0) {
925        attributeFilterParent.find(".is-processed .hidden").removeClass("hidden");
926    }
927    else {
928        notProcessedAttributeFilters.each(function (index, item) {
929            var filterItems = [];
930            const $item = jQuery(item);
931            var itemParam = {
932                AttributeId: $item.data("aid"),
933                NodeId: $item.data("nodeid"),
934                FieldId: $item.data("fieldid"),
935                Identifier: item.id
936            }
937            filterItems.push(itemParam);
938
939            renderEngine.service.PostAction("CheckAttributeVisibility", { FilterItems: filterItems, RequestFilter: fieldRequestFilter }).then(function (result) {
940                if (!!result && result.Success) {
941                    for (var i = 0; i < result.Items.length; i++) {
942                        var item = result.Items[i];
943                        const $itemDiv = jQuery('#' + item.Identifier);
944                        $itemDiv.prevAll(".is-processed").find(".hidden.AttributeBox").removeClass("hidden");
945                        if (item.Visible) {
946                            $itemDiv.replaceWith(item.RenderedData);
947                        }
948                        else {
949                            $itemDiv.replaceWith('');
950                        }
951
952                        var nextItem = $itemDiv.next();
953                        if (nextItem.length > 0 && !nextItem.hasClass("not-process")) {
954                            nextItem.find("div.AttributeBox").removeClass("hidden");
955                        }
956                    }
957                }
958                jQuery(attributeFilterParent).removeClass("attribute-spinner");
959
960                itemsProcessed++;
961
962                if (itemsProcessed >= itemsToProcess) { //we're done, let's show the menu
963                    attributeFilterParent.css("height", "");
964                    renderEngine.unhideProductMenu();
965                }
966            });
967        });
968    }
969};
970
971function updateFilterContentExtra(renderEngine, filterData) {
972    var secondaryFilterId = "secondary-filter-" + filterData.ClientId;
973
974    if (filterData != null && filterData.FilterResponse != null && filterData.FilterResponse.Success) {
975        if (filterData.FilterResponse.ClientId == "secondary-filter") {
976            //this must be added by us right before the field we're updating with the filter
977            jQuery("#" + secondaryFilterId).remove();
978
979        }
980        var targetKey = filterData.FilterResponse.ClientId;
981
982        if (targetKey === null)
983            return; //nothing to do
984
985        if (targetKey.indexOf("#") < 0) {
986            targetKey = "#" + targetKey;
987        }
988        
989        var targetDiv = jQuery(targetKey);
990        if (targetDiv !== undefined && targetDiv.length > 0) {
991            if (filterData.FilterResponse.Response.length > 0) {
992                //keep old height of filter if we had one already...
993                const $targetDiv = jQuery(targetDiv);
994                const oldHeight = $targetDiv.height();
995                if ($targetDiv.children().length < 0)
996                    oldHeight = 0;
997                else {
998                    $targetDiv.css("height", oldHeight + "px");
999                }
1000                $targetDiv.html(filterData.FilterResponse.Response);
1001                $targetDiv.attr("data-target-field-client-id", filterData.ClientId);
1002                loadAttributeFilterNotYetLoaded(renderEngine, targetDiv, filterData.ManufacturerId, true);
1003
1004            } else {
1005                const filterContainer = jQuery(".container-2016 has-filters");
1006                if (filterContainer !== null && filterContainer !== undefined) {
1007                    jQuery(filterContainer).addClass("no-filters");
1008                    jQuery(filterContainer).removeClass("has-filters");
1009                    renderEngine.unhideProductMenu();
1010                }
1011            }
1012            if (!renderEngine.attributeFilterInitiated) {
1013                mcWeb.attributeFilte.init(filterData.ShowCompressedFilter);
1014                renderEngine.attributeFilterInitiated = true;
1015            }
1016        }
1017        else if (filterData.FilterResponse.ClientId == "secondary-filter") {
1018            targetDiv = jQuery("#" + filterData.ClientId).closest(".web-pub-field")[0];
1019            jQuery(targetDiv).before("<div class=\'secondary-filter' id=\'" + secondaryFilterId + "'></div>");
1020            jQuery("#" + secondaryFilterId).html(filterData.FilterResponse.Response);
1021        }
1022    }
1023};

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.