PageSourceSearch

https://www.ravnsgarage.no/themes/javascript/gca.func.global.js,qv=202608260644.pagespeed.ce.emE3GhA0NU.js

js ravnsgarage.no collected 2026-10-10 01:36:26 UTC 51,855 bytes, 1,469 lines download raw bytes

1class GcaBase {
2    loadPrices() {
3        let detailPrice = $("div[data-price-trigger='detail']");
4        let listPrices  = $("div[data-price-trigger='list']");
5        let listPids    = [];
6
7        if (listPrices && listPrices.length > 0) {
8            listPrices.each(function(i, div) {
9                let currentPrice    = $(div).find("[data-type='current']");
10                let hasCurrentPrice = currentPrice && currentPrice.text().length > 1;
11                if (!hasCurrentPrice) {
12                    listPids.push( $(div).data("price-id") );
13                }
14            });
15        }
16
17        if (detailPrice.length > 0) {
18            let pid = $(detailPrice).data("price-id");
19            jQuery.ajax({
20                url: "/ajax?action=checkPrice&pid=" + pid + "&qty=1",
21                cache: false,
22                type: "get",
23                dataType: "json"
24            }).done(function(data) {
25                let price     = data.body;
26
27                if (price !== undefined && price.hasOwnProperty("current")) {
28                    let targetEl  = $("div[data-price-trigger='detail'][data-price-id='" + pid + "']");
29                    let currentEl = targetEl.find("[data-type=current]");
30
31                    currentEl.html(price.current);
32                    currentEl.attr("itemprop","price").attr("content", price["current-raw"]);
33
34                    if (price.old) {
35                        currentEl.addClass("has-old-price");
36                        targetEl.find("[data-type=old]").html(price.old);
37                        targetEl.find("[data-type=old]").attr("itemprop","highPrice").attr("content", price["old-raw"]);
38                    }
39
40                    if (price.currency) {
41                        targetEl.find("[data-type=currency]").attr("itemprop","priceCurrency").attr("content", price.currency);
42                    }
43
44                    targetEl.find("[data-type=discount]").html(price.discount);
45                    targetEl.find("[data-type=vatDesc]").show();
46                }
47            });
48        }
49
50        if (listPids && listPids.length > 0) {
51            jQuery.ajax({
52                url: "/ajax?action=checkPricesInList&pids=" + listPids,
53                cache: false,
54                type: "get",
55                dataType: "json"
56            }).done(function(data) {
57                let prices = data.body;
58                for (let i in prices) {
59                    let price     = prices[i];
60                    let targetEl  = $("div[data-price-trigger='list'][data-price-id='"+price.id+"']");
61                    let currentEl = targetEl.find("[data-type=current]");
62
63                    currentEl.html(price.current);
64                    currentEl.attr("itemprop","price").attr("content", price["current-raw"]);
65
66                    if (price.old) {
67                        currentEl.addClass("has-old-price");
68                        targetEl.find("[data-type=old]").html(price.old);
69                        targetEl.find("[data-type=old]").attr("itemprop","highPrice").attr("content", price["old-raw"]);
70                    }
71
72                    if (price.currency) {
73                        targetEl.find("[data-type=currency]").attr("itemprop","priceCurrency").attr("content", price.currency);
74                    }
75
76                    targetEl.find("[data-type=discount]").html(price.discount);
77                }
78            });
79        }
80    }
81
82    loadStocks() {
83        let detailStocks = $("span[data-stock-trigger='detail']");
84        let listStocks   = $("div[data-stock-trigger='list']");
85        let listPids     = [];
86
87        if (listStocks && listStocks.length > 0) {
88            listStocks.each(function(i, div) {
89                if ($(div).text().trim().length === 0) {
90                    listPids.push( $(div).data("stock-id") );
91                }
92            });
93        }
94
95        if (detailStocks.length > 0) {
96            let pid = $(detailStocks).data("stock-id");
97            jQuery.ajax({
98                url: "/ajax?action=checkStock&pid="+pid,
99                cache: false,
100                type: "get"
101            }).done(function(data) {
102                let target = $("span[data-stock-trigger='detail'][data-stock-id='"+pid+"']");
103                target.html(data);
104            });
105        }
106
107        if (listPids && listPids.length > 0) {
108            jQuery.ajax({
109                url: "/ajax?action=checkStocksInList&pids="+listPids,
110                cache: false,
111                type: "get"
112            }).done(function(data) {
113                let htmlContent = jQuery.parseHTML(data);
114                let prices = $(htmlContent).find(".productStock");
115                prices.each(function(i, el) {
116                    let target = $("div[data-stock-trigger='list'][data-stock-id='"+el.id+"']");
117                    target.html($(el).html());
118                })
119            });
120        }
121    }
122
123    loadAddToBasketButtons() {
124        let detailButtons = $("[data-addtobasket-trigger='detail']");
125        let listButtons   = $("[data-addtobasket-trigger='list']");
126        let searchButtons = $("[data-addtobasket-trigger='search']");
127        let listPids      = [];
128        let searchPids    = [];
129
130        if (listButtons && listButtons.length > 0) {
131            listButtons.each(function(i, div) {
132                if ($(div).text().trim().length === 0) {
133                    listPids.push( $(div).data("addtobasket-id") );
134                }
135            });
136        }
137
138        if (searchButtons && searchButtons.length > 0) {
139            searchButtons.each(function(i, span) {
140                if ($(span).text().trim().length === 0) {
141                    searchPids.push( $(span).data("addtobasket-id") );
142                }
143            });
144        }
145
146        if(detailButtons.length > 0) {
147            let pid = $(detailButtons).data("addtobasket-id");
148            jQuery.ajax({
149                url: "/ajax?action=getAddToBasketButtons&type=detail&pid="+pid,
150                cache: false,
151                type: "get"
152            }).done(function(data) {
153                let target = $("[data-addtobasket-trigger='detail'][data-addtobasket-id='"+pid+"']");
154                target.html( data );
155            });
156        }
157
158        if(listPids && listPids.length > 0) {
159            jQuery.ajax({
160                url: "/ajax?action=getAddToBasketButtons&type=list&pids="+listPids,
161                cache: false,
162                type: "get"
163            }).done(function(data) {
164                let htmlContent = jQuery.parseHTML(data);
165                let buttons = $(htmlContent).find(".addtobasketButton");
166                buttons.each(function(i, el) {
167                    let target = $("[data-addtobasket-trigger='list'][data-addtobasket-id='"+el.id+"']");
168                    target.html( $(el).html() );
169                })
170            });
171        }
172
173        if(searchPids && searchPids.length > 0) {
174            jQuery.ajax({
175                url: "/ajax?action=getAddToBasketButtons&type=search&pids="+searchPids,
176                cache: false,
177                type: "get"
178            }).done(function(data) {
179                let htmlContent = jQuery.parseHTML(data);
180                let buttons = $(htmlContent).find(".addtobasketButton");
181                buttons.each(function(i, el) {
182                    let target = $("[data-addtobasket-trigger='search'][data-addtobasket-id='"+el.id+"']");
183                    target.html( $(el).html() );
184                })
185            });
186        }
187    }
188
189    loadAddToBasketQtyButtons() {
190        $(document).on("click", ".increaseQty", function(e) {
191            let qtyInput = $(this).prev("input");
192            let qtyStep  = Number(qtyInput.data("qty-step"));
193            let qtyValue = Number(qtyInput.val().replace(",","."));
194            let newQty   = (qtyValue + qtyStep).toFixed(2);
195            qtyInput.val(gca.getNearestQty(newQty, qtyStep));
196            qtyInput.change()
197            e.stopImmediatePropagation();
198        });
199
200        $(document).on("click", ".decreaseQty", function(e) {
201            let qtyInput = $(this).next("input");
202            let qtyStep  = Number(qtyInput.data("qty-step"));
203            let qtyValue = Number(qtyInput.val().replace(",","."));
204            if (qtyValue > qtyStep) {
205                let newQty = (qtyValue - qtyStep).toFixed(2);
206                qtyInput.val(gca.getNearestQty(newQty, qtyStep));
207                qtyInput.change()
208            }
209            e.stopImmediatePropagation();
210        });
211    }
212
213    getCity(postalCode, callback) {
214        jQuery.ajax({
215            url: "/ajax?action=getAddressCity&postalcode=" + postalCode,
216            cache: false,
217            type: "get",
218            error: function (xhr, ajaxOptions, thrownError) {
219                callback(null);
220            }
221        }).done(function(city) {
222            callback(city.trim());
223        });
224    }
225
226    loadPostCodeInputs() {
227        let self = this;
228
229        $(document).on('blur','.postCodeInput', function() {
230            let code         = $(this).val();
231            let cityId       = $(this).data('city_element');
232            let cityInput    = $('input#' + cityId);
233            let countryId    = $(this).data('country_element');
234            let countryInput = $('select#' + countryId);
235
236            if (countryInput.val() === "NO") {
237                self.getCity(code, function(city) {
238                    cityInput.val(city);
239                });
240            }
241        });
242    }
243
244    loadAdminMenu() {
245        let target = $("[data-ajax_target='top-admin-menu']");
246
247        if (target.attr("data-entity-id") !== undefined) {
248            let entityType = target.data("entity-type");
249            let entityId   = target.data("entity-id");
250            let edit       = target.data("edit");
251
252            jQuery.ajax({
253                dataType: "html",
254                url: "/ajax?action=getAdminMenuV2&entityType=" + entityType + "&entityId=" + entityId + "&edit=" + edit,
255                cache: false,
256                type: "get",
257                success: function(data) {
258                    target.html(data);
259                    jQuery.getScript( "/themes/javascript/gca.func.admin.js");
260                },
261                error: function(jqXHR, textStatus, errorThrown) {
262                    //console.log(textStatus);
263                }
264            });
265        }
266    }
267
268    loadUserButtonLinks() {
269        jQuery.ajax({
270            dataType: "html",
271            url: "/ajax?action=getUserButtonLinks",
272            cache: false,
273            type: "get"
274        }).done(function(data) {
275            $("[data-ajax_target='user-button-links']").html(data);
276        });
277    }
278
279    loadBasketAmount() {
280        jQuery.ajax({
281            url: "/ajax?action=getBasketAmount",
282            cache: false,
283            type: "get"
284        }).done(function(data) {
285            gca.updateAjaxTarget("basket-amount", data);
286        });
287    }
288
289    loadBasketSize() {
290        jQuery.ajax({
291            url: "/ajax?action=getBasketSize",
292            cache: false,
293            type: "get"
294        }).done(function(data) {
295            gca.updateAjaxTarget("basket-size", data);
296        });
297    }
298
299    loadRecentProducts() {
300        jQuery.ajax({
301            url: "/ajax?action=getRecentProducts",
302            cache: false,
303            type: "get"
304        }).done(function(data) {
305            $("[data-ajax_target='recent-products']").html(data);
306        });
307    }
308
309    loadProductData() {
310        gca.loadPrices();
311        gca.loadStocks();
312        gca.loadAddToBasketButtons();
313        gca.loadAddToBasketQtyButtons();
314    }
315
316    loadFavoritesStuff() {
317        jQuery.ajax({
318            dataType: "json",
319            url: "/ajax?action=getFavorites&type=json",
320            cache: false,
321            type: "get"
322        }).done(function(data) {
323            gca.updateFavoriteIcons(data.favorites);
324            gca.updateFavoritesSize(data.size);
325        });
326    }
327
328    loadMenu() {
329        let config = {
330            over: function() {
331                $('.dropdown-xl-content', this).stop( true, true ).fadeIn(150);
332                $(this).toggleClass('open');
333            }, timeout: 100,
334            out: function() {
335                $('.dropdown-xl-content', this).stop( true, true ).fadeOut(150);
336                $(this).toggleClass('open');
337            }
338        };
339
340        $(".dropdown-xl-trigger").hoverIntent(config);
341    }
342
343    // TODO: Remove??
344    loadCookieButton() {
345        $('.cookie-close').click(function(){
346            $('.cookies').fadeOut(500);
347        });
348    }
349
350    executeCustomFunctions() {
351        let elements = $('[data-ajax=true][data-update=true]');
352        let actions  = {};
353
354        if (elements.length > 0) {
355            jQuery.each(elements, function( index, element ) {
356                let action = $(element).data('action');
357                if (!actions[action]) {
358                    actions[action] = [];
359                }
360                actions[action].push(element);
361            });
362
363            for (let action in actions) {
364                if (actions.hasOwnProperty(action)) {
365                    gca.executeFunctionByName(action, window, actions[action]);
366                }
367            }
368        }
369    }
370
371    executeFunctionByName(functionName, context) {
372        let args = Array.prototype.slice.call(arguments, 2);
373        let namespaces = functionName.split(".");
374        let func = namespaces.pop();
375        for (let i = 0; i < namespaces.length; i++) {
376            context = context[namespaces[i]];
377        }
378        return context[func].apply(context, args);
379    }
380
381    scrollToTop() {
382        $(window).scroll( function(){
383            let a = $(window).scrollTop();
384            if (a > 250) {
385                $('.scrollToTop').addClass('show-scroller');
386            }
387            else {
388                $('.scrollToTop').removeClass('show-scroller');
389            }
390        });
391        $('.scrollToTop').on('click', function() {
392            let completeCalled = false;
393            $("html, body").animate(
394                { scrollTop: "0" },
395                {
396                    complete : function(){
397                        if(!completeCalled){
398                            completeCalled = true;
399                        }
400                    }
401                }
402            );
403        });
404    }
405
406    setupAddToBasketEvents() {
407        $("body").on("click", "button[data-trigger='addToBasket']", function() {
408            let el      = $(this);
409            let wrapper = el.closest("[data-itag]");
410            let iTag    = wrapper.data("itag");
411            let pid     = iTag.split("_")[0];
412            let qty     = 1;
413            let withQty = el.data("with-qty");
414
415            if (withQty) {
416                let input = wrapper.find("input[data-qty-id='" + pid + "']");
417                if (input.length === 0) {
418                    // Fallback..
419                    input = $("input[data-qty-id=" + pid + "]");
420                }
421                if (input.length === 1) {
422                    qty = input.val();
423                }
424            }
425            else if (el.data("qty-default").length > 0) {
426                qty = el.data("qty-default");
427            }
428
429            gca.addToBasket2(pid, qty);
430            return false;
431        });
432    }
433
434    // Favorites
435    getFavorites(callback) {
436        jQuery.ajax({
437            dataType: "html",
438            url: "/ajax?action=getFavorites",
439            cache: false,
440            type: "get"
441        }).done(function(data) {
442            $("[data-ajax_target='favorites']").html(data);
443            gca.loadFavoritesStuff();
444            callback(true);
445        });
446    }
447
448    toggleFavorites(pid) {
449        let element = $("button[data-favorite-id='" + pid + "']");
450        let label = element.data("title-add");
451        if (element.attr("data-favorite-state") === "true") {
452            gca.removeFromFavorites(pid);
453        }
454        else {
455            gca.addToFavorites(pid);
456            label = element.data("title-remove");
457        }
458        element.find("span").text(label);
459    }
460
461    addToFavorites(pid) {
462        jQuery.ajax({
463            dataType: "json",
464            url: "/ajax?action=addToFavorites&pid="+pid,
465            cache: false,
466            type: "get"
467        }).done(function(data) {
468            gca.updateFavoriteIcons(data.body.favorites);
469            gca.updateFavoritesSize(data.body.size);
470        });
471    }
472
473    removeFromFavorites(pid) {
474        jQuery.ajax({
475            dataType: "json",
476            url: "/ajax?action=removeFromFavorites&pid="+pid,
477            cache: false,
478            type: "get"
479        }).done(function(data) {
480            gca.updateFavoriteIcons(data.body.favorites);
481            gca.updateFavoritesSize(data.body.size);
482            if (window.location.href.indexOf("/favorites") > 0) {
483                gca.getFavorites(function (callback) {
484
485                });
486            }
487        });
488    }
489
490    addFavoritesToBasket() {
491        jQuery.ajax({
492            dataType: "html",
493            url: "/ajax?action=addFavoritesToBasket",
494            cache: false,
495            type: "post"
496        }).done(function(data) {
497            gca.updateBasketElements(data);
498            let htmlContent   = jQuery.parseHTML(data);
499            let notifications = $(htmlContent).find('div#notifications').html();
500            $("#notifications").html(notifications);
501            gca.removeNotification(3000);
502        });
503    }
504
505    updateFavoritesSize(size) {
506        $("[data-ajax_target='favorites-size']").html(size);
507    }
508
509    updateFavoriteIcons(productIds) {
510        let listElements = $(document).find("[data-favorite-type='list']");
511        let detailElements = $(document).find("[data-favorite-type='detail']");
512
513        // Mark all as false..
514        if (listElements.length > 0) {
515            listElements.each(function() {
516                $(this).removeClass("active");
517                $(this).attr("data-favorite-state", "false");
518            });
519        }
520
521        if (detailElements.length > 0) {
522            detailElements.each(function() {
523                $(this).removeClass("active");
524                $(this).attr("data-favorite-state", "false");
525            });
526        }
527
528        // Mark only active..
529        for (let key in productIds) {
530            let productId = productIds[key];
531            let activeElement = $(document).find("[data-favorite-id='" + productId + "']");
532            let label = activeElement.data("title-remove");
533
534            if (activeElement.length > 0) {
535                if (activeElement.attr('data-favorite-type') === 'detail') {
536                    activeElement.addClass("active");
537                    activeElement.attr("data-favorite-state", "true");
538                    activeElement.find("span").text(label);
539                }
540                else {
541                    activeElement.addClass("active");
542                    activeElement.attr("data-favorite-state", "true");
543                    activeElement.find("span").text(label);
544                }
545            }
546        }
547    }
548
549    // Breadcrumbs
550    getBreadcrumbs2(type, id, parent) {
551        let url = "/ajax?action=getBreadcrumbs2&type=" + type + "&id=" + id;
552        if (parent) {
553            url = url + "&parent=" + parent;
554        }
555
556        jQuery.ajax({
557            dataType: "html",
558            url: url,
559            cache: false,
560            type: "get"
561        }).done(function(data) {
562            $("[data-ajax_trigger='breadcrumbs']").html(data);
563        });
564    }
565
566    setupProductClickEvents() {
567        $(document).on("click", "[data-link-item-type='product']", function() {
568            const el = $(this).closest(".product-block");
569            gca.pushSelectItemEventToGoogle(el);
570        })
571    }
572
573    toggleBreadcrumbs() {
574        let toggleButton = $('i.toggle-breadcrumb');
575        if (toggleButton.hasClass('fa-angle-down')) {
576            toggleButton.removeClass('fa-angle-down').addClass('fa-angle-up')
577        } else {
578            toggleButton.removeClass('fa-angle-up').addClass('fa-angle-down')
579        }
580
581        $('.hidden-breadcrumb').slideToggle();
582    }
583
584    addMultipleToBasket() {
585        $('.add-more-btn i').attr('hidden', false);
586        let products = [];
587
588        // Get raw input value..
589        let rawInput = $("#quickCartInput").val();
590
591        // Split into separate lines..
592        let lines = rawInput.split(/\r?\n/);
593
594        // Split each line into product number and quantity..
595        jQuery.each(lines, function(i, value) {
596            let line = jQuery.trim(value);
597            products.push(line.replace('\t', ':'));
598        });
599
600        let delimitedString = products.join(",");
601        this.addMultipleToBasketAjax(delimitedString);
602    }
603
604    addMultipleToBasketAjax(delimitedString) {
605        jQuery.ajax({
606            dataType: "html",
607            url: "/ajax?action=addMultipleToBasket&products=" + delimitedString,
608            cache: false,
609            type: "post"
610        }).fail(function(jqXHR, error, errorThrown) {
611            if (jqXHR.status === 405) {
612                jQuery.ajax({
613                    dataType: "html",
614                    url: "/ajax?action=notify&type=addToBasketNotAllowed",
615                    cache: false,
616                    type: "get"
617                }).done(function(data) {
618                    gca.notifyAddToBasket(data);
619                    $('.add-more-btn i').attr('hidden', true);
620                });
621            }
622        }).done(function(data) {
623            gca.updateBasketElements(data);
624            gca.loadStocks();
625            let htmlContent   = jQuery.parseHTML(data);
626            let notifications = $(htmlContent).find('div#notifications').html();
627            $("#notifications").html(notifications);
628            $('.add-more-btn').removeClass('loading');
629
630            setTimeout(function() {
631                $('.add-more-btn i').attr('hidden', true);
632
633                $("#notifications").find('.alert-success, .alert-warning').fadeOut('slow', function() {
634                    $(this).remove();
635                });
636            }, 3000);
637        });
638    }
639
640    updateBasketElements(data) {
641        let htmlContent = jQuery.parseHTML(data);
642        let basketSize = $(htmlContent).find('div#basketSize').text();
643        let basketAmount = $(htmlContent).find('div#basketAmount').text();
644        let basketTable = $(htmlContent).find('div#basketTable').html();
645        let basketAmounts = $(htmlContent).find('div#basketAmounts').html();
646        gca.updateAjaxTarget("basket-size", basketSize);
647        gca.updateAjaxTarget("basket-amount", basketAmount);
648        gca.updateAjaxTarget("basket-table", basketTable);
649        gca.updateAjaxTarget("basket-amounts", basketAmounts);
650    }
651
652    notifyAddToBasket(data) {
653        let a =  $('#notifications');
654        a.hide().html('<div class="notification">' + data + '</div>').fadeIn('slow');
655        setTimeout(function() {
656            a.find('.alert-success, .alert-warning').fadeOut('slow', function() {
657                $(this).remove();
658            });
659        }, 3000);
660    }
661
662    removeFromBasket(blId, pid, qty) {
663        jQuery.ajax({
664            dataType: "html",
665            url: "/ajax?action=removeFromBasket&blid=" + blId,
666            cache: false,
667            type: "get"
668        }).done(function(data) {
669            gca.updateBasketElements(data);
670            gca.loadStocks();
671            if (pid !== undefined && qty !== undefined) {
672                gca.pushRemoveFromBasketEventGoogle(pid, qty);
673            }
674        });
675    }
676
677    copyBasket(bid) {
678        gca.showPageLoader();
679        jQuery.ajax({
680            dataType: "html",
681            url: "/ajax?action=copyBasket&bid="+bid,
682            cache: false,
683            type: "get"
684        }).fail(function(jqXHR, error, errorThrown) {
685            if (jqXHR.status === 405) {
686                jQuery.ajax({
687                    dataType: "html",
688                    url: "/ajax?action=notify&key=basket.copy.expired&type=warning",
689                    cache: false,
690                    type: "get"
691                }).done(function(data) {
692                    gca.notifyAddToBasket(data);
693                });
694            }
695        }).done(function(data) {
696            gca.hidePageLoader();
697            gca.loadBasketSize();
698            gca.loadBasketAmount();
699
700            jQuery.ajax({
701                dataType: "html",
702                url: "/ajax?action=notify&key=basket.copy.success&type=success",
703                cache: false,
704                type: "get"
705            }).done(function(data) {
706                gca.notifyAddToBasket(data);
707            });
708        });
709    }
710
711    checkBasketFreight() {
712        let postcode = $("input[name=postcode]").val();
713        if (postcode && postcode.length > 0) {
714            jQuery.ajax({
715                url: "/ajax?action=checkFreight&postcode=" + postcode,
716                cache: false,
717                type: "get"
718            }).done(function(data) {
719                $("#freightResponse").html(data.trim());
720                const myModalAlternative = new bootstrap.Modal('#freightModal', {});
721                myModalAlternative.show();
722            });
723        }
724    }
725
726    getNearestQty(qty, multiple) {
727        let nearestQty = ((qty / multiple) * multiple);
728        return nearestQty % 1 !== 0 ? nearestQty.toFixed(2) : nearestQty;
729    }
730
731    // Notifications
732    notify(type, message, duration, param1, param2, param3, param4, param5) {
733        jQuery.ajax({
734            dataType: "html",
735            url: "/ajax?action=notify&type="+encodeURIComponent(type)+"&key="+encodeURIComponent(message)+"&param1="+encodeURIComponent(param1)+"&param2="+encodeURIComponent(param2)+"&param3="+encodeURIComponent(param3)+"&param4="+encodeURIComponent(param4)+"&param5="+encodeURIComponent(param5),
736            cache: false,
737            type: "get"
738        }).done(function(element) {
739            $("#notifications").html(element);
740        });
741
742        if (duration && duration > 0) {
743            setTimeout(function() {
744                $('#notifications').children().hide(function() {
745                    $(this).remove();
746                });
747            }, (duration*1000));
748        }
749    }
750
751    checkNotifications() {
752        jQuery.ajax({
753            dataType: "html",
754            url: "/ajax?action=checkNotifications",
755            cache: false,
756            type: "get"
757        }).done(function(element) {
758            $("#notifications").append(element);
759        });
760    }
761
762    removeNotification(delay) {
763        setTimeout(function() {
764            $('#notifications').children().hide(function() {
765                $(this).remove();
766            });
767        }, delay);
768    }
769
770    // Menu stuff..
771    toggleOffCanvasMenu(pos) {
772        let element = $(".off-canvas-menu." + pos);
773        let overlay = $(".off-canvas-overlay");
774        if (element.width() > 0) {
775            element.removeClass("visible");
776            overlay.hide();
777        }
778        else {
779            element.addClass("visible");
780            overlay.show();
781        }
782    }
783
784    hideOffCanvasMenues() {
785        let leftMenu = $(".off-canvas-menu.left");
786        let rightMenu = $(".off-canvas-menu.right");
787        let overlay = $(".off-canvas-overlay");
788
789        leftMenu.removeClass("visible");
790        rightMenu.removeClass("visible");
791        overlay.hide();
792    }
793
794    // Misc
795    pushRemoveFromBasketEventGoogle(pid, qty) {
796        jQuery.ajax({
797            dataType: "json",
798            url: "/ajax?action=allowTrack&recipient=google&service=analytics&event=removeFromCart",
799            cache: false,
800            type: "get"
801        }).done(function(allowed) {
802            if (allowed === true) {
803                jQuery.ajax({
804                    dataType: "json",
805                    url: "/ajax?action=getProduct&pid="+pid+"&qty="+qty,
806                    cache: false,
807                    type: "get"
808                }).done(function(product) {
809                    if (product.number.length > 0) {
810                        dataLayer.push({
811                            'event': 'remove_from_cart',
812                            'currency': "NOK",
813                            'value': product.price,
814                            'items': [
815                                {
816                                    item_id: product.number,
817                                    item_name: product.title,
818                                    price: product.price,
819                                    quantity: qty
820                                }
821                            ]
822                        });
823                    }
824                });
825            }
826        });
827    }
828
829    pushAddToBasketEventGoogle(pid, qty) {
830        jQuery.ajax({
831            dataType: "json",
832            url: "/ajax?action=allowTrack&recipient=google&service=analytics&event=addToCart",
833            cache: false,
834            type: "get"
835        }).done(function(allowed) {
836            if (allowed === true) {
837                jQuery.ajax({
838                    dataType: "json",
839                    url: "/ajax?action=getProduct&pid="+pid+"&qty="+qty,
840                    cache: false,
841                    type: "get"
842                }).done(function(product) {
843                    if (product.number.length > 0) {
844                        dataLayer.push({
845                            'event': 'add_to_cart',
846                            'currency': "NOK",
847                            'value': product.price,
848                            'items': [
849                                {
850                                    item_id: product.number,
851                                    item_name: product.title,
852                                    price: product.price,
853                                    quantity: Number(qty)
854                                }
855                            ]
856                        });
857                    }
858                });
859            }
860        });
861    }
862
863    pushAddToBasketEventGoogleLegacy(pid, qty) {
864        jQuery.ajax({
865            dataType: "json",
866            url: "/ajax?action=allowTrack&recipient=google&event=addToBasket",
867            cache: false,
868            type: "get"
869        }).done(function(allowed) {
870            if (allowed === true) {
871                jQuery.ajax({
872                    dataType: "json",
873                    url: "/ajax?action=getProduct&pid="+pid+"&qty="+qty,
874                    cache: false,
875                    type: "get"
876                }).done(function(product) {
877                    if (product.number.length > 0) {
878                        dataLayer.push({
879                            'event': 'addToCart',
880                            'ecommerce': {
881                                'currencyCode': 'NOK',
882                                'add': {
883                                    'products': [{
884                                        'name': product.title,
885                                        'id': product.number,
886                                        'price': product.price,
887                                        'brand': product.vendor,
888                                        'quantity': qty
889                                    }]
890                                }
891                            }
892                        });
893                    }
894                });
895            }
896        });
897    }
898
899    pushProductViewToGoogle(number, title, price, currency) {
900        jQuery.ajax({
901            dataType: "json",
902            url: "/ajax?action=allowTrack&recipient=google&service=analytics&event=viewItem",
903            cache: false,
904            type: "get"
905        }).done(function(allowed) {
906            if (allowed === true) {
907                dataLayer.push({ecommerce: null});
908                dataLayer.push({
909                    'event': 'view_item',
910                    'ecommerce': {
911                        'items': [{
912                            'item_name': title,
913                            'item_id': number,
914                            'price': price,
915                            'currency': currency
916                        }]
917                    }
918                });
919            }
920        });
921    }
922
923    pushProductListsToGoogle() {
924        jQuery.ajax({
925            dataType: "json",
926            url: "/ajax?action=allowTrack&recipient=google&service=analytics&event=viewItemList",
927            cache: false,
928            type: "get"
929        }).done(function(allowed) {
930            if (allowed === true) {
931                const productLists = $("[data-list-entity='products']");
932                if (productLists !== undefined) {
933                    productLists.each(function(i, listEl) {
934                        gca.pushProductListToGoogle($(listEl), true);
935                    });
936                }
937            }
938        });
939    }
940
941    pushProductListToGoogle(listEl, allowed) {
942        if (listEl !== undefined && listEl.length > 0) {
943            if (allowed) {
944                const listType        = listEl.attr("data-product-list-type");
945                const productElements = listEl.find(".product-block");
946                let products   = [];
947
948                productElements.each(function (i, el) {
949                    const sku  = $(el).find("[itemprop='sku']").attr("content");
950                    const name = $(el).find("[itemprop='name']").attr("content");
951                    products.push({
952                        "item_id": sku,
953                        "item_name": name
954                    });
955                });
956
957                dataLayer.push({
958                    'event': 'view_item_list',
959                    'ecommerce': {
960                        'currency': "NOK",
961                        'item_list_id': listType,
962                        'items': products
963                    }
964                });
965            }
966            else if (allowed === undefined) {
967                jQuery.ajax({
968                    dataType: "json",
969                    url: "/ajax?action=allowTrack&recipient=google&service=analytics&event=viewItemList",
970                    cache: false,
971                    type: "get"
972                }).done(function(allowed) {
973                    if (allowed === true) {
974                        gca.pushProductListToGoogle(listEl, true);
975                    }
976                });
977            }
978        }
979    }
980
981    pushSelectItemEventToGoogle(el) {
982        if (el !== undefined && el.length > 0) {
983            jQuery.ajax({
984                dataType: "json",
985                url: "/ajax?action=allowTrack&recipient=google&service=analytics&event=selectItem",
986                cache: false,
987                type: "get"
988            }).done(function(allowed) {
989                if (allowed === true) {
990                    const sku  = $(el).find("[itemprop='sku']").attr("content");
991                    const name = $(el).find("[itemprop='name']").attr("content");
992                    let listType = "";
993
994                    // Try to find list type..
995                    const listWrapper = $(el).closest("[data-product-list-type]");
996                    if (listWrapper !== undefined && listWrapper.length > 0) {
997                        listType = listWrapper.data("product-list-type");
998                    }
999
1000                    dataLayer.push({
1001                        'event': 'select_item',
1002                        'ecommerce': {
1003                            'item_list_id': listType,
1004                            'items': [
1005                                {
1006                                    'item_id': sku,
1007                                    'item_name': name
1008                                }
1009                            ]
1010                        }
1011                    });
1012                }
1013            });
1014        }
1015    }
1016
1017    pushAddToBasketEventFacebook(pid, qty) {
1018        jQuery.ajax({
1019            dataType: "json",
1020            url: "/ajax?action=allowTrack&recipient=facebook&event=addToBasket",
1021            cache: false,
1022            type: "get"
1023        }).done(function(allowed) {
1024            if (allowed === true) {
1025                jQuery.ajax({
1026                    dataType: "json",
1027                    url: "/ajax?action=getProduct&pid=" + pid + "&qty=" + qty,
1028                    cache: false,
1029                    type: "get"
1030                }).done(function (product) {
1031                    fbq('track', 'AddToCart', {
1032                        content_name: product.title,
1033                        content_ids: [product.number],
1034                        content_type: 'product',
1035                        value: product.price,
1036                        currency: product.currency
1037                    });
1038                });
1039            }
1040        });
1041    }
1042
1043    pushProductViewToFacebook(number, title, price, currency) {
1044        jQuery.ajax({
1045            dataType: "json",
1046            url: "/ajax?action=allowTrack&recipient=facebook&event=viewItem",
1047            cache: false,
1048            type: "get"
1049        }).done(function(allowed) {
1050            if (allowed === true) {
1051                fbq('track', 'ViewContent', {
1052                    content_type: 'product',
1053                    content_ids: [number],
1054                    content_name: title,
1055                    value: price,
1056                    currency: currency
1057                });
1058            }
1059        });
1060    }
1061
1062    addWishlistItem(wishlistId, productId, wishlistName) {
1063        let el = $('.added-to-wishlist');
1064        el.show();
1065        el.find('.wishlist-dummy').text(wishlistName);
1066        jQuery.ajax({
1067            dataType: "html",
1068            url: "/ajax-wishlist?action=addWishlistItem&wishlistId="+wishlistId+"&productId="+productId,
1069            cache: true,
1070            type: "get"
1071        }).done(function(data) {
1072            setTimeout(function(){$('.added-to-wishlist').hide();}, 3000);
1073            $("i#wishlist_"+wishlistId).removeClass("fa-angle-right").addClass("fa-check");
1074        });
1075    }
1076
1077    toggleWishlistSelector() {
1078        $('#wishlistsSelector').slideToggle(300);
1079        $(this).toggleClass('close');
1080    }
1081
1082    copyOrder(oid) {
1083        gca.showPageLoader();
1084
1085        jQuery.ajax({
1086            dataType: "json",
1087            url: "/ajax?action=copyOrder&oid="+oid,
1088            cache: false,
1089            type: "post"
1090        }).done(function(response) {
1091            let notification = response.notification;
1092            gca.updateAjaxTarget("basket-size", response.meta.size);
1093            gca.updateAjaxTarget("basket-amount", response.meta.amount);
1094            gca.hidePageLoader();
1095            gca.notify(notification.type, notification.key, notification.duration, notification.param1, notification.param2, notification.param3, notification.param4, notification.param5);
1096        });
1097    }
1098
1099    updateAjaxTarget(target, content) {
1100        $("[data-ajax_target='" + target + "']").html(content);
1101    }
1102
1103    display(view, pageId) {
1104        let hasPage = pageId !== undefined && pageId.trim().length > 0
1105        if(!hasPage) {
1106            pageId = -1
1107        }
1108        gca.setListView(view, pageId);
1109        let container = $("[data-list-target='products']");
1110
1111        if (!$(".show-more").is(":visible")) {
1112            if (view === 'list') {
1113                container.find('.product-grid').each(function() {
1114                    if (!$(this).hasClass("no-change")) {
1115                        $(this).attr('class', 'product-list');
1116                        $(this).find('.product-block').each(function(index, element) {
1117                            $(element).parent().addClass("col-fullwidth");
1118                        });
1119                        $(".product-filter .display .grid").removeClass("active");
1120                        $(".product-filter .display .list").addClass("active");
1121                    }
1122                });
1123            } else {
1124                container.find('.product-list').attr('class', 'product-grid');
1125
1126                container.find('.products-block .product-block').each(function(index, element) {
1127                    $(element).parent().removeClass("col-fullwidth");
1128                });
1129
1130                container.find(".product-filter .display .list").removeClass("active");
1131                container.find(".product-filter .display .grid").addClass("active");
1132            }
1133        }
1134    }
1135
1136    selectorDropdown() {
1137        let dropdown = $('.selector-dropdown li').on('click', function() {
1138            let isDisabled = $(this).find('a').hasClass("disabled");
1139            let outPut     = $(this).find('a').text();
1140            let button     = $(this).parent().parent().find('.btn-selector span');
1141            if (!isDisabled) {
1142                button.text(outPut).not('i');
1143            }
1144        });
1145    }
1146
1147
1148    scrollToTarget(href) {
1149        $('html, body').animate({
1150            scrollTop: $(href).offset().top
1151        }, 500, function () {
1152            window.location.hash = href;
1153        });
1154    }
1155
1156    getUrlParameter(sParam) {
1157        let sPageURL = decodeURIComponent(window.location.search.substring(1)),
1158            sURLVariables = sPageURL.split('&'),
1159            sParameterName,
1160            i;
1161
1162        for (i = 0; i < sURLVariables.length; i++) {
1163            sParameterName = sURLVariables[i].split('=');
1164
1165            if (sParameterName[0] === sParam) {
1166                return sParameterName[1] === undefined ? true : sParameterName[1];
1167            }
1168        }
1169    };
1170
1171    addURLParameterWithoutReload(key, value) {
1172        let url       = window.location.href;
1173        let urlParts  = url.split("?");
1174        let baseURL   = urlParts[0];
1175        let params    = urlParts[1];
1176
1177        let newParam = key + "=" + value;
1178        url = baseURL + "?" + params + "&" + newParam;
1179        window.history.replaceState(null, null, url);
1180    }
1181
1182    updateURLParameterWithoutReload(key, value) {
1183        let url       = window.location.href;
1184        let urlParts  = url.split("?");
1185        let baseURL   = urlParts[0];
1186        let params    = urlParts[1];
1187        let newParams = "";
1188        let temp      = "";
1189
1190        if (params) {
1191            const parts = params.split("&");
1192            for (let i = 0; i < parts.length; i++) {
1193                if (parts[i].split('=')[0] !== key){
1194                    newParams += temp + parts[i];
1195                    temp = "&";
1196                }
1197            }
1198        }
1199
1200        let newParam = temp + key + "=" + value;
1201        url = baseURL + "?" + newParams + newParam;
1202        window.history.replaceState(null, null, url);
1203    }
1204
1205    removeURLParameterWithoutReload(key) {
1206        let url       = window.location.href;
1207        let urlParts  = url.split("?");
1208        let baseURL   = urlParts[0];
1209        let params    = urlParts[1];
1210        let newParams = "";
1211        let temp      = "";
1212
1213        if (params) {
1214            const parts = params.split("&");
1215            for (let i = 0; i < parts.length; i++) {
1216                if (parts[i].split('=')[0] === key) {
1217                    continue;
1218                }
1219                newParams += temp + parts[i];
1220                temp = "&";
1221            }
1222        }
1223
1224        url = baseURL;
1225        if (newParams && newParams.length > 0) {
1226            url = baseURL + "?" + newParams;
1227        }
1228        window.history.replaceState(null, null, url);
1229    }
1230
1231    setListView(view, pageId) {
1232        sessionStorage.setItem('listView|' + pageId, view);
1233    }
1234
1235    getCookie(cname) {
1236        let name = cname + "=";
1237        let decodedCookie = decodeURIComponent(document.cookie);
1238        let ca = decodedCookie.split(';');
1239        for (let i = 0; i < ca.length; i++) {
1240            let c = ca[i];
1241            while (c.charAt(0) === ' ') {
1242                c = c.substring(1);
1243            }
1244            if (c.indexOf(name) === 0) {
1245                return c.substring(name.length, c.length);
1246            }
1247        }
1248        return "";
1249    }
1250
1251    createCookie(name, value, days) {
1252        let expires;
1253
1254        if (days) {
1255            let date = new Date();
1256            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
1257            expires = "; expires=" + date.toGMTString();
1258        } else {
1259            expires = "";
1260        }
1261        document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
1262    }
1263
1264    eraseCookie(name) {
1265        this.createCookie(name, "", -1);
1266    }
1267
1268    getListView(pageId) {
1269        let view = sessionStorage.getItem('listView|' + pageId);
1270        if (view === undefined) {
1271            view = sessionStorage.getItem('listView|undefined');
1272        }
1273        return view;
1274    }
1275
1276    addToBasket2(pid, qty) {
1277        jQuery.ajax({
1278            dataType: "json",
1279            url: "/ajax?action=addToBasket&pid="+pid+"&qty="+qty,
1280            cache: false,
1281            type: "post"
1282        }).done(function(response) {
1283            let notification = response.notification;
1284            if (response.status === "OK") {
1285                gca.pushAddToBasketEventGoogle(pid, qty);
1286                gca.pushAddToBasketEventGoogleLegacy(pid, qty);
1287                gca.pushAddToBasketEventFacebook(pid, qty);
1288                gca.updateAjaxTarget("basket-size", response.meta.size);
1289                gca.updateAjaxTarget("basket-amount", response.meta.amount);
1290            }
1291            gca.notify(notification.type, notification.key, notification.duration, notification.param1, notification.param2, notification.param3, notification.param4, notification.param5);
1292        });
1293    }
1294
1295    handleFirstTab(e) {
1296        if (e.keyCode === 9) {
1297            document.body.classList.add('user-is-tabbing');
1298            window.removeEventListener('keydown', gca.handleFirstTab);
1299            window.addEventListener('mousedown', gca.handleMouseDownOnce);
1300        }
1301    }
1302
1303    handleMouseDownOnce() {
1304        document.body.classList.remove('user-is-tabbing');
1305        window.removeEventListener('mousedown', gca.handleMouseDownOnce);
1306        window.addEventListener('keydown', gca.handleFirstTab);
1307    }
1308
1309    isInViewport(element) {
1310        let elementTop     = element.offset().top;
1311        let elementBottom  = elementTop + element.outerHeight();
1312        let viewportTop    = $(window).scrollTop();
1313        let viewportBottom = viewportTop + $(window).height();
1314        return elementBottom > viewportTop && elementTop < viewportBottom;
1315    }
1316
1317    showPageLoader() {
1318        $("#page-loader").attr("hidden", false);
1319    }
1320
1321    hidePageLoader() {
1322        $("#page-loader").attr("hidden", true);
1323    }
1324
1325    openSubCategoriesLevel(id, level) {
1326        jQuery.ajax({
1327            url: "/ajax?action=getSubCategoryLevel&id=" + id + "&level=" + level,
1328            cache: false,
1329            type: "get"
1330        }).done(function (data) {
1331            let htmlContent = jQuery.parseHTML(data);
1332            let subCategories = $(htmlContent).find('div#subCategories').html();
1333
1334            if(level === 1) {
1335                gca.updateAjaxTarget("sub-category-level-2", subCategories);
1336                $("#level-2").addClass("show");
1337                $(".menu-nav > .category-link").addClass("show").attr('onClick', 'gca.oneLevelBack(1);');
1338            } else if(level === 2) {
1339                gca.updateAjaxTarget("sub-category-level-3", subCategories);
1340                $("#level-3").addClass("show");
1341                $(".menu-nav > .category-link").addClass("show").attr('onClick', 'gca.oneLevelBack(2);');
1342            }
1343        });
1344    }
1345
1346    oneLevelBack(level) {
1347        if(level === 1) {
1348            $("#level-2").removeClass("show");
1349            $(".menu-nav > .category-link").removeClass("show");
1350        } else if(level === 2) {
1351            $("#level-3").removeClass("show")
1352            $(".menu-nav > .category-link").attr('onClick', 'gca.oneLevelBack(1);');
1353        }
1354    }
1355
1356    waitForElement(selector) {
1357        return new Promise(resolve => {
1358            if (document.querySelector(selector)) {
1359                return resolve(document.querySelector(selector));
1360            }
1361
1362            const observer = new MutationObserver(mutations => {
1363                if (document.querySelector(selector)) {
1364                    observer.disconnect();
1365                    resolve(document.querySelector(selector));
1366                }
1367            });
1368
1369            observer.observe(document.body, {
1370                childList: true,
1371                subtree: true
1372            });
1373        });
1374    }
1375
1376    showLocationTopMenu() {
1377        const locationSelector = document.querySelectorAll('.location-select');
1378        locationSelector.forEach(locationSelect => {
1379            if (locationSelect !== null && locationSelect.length > 0) {
1380                const location = document.querySelectorAll('.location-select > option:checked');
1381                const locationTitle = document.querySelectorAll(".locale-button-desktop .dropdown-toggle .
1381international-title");
1382                const titleFlag = document.querySelectorAll('.locale-button-desktop .dropdown-toggle img');
1383
1384                location.forEach(local => {
1385                    locationTitle.forEach(title => {
1386                        title.innerText = local.value.toUpperCase();
1387                    })
1388
1389                    titleFlag.forEach(flag => {
1390                        flag.src = "/themes/flags/" + local.value.toLowerCase() + ".svg";
1391                    })
1392                })
1393            }
1394        })
1395    }
1396
1397    showLocationDesktop(el) {
1398        let formEl = document;
1399        if (el !== undefined && el.length > 0) {
1400            formEl = el.closest(".location-form");
1401        }
1402
1403        const locationSelector = document.querySelectorAll('.location-select');
1404
1405        locationSelector.forEach(locationSelect => {
1406            if (locationSelect !== null && locationSelect.length > 0) {
1407                const location = formEl.querySelectorAll('.location-select > option:checked');
1408                const language = formEl.querySelectorAll('.language-select > option:checked');
1409                const countryFlag = formEl.querySelectorAll(".country-container .country-flag");
1410                const countryName = formEl.querySelectorAll(".country-container > span");
1411                const languageFlag = formEl.querySelectorAll(".language-container .country-flag");
1412                const languageName = formEl.querySelectorAll(".language-container > span");
1413                const currencyContainer = formEl.querySelectorAll(".active-currency");
1414
1415                location.forEach(local => {
1416                    countryFlag.forEach(flag => {
1417                        flag.src = "/themes/flags/" + local.value.toLowerCase() + ".svg";
1418                    })
1419
1420                    countryName.forEach(name => {
1421                        name.innerText = local.innerText.trim();
1422                    })
1423
1424                    currencyContainer.forEach(currency => {
1425                        currency.innerHTML = local.dataset.currency;
1426                    })
1427                })
1428
1429                language.forEach(lang => {
1430                    languageFlag.forEach(flag => {
1431                        flag.src = "/themes/flags/" + lang.dataset.language.toLowerCase() + ".svg";
1432                    })
1433
1434                    languageName.forEach(name => {
1435                        name.innerText = lang.innerText.trim();
1436                    })
1437                })
1438
1439                const saveBtn = document.querySelectorAll(".locale-button-desktop .btn-primary");
1440                saveBtn.forEach(btn => {
1441                    btn.addEventListener("click", () => {
1442                        gca.showLocationTopMenu();
1443                    })
1444                })
1445            }
1446        })
1447    }
1448
1449    debounced(delay, fn) {
1450        let timerId;
1451        return function (...args) {
1452            if (timerId) {
1453                clearTimeout(timerId);
1454            }
1455            timerId = setTimeout(() => {
1456                fn(...args);
1457                timerId = null;
1458            }, delay);
1459        }
1460    }
1461}
1462
1463class GcaRange {
1464    constructor(min, max) {
1465        this.min = min;
1466        this.max = max;
1467    }
1468}
1469

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.