PageSourceSearch

https://www.hepcat.se/M1/Production/scripts/responsive-base-core.js?v=180609

js hepcat.se collected 2026-09-25 00:29:31 UTC 50,951 bytes, 1,119 lines download raw bytes

1/* jshint -W057 */
2/* jshint -W058 */
3
4//
5//  "J"
6//
7
8var J = new function () {
9    "use strict";
10
11    this.name = "Jetshop Responsive Base Framework";
12    this.version = "1.4.6";
13    this.responsive = true; // use bool J.responsive to determine if framework is present
14    this.cart = false;
15    this.data = {
16        currency: false,
17        culture: false,
18        priceList: false,
19        priceListId: false,
20        categoryId: false,
21        productId: false,
22        language: false,
23        manufacturerList: false,
24        jetshopData: false
25    };
26    this.config = {
27        cssFallbackUrl: "/M1/Production/css/responsive-base.css", // Used for injection in pages lacking script management, not a part of JetshopData.Urls
28        breakpoints: [], // Will be added from build-process
29        apiCachePrefix: "cache-",
30        foundationConfig: {
31            equalizer: {
32                equalize_on_stack: true
33            }
34        },
35        sameHeightConfig: {
36            byRow: true,
37            property: 'height',
38            target: null,
39            remove: false
40        },
41        urls: {
42            "CountryRootUrl": "/",
43            "CheckoutUrl": "/checkout",
44            "StageCheckoutUrl": "/stage/checkout",
45            "ServicesUrl": "/Services",
46            "MyPagesUrl": "/my-pages",
47            "StageMyPagesUrl": "/stage/my-pages",
48            "LoginUrl": "/login",
49            "StageLoginUrl": "/stage/login",
50            "SignupUrl": "/signup",
51            "SignoutUrl": "/signout",
52            "MyPagesOrdersUrl": "/my-pages/orders",
53            "MyPagesOrderdetailsUrl": "/my-pages/orderdetails",
54            "MyPagesSettingsUrl": "/my-pages/settings",
55            "TermsAndConditionsUrl": "kopvillkor",
56            "CartServiceUrl": "/Services/CartInfoService.asmx/LoadCartInfo"
57        }
58    };
59    this.checker = {
60        isStage: false,
61        isLoggedIn: false,
62        isTouch: false,
63        isStartPage: false,
64        isProductPage: false,
65        isCategoryPage: false,
66        isCategoryAdvancedPage: false,
67        isNewsPage: false,
68        isSearchResultPage: false,
69        isCheckoutPage: false,
70        isStandardPage: false,
71        isOrderConfirmationPage: false,
72        isManufacturerPage: false,
73        isManufacturerAdvancedPage: false,
74        isSiteMapPage: false,
75        isMyPage: false,
76        isVatIncluded: false,
77        isCheckoutHttps: false
78    };
79    this.translations = [
80        {
81            cart: {
82                sv: "Kundvagn",
83                en: "Cart",
84                da: "Indkøbskurv",
85                nb: "Handlevogn",
86                fi: "Ostoskärry"
87            }
88        }
89    ];
90    this.translate = function (translationObject) {
91        for (var key in J.translations) {
92            if (translationObject in J.translations[key]) {
93                if (typeof J.translations[key][translationObject][J.data.language] !== "undefined") {
94                    return J.translations[key][translationObject][J.data.language];
95                }
96                else if (typeof J.translations[key][translationObject]["en"] !== "undefined") {
97                    console.log("No translation found for '" + translationObject + "' in language " + J.data.language + ". Returning English.");
98                    return J.translations[key][translationObject]["en"];
99                }
100                else if (typeof J.translations[key][translationObject]["sv"] !== "undefined") {
101                    console.log("No translation found for '" + translationObject + "' in language " + J.data.language + ". Returning Swedish.");
102                    return J.translations[key][translationObject]["sv"];
103                } else {
104                    console.log("No translation found for '" + translationObject + "' in language " + J.data.language);
105                }
106            }
107        }
108        console.log("No translation found for " + translationObject + "'");
109        return false;
110    };
111    this.setPageType = function () {
112        if (typeof JetshopData !== "undefined") {
113            if (JetshopData.IsStartPage || $("html.page-default").length) {
114                this.checker.isStartPage = true;
115                return "start-page";
116            } else if ($(".page-showproduct").length > 0) {
117                this.checker.isProductPage = true;
118                return "product-page";
119            } else if ($(".page-listproductswithimageadvanced").length > 0) {
120                this.checker.isCategoryAdvancedPage = true;
121                return "category-advanced-page";
122            } else if ($(".page-listproducts").length > 0) {
123                this.checker.isCategoryPage = true;
124                return "category-page";
125            } else if ($("html.page-responsive-orderconfirmed").length > 0) {
126                this.checker.isOrderConfirmationPage = true;
127                return "orderconfirmation-page";
128            } else if ($("html.page-orderdetailsconfirmed").length > 0) {
129                this.checker.isOrderConfirmationPage = true;
130                return "orderconfirmation-page";
131            } else if ($("html.page-listmanufacturerproductswithimageadvanced").length > 0) {
132                this.checker.isManufacturerAdvancedPage = true;
133                return "manufacturer-advanced-page";
134            } else if ($("html.page-listmanufacturerproducts").length > 0) {
135                this.checker.isManufacturerPage = true;
136                return "manufacturer-page";
137            } else if ($("html.page-shownews").length > 0) {
138                this.checker.isNewsPage = true;
139                return "news-page";
140            } else if ($("html.page-responsive-checkout").length > 0) {
141                this.checker.isCheckoutPage = true;
142                return "checkout-page";
143            } else if ($("html.page-checkout").length > 0) {
144                this.checker.isCheckoutPage = true;
145                return "checkout-page";
146            } else if ($("html.page-showsearchresult").length > 0) {
147                this.checker.isSearchResultPage = true;
148                return "searchresult-page";
149            } else if ($("html.page-mypages").length > 0) {
150                this.checker.isMyPage = true;
151                return "my-page";
152            } else if ($("html.page-showpage").length > 0) {
153                this.checker.isStandardPage = true;
154                return "standard-page";
155            } else if ($("html.page-sitemap").length > 0) {
156                this.checker.isSiteMapPage = true;
157                return "sitemap-page";
158            } else {
159                // Default pageType
160                this.checker.isStandardPage = true;
161                return "standard-page";
162            }
163        } else {
164            return false;
165        }
166    };
167    this.switch = {
168        width: "",
169        queues: {
170            small: [],
171            mediumUp: [],
172            medium: [],
173            largeUp: []
174        },
175        addToSmall: function (queueItem) {
176            this.queues.small.push(queueItem);
177        },
178        addToMediumUp: function (queueItem) {
179            this.queues.mediumUp.push(queueItem);
180        },
181        addToMedium: function (queueItem) {
182            this.queues.medium.push(queueItem);
183        },
184        addToLargeUp: function (queueItem) {
185            this.queues.largeUp.push(queueItem);
186        },
187        check: function () {
188            var queries = Foundation.media_queries;
189            if (window.matchMedia(queries.xxlarge).matches) this.set("xxlarge");
190            else if (window.matchMedia(queries.xlarge).matches) this.set("xlarge");
191            else if (window.matchMedia(queries.large).matches) this.set("large");
192            else if (window.matchMedia(queries.medium).matches) this.set("medium");
193            else if (window.matchMedia(queries.small).matches) this.set("small");
194        },
195        set: function (width) {
196            if (width != this.width) {
197                var oldWidth = this.width;
198                if (oldWidth === "small") executeQueue(this.queues.mediumUp);
199                if (width === "small") executeQueue(this.queues.small);
200                else if (width === "medium") executeQueue(this.queues.medium);
201                else if (Foundation.utils.is_large_up()) {
202                    if (oldWidth === "large" || oldWidth === "xlarge" || oldWidth === "xxlarge") {
203                        var noop;
204                    } else {
205                        executeQueue(this.queues.largeUp);
206                    }
207                }
208                $(window).trigger('switch', [width, oldWidth]);
209                $("body").removeClass(removeClassRegexp(/^device-width-/)).addClass("device-width-" + width);
210                this.width = width;
211            }
212        }
213    };
214    this.components = {
215        browserDetect: function () {
216            J.browser = bowser;
217            addBodyClass("browser-" + convertStringToURLFriendly(bowser.name));
218            if (bowser.msie) {
219                addBodyClass("msie");
220                if (bowser.version == 7) addBodyClass("ie7 msie-old");
221                if (bowser.version == 8) addBodyClass("ie8 msie-old");
222                if (bowser.version == 9) addBodyClass("ie9");
223                if (bowser.version == 10) addBodyClass("ie10");
224                if (bowser.version == 11) addBodyClass("ie11");
225            } else {
226                addBodyClass("not-msie");
227            }
228            if (bowser.webkit) addBodyClass("webkit");
229            if (bowser.blink) addBodyClass("blink");
230            if (bowser.gecko) addBodyClass("gecko");
231            if (bowser.ios) addBodyClass("ios");
232            if (bowser.android) addBodyClass("android");
233            if (bowser.mobile) addBodyClass("mobile");
234            if (bowser.tablet) addBodyClass("tablet");
235            if (bowser.tablet || bowser.mobile || ('ontouchstart' in window)) J.checker.isTouch = true;
236        },
237
238        initFoundation: function () {
239            $(document).foundation(J.config.foundationConfig);
240        },
241
242        registerHandlebarHelpers: function () {
243            Handlebars.registerHelper('translate', function (obj) {
244                var str = arguments[0];
245                return J.translate(str);
246            });
247            Handlebars.registerHelper('config', function (obj) {
248                var str = arguments[0];
249                if (str.indexOf(".") !== -1) {
250                    var strPart = str.split(".");
251                    return J.config[strPart[0]][strPart[1]];
252                } else {
253                    return J.config[str];
254                }
255            });
256            Handlebars.registerHelper('currentCulture', function () {
257                if (J.data.jetshopData) return J.data.culture;
258            });
259            Handlebars.registerHelper('currentLanguage', function () {
260                if (J.data.jetshopData) return J.data.language;
261            });
262            Handlebars.registerHelper('currency', function (value) {
263                if (J.data.jetshopData) {
264                    return value + ' ' + J.data.currency.symbol;
265                }
266                else {
267                    return value;
268                }
269            });
270            Handlebars.registerHelper('isVatIncluded', function () {
271                if (J.data.jetshopData) return J.checker.isVatIncluded
272            });
273            Handlebars.registerHelper('isStage', function () {
274                if (J.data.jetshopData) return J.checker.isStage;
275            });
276            Handlebars.registerHelper('unescape', function (obj) {
277                var doc = new DOMParser().parseFromString(arguments[0], "text/html");
278                return doc.documentElement.textContent;
279            });
280            Handlebars.registerHelper("ifValue", function (conditional, options) {
281                if (conditional == options.hash.equals) {
282                    return options.fn(this);
283                } else {
284                    return options.inverse(this);
285                }
286            });
287            Handlebars.registerHelper("replace", function (str, a, b) {
288                // Example: {{{replace ImageUrl.Url "small" "medium"}}}
289                if (str && typeof str === 'string') {
290                    if (!a || typeof a !== 'string') return str;
291                    if (!b || typeof b !== 'string') b = '';
292                    return str.split(a).join(b);
293                }
294            });
295            Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
296                switch (operator) {
297                    case '==':
298                        return (v1 == v2) ? options.fn(this) : options.inverse(this);
299                    case '===':
300                        return (v1 === v2) ? options.fn(this) : options.inverse(this);
301                    case '!=':
302                        return (v1 != v2) ? options.fn(this) : options.inverse(this);
303                    case '!==':
304                        return (v1 !== v2) ? options.fn(this) : options.inverse(this);
305                    case '<':
306                        return (v1 < v2) ? options.fn(this) : options.inverse(this);
307                    case '<=':
308                        return (v1 <= v2) ? options.fn(this) : options.inverse(this);
309                    case '>':
310                        return (v1 > v2) ? options.fn(this) : options.inverse(this);
311                    case '>=':
312                        return (v1 >= v2) ? options.fn(this) : options.inverse(this);
313                    case '&&':
314                        return (v1 && v2) ? options.fn(this) : options.inverse(this);
315                    case '||':
316                        return (v1 || v2) ? options.fn(this) : options.inverse(this);
317                    default:
318                        return options.inverse(this);
319                }
320            });
321            Handlebars.registerHelper('classFriendly', function(v1) {
322                var str = arguments[0];
323                //Lower case everything
324                str = str.toLowerCase();
325                //Convert whitespaces and underscore to dash
326                str = str.replace(/\s/g, "-");
327                return str;
328            });
329        },
330
331        getManufacturerList: function () {
332            if ($(".manufacturer-selector-wrapper").length > 0) {
333                J.data.manufacturerList = [];
334                $('.manufacturer-selector-body option').each(function () {
335                    var temp = {
336                        id: $(this).val(),
337                        name: $(this).context.innerHTML,
338                        url: "/-m-" + $(this).val() + ".aspx"
339                    };
340                    J.data.manufacturerList.push(temp);
341                });
342                J.data.manufacturerList.splice(0, 1);
343                //$(".manufacturer-selector-wrapper").remove();
344            }
345        },
346
347        startPageObjects: function () {
348            //TODO this code is crap, please rewrite me
349            if ($("#startpage_list").length > 0) {
350                var data = [];
351                var groupedItemList = {};
352                var objects = $('#startpage_list div[class*="item-"]');
353                objects.each(function () {
354                    var classes = $(this).attr("class").toString().split(' ');
355                    var item = classes.filter(function (item) {
356                        return item.indexOf("item-") > -1;
357                    });
358                    var rowNumber = item[0].split("-")[1];
359                    $(this).wrap("<li class='li-group-" + rowNumber + "'></li>");
360                    $(this).addClass("group-" + rowNumber + " startpage-object product-wrapper");
361                    data.push(rowNumber);
362                });
363                for (var x = 0; x < data.length; x++) {
364                    var row = data[x];
365                    if (!groupedItemList[row]) {
366                        groupedItemList[row] = [];
367                    }
368                    groupedItemList[row].push(data[x]);
369                }
370                for (var key in groupedItemList) {
371                    var len = groupedItemList[key].length;
372                    $(".li-group-" + key)
373                        .wrapAll("<ul class='row-line row-line-" + key + " items-" + len + " resetfloat' />")
374                        .matchHeight(J.config.sameHeightConfig);
375                }
376                $('.startpage-product-item .product-name').matchHeight(J.config.sameHeightConfig);
377            }
378        },
379
380        tabSystem: function () {
381            if ($("#ctl00_ctl00_main_rightmain_ctl00_ctl00_tabContent_pnlProductDescription_tab").length > 0) {
382                var descriptionTab = $("#ctl00_ctl00_main_rightmain_ctl00_ctl00_tabContent_pnlProductDescription_tab");
383                var descriptionContent = $("#ctl00_ctl00_main_rightmain_ctl00_ctl00_tabContent_pnlProductDescription");
384                var specificationsTab = $("#__tab_ctl00_ctl00_main_rightmain_ctl00_ctl00_tabContent_pnlProductSpecifications");
385                var relatedTab = $("#__tab_ctl00_ctl00_main_rightmain_ctl00_ctl00_tabContent_pnlRelatedProducts");
386            } else {
387                var descriptionTab = $("#ctl00_main_ctl00_ctl00_tabContent_pnlProductDescription_tab");
388                var descriptionContent = $("#ctl00_main_ctl00_ctl00_tabContent_pnlProductDescription_productDescription");
389                var specificationsTab = $("#ctl00_main_ctl00_ctl00_tabContent_pnlProductSpecifications_tab");
390                var relatedTab = $("#ctl00_main_ctl00_ctl00_tabContent_pnlRelatedProducts_tab");
391            }
392            var specificationsContent = $(".template-wrapper");
393            var relatedContent = $(".relatedTable");
394            var hasDescriptionContent = false;
395            var hasSpecificationsTab = false;
396            var hasRelatedTab = false;
397            $(".product-tabs").after("<div id='tab-wrapper' class='resetfloat'></div>");
398            if (descriptionContent.text().length > 0) {
399                hasDescriptionContent = true;
400            }
401            if (specificationsTab.length > 0) {
402                hasSpecificationsTab = true;
403            }
404            if (relatedTab.length > 0) {
405                hasRelatedTab = true;
406            }
407            function hideTabSystem() {
408                $(".product-tabs").hide();
409                $(".product-toolbar").addClass("no-product-tabs");
410            }
411
412            function renderTab(classes, content, header) {
413                var str = "";
414                str += "<div class='tab-item " + classes + "'>";
415                str += "<h2 class='tab-item-header'>" + header + "</h2>";
416                str += "<div class='tab-item-inner'>";
417                str += "</div>";
418                str += "</div>";
419                $("#tab-wrapper").append(str);
420                $("#tab-wrapper .tab-item:last-child .tab-item-inner").append(content);
421            }
422
423            if (hasDescriptionContent === false && hasSpecificationsTab === false && hasRelatedTab === false) {
424                hideTabSystem();
425            }
426            if (hasDescriptionContent) {
427                renderTab("tab-description", descriptionContent, descriptionTab.text());
428            }
429            if (hasSpecificationsTab) {
430                renderTab("tab-specification template-wrapper", specificationsContent, specificationsTab.text());
431            }
432            if (hasRelatedTab) {
433                renderTab("tab-related", relatedContent, relatedTab.text());
434            }
435            $(".product-tabs").hide();
436        },
437
438        productThumbnails: function () {
439            $("a.ProductThumbnail").each(
440                function () {
441                    $(this).wrap("<li></li>");
442                }
443            );
444            $(".ProductThumbnailsBody li").wrapAll("<ul class='small-block-grid-3 medium-block-grid-4 large-block-grid-5 xlarge-block-grid-6'></ul>");
445        },
446
447        leftCategoryMenu: function () {
448            var categoryNav = $("#category-navigation");
449            $("#left-nav-placeholder").prepend("<nav role='navigation' id='left-cat-nav'><ul class='lv1'>" + categoryNav.html() + "</ul></nav>");
450        },
451
452        pagingControll: function () {
453            if ($(".paging-control-box .showall").length > 0) {
454                $(".paging-control.lower").append("<div id='paging-control-switch'></div>");
455                $("#paging-control-switch").append($(".paging-control-box .showall"));
456                $(".paging-control-box .showall").remove();
457            }
458        },
459
460        jetshopLogo: function () {
461            $(".infoTextLogo img").attr("src", "/M1/stage/images/responsive-base/powered-by-jetshop-lightgrey.png");
462            // Available colors are:
463            // powered-by-jetshop-darkgrey.png, powered-by-jetshop-lightgrey.png, powered-by-jetshop-white.png, powered-by-jetshop-black.png
464        },
465
466        footer: function () {
467            var footerBoxCount = $(".footer-box").length;
468            $("#footer").addClass("footer-box-count-" + footerBoxCount);
469        },
470
471        removeEmptyCurrencySelector: function () {
472            var currencySelector = $("#footer").find(".currency-selector-wrapper");
473            if (currencySelector.length) {
474                if ($(".currency-selector-item").length) {
475                    currencySelector.addClass("currency-selector-radios");
476                } else {
477                    currencySelector.addClass("currency-selector-dropdown");
478                    var currencySelectorOptions = currencySelector.find("option");
479                    if (currencySelectorOptions.length < 2) {
480                        currencySelector.remove()
481                    }
482                }
483            }
484        },
485
486        removeEmptyCultureSelector: function () {
487            var cultureSelector = $("#footer").find(".culture-selector-wrapper");
488            if (cultureSelector.length) {
489                if (cultureSelector.find(".culture-selector-clickable").length) {
490                    // Type: Flags
491                    if (cultureSelector.find("input").length < 2) {
492                        cultureSelector.remove();
493                    }
494                } else if (cultureSelector.find("option").length) {
495                    // Type: DropDown
496                    cultureSelector.addClass("culture-selector-dropdown");
497                    if (cultureSelector.find("option").length < 2) {
498                        cultureSelector.remove();
499                    }
500                } else {
501                    // Type: Text
502                    if (cultureSelector.find("a").length < 2) {
503                        cultureSelector.remove();
504                    }
505                }
506            }
507        },
508
509        removeEmptyAttributeWrappers: function () {
510            $(".product-attributes > div").each(function () {
511                if (!$(this).find("select").length) {
512                    $(this).remove();
513                }
514            });
515        },
516
517        removeEmptySpecificationWrapper: function () {
518            var specificationWrapper = $(".product-specification-wrapper");
519            if (!specificationWrapper.find("select").length) {
520                specificationWrapper.remove();
521            }
522        },
523
524        categoryWrapper: function () {
525            $('.product-name').matchHeight(J.config.sameHeightConfig);
526            $('.product-list-description').matchHeight(J.config.sameHeightConfig);
527        },
528
529        categoryDescription: function () {
530            var categoryDescElement = $(".category-header-wrapper");
531            var pagingTopElement = $(".paging-control-box");
532            var currentPageNum = getUrlParameters().pagenum;
533            if (currentPageNum > 1) {
534                categoryDescElement.hide();
535            } else {
536                categoryDescElement.insertBefore(pagingTopElement);
537            }
538        },
539
540        orderConfirm: function () {
541            if (typeof Checkout == "undefined") {
542                // Not a responsive checkout so we need to alter the DOM
543                // NOTE: This function will soon be deprecated
544                $(".PlacedOrderDetailsFooter").append("<div id='order-details-placeholder'></div>");
545                var confirmation = {};
546                var str = "";
547                var text = $("td.PlacedOrderDetailsShipping").html();
548                $("#order-details-placeholder").append(text);
549                $("td.PlacedOrderDetailsShipping").remove();
550                confirmation.header = $(".PlacedOrderDetailsHeaderCell1").html();
551                confirmation.number = $(".PlacedOrderDetailsHeaderCell2:eq(0)").html();
552                confirmation.date = $(".PlacedOrderDetailsHeaderCell2:eq(1)").html();
553                str += "<div id='order-confirmation-header'>";
554                str += confirmation.number;
555                str += "<br />";
556                str += confirmation.date;
557                str += "</div>";
558                $(".fastcartconfirmationpagetitle").before(confirmation.header);
559                $(".fastcartconfirmationpagetitle").after(str);
560                $(".PlacedOrderDetailsHeader table tr:eq(0)").remove();
561            }
562        },
563
564        stockNotification: function () {
565            // Load correct CSS in stock notification popup
566            $('.fancybox-iframe-stock-notification').fancybox({
567                type: 'iframe',
568                autoDimensions: true,
569                autoScale: true,
570                afterLoad: function () {
571                    $("iframe.fancybox-iframe").contents().find("head").append('<link href="' + J.config.cssFallbackUrl + '" rel="stylesheet" type="text/css" />');
572                }
573            });
574        },
575
576        initCart: function () {
577            J.cart = {
578                cartId: getCookie("JetShop_CartID")
579            };
580        },
581
582        updateCartData: function (xhr, cartApiData) {
583            if (xhr) {
584                // ADD DATA ONLY REACHABLE FROM REGULAR CART UPDATE (FREE SHIPPING TEXT)
585                var xmlDOM = (new DOMParser()).parseFromString(xhr.responseText, 'text/xml');
586                var temp = $(xmlDOM.getElementsByTagName("DivContents")[0].childNodes[1]).text();
587                var tempObj = $(temp).text();
588                if (tempObj.length > 0) {
589                    J.cart.freeFreightMessage = $(temp).text();
590                } else {
591                    J.cart.freeFreightMessage = false;
592                }
593            }
594            if (cartApiData) {
595                // DATA ALREADY EXISTS - UPDATE CART OBJECT
596                $.each(cartApiData, function (propName, propValue) {
597                    J.cart[propName] = propValue;
598                });
599                // Trigger event for modules
600                $(window).triggerHandler('cart-updated');
601            } else {
602                // NO EXISTING DATA - GET FROM API
603                var restUrl = J.config.urls.ServicesUrl + "/rest/v2/json/" + JetshopData.Culture + "/" + JetshopData.Currency + "/shoppingcart/" + J.cart.cartId;
604                if (J.checker.isLoggedIn) {
605                    restUrl += "?pricelistid=" + JetshopData.PriceListId;
606                }
607                $.ajax({
608                    type: "GET",
609                    cache: false,
610                    url: restUrl,
611                    success: function (data) {
612                        var json = typeof data == 'object' ? data : $.parseJSON('(' + data + ')');
613                        cartApiData = json[0];
614                        // SEND RECEIVED DATA BACK INTO DATA UPDATE FUNCTION
615                        J.components.updateCartData(false, cartApiData);
616                    },
617                    error: function (request, status, error) {
618                        console.error("Cart data fetch error:");
619                        console.log(error);
620                    }
621                });
622                $(window).triggerHandler('cart-loaded');
623            }
624        },
625
626        addItemToCart: function (cartItem, callback) {
627            // Example cartItem object
628            // var cartItem = {
629            //     "ProductId": 329,
630            //     "AttributeId": 0,
631            //     "Quantity": 1
632            // };
633            var product_json = JSON.stringify(cartItem);
634            var restUrl = J.config.urls.ServicesUrl + "/rest/v2/json/shoppingcart/" + J.cart.cartId;
635            if (J.checker.isLoggedIn) {
636                restUrl += "?pricelistid=" + JetshopData.PriceListId;
637            }
638            $.ajax({
639                type: "POST",
640                cache: false,
641                url: restUrl,
642                data: product_json,
643                dataType: "json",
644                contentType: "application/json",
645                success: function (data) {
646                    Services.cartService.reload();
647                    if (callback)
648                        callback(data);
649                },
650                error: function (jqXHR, textStatus, errorThrown) {
651                    console.error('Ajax Error in J.components.addItemToCart:');
652                    console.log(jqXHR);
653                    console.log(textStatus);
654                    console.log(errorThrown);
655                }
656            });
657        },
658
659        deleteFromCart: function (recId) {
660            var restUrl = J.config.urls.ServicesUrl + "/rest/v2/json/" + J.data.culture + "/" + J.data.currency.name + "/shoppingcart/delete/" + J.cart.cartId;
661            if (JetshopData.IsLoggedIn) {
662                restUrl += "?pricelistid=" + J.data.priceListId;
663            }
664            $.ajax({
665                type: "POST",
666                cache: false,
667                url: restUrl,
668                data: '{ "RecId": ' + recId + '}',
669                dataType: 'json',
670                contentType: 'application/json',
671                success: function (data) {
672                    var json = typeof data == 'object' ? data : $.parseJSON('(' + data + ')');
673                    var cartApiData = json[0];
674                    J.components.updateCartData(false, cartApiData);
675                    Services.cartService.reload();
676                    if (J.checker.isCheckoutPage) {
677                        location.reload();
678                    }
679                },
680                error: function (jqXHR, textStatus, errorThrown) {
681                    console.error('Ajax Error in J.components.deleteFromCart:');
682                    console.log(jqXHR);
683                    console.log(textStatus);
684                    console.log(errorThrown);
685                }
686            });
687        },
688
689        emptyCart: function () {
690            if (J.cart.ProductsCart.length > 0) {
691                for (var item in J.cart.ProductsCart) {
692                    J.components.deleteFromCart(J.cart.ProductsCart[item]["RecId"]);
693                }
694            }
695        }
696
697    };
698    this.pages = {
699        allPages: function () {
700            J.components.initCart();
701            J.api.helpers.initCache();
702            // Product page
703            if (J.checker.isProductPage) {
704                addBodyClass("product-page-id-" + J.data.productId);
705            }
706            // Category pages
707            if (J.checker.isCategoryPage || J.checker.isCategoryAdvancedPage) {
708                addBodyClass("category-page-id-" + J.data.categoryId);
709            }
710        },
711        queues: {
712            "all-pages": [],
713            "start-page": [],
714            "product-page": [],
715            "category-advanced-page": [],
716            "category-page": [],
717            "orderconfirmation-page": [],
718            "manufacturer-advanced-page": [],
719            "manufacturer-page": [],
720            "news-page": [],
721            "checkout-page": [],
722            "searchresult-page": [],
723            "my-page": [],
724            "changepassword-page": [],
725            "standard-page": [],
726            "sitemap-page": []
727        },
728        addToQueue: function (pageType, queueItem) {
729            if (typeof J.pages.queues[pageType] != "undefined") {
730                J.pages.queues[pageType].push(queueItem);
731            } else {
732                console.log("Error: Queue-type not found: " + pageType);
733            }
734        },
735        init: function () {
736            J.pages.allPages();
737            executeQueue(J.pages.queues["all-pages"]);
738            if (typeof J.pages.queues[J.pageType] != "undefined") {
739                executeQueue(J.pages.queues[J.pageType]);
740            } else {
741                console.log("Error: Queue-type not found: " + J.pageType);
742            }
743        }
744    };
745
746    this.api = {
747        helpers: {
748            baseUrl: function (includeCulture, includeCurrency) {
749                var portNumber = "";
750                if (window.location.port.length) {
751                    portNumber = ":" + window.location.port;
752                }
753                var url = window.location.protocol + "//" + window.location.hostname + portNumber + J.config.urls.ServicesUrl + "/Rest/v2/json/";
754                if (includeCulture) url += J.data.culture + "/";
755                if (includeCurrency) url += J.data.currency.name + "/";
756                return url;
757            },
758            //TODO when adding cart-services we need to add method postAjax
759            getAjax: function (service, callback, callbackOptions, enableCache, cacheMinutes, includeCulture, includeCurrency) {
760                //console.info("Actually fetching AJAX: " + service);
761                $.ajax({
762                    type: "GET",
763                    url: J.api.helpers.baseUrl(includeCulture, includeCurrency) + service,
764                    success: function (data, textStatus, jqXHR) {
765                        if (enableCache) {
766                            J.api.helpers.save(J.api.helpers.baseUrl(includeCulture, includeCurrency) + service, cacheMinutes, data);
767                        }
768                        if (typeof callback == "function") {
769                            callback(data, callbackOptions);
770                        } else if (typeof callback.success == "function") {
771                            callback.success(data, callbackOptions, textStatus, jqXHR);
772                        }
773                    },
774                    error: function (xhr, ajaxOptions, thrownError) {
775                        var error = {"error": "Ajax fail", "service": service};
776                        J.api.helpers.raiseError(error);
777                        if (typeof callback.error == "function") {
778                            callback.error(callbackOptions, xhr, ajaxOptions, thrownError);
779                        }
780                    },
781                    complete: function (event, xhr) {
782                        if (typeof callback.complete == "function") {
783                            var data = JSON.parse(event.responseText);
784                            callback.complete(data, callbackOptions, event, xhr);
785                        }
786                    }
787                });
788            },
789            getter: function (service, callback, callbackOptions, enableCache, cacheMinutes, includeCulture, includeCurrency) {
790                if (typeof callback === "undefined") {
791                    var error = {"error": "No callback", "service": service};
792                    J.api.helpers.raiseError(error);
793                } else {
794                    if (typeof callback.before == "function") {
795                        callback.before(callbackOptions);
796                    }
797                    if (enableCache) {
798                        var itemId = J.api.helpers.baseUrl(includeCulture, includeCurrency) + service;
799                        if (J.api.helpers.read(itemId, cacheMinutes)) {
800                            var cachedItem = J.api.helpers.read(itemId, cacheMinutes);
801                            var minutes = Math.floor(Math.abs(cachedItem.timeStamp - Date.now()) / 60000);
802                            if (minutes < cacheMinutes) {
803                                var jqXHR = cachedItem.data;
804                                var textStatus = "success";
805                                if (typeof callback == "function") {
806                                    callback(cachedItem.data, callbackOptions);
807                                } else if (typeof callback.success == "function") {
808                                    callback.success(cachedItem.data, callbackOptions, textStatus, jqXHR);
809                                }
810                                if (typeof callback.complete == "function") {
811                                    callback.complete(cachedItem.data, callbackOptions, textStatus);
812                                }
813                            } else {
814                                J.api.helpers.getAjax(service, callback, callbackOptions, enableCache, cacheMinutes, includeCulture, includeCurrency);
815                            }
816                        } else {
817                            J.api.helpers.getAjax(service, callback, callbackOptions, enableCache, cacheMinutes, includeCulture, includeCurrency);
818                        }
819                    } else {
820                        J.api.helpers.getAjax(service, callback, callbackOptions, enableCache, cacheMinutes, includeCulture, includeCurrency);
821                    }
822                }
823            },
824            read: function (id, cacheMinutes) {
825                var item = localStorage.getItem(J.config.apiCachePrefix + id);
826                if (item === "") {
827                    return false;
828                } else {
829                    return JSON.parse(item);
830                }
831            },
832            save: function (id, cacheMinutes, data) {
833                //log("Caching item: " + id);
834                var storage = isLocalStorageNameSupported();
835                if (storage) {
836                    var timeStamp = Date.now();
837                    var item = {"cacheMinutes": cacheMinutes, "timeStamp": timeStamp, "data": data};
838                    try {
839                        localStorage.setItem(J.config.apiCachePrefix + id, JSON.stringify(item));
840                    } catch (e) {
841                        if (J.api.helpers.isQuotaExceeded(e)) {
842                            console.log("Error storing local storage");
843                        }
844                    }
845                }
846            },
847            clearCache: function () {
848                for (var key in window.localStorage) {
849                    if (key.indexOf(J.config.apiCachePrefix) != -1) {
850                        localStorage.removeItem(key);
851                    }
852                }
853                console.log("Cleared cache.");
854            },
855            clearOutdatedCache: function () {
856                var seconds = 60;
857                var milliseconds = 1000;
858                var now = Date.now();
859                for (var key in window.localStorage) {
860                    if (key.indexOf(J.config.apiCachePrefix) != -1) {
861                        var item = window.localStorage[key];
862                        var localStoragePostTimestamp = JSON.parse(item).timeStamp;
863                        var localStoragePostMinutes = JSON.parse(item).cacheMinutes;
864                        var timestampWithMinutes = localStoragePostTimestamp + (localStoragePostMinutes * seconds * milliseconds)
865
866                        if (timestampWithMinutes < now) {
867                            delete localStorage[key];
868                        }
869                    }
870                }
871            },
872            initCache: function () {
873                if ($(".vat-selector-input input").length > 0) {
874                    var vatInputElementId = $(".vat-selector-input input").attr("id");
875                    var vatInputElement = document.getElementById(vatInputElementId);
876                    vatInputElement.onclick = function () {
877                        J.api.helpers.clearCache();
878                        setTimeout('__doPostBack(\'' + vatInputElementId + '\',\'\')', 0);
879                    }
880                }
881            },
882            raiseError: function (error) {
883                console.error(error);
884            },
885            isQuotaExceeded: function (error) {
886                var quotaExceeded = false;
887                if (error) {
888                    if (error.code) {
889                        switch (error.code) {
890                            case 22:
891                                quotaExceeded = true;
892                                break;
893                            case 1014:
894                                // Firefox
895                                if (error.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
896                                    quotaExceeded = true;
897                                }
898                                break;
899                        }
900                    } else if (error.number === -2147024882) {
901                        // Internet Explorer 8
902                        quotaExceeded = true;
903                    }
904                }
905                return quotaExceeded;
906            },
907            createObject: function (data) {
908                return {
909                    TotalProducts: data.length,
910                    TotalPages: 1,
911                    ProductsInPage: data.length,
912                    ProductItems: data,
913                    PageSize: 0
914                };
915            }
916        },
917        product: {
918            get: function (callback, callbackOptions, enableCache, cacheMinutes, id) {
919                if (typeof id === "number") {
920                    var service = "products/" + id;
921                    J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes);
922                } else {
923                    var list = id;
924                    var products = [];
925                    var add = function (data) {
926                        products.push(data.ProductItems[0]);
927                        if (list.length == products.length) {
928                            var obj = J.api.helpers.createObject(products);
929                            callback(obj, callbackOptions);
930                        }
931                    };
932                    for (var i = 0; i < list.length; i++) {
933                        J.api.product.get(add, i, enableCache, cacheMinutes, list[i]);
934                    }
935                }
936            },
937            getRelatedProducts: function (callback, callbackOptions, enableCache, cacheMinutes, id) {
938                var products = [];
939                var total = 0;
940                var get = function (productData, id) {
941                    var list = productData.ProductItems[0].RelatedProducts;
942                    total = list.length;
943                    for (var i = 0; i < list.length; i++) {
944                        J.api.product.get(add, null, enableCache, cacheMinutes, list[i]);
945                    }
946                };
947                var add = function (data) {
948                    products.push(data.ProductItems[0]);
949                    if (total == products.length) {
950                        callback(products, callbackOptions);
951                    }
952                };
953                J.api.product.get(get, id, enableCache, cacheMinutes, id);
954            }
955        },
956        category: {
957            get: function (callback, callbackOptions, enableCache, cacheMinutes, id, displayTypeWeb) {
958                var service = "categories/" + id;
959                if (displayTypeWeb) service += "?displayType=web";
960                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, false);
961            },
962            getProducts: function (callback, callbackOptions, enableCache, cacheMinutes, id, full, rows, page) {
963                var service = "categories/" + id + "/products";
964                if (full) service += "/full";
965                if (rows) service += "/" + rows;
966                if (page) service += "/" + page;
967                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, true);
968            },
969            list: function (callback, callbackOptions, enableCache, cacheMinutes) {
970                var service = "categories";
971                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, false);
972            }
973        },
974        page: {
975            get: function (callback, callbackOptions, enableCache, cacheMinutes, id) {
976                var service = "pages/" + id;
977                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, false);
978            },
979            list: function (callback, callbackOptions, enableCache, cacheMinutes) {
980                var service = "pages";
981                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, false);
982            }
983        },
984        search: function (callback, callbackOptions, enableCache, cacheMinutes, str
984ing, full, rows, page) {
985            var service = "search/";
986            if (full) {
987                service += "full/" + string;
988            } else {
989                service += string;
990            }
991            if (rows) service += "/" + rows;
992            if (page) service += "/" + page;
993            J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, false);
994        },
995        settings: {
996            get: function (callback, callbackOptions, enableCache, cacheMinutes) {
997                var service = "settings";
998                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, false, false);
999            }
1000        },
1001        startpage: {
1002            get: function (callback, callbackOptions, enableCache, cacheMinutes, id) {
1003                var service = "customstartpage/" + id;
1004                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, true, false);
1005            },
1006            list: function (callback, callbackOptions, enableCache, cacheMinutes) {
1007                var service = "customstartpages";
1008                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, false, false);
1009            },
1010            active: function (callback, callbackOptions, enableCache, cacheMinutes) {
1011                var service = "activestartpage";
1012                J.api.helpers.getter(service, callback, callbackOptions, enableCache, cacheMinutes, false, false);
1013            }
1014        },
1015        // TODO: Incomplete function
1016        filter: {
1017            get: function(postData, callback, callbackOptions, categoryId) {
1018                var service = "filters/"+ categoryId;
1019                // TODO: Create a popster method
1020                // J.api.helpers.poster(service, postData, callback, callbackOptions, categoryId);
1021            }
1022        }
1023    };
1024
1025    ////////////////////////////////////////////////////////////////////////////////////////////////////
1026    //
1027    //  Method calls, init and setup
1028    //
1029    if (typeof JetshopData !== "undefined") {
1030        this.data.channelInfo = JetshopData.ChannelInfo;
1031        this.data.language = JetshopData.Language;
1032        this.data.culture = JetshopData.Culture;
1033        this.data.priceList = JetshopData.PriceList;
1034        this.data.priceListId = JetshopData.PriceListId;
1035        this.data.categoryId = JetshopData.CategoryId;
1036        this.data.productId = JetshopData.ProductId;
1037        this.checker.isStage = JetshopData.IsStage;
1038        this.checker.isLoggedIn = JetshopData.IsLoggedIn;
1039        this.checker.isVatIncluded = JetshopData.VatIncluded;
1040        this.checker.isCheckoutHttps = JetshopData.IsCheckoutHttps;
1041        this.data.currency = {
1042            name: JetshopData.Currency,
1043            separator: JetshopData.CurrencyDecimalSeparator,
1044            display: JetshopData.CurrencyDisplay,
1045            symbol: JetshopData.CurrencySymbol
1046        };
1047        this.data.jetshopData = true;
1048        if (typeof JetshopData.Urls !== "undefined") {
1049            this.config.urls = JetshopData.Urls;
1050        }
1051    } else {
1052        J.data.channelInfo = {
1053            Active: 1,
1054            Data: {
1055                1: {
1056                    Cultures: [
1057                        "da-DK",
1058                        "de-DE",
1059                        "en-GB",
1060                        "fi-FI",
1061                        "nb-NO",
1062                        "sv-SE"
1063                    ],
1064                    Currencies: [
1065                        "SEK"
1066                    ],
1067                    DefaultCulture: "sv-SE",
1068                    DefaultCurrency: "SEK",
1069                    Name: "SV"
1070                }
1071            },
1072            Total: 1
1073        };
1074        J.data.language = "en";
1075        J.data.categoryId = 0;
1076        J.data.productId = 0;
1077    }
1078    this.pageType = this.setPageType();
1079    //
1080    //  Initialize
1081    //
1082    this.init = function () {
1083        addBodyClass(this.pageType);
1084        J.api.helpers.clearOutdatedCache();
1085        J.components.initFoundation();
1086        J.components.registerHandlebarHelpers();
1087        J.components.browserDetect();
1088        J.pages.init();
1089        J.switch.check();
1090        J.deviceWidth = J.switch.width;
1091        $(document).foundation('reflow');
1092
1093        // Bind detection of cart update
1094        $(document).ajaxComplete(function (event, xhr, settings) {
1095            var data = {};
1096            if (settings.url === J.config.urls.CartServiceUrl && xhr.readyState == 4 && xhr.status == 200) {
1097                $(window).triggerHandler('cart-load');
1098                J.components.updateCartData(xhr, false);
1099            }
1100        });
1101
1102        //$(window).trigger('resize');
1103
1104        // Prevent J.init function from firing twice
1105        J.init = function () {
1106            console.error("J.init() triggered twice. Check base.master & script management!");
1107        };
1108    };
1109};
1110
1111// On resize
1112$(window).resize(function () {
1113    J.switch.check();
1114    J.deviceWidth = J.switch.width;
1115});
1116
1117$(document).ready(function () {
1118    J.init();
1119});

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.