1var productListData = {}; 2var productListDataRaw = {}; 3var productList__pprLoaded = false; 4var productSingleData = {}; 5var checkoutPaymentResponseData = {}; 6var productInventory = bjorkInventory(); 7 8var WESTORE = { 9 ROUTE: {}, 10 CART: {}, 11 CHECKOUT: {}, 12 PRODUCT_LIST: {}, 13 PRODUCT_SINGLE: {}, 14 CONFIG: { 15 targetElement: '#store-container', 16 targetElementCart: '#store-container-cart', 17 targetWidgetIcons: '', 18 widget_icons_top: 20, 19 widget_icons_right: 20, 20 widget_icons_expand: true, 21 store_load_method: 'CMS', 22 country: 'SE', 23 url_root: '', 24 url_page_checkout: '', 25 url_page_checkout_confirmation: '', 26 url_page_search: '', 27 url_page_product: '', 28 url_cms_root: '', 29 url_script_product_list: '', 30 url_script_product_single: '', 31 url_script_product_single_quantity: '', 32 url_product_images: '', 33 url_domain_api: 'https://api.epage.se', 34 label_search: 'Search', 35 clog_active: false, 36 }, 37 init: function(data) { 38 39 if(data.CONFIG.targetElement) { this.CONFIG.targetElement = data.CONFIG.targetElement; } 40 if(data.CONFIG.targetElementCart) { this.CONFIG.targetElementCart = data.CONFIG.targetElementCart; } 41 if(data.CONFIG.targetWidgetIcons) { this.CONFIG.targetWidgetIcons = data.CONFIG.targetWidgetIcons; } 42 if(data.CONFIG.widget_icons_top) { this.CONFIG.widget_icons_top = data.CONFIG.widget_icons_top; } 43 if(data.CONFIG.widget_icons_right) { this.CONFIG.widget_icons_right = data.CONFIG.widget_icons_right; } 44 if(data.CONFIG.widget_icons_expand) { this.CONFIG.widget_icons_expand = data.CONFIG.widget_icons_expand; } 45 if(data.CONFIG.store_load_method) { this.CONFIG.store_load_method = data.CONFIG.store_load_method; } 46 if(data.CONFIG.country) { this.CONFIG.country = data.CONFIG.country; } 47 if(data.CONFIG.url_root) { this.CONFIG.url_root = data.CONFIG.url_root; } 48 if(data.CONFIG.url_page_checkout) { this.CONFIG.url_page_checkout = data.CONFIG.url_page_checkout; } 49 if(data.CONFIG.url_page_checkout_confirmation) { this.CONFIG.url_page_checkout_confirmation = data.CONFIG.url_page_checkout_confirmation; } 50 if(data.CONFIG.url_page_search) { this.CONFIG.url_page_search = data.CONFIG.url_page_search; } 51 if(data.CONFIG.url_page_product) { this.CONFIG.url_page_product = data.CONFIG.url_page_product; } 52 if(data.CONFIG.url_cms_root) { this.CONFIG.url_cms_root = data.CONFIG.url_cms_root; } 53 if(data.CONFIG.url_script_product_list) { this.CONFIG.url_script_product_list = data.CONFIG.url_script_product_list; } 54 if(data.CONFIG.url_script_product_single) { this.CONFIG.url_script_product_single = data.CONFIG.url_script_product_single; } 55 if(data.CONFIG.url_script_product_single_quantity) { this.CONFIG.url_script_product_single_quantity = data.CONFIG.url_script_product_single_quantity; } 56 if(data.CONFIG.url_product_images) { this.CONFIG.url_product_images = data.CONFIG.url_product_images; } 57 if(data.CONFIG.url_domain_api) { this.CONFIG.url_domain_api = data.CONFIG.url_domain_api; } 58 if(data.CONFIG.label_search) { this.CONFIG.label_search = data.CONFIG.label_search; } 59 if(data.CONFIG.clog_active) { this.CONFIG.clog_active = data.CONFIG.clog_active; } 60 61 this.ROUTE.init(data.ROUTE); 62 this.CART.init(data.CART); 63 this.CHECKOUT.init(data.CHECKOUT); 64 this.PRODUCT_LIST.init(data.PRODUCT_LIST); 65 this.PRODUCT_SINGLE.init(data.PRODUCT_SINGLE); 66 67 $(window).load(function() { 68 WESTORE.jQueryActions(); 69 }); 70 71 }, 72 clog: function (output) { 73 if(this.CONFIG.clog_active) { console.log(output); } 74 }, 75 clog_time_start: function (output) { 76 if(this.CONFIG.clog_active) { console.time(output); } 77 }, 78 clog_time_end: function (output) { 79 if(this.CONFIG.clog_active) { console.timeEnd(output); } 80 }, 81 get_display_price: function(price) { 82 83 // Cut down to 2 decimals 84 if(!Number.isInteger(price)) { 85 price = Math.round(price * 100) / 100; 86 price = price.toFixed(2); 87 } 88 89 // Adds commas 90 var display_price = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'SEK' }).format(price); 91 display_price = display_price.replaceAll(".00", ""); // Remove trailing .00 92 display_price = display_price.replace(/\xA0/g, ""); // Removes 93 display_price = display_price.replace("SEK", ""); 94 display_price = display_price.replace(",", " "); 95 96 // Adds currency 97 if(this.CONFIG.country == "US") { display_price = `$${display_price}`; } 98 else { display_price = `${display_price} kr`; } 99
100 return display_price; 101 102 }, 103 widgetIcons__render: function() { 104 105 var $widgetContainer = $(WESTORE.CONFIG.targetWidgetIcons); 106 var html = ""; 107 var cartHTML = ""; 108 var searchHTML = ""; 109 var searchExpandClass = ""; 110 111 if(WESTORE.CONFIG.widget_icons_expand) { searchExpandClass = " expandable collapsed"; } 112 113 // Search html 114 var searchHTML = ` 115 <div class="store-widget-item store-widget-item-search"> 116 <div class="store-search-text${searchExpandClass}"> 117 <input type="text" class="storeWidgetTarget-searchInput" placeholder="${WESTORE.CONFIG.label_search}"></input> 118 </div> 119 <button class="store-widget-button store-search-button storeWidgetTarget-searchButton" type="submit"> 120 <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="24" height="24"> 121 <g fill="none" stroke="currentColor" stroke-width="2.1"> 122 <circle cx="10" cy="10" r="8"></circle> 123 <line y2="23" x2="23" y1="16" x1="16" ></line> 124 </g> 125 </svg> 126 </button> 127 </div> 128 `; 129 130 // Check if Cart icon already exists, then add it 131 if(WESTORE.CART.cart_active && !$(".cart-icon").length) { 132 cartHTML = ` 133 <div class="store-widget-item store-widget-item-cart"> 134 <div class="store-widget-button store-cart-button storeWidgetTarget-cartButton"> 135 <div class="store-cart-button-counter storeWidgetTarget-cartButtonCounter hide"></div> 136 <svg class="store-cart-button-svg" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="24px" height="24px" preserveAspectRatio="xMidYMid meet" viewBox="2 2 20 20"> 137 <g fill="none" stroke="currentColor" stroke-width="1.5"> 138 <path d="M3 3h2l.5 3m0 0L7 15h11l3-9H5.5z"></path> 139 <circle cx="8" cy="20" r="1"></circle><circle cx="17" cy="20" r="1"></circle> 140 </g> 141 </svg> 142 </div> 143 </div> 144 `; 145 } 146 147 html = `${searchHTML}${cartHTML}`; 148 149 if($widgetContainer.length) { 150 html = `<div id="store-widget-icons">${html}</div>`; 151 $widgetContainer.html(html); 152 } 153 else { 154 html = `<div id="store-widget-icons" style="position: fixed; z-index: 9998; top: ${WESTORE.CONFIG.widget_icons_top}px; right: ${WESTORE.CONFIG.widget_icons_right}px">${html}</div>`; 155 $('body').prepend(html); 156 } 157 158 }, 159 widgetIcons__search_expand: function() { 160 161 var $text = $("#store-widget-icons .store-widget-item-search .store-search-text"); 162 163 if(WESTORE.CONFIG.widget_icons_expand && $text.hasClass("collapsed")) { 164 165 $text.find('input').focus(); 166 167 $text.stop().animate({ 168 "opacity": 1, 169 "width": "240px", 170 "padding-left": "24px", 171 "padding-right": "4px" 172 }, 200, function() { 173 $text.addClass("expanded"); 174 }); 175 176 } 177 178 }, 179 widgetIcons__search_collapse: function() { 180 181 var $text = $("#store-widget-icons .store-widget-item-search .store-search-text"); 182 183 if(WESTORE.CONFIG.widget_icons_expand && $text.hasClass("expanded")) { 184 185 $text.stop().animate({ 186 "opacity": 0, 187 "width": "0px", 188 "padding-left": "0px", 189 "padding-right": "0px" 190 }, 200, function() { 191 $text.find('input').val('').blur(); 192 $text.addClass("collapsed"); 193 }); 194 195 } 196 197 }, 198 jQueryActions: function() { 199 200 // Cart button clicked 201 $("body").on("click", ".storeWidgetTarget-cartButton", function(e) { 202 $("#cart").css('display', 'flex'); 203 }); 204 205 // Search open 206 $("body").on("mouseenter", "#store-widget-icons .store-widget-item-search", function(e) { 207 WESTORE.widgetIcons__search_expand(); 208 }); 209 210 // Search open 211 $("body").on("mouseleave", "#store-widget-icons .store-widget-item-search", function(e) { 212 WESTORE.widgetIcons__search_collapse(); 213 }); 214 215 // Search button clicked 216 $("body").on("click", ".storeWidgetTarget-searchButton", function(e) { 217 var $searchInput = $(this).parents('.storeWidgetTarget-search').find('storeWidgetTarget-searchInput'); 218 if(!$searchInput.length) { $searchInput = $('input.storeWidgetTarget-searchInput'); } 219 var search_raw = $searchInput.val(); 220 var search = encodeURIComponent(search_raw); 221 if(search) { 222 223 var url = WESTORE.CONFIG.url_root+WESTORE.CONFIG.url_page_search+'?search='+search; 224 225 WESTORE.ROUTE.loadPageContent(url); 226 227 if(WESTORE.CONFIG.widget_icons_expand) { WESTORE.widgetIcons__search_collapse(); } 228 else { $("input.storeWidgetTarget-searchInput").val("").blur(); } 229 230 } 231 else { 232 $("input.storeWidgetTarget-searchInput").focus(); 233 } 234 }); 235 236 // Search enter 237 $('body').on('keydown', 'input.storeWidgetTarget-searchInput', function(e) { 238 if ( e.keyCode == 13 ) { 239 var search_raw = $("input.storeWidgetTarget-searchInput").val(); 240 var search = encodeURIComponent(search_raw); 241 if(search) { 242 243 var url = WESTORE.CONFIG.url_root+WESTORE.CONFIG.url_page_search+'?search='+search; 244 WESTORE.ROUTE.loadPageContent(url); 245 if(WESTORE.CONFIG.widget_icons_expand) { WESTORE.widgetIcons__search_collapse(); } 246 else { $("input.storeWidgetTarget-searchInput").val("").blur(); } 247 248 } 249 else { 250 $("input.storeWidgetTarget-searchInput").focus(); 251 } 252 } 253 }); 254 255 }, 256 257}; 258var contentLoadLastURL = ''; 259 260WESTORE.ROUTE = { 261 pages: { 262 index_status_default: 'index', 263 pages: {} 264 265 }, 266 init: function(data) { 267 268 if(data.index_status_default) { this.index_status_default = data.index_status_default; } 269 if(data.pages) { this.pages = data.pages; } 270 271 $(function() { 272
273 if(WESTORE.CONFIG.store_load_method == "LIVE_EXTERNAL") { 274 // CONTENT LOAD - When browser history changes (Back, Forward, etc). Is set by window.history.pushState() 275 window.onpopstate = function(event) { 276 277 var stateObj = event.state; // Passed object from replaceState() or pushState() 278 279 // Trigger windows reload when popstate comes from epLoadType. Ignore other popstates, like anchors 280 if(stateObj && typeof stateObj === "object") { 281 if(stateObj.hasOwnProperty('epLoadType')) { 282 if(stateObj.epLoadType == "normal" || stateObj.epLoadType == "content") { 283 window.location.href = document.location; 284 } 285 } 286 } 287 }; 288 289 // CONTENT LOAD - For pages that was normally loaded, replaceState to add "epLoadType = normal" to the stateObj 290 window.history.replaceState({'epLoadType': 'normal'}, document.title, ''); 291 292 } 293 294 $(function() { 295 WESTORE.ROUTE.jQueryActions(); 296 }); 297 298 299 }); 300 301 $(window).load(function() { 302 303 var product_page_set_lists = {'all': productListPageSets['all']}; 304 WESTORE.PRODUCT_LIST.load_file_lists(product_page_set_lists, function(status){ 305 306 if(status == "success") { 307 WESTORE.widgetIcons__render(); 308 WESTORE.CART.load_from_localStorage(); 309 //WESTORE.CART.create_cart_from_url("eyIxMDMwIjoiNSIsIjEwMzEiOiI1IiwiMTAzOSI6IjUiLCIxMDQzIjo1fQ"); 310 } 311 else { 312 console.log("Product list(all) could not be loaded. Timeout.") 313 } 314 315 }); 316 }); 317 318 }, 319 // Route data 320 routeData: function(urlBrowser) { 321 322 var pages = this.pages; 323 var pagetype = ''; 324 var pageID = 0; 325 var name = ''; 326 var title = ''; 327 var searchwords = ''; 328 var description = ''; 329 var index_status = this.index_status_default; 330 var product_urlID = ''; 331 332 // Get urlID 333 var urlPagePrefix = WESTORE.CONFIG.url_root; // Ex: "/" or "/sitename/" or "https://www.somedomain.com/" 334 var urlID = urlBrowser; 335 if(urlID.indexOf(urlPagePrefix) === 0) { urlID = urlID.substring(urlPagePrefix.length); } // Left trim urlPagePrefix 336 urlID = urlID.split('?')[0]; // Remove parameters 337 338 // Product page (urlID = "product/...") 339 if(urlID.indexOf(WESTORE.CONFIG.url_page_product) === 0) { 340 341 // This page data will be updated after the product is loaded. 342 // For SEO/Google this is ok. metas that are updated with Javascript will be registrated. 343 var pagename_tmp = "Produkt"; 344 345 pagetype = "STORE_PRODUCT_SINGLE"; 346 if(WESTORE.CONFIG.country == "US") { pagename_tmp = "Product"; } 347 name = pagename_tmp; 348 title = pagename_tmp; 349 searchwords = pagename_tmp; 350 description = pagename_tmp; 351 index_status = this.index_status_default; 352 353 } 354 // Product list and system pages 355 else if(pages.hasOwnProperty(urlID)) { 356 357 pagetype = pages[urlID].pagetype; 358 pageID = pages[urlID].pageID; 359 name = pages[urlID].name; 360 title = pages[urlID].title; 361 searchwords = pages[urlID].searchwords; 362 description = pages[urlID].description; 363 index_status = pages[urlID].index_status; 364 365 } 366 367 return {urlID: urlID, pagetype: pagetype, pageID: pageID, name: name, title: title, searchwords: searchwords, description: description, index_status: index_status}; 368 369 }, 370 // Load page from Index header 371 loadPage: function() { 372 373 var urlBrowser = window.location.pathname; 374 var pageData = this.routeData(urlBrowser); 375 376 var urlID = pageData.urlID; 377 var pagetype = pageData.pagetype; 378 var pageID = pageData.pageID; 379 380 // Product single 381 if(pagetype == 'STORE_PRODUCT_SINGLE') { 382 383 product_urlID = urlID; 384 product_urlID = product_urlID.substring(WESTORE.CONFIG.url_page_product.length); 385 if(product_urlID) { 386 387 // Loads file (only loads file if productSingleData is not already set) 388 WESTORE.PRODUCT_SINGLE.load_file(product_urlID, function(product) { 389 pageData.title = product.site_title; 390 pageData.description = product.site_description; 391 pageData.searchwords = product.site_title; 392 WESTORE.ROUTE.update_metatags(pageData); 393 WESTORE.PRODUCT_SINGLE.render(product_urlID); 394 }); 395 } 396 397 } 398 // Product list 399 // If STORE_PRODUCT_LIST is from CMS, then its loaded from content_load.js instead 400 else if(pageID == 'STORE_SEARCH' || pagetype == 'STORE_PRODUCT_LIST') { 401 402 var listID = pageID; 403 if(pageID == 'STORE_SEARCH') { listID = 'all'; } 404 405 // Loads file (only loads file if productListData is not already set) 406 var product_page_set_lists = {[listID]: productListPageSets[listID]}; 407 WESTORE.PRODUCT_LIST.load_file_lists(product_page_set_lists, function(status) { 408 409 if(status == "success") { 410 var url_base = urlBrowser.split('?')[0]; // Remove parameters 411 WESTORE.PRODUCT_LIST.render(pageData, url_base); 412 } 413 else { 414 console.log("Product list("+listID+") could not be loaded. Timeout.") 415 } 416 417 }); 418 419 } 420 // Checkout 421 if(pageID == 'STORE_CHECKOUT') { 422 423 $(function() { 424 WESTORE.CHECKOUT.render_page(); 425 }); 426 427 } 428 // Checkout confirmation 429 if(pageID == 'STORE_CHECKOUT_CONFIRMATION') { 430 431 $(function() { 432 WESTORE.CHECKOUT.render_confirmation_page(); 433 }); 434 435 } 436 437 }, 438 // Load page from Ajax 439 loadPageContent: function(urlBrowser = '') { 440 441 if(contentLoadLastURL != urlBrowser) { 442 443 var pageData = this.routeData(urlBrowser); 444 445 var urlID = pageData.urlID; 446 var pagetype = pageData.pagetype; 447 var pageID = pageData.pageID; 448 449 // Product single 450 if(pagetype == 'STORE_PRODUCT_SINGLE') { 451 452 var product_urlID = urlID; 453 product_urlID = product_urlID.substring(WESTORE.CONFIG.url_page_product.length); 454 if(product_urlID) { 455 456 WESTORE.ROUTE.contentLoad__animation_start(); 457 458 // Loads file (only loads file if productSingleData is not already set) 459 WESTORE.PRODUCT_SINGLE.load_file(product_urlID, function(product) { 460 461 pageData.title = product.site_title; 462 pageData.description = product.site_description; 463 pageData.searchwords = product.site_title; 464 465 WESTORE.ROUTE.contentLoad__done(urlBrowser, pageData); 466 WESTORE.PRODUCT_SINGLE.render(product_urlID); 467 WESTORE.ROUTE.contentLoad__animation_done(); 468 469 }); 470 471 } 472 473 } 474 // Product list 475 else if(pageID == 'STORE_SEARCH' || pagetype == 'STORE_PRODUCT_LIST') { 476 477 var listID = pageID; 478 if(pageID == 'STORE_SEARCH') { listID = 'all'; } 479 480 WESTORE.ROUTE.contentLoad__animation_start(); 481 482 // Loads file (only loads file if productListData is not already set) 483 var product_page_set_lists = {[listID]: productListPageSets[listID]}; 484 WESTORE.PRODUCT_LIST.load_file_lists(product_page_set_lists, function(status) { 485 486 if(status == "success") { 487 var url_base = urlBrowser.split('?')[0]; // Remove parameters 488 WESTORE.ROUTE.contentLoad__done(urlBrowser, pageData); 489 WESTORE.PRODUCT_LIST.render(pageData, url_base); 490 WESTORE.ROUTE.contentLoad__animation_done(); 491 } 492 else { 493 console.log("Product list("+listID+") could not be loaded. Timeout.") 494 } 495 496 }); 497 498 } 499 // Checkout 500 else if(pageID == 'STORE_CHECKOUT') { 501 WESTORE.ROUTE.contentLoad__animation_start(); 502 WESTORE.ROUTE.contentLoad__done(urlBrowser, pageData); 503 WESTORE.CHECKOUT.render_page(); 504 WESTORE.ROUTE.contentLoad__animation_done(); 505 } 506 // Checkout confirmation 507 else if(pageID == 'STORE_CHECKOUT_CONFIRMATION') { 508 WESTORE.ROUTE.contentLoad__animation_start(); 509 WESTORE.ROUTE.contentLoad__done(urlBrowser, pageData); 510 WESTORE.CHECKOUT.render_confirmation_page(); 511 WESTORE.ROUTE.contentLoad__animation_done(); 512 } 513 514 } 515 516 }, 517 contentLoad__animation_start: function() { 518 519 if(WESTORE.CONFIG.store_load_method == "CMS") { $('.page-preloader').show(); } 520 else { 521 window.weStoreLoaderTimeOut = setTimeout(function(){ 522 $('body').append('<div class="contentLoader"><div class="contentLoaderBar contentLoaderAnimation'+(Math.floor(Math.random()*3)+1)+'"></div></div>'); 523 }, 100); 524 } 525 526 }, 527 contentLoad__animation_done: function() { 528 529 if(WESTORE.CONFIG.store_load_method == "CMS") { $('.page-preloader').hide(); } 530 else { 531 clearTimeout(window.weStoreLoaderTimeOut); 532 $(".contentLoader").delay(100).fadeOut(100, function() { 533 $(this).remove(); 534 }); 535 } 536 537 }, 538 contentLoad__done: function (urlBrowser, pageData) { 539 540 var pageID = pageData.pageID; 541 var metaTitle = pageData.title; 542 var metaDescription = pageData.description; 543 var metaSearchwords = pageData.searchwords; 544 var metaRobots = pageData.index_status; 545
546 // Windows push state 547 window.history.pushState({'epLoadType': 'content'}, metaTitle, urlBrowser); 548 549 contentLoadLastURL = urlBrowser; 550 551 WESTORE.ROUTE.update_metatags(pageData); 552 553 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 554 555 // Global JavaScript varibles 556 siteData.pageID = pageID; 557 558 //$('#rowlist_CONTENT .rowlist_content_container').html("<div id='store-container' class='row_content'></div>"); 559 $('html, body').animate({scrollTop: 0}, 0); 560 561 $(".CSS_TOP_NAV .item.selected").removeClass('selected'); 562 $(".CSS_TOP_NAV .item[data-pageID='"+pageID+"']").addClass('selected'); 563 564 if(WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 565 566 if(typeof scrollBlockAnimation === "function") { scrollBlockAnimation(); } 567 568 if(pageID) { 569 570 // API Visitors 571 $.ajax({ 572 dataType: "script", 573 url: WESTORE.CONFIG.url_domain_api+"/statistics/visit?id="+siteData.apiID+"&page="+pageID+"&uv="+(Math.floor((Math.random()*1000000)+1)), 574 cache: true, 575 success: function(jsRespons) {} 576 }); 577 578 } 579 580 } 581 else { 582 583 } 584 585 } 586 587 // 3rd Party trackers - Must be executed after window.history.pushState() is updated 588 if (typeof ga !== 'undefined') { ga('send', 'pageview'); } // Google analytics 589 if (typeof fbq !== 'undefined') { fbq('track', 'PageView'); } // Facebook Pixel Code 590 591 }, 592 update_metatags: function (pageData) { 593 594 if(WESTORE.CONFIG.store_load_method != "CMS") { 595 596 var pageID = pageData.pageID; 597 var metaTitle = pageData.title; 598 var metaDescription = pageData.description; 599 var metaSearchwords = pageData.searchwords; 600 var metaRobots = pageData.index_status; 601 602 $("meta[property='og:site_name']").attr('content', metaTitle); 603 $("meta[property='og:title']").attr('content', metaTitle); 604 $("meta[property='og:description']").attr('content', metaDescription); 605 $("meta[name='twitter:title']").attr('content', metaTitle); 606 $("meta[name='twitter:description']").attr('content', metaDescription); 607 $("meta[name='description']").attr('content', metaDescription); 608 $("meta[name='keywords']").attr('content', metaSearchwords); 609 $("meta[name='robots']").attr('content', metaRobots); 610 $('title').text(metaTitle); 611 612 } 613 614 }, 615 jQueryActions: function() { 616 617 // Content load - Checkout link clicked 618 $("body").on("click", "a.storeContentLoadLinkCheckout", function(event) { 619 event.preventDefault(); 620 WESTORE.ROUTE.loadPageContent($(this).attr('href')); 621 $("#cart").css('display', 'none'); 622 }); 623 624 // Content load - Product link clicked 625 $("body").on("click", "a.storeContentLoadLinkProduct", function(event) { 626 event.preventDefault(); 627 WESTORE.ROUTE.loadPageContent($(this).attr('href')); 628 }); 629 630 // Content load - Product page link clicked 631 $("body").on("click", "a.storeContentLoadLinkPage", function(event) { 632 633 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 634 contentLoad.load($(this), event); 635 } 636 else { 637 event.preventDefault(); 638 WESTORE.ROUTE.loadPageContent($(this).attr('href')); 639 } 640 641 }); 642 643 } 644}; 645WESTORE.CART = { 646 cartID: 'prdcrt_v4', 647 cart_active: false, 648 cart_is_created: false, 649 cart: {}, // {1001: {id: 1001, urlID: 'some/url', quantity: 1}, 1002: {...}}, productID and urlID is used as Keys to compare 650 label_title: 'Cart', 651 label_table_price: 'Price', 652 label_table_sum: 'Total',
653 label_cart_empty: 'Your cart is empty', 654 label_total_sum: 'Total price', 655 label_button_checkout: 'Go to checkout', 656 label_button_close: 'Keep shopping', 657 init: function(data) { 658 659 if(data.cart_active) { this.cart_active = data.cart_active; } 660 661 if(data.label_title) { this.label_title = data.label_title; } 662 if(data.label_table_price) { this.label_table_price = data.label_table_price; } 663 if(data.label_table_sum) { this.label_table_sum = data.label_table_sum; } 664 if(data.label_cart_empty) { this.label_cart_empty = data.label_cart_empty; } 665 if(data.label_total_sum) { this.label_total_sum = data.label_total_sum; } 666 if(data.label_button_checkout) { this.label_button_checkout = data.label_button_checkout; } 667 if(data.label_button_close) { this.label_button_close = data.label_button_close; } 668 669 $(function() { 670 WESTORE.CART.jQueryActions(); 671 }); 672 673 }, 674 add: function(productData, quantity) { 675 676 if(this.cart_is_created) { 677 678 var cart = this.cart; 679 var productID = productData.productID; 680 quantity = parseInt(quantity); 681 682 if(cart.hasOwnProperty(productID)) { 683 cart[productID].quantity++; 684 WESTORE.clog('Product was added to the cart (increased to '+cart[productID].quantity+')'); 685 WESTORE.clog(cart[productID]); 686 } 687 else { 688 689 var product = { 690 productID: productID, 691 urlID: productData.urlID, 692 quantity: quantity, 693 }; 694 695 // Add the product 696 cart[productID] = product; 697 698 WESTORE.clog('Product was added to cart (new)'); 699 WESTORE.clog(product); 700 701 } 702 703 this.save_to_localStorage(); 704 this.render_products(); 705 706 } 707 else { 708 WESTORE.clog('Product could not be added because the cart is not created'); 709 } 710 711 }, 712 remove: function(productID) { 713 714 var cart = this.cart; 715 if(cart.hasOwnProperty(productID)) { 716 717 // Remove element 718 var $productItem = $("#cart .product-item[data-productID='"+productID+"']"); 719 $productItem.remove(); 720 721 WESTORE.clog('Product was removed from the cart'); 722 WESTORE.clog(cart[productID]); 723 724 delete cart[productID]; 725 this.save_to_localStorage(); 726 this.render_summary_values(); 727 728 } 729 730 }, 731 remove_all: function(productID) { 732 733 $("#cart .product-item").remove(); 734 735 this.cart = {}; 736 this.save_to_localStorage(); 737 this.render_summary_values(); 738 739 WESTORE.clog('The cart was cleared'); 740 741 }, 742 quantity_change: function(productID, quantity) { 743 744 var cart = this.cart; 745 if(cart.hasOwnProperty(productID)) { 746 747 quantity = parseInt(quantity); 748 if(!quantity || quantity < 1) { quantity = 1; } 749 else if (quantity > 999) { quantity = 999; } 750 751 var product = WESTORE.CART.get_product(productID); 752 var price_after_deduction = product.price_after_deduction; 753 var total = price_after_deduction * quantity; 754 total = WESTORE.get_display_price(total); 755 756 // Update cart html 757 var $productItem = $("#cart .product-item[data-productID='"+productID+"']"); 758 var $input = $productItem.find('input.quantity'); 759 $input.val(quantity) 760 $productItem.find('.total').html(total); 761 762 // Update cart quantity 763 if(cart.hasOwnProperty(productID)) { 764 this.cart[productID].quantity = quantity; 765 } 766 767 WESTORE.clog('Product quantity was updated in the cart'); 768 WESTORE.clog(cart[productID]); 769 770 this.save_to_localStorage(); 771 this.render_summary_values(); 772 773 } 774 775 }, 776 is_cart_empty: function() { 777 if($.isEmptyObject(this.cart)) { return true; } 778 else { return false; } 779 }, 780 get_product: function(productID) { 781 782 var product = {}; 783 var productList = productListData['all']['products_object']; 784 785 if(productList.hasOwnProperty(productID)) { 786 787 var productReal = productList[productID]; 788 789 var price = productReal.price; 790 var price_deduction_percent = productReal.price_deduction; 791 var price_deduction_amount = price * price_deduction_percent / 100; 792 var price_after_deduction = price - price_deduction_amount; 793 794 display_price = WESTORE.get_display_price(price); 795 display_price_after_deduction = WESTORE.get_display_price(price_after_deduction); 796 797 product = { 798 'title': productReal.title, 799 'price': price, 800 'price_deduction': price_deduction_percent, 801 'price_after_deduction': price_after_deduction, 802 'tax': productReal.tax, 803 'urlID': productReal.urlID, 804 'image': productReal.image 805 }; 806 807 } 808 809 return product; 810 811 }, 812 create_cart_from_url: function(base64_url) { 813 814 var cart = {}; 815 var cart_url_object = base64url_decode(base64_url); 816 817 for (var productID in cart_url_object) { 818 819 var product = this.get_product(productID); 820 var quantity = cart_url_object[productID]; 821 quantity = parseInt(quantity); 822 823 // Add the product 824 cart[productID] = { 825 productID: productID, 826 urlID: product.urlID, 827 quantity: quantity, 828 }; 829 830 } 831 832 this.cart = cart; 833 this.save_to_localStorage(); 834 this.load_from_localStorage(); 835 836 }, 837 load_from_localStorage: function() { 838 839 if(this.cart_active) { 840 841 var error = ''; 842 if(!$(WESTORE.CONFIG.targetElementCart).length) { error += 'CART ERROR - Container element do not exists.\n'; } 843 844 if(error) { 845 WESTORE.clog('CART - Trying to load cart but error occured\n'+error); 846 } 847 else { 848 849 WESTORE.clog("CART - Start, Loaded existing cart: "); 850 851 var cart = {}; 852 var cartID = this.cartID; 853 854 if(localStorage[cartID]) { 855 var string_base64 = localStorage[cartID]; 856 cart = base64url_decode(string_base64); 857 } 858 859 this.local_cart_cleanup(cart); 860 this.cart = cart; 861 862 WESTORE.clog(cart); 863 864 this.render_cart(); 865 866 this.cart_is_created = true; 867 868 } 869 870 } 871 else { 872 WESTORE.clog('CART - Inactive'); 873 } 874 875 }, 876 local_cart_cleanup: function(cart) { 877 878 for (var productID in cart) { 879 880 var remove = true; 881 882 var product_from_cart = cart[productID]; 883 var product_from_db = this.get_product(productID); 884 885 // Match check - productID and urlID is used as Keys to compare product from cart exists in productList 886 if(product_from_cart.hasOwnProperty('urlID') && product_from_db.hasOwnProperty('urlID')) { 887 if(product_from_cart.urlID == product_from_db.urlID) { 888 remove = false; 889 } 890 } 891 892 893 if(remove) { 894 WESTORE.clog('Product do not exists and is removed from cart'); 895 WESTORE.clog(cart[productID]); 896 delete cart[productID]; 897 this.save_to_localStorage(); 898 } 899 900 } 901 902 this.cart = cart; 903 904 }, 905 save_to_localStorage: function() { 906 907 var string_base64 = base64url_encode(this.cart); 908 localStorage[this.cartID] = string_base64; 909 910 }, 911 render_cart: function() { 912 913 $cartContainer = $(WESTORE.CONFIG.targetElementCart); 914 915 // Cart html 916 html = ` 917 <div id='cart' class=''> 918 <div class='cart-header'> 919 <span>${this.label_title}</span> 920 <span class='button-cross button-close'> 921 <svg xmlns='http://www.w3.org/2000/svg' class='h-6 w-6' fill='none' viewBox='0 0 24 24' stroke='currentColor'><path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 18L18 6M6 6l12 12'></path></svg> 922 </span> 923 </div> 924 <div class='cart-content'> 925 <div class='product-list'></div> 926 </div> 927 <div class='cart-footer'> 928 <div class='cart-total-sum hide'> 929 <span class='cart-total-sum-label'>${this.label_total_sum}:</span> 930 <span class='cart-total-sum-value'></span> 931 </div> 932 <a class='buttons button-checkout hide storeContentLoadLinkCheckout' href='${WESTORE.CONFIG.url_root}checkout'>${this.label_button_checkout}</a> 933 <span class='buttons button-continue button-close'>${this.label_button_close}</span> 934 </div> 935 </div> 936 `; 937 938 $cartContainer.html(html); 939 this.render_products(); 940 941 }, 942 render_products: function(cart) { 943 944 var cart = this.cart; 945 var isEmpty = true; 946 947 var html = ` 948 <div class='product-item row-header'> 949 <div class='column'></div> 950 <div class='column'></div> 951 <div class='column'>${this.label_table_price}</div> 952 <div class='column'>${this.label_table_sum}</div> 953 <div class='column'></div> 954 <div class='column'></div> 955 </div> 956 `; 957 958 959 for (var productID in cart) { 960 961 var product = this.get_product(productID); 962 var quantity = cart[productID].quantity; 963 var price = product.price; 964 var price_after_deduction = product.price_after_deduction; 965 var priceTotal = price_after_deduction * quantity; 966 967 var display_price_total = WESTORE.get_display_price(priceTotal); 968 var display_price = WESTORE.get_display_price(price); 969 var display_price_after_deduction = WESTORE.get_display_price(price_after_deduction); 970 971 if(price != price_after_deduction) { 972 display_price = `<span class='display-price-before-deduction'>${display_price}</span> <span class='display-price-after-deduction'>${display_price_after_deduction}</span>`; 973 } 974 975 var productImage = `<img src="${WESTORE.CONFIG.url_root}/assets/images/product_thumbnail_320x320.jpg">`; 976 if(product.image) { productImage = `<img src="${WESTORE.CONFIG.url_product_images}picture_thumbs/${product.image}">`; } 977 978 html += ` 979 <div class='product-item row' data-productID='${productID}'> 980 <div class='column thumbnail'><a href='${WESTORE.CONFIG.url_root}product/${product.urlID}'>${productImage}</a></div> 981 <div class='column'>${product.title}</div> 982 <div class='column'>${display_price}</div> 983 <div class='column total'>${display_price_total}</div> 984 <div class='column'><input class='quantity' type='number' min='1' max='999' value='${quantity}'></input></div> 985 <div class='column'><div class='icon icon-bin removeButton'></div></div> 986 </div> 987 `; 988 989 } 990 991 $("#cart .product-list").html(html); 992 this.render_summary_values();
993 994 }, 995 render_summary_values: function() { 996 997 var cart = this.cart; 998 var counter = 0; 999 var priceTotal = 0; 1000 var $widgetCounter = $(".storeWidgetTarget-cartButtonCounter"); 1001 1002 for (var productID in cart) { 1003 var product = this.get_product(productID); 1004 priceTotal += (product.price_after_deduction * cart[productID].quantity); 1005 counter += cart[productID].quantity; 1006 } 1007 1008 priceTotal = WESTORE.get_display_price(priceTotal); 1009 1010 $("#cart .cart-total-sum-value").html(priceTotal); 1011 $widgetCounter.html(counter); // The global icon 1012 1013 if(counter > 0) { 1014 $("#cart .cart-total-sum").removeClass('hide'); 1015 $("#cart .button-checkout").removeClass('hide'); 1016 $widgetCounter.removeClass('hide'); // The global icon 1017 } 1018 else { 1019 $("#cart .cart-total-sum").addClass('hide'); 1020 $("#cart .button-checkout").addClass('hide'); 1021 $("#cart .product-list").html('<div class="empty">'+this.label_cart_empty+'</div>'); 1022 1023 $widgetCounter.addClass('hide'); // The global icon 1024 } 1025 1026 }, 1027 get_base64_string: function() { 1028 1029 var cart = this.cart; 1030 var products = {}; 1031 1032 for (var productID in cart) { 1033 products[productID] = cart[productID].quantity; 1034 } 1035 1036 return btoa(JSON.stringify(products)); 1037 1038 }, 1039 get_url: function() { 1040 1041 var base64string = this.get_base64_string(); 1042 return base64url_encode(base64string); 1043 1044 }, 1045 jQueryActions: function() { 1046 1047 $("body").on("click", "#cart .button-close", function(e) { 1048 $("#cart").css('display', 'none'); 1049 }); 1050 1051 // Remove button clicked 1052 $("body").on("click", "#cart .product-item .removeButton", function(e) { 1053 1054 e.preventDefault(); 1055 var productID = $(this).parents('.product-item').attr('data-productID'); 1056 1057 // Remove from cart data 1058 WESTORE.CART.remove(productID); 1059 1060 }); 1061 1062 // Quantity change 1063 $("body").on("change", "#cart .product-item .quantity", function(e) { 1064 1065 e.preventDefault(); 1066 1067 var $input = $(this); 1068 var $productItem = $input.parents('.product-item'); 1069 var productID = $productItem.attr('data-productID'); 1070 var quantity = parseInt($input.val()); 1071 1072 WESTORE.CART.quantity_change(productID, quantity); 1073 1074 1075 }); 1076 1077 // When document is clicked 1078 $(document).mousedown(function(event) { 1079 1080 // Check if event action was left mouse button 1081 if(event.which == 1) { 1082 var $clickedTarget = $(event.target); 1083 if($clickedTarget.attr("id") != "cart" && !$clickedTarget.parents("#cart").length) { 1084 $('#cart').css('display', 'none'); 1085 } 1086 } 1087 1088 }); 1089 1090 } 1091 1092}; 1093WESTORE.CHECKOUT = { 1094 label_title_cart: 'Cart', 1095 label_title_payment: 'Shipping & payment', 1096 label_table_price: 'Price', 1097 label_table_sum: 'Total', 1098 label_cart_empty: 'Your cart is empty', 1099 label_total_sum: 'Total price', 1100 label_total_sum_before: 'Price before discount', 1101 label_total_discount: 'Discount', 1102 label_discountcode_input: 'Discount code', 1103 label_discountcode_button: 'Register', 1104 label_button_back_cart: 'Change', 1105 label_button_payment: 'Go to payment', 1106 label_productOutStock: 'Out of stock', 1107 order_total_sum: 0, 1108 discount_applied_code: '', 1109 discount_applied_data: {}, 1110 init: function(data) { 1111 1112 if(data.label_title_cart) { this.label_title_cart = data.label_title_cart; } 1113 if(data.label_title_payment) { this.label_title_payment = data.label_title_payment; } 1114 if(data.label_table_price) { this.label_table_price = data.label_table_price; } 1115 if(data.label_table_sum) { this.label_table_sum = data.label_table_sum; } 1116 if(data.label_total_sum) { this.label_total_sum = data.label_total_sum; } 1117 if(data.label_total_sum_before) { this.label_total_sum_before = data.label_total_sum_before; } 1118 if(data.label_total_discount) { this.label_total_discount = data.label_total_discount; } 1119 if(data.label_discountcode_input) { this.label_discountcode_input = data.label_discountcode_input; } 1120 if(data.label_discountcode_button) { this.label_discountcode_button = data.label_discountcode_button; } 1121 if(data.label_button_back_cart) { this.label_button_back_cart = data.label_button_back_cart; } 1122 if(data.label_button_payment) { this.label_button_payment = data.label_button_payment; } 1123 if(data.label_productOutStock) { this.label_productOutStock = data.label_productOutStock; } 1124 1125 $(function() { 1126 WESTORE.CHECKOUT.jQueryActions(); 1127 }); 1128 1129 }, 1130 quantity_change: function(productID, quantity) { 1131 1132 var cart = this.cart; 1133 if(cart.hasOwnProperty(productID)) { 1134 1135 quantity = parseInt(quantity); 1136 if(!quantity || quantity < 1) { quantity = 1; } 1137 else if (quantity > 999) { quantity = 999; } 1138 1139 var product = WESTORE.CART.get_product(productID); 1140 var price = product.price; 1141 var total = price * quantity; 1142 total = WESTORE.get_display_price(total); 1143 1144 // Update cart html 1145 var $productItem = $("#cart .product-item[data-productID='"+productID+"']"); 1146 var $input = $productItem.find('input.quantity'); 1147 $input.val(quantity) 1148 $productItem.find('.total').html(total); 1149 1150 // Update checkout html 1151 var $productItem = $("#checkout .product-item[data-productID='"+productID+"']"); 1152 var $input = $productItem.find('input.quantity'); 1153 $input.val(quantity) 1154 $productItem.find('.total').html(total); 1155 1156 // Update cart quantity 1157 if(cart.hasOwnProperty(productID)) { 1158 this.cart[productID].quantity = quantity; 1159 } 1160 1161 WESTORE.clog('Product quantity was updated in the cart'); 1162 WESTORE.clog(cart[productID]); 1163 1164 this.save_to_localStorage(); 1165 this.render_summary_values();
1166 1167 } 1168 1169 }, 1170 remove: function(productID) { 1171 1172 var cart = this.cart; 1173 if(cart.hasOwnProperty(productID)) { 1174 1175 // Remove element 1176 var $productItem = $("#cart .product-item[data-productID='"+productID+"']"); 1177 $productItem.remove(); 1178 1179 // Update checkout html 1180 var $productItem = $("#checkout .product-item[data-productID='"+productID+"']"); 1181 $productItem.remove(); 1182 1183 WESTORE.clog('Product was removed from the cart'); 1184 WESTORE.clog(cart[productID]); 1185 1186 delete cart[productID]; 1187 this.save_to_localStorage(); 1188 this.render_summary_values(); 1189 1190 } 1191 1192 }, 1193 render_page: function() { 1194 1195 // Resets discount code 1196 WESTORE.CHECKOUT.discount_applied_code = ''; 1197 WESTORE.CHECKOUT.discount_applied_data = {}; 1198 1199 html = ` 1200 <div id="checkout" class="store-content"> 1201 <div class="section section-cart open"> 1202 <div class="title">${this.label_title_cart}</div> 1203 <div class="content"> 1204 <div class="product-list"></div> 1205 <div class="checkout-discountcode"> 1206 <input type="text" placeholder="${this.label_discountcode_input}"> 1207 <div class="button-discountcode">${this.label_discountcode_button}</div> 1208 <div class="checkout-discountcode-response"></div> 1209 </div> 1210 <div class="content-footer hide"> 1211 <div class="content-footer-left"> 1212 <div class="checkout-total-table"> 1213 <div class='checkout-total-tr checkout-total-discount-tr hide'> 1214 <div class='checkout-total-td'>${this.label_total_sum_before}:</div> 1215 <div class='checkout-total-td checkout-total-value-before'></div> 1216 </div> 1217 <div class='checkout-total-tr checkout-total-discount-tr hide'> 1218 <div class='checkout-total-td'>${this.label_total_discount}:</div> 1219 <div class='checkout-total-td checkout-total-value-discount'></div> 1220 </div> 1221 <div class='checkout-total-tr'> 1222 <div class='checkout-total-td'>${this.label_total_sum}:</div> 1223 <div class='checkout-total-td checkout-total-value-sum'></div> 1224 </div> 1225 </div> 1226 </div> 1227 <div class="content-footer-right"> 1228 <button class="button button-next">${this.label_button_payment}</button> 1229 </div> 1230 </div> 1231 </div> 1232 <div class="error-content"></div> 1233 <div><button class="button button-change">${this.label_button_back_cart}</button></div> 1234 </div> 1235 1236 <div class="section section-payment hide"> 1237 <div class="title">${this.label_title_payment}</div> 1238 <div class="content"> 1239 <div class="klarna-placeholder"> 1240 <!-- Klarna loads here --> 1241 </div> 1242 </div> 1243 <div class="error-content"></div> 1244 </div> 1245 </div> 1246 `; 1247 1248 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 1249 html = ` 1250 <div class="row_content"> 1251 ${html} 1252 </div> 1253 `; 1254 } 1255 1256 $(WESTORE.CONFIG.targetElement).html(html); 1257 1258 WESTORE.CHECKOUT.wait_for_cart(function(status) { 1259 if(status == "success") { 1260 WESTORE.CHECKOUT.render_product_list(); 1261 } 1262 else { 1263 WESTORE.clog("CHECKOUT - Products did not load from cart, something went wrong."); 1264 } 1265 }); 1266 1267 }, 1268 render_product_list: function() { 1269 1270 var cart = WESTORE.CART.cart; 1271 var productsFound = false; 1272 1273 var html = ` 1274 <div class='product-item row-header'> 1275 <div class='column'></div> 1276 <div class='column'></div> 1277 <div class='column'>${this.label_table_price}</div> 1278 <div class='column'>${this.label_table_sum}</div> 1279 <div class='column'></div> 1280 <div class='column'></div> 1281 <div class='column'></div> 1282 </div> 1283 `; 1284 1285 for (var productID in cart) { 1286 1287 productsFound = true; 1288 var product = WESTORE.CART.get_product(productID); 1289 var quantity = cart[productID].quantity; 1290 var price = product.price; 1291 var price_after_deduction = product.price_after_deduction; 1292 var priceTotal = price_after_deduction * quantity; 1293 1294 var display_price_total = WESTORE.get_display_price(priceTotal); 1295 var display_price = WESTORE.get_display_price(price); 1296 var display_price_after_deduction = WESTORE.get_display_price(price_after_deduction); 1297 1298 if(price != price_after_deduction) { 1299 display_price = `<span class='display-price-before-deduction'>${display_price}</span> <span class='display-price-after-deduction'>${display_price_after_deduction}</span>`; 1300 } 1301 1302 var productImage = `<img src="${WESTORE.CONFIG.url_root}/assets/images/product_thumbnail_320x320.jpg">`; 1303 if(product.image) { productImage = `<img src="${WESTORE.CONFIG.url_product_images}picture_thumbs/${product.image}">`; } 1304 1305 html += ` 1306 <div class='product-item row' data-productID='${productID}'> 1307 <div class='column thumbnail'><a href='${WESTORE.CONFIG.url_cms_root}product/${product.urlID}'>${productImage}</a></div> 1308 <div class='column'>${product.title}</div> 1309 <div class='column'>${display_price}</div> 1310 <div class='column total'>${display_price_total}</div> 1311 <div class='column'><input class='quantity' type='number' min='1' max='999' value='${quantity}'></input></div> 1312 <div class='column'><div class='icon icon-bin removeButton'></div></div> 1313 </div> 1314 `; 1315 1316 } 1317 1318 if(productsFound) { 1319 $("#checkout .product-list").html(html); 1320 $("#checkout .section-cart .content-footer").removeClass('hide'); 1321 $("#checkout .section.section-payment").removeClass('hide'); 1322 this.render_summary_values();
1323 } 1324 1325 }, 1326 render_summary_values: function() { 1327 1328 var cart = WESTORE.CART.cart; 1329 var productsFound = false; 1330 var discount_amount_exists = false; 1331 var discount_data = this.discount_applied_data; 1332 var price_total_sum = 0; 1333 1334 for (var productID in cart) { 1335 var product = WESTORE.CART.get_product(productID); 1336 var quantity = cart[productID].quantity; 1337 var price = product.price; 1338 var price_after_deduction = product.price_after_deduction; 1339 var price_total = price_after_deduction * quantity; 1340 1341 price_total_sum += price_total; 1342 productsFound = true; 1343 1344 } 1345 1346 this.order_total_sum = price_total_sum; 1347 1348 if(productsFound) { 1349 1350 // Check if discount percent or amount will change final sum 1351 if(discount_data.hasOwnProperty('code')) { 1352 var discount_types = ["P", "A", "G"]; 1353 if(discount_types.includes(discount_data.type)) { 1354 if(discount_data.type == "P" && discount_data.percent > 0) { discount_amount_exists = true; } 1355 else if(discount_data.amount > 0) { discount_amount_exists = true; } 1356 } 1357 } 1358 1359 // Apply discount 1360 if(discount_amount_exists) { 1361 1362 var price_total_sum_discount = 0; 1363 var discount_amount_left = 0; 1364 1365 if(discount_data.type == "P") { price_total_sum_discount = price_total_sum * (discount_data.percent / 100); } 1366 else { price_total_sum_discount = discount_data.amount; } 1367 1368 price_total_sum_discount = this.round_decimals(price_total_sum_discount); 1369 1370 discount_amount_left = price_total_sum_discount - price_total_sum; 1371 if(discount_amount_left < 0) { discount_amount_left = 0; } 1372 1373 if(price_total_sum_discount > price_total_sum) { price_total_sum_discount = price_total_sum; } 1374 1375 var price_total_sum_before = price_total_sum; 1376 price_total_sum = price_total_sum - price_total_sum_discount; 1377 1378 var price_total_sum_before_string = WESTORE.get_display_price(price_total_sum_before); 1379 var price_total_sum_discount_string = WESTORE.get_display_price(price_total_sum_discount); 1380 $("#checkout .checkout-total-value-discount").html("-"+price_total_sum_discount_string); 1381 $("#checkout .checkout-total-value-before").html(price_total_sum_before_string); 1382 1383 if(discount_data.type == "G") { 1384 var discount_amount_left_string = `(${discount_amount_left}kr kvar)`; 1385 if(WESTORE.CONFIG.country == "US") { discount_amount_left_string = `($${discount_amount_left} remaining)`; } 1386 $("#checkout .checkout-discountcode-response .type-giftcard").html(discount_amount_left_string); 1387 } 1388 1389 } 1390 1391 var price_total_sum_string = WESTORE.get_display_price(price_total_sum); 1392 $("#checkout .checkout-total-value-sum").html(price_total_sum_string); 1393 1394 $("#checkout .section-cart .content-footer").removeClass('hide'); 1395 $("#checkout .section.section-payment").removeClass('hide'); 1396 } 1397 else { 1398 $("#checkout .section-cart .content-footer").addClass('hide'); 1399 $("#checkout .section.section-payment").addClass('hide'); 1400 $("#checkout .product-list").html('<div class="empty">'+WESTORE.CART.label_cart_empty+'</div>'); 1401 } 1402 1403 if(discount_amount_exists) { 1404 $("#checkout .checkout-total-discount-tr").removeClass("hide"); 1405 } 1406 else { 1407 $("#checkout .checkout-total-discount-tr").addClass("hide"); 1408 } 1409 1410 1411 }, 1412 render_confirmation_page: function() { 1413 1414 var urlObject = window.location; 1415 var url_params = new URLSearchParams(urlObject.search); 1416 var html = ""; 1417 1418 var siteID = ''; 1419 var orderID = ''; 1420 if(url_params.has('id')) { siteID = url_params.get('id'); } 1421 if(url_params.has('order_id')) { orderID = url_params.get('order_id'); } 1422 1423 // Resets discount code 1424 WESTORE.CHECKOUT.discount_applied_code = ''; 1425 WESTORE.CHECKOUT.discount_applied_data = {}; 1426 1427 // Fetch order confirmation page from API 1428 if(siteID && orderID) { 1429 1430 var url = WESTORE.CONFIG.url_domain_api+"/checkout/confirmation"; 1431 var postPara = {'siteID': siteID, 'klarna_order_id': orderID}; 1432 1433 $.post(url, postPara, function(jsonResponse){ 1434 1435 if (jsonResponse.status == "success") { 1436 1437 html = jsonResponse.html_snippet; 1438 1439 } 1440 // If klarna confirmation page could not be generated then provide a generic confirmation page 1441 else { 1442 1443 var orderIDstring = ''; 1444 1445 if(WESTORE.CONFIG.country == "US") { 1446 1447 if(orderID) { 1448 orderIDstring = ` 1449 <br><br><br>Your order id is: ${orderID} 1450 <br><b>${orderID}</b> 1451 `; 1452 } 1453 1454 html = ` 1455 <h1>Thank you for your order!</h1> 1456 <div class='checkout-confirmation-content'> 1457 We will now start to process your order.<br> 1458 A confirmation email from Klarna will be sent to you. 1459 ${orderIDstring} 1460 </div> 1461 `; 1462 1463 } 1464 else { 1465 1466 if(orderID) { 1467 orderIDstring = ` 1468 <br><br><br>Ditt ordernummer är: 1469 <br><b>${orderID}</b> 1470 `; 1471 } 1472 1473 html = ` 1474 <h1>Tack för din beställning!</h1> 1475 <div class='checkout-confirmation-content'> 1476 Vi kommer nu att börja behandla din beställning.<br> 1477 Ett bekräftelsemail från klarna kommer skickas till dig. 1478 ${orderIDstring} 1479 </div> 1480 `; 1481 1482 } 1483 1484 } 1485 1486 html = ` 1487 <div id="checkout-confirmation" class="store-content"> 1488 ${html} 1489 </div> 1490 `; 1491 1492 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 1493 html = ` 1494 <div class="row_content"> 1495 ${html} 1496 </div> 1497 `; 1498 } 1499 1500 $(WESTORE.CONFIG.targetElement).html(html); 1501 WESTORE.CART.remove_all(); 1502 1503 });
1504 1505 } 1506 1507 }, 1508 wait_for_cart: function(callBackFunction) { 1509 1510 var counter = 0; 1511 var interval = window.setInterval(function() { 1512 1513 if(WESTORE.CART.cart_is_created) { 1514 window.clearInterval(interval); 1515 callBackFunction('success'); 1516 } 1517 else if(counter >= 1000) { 1518 window.clearInterval(interval); 1519 callBackFunction('failed'); // Error 1520 } 1521 counter++; 1522 1523 }, 10); 1524 1525 }, 1526 round_decimals: function(price) { 1527 1528 // Cut down to 2 decimals 1529 if(price && !Number.isInteger(price)) { 1530 price = Math.round(price * 100) / 100; 1531 price = price.toFixed(2); 1532 price = parseFloat(price); 1533 } 1534 1535 return price; 1536 1537 }, 1538 jQueryActions: function() { 1539 1540 var klarna_order_id = ''; 1541 1542 // Remove button clicked 1543 $("body").on("click", "#checkout .product-item .removeButton", function(e) { 1544 1545 e.preventDefault(); 1546 var productID = $(this).parents('.product-item').attr('data-productID'); 1547 WESTORE.CHECKOUT.remove(productID); 1548 WESTORE.CART.remove(productID); 1549 1550 }); 1551 1552 // Quantity change 1553 $("body").on("change", "#checkout .product-item .quantity", function(e) { 1554 1555 e.preventDefault(); 1556 var $input = $(this); 1557 var $productItem = $input.parents('.product-item'); 1558 var productID = $productItem.attr('data-productID'); 1559 var quantity = parseInt($input.val()); 1560 WESTORE.CHECKOUT.quantity_change(productID, quantity); 1561 WESTORE.CART.quantity_change(productID, quantity); 1562 1563 }); 1564 1565 // Cart button clicked 1566 var discount_code_inputed = ''; 1567 $("body").on("click", "#checkout .button-discountcode", function(e) { 1568 1569 var code = $("#checkout .checkout-discountcode input").val(); 1570 1571 if(!code) { 1572 discount_code_inputed = ''; 1573 WESTORE.CHECKOUT.discount_applied_code = ''; 1574 WESTORE.CHECKOUT.discount_applied_data = {}; 1575 $("#checkout .checkout-discountcode-response").html(""); 1576 WESTORE.CHECKOUT.render_summary_values(); 1577 } 1578 else if(code != discount_code_inputed) { 1579 1580 discount_code_inputed = code; 1581 1582 var url_api = WESTORE.CONFIG.url_domain_api+"/discountcode/get"; 1583 var order_amount = WESTORE.CHECKOUT.order_total_sum; 1584 1585 var langCode = 'se'; 1586 if(WESTORE.CONFIG.country == "US") { langCode = 'en'; } 1587 1588 WESTORE.CHECKOUT.discount_applied_code = ''; 1589 WESTORE.CHECKOUT.discount_applied_data = {}; 1590 1591 $.ajax({
1592 dataType: "json", 1593 url: url_api, 1594 type: "post", 1595 data: {id: siteData.apiID, code: code, order_amount: order_amount, langCode: langCode}, 1596 cache: false, 1597 success: function(jsonResponse) { 1598 1599 // Success 1600 if(jsonResponse.status == 'success') { 1601 1602 var discountData = jsonResponse.discountcode_data; 1603 var amount_string = ""; 1604 if(discountData.type == "P") { amount_string = `${discountData.percent}%`; } 1605 else { 1606 1607 if(WESTORE.CONFIG.country == "US") { amount_string = `$${discountData.amount}`; } 1608 else { amount_string = `${discountData.amount}kr`; } 1609 1610 if(discountData.type == "G") { amount_string = `${amount_string} <span class='type-giftcard'></span>`; } 1611 } 1612 1613 $("#checkout .checkout-discountcode-response").html(`<span class='success'>${discountData.code} ${amount_string}</span>`); 1614 WESTORE.CHECKOUT.discount_applied_code = discountData.code; 1615 WESTORE.CHECKOUT.discount_applied_data = discountData; 1616 1617 } 1618 // Error 1619 else { 1620 1621 $("#checkout .checkout-discountcode-response").html("<span class='error'>"+jsonResponse.error_log_message+"</span>"); 1622 1623 } 1624 1625 if(jsonResponse.hasOwnProperty('devMessages')) { 1626 WESTORE.clog(jsonResponse.devMessages); 1627 } 1628 1629 WESTORE.CHECKOUT.render_summary_values(); 1630 1631 }, 1632 error: function (xhr, ajaxOptions, thrownError) { 1633 1634 $("#checkout .checkout-discountcode-response").html("<span class='error'>Load error x38101</span>"); 1635 WESTORE.clog('CHECKOUT PAYMENT - Ajax error (Status: '+xhr.status+') (Error: '+thrownError+')'); 1636 WESTORE.CHECKOUT.render_summary_values(); 1637 1638 } 1639 1640 }); 1641 1642 } 1643 1644 }); 1645 1646 // Button - Cart change 1647 $("body").on("click", "#checkout .section-cart .button-change", function(e) { 1648 1649 $("#checkout .section").removeClass("open").removeClass("completed").removeClass("error"); 1650 $("#checkout .section-cart").addClass("open"); 1651 $("#checkout .section-payment .klarna-placeholder").html(""); 1652 $(document).scrollTop(0); 1653 1654 }); 1655 1656 // Button - Go to payment 1657 $("body").on("click", ".section-cart .button-next", function(e) { 1658 1659 if(!WESTORE.CART.is_cart_empty()) { 1660 1661 $("#checkout .section").removeClass("open").removeClass("completed").removeClass("error"); 1662 $("#checkout .section-cart").addClass("completed"); 1663 1664 $(document).scrollTop(0); 1665 1666 var error_title = "Woopsie Daisy!"; 1667 var error_subtitle = "Looks like we hit a bump"; 1668 var error_subtitle_multiple = "Looks like we hit a few bumps"; 1669 var error_subtitle_end = "We are so sorry this happend!<br>If you need our help, please feel free to contact our support!"; 1670 1671 var order_id = klarna_order_id; 1672 var discountcode = WESTORE.CHECKOUT.discount_applied_code; 1673 var cart_items = WESTORE.CART.get_base64_string(); 1674 var postURL = WESTORE.CONFIG.url_cms_root+"load/content/checkout/payment?order_id="+order_id+"&discountcode="+discountcode+"&cart_items="+cart_items; 1675 checkoutPaymentResponseData = {}; 1676 1677 WESTORE.clog("CHECKOUT PAYMENT - Loading klarna..."); 1678 1679 // Call Javascript file 1680 $.ajax({
1681 dataType: "script", 1682 url: postURL, 1683 cache: false, 1684 success: function(jsContentResponse) { 1685 1686 var html = ''; 1687 var error = false; 1688 var error_content = ''; 1689 var error_target = 'payment'; 1690 1691 if (!checkoutPaymentResponseData.hasOwnProperty('data')) { 1692 error = true; 1693 error_content = 'No response data'; 1694 WESTORE.clog('CHECKOUT PAYMENT - Ajax error (No response data)'); 1695 } 1696 else { 1697 1698 var data = checkoutPaymentResponseData.data; 1699 1700 // Success 1701 if(data.status == 'success') { 1702 if(data.klarnaResponse.hasOwnProperty('order_id')) { 1703 klarna_order_id = data.klarnaResponse.order_id; 1704 } 1705 if(data.klarnaResponse.hasOwnProperty('html_snippet')) { 1706 html = data.klarnaResponse.html_snippet; 1707 $("#checkout .section-payment .klarna-placeholder").html(html); 1708 $("#checkout .section-payment").addClass("open"); 1709 } 1710 } 1711 // Error 1712 else { 1713 1714 error = true; 1715 1716 if(data.hasOwnProperty('errorMessages')) { 1717 for (var i = 0; i < data.errorMessages.length; i++) { 1718 error_content += '<li>'+data.errorMessages[i]+'</li>'; 1719 } 1720 if(data.errorMessages.length > 1) { error_subtitle = error_subtitle_multiple; } 1721 } 1722 1723 // Error status: error-products 1724 if(data.status == 'error-products') { 1725 error_subtitle_end = "We are so sorry!<br>Could you be so kind and see if you can adjust this.<br>If not possible, please feel free to contact our support!"; 1726 error_target = 'cart'; 1727 } 1728 // Error status: error-load-payment 1729 else { 1730 error_target = 'vart'; 1731 } 1732 1733 } 1734 1735 if(data.hasOwnProperty('devMessages')) { 1736 WESTORE.clog(data.devMessages); 1737 } 1738 1739 } 1740 1741 if(error) { 1742 1743 html = ` 1744 <div class='checkout-load-payment-error'> 1745 <div class='checkout-load-payment-error-title'>${error_title}</div> 1746 <div class='checkout-load-payment-error-subtitle'>${error_subtitle}</div> 1747 <div class='checkout-load-payment-error-content'>${error_content}</div> 1748 <div class='checkout-load-payment-error-subtitle-end'>${error_subtitle_end}</div> 1749 </div> 1750 `; 1751 1752 if(error_target == 'cart') { 1753 $("#checkout .section-cart").addClass("error"); 1754 $("#checkout .section-cart .error-content").html(html); 1755 } 1756 else { 1757 $("#checkout .section-payment").addClass("error"); 1758 $("#checkout .section-payment .error-content").html(html); 1759 } 1760 1761 } 1762 1763 }, 1764 error: function (xhr, ajaxOptions, thrownError) { 1765 1766 var html = ` 1767 <div class='checkout-load-payment-error'> 1768 <div class='checkout-load-payment-error-title'>${error_title}</div> 1769 <div class='checkout-load-payment-error-subtitle'>${error_subtitle}</div> 1770 <div class='checkout-load-payment-error-content'>Load error x38101</div> 1771 <div class='checkout-load-payment-error-subtitle-end'>${error_subtitle_end}</div> 1772 </div> 1773 `; 1774 1775 $("#checkout .section-payment").addClass("error"); 1776 $("#checkout .section-payment .error-content").html(html); 1777 WESTORE.clog('CHECKOUT PAYMENT - Ajax error (Status: '+xhr.status+') (Error: '+thrownError+')'); 1778 1779 } 1780 1781 }); 1782 1783 } 1784 1785 }); 1786 1787 } 1788 1789}; 1790WESTORE.PRODUCT_LIST = { 1791 called_files: {}, 1792 lazyLoad: false, 1793 langCode: 'en', 1794 max_price: 100, 1795 sortID_default: 1001, 1796 label_items_count: 'products found in', 1797 label_items_empty: 'No products here', 1798 label_search_placeholder: 'Search', 1799 label_range_price: 'Price', 1800 label_range_price_unit: '$', 1801 label_range_to: 'to', 1802 label_filter_reset: 'Reset filters', 1803 label_filter_show: 'Show filters', 1804 label_filter_hide: 'Show hits',
1805 label_filter_expand: 'Show more...', 1806 label_filter_collapse: 'Show less...', 1807 label_filter_vendor: 'Brand', 1808 label_sort_title_asc: 'Name, A-Z', 1809 label_sort_title_desc: 'Name, Z-A', 1810 label_sort_brand_asc: 'Brand, A-Z', 1811 label_sort_brand_desc: 'Brand, Z-A', 1812 label_sort_price_asc: 'Price, lowest first', 1813 label_sort_price_desc: 'Price, highest first', 1814 init: function(data) { 1815 1816 if(data.langCode) { this.langCode = data.langCode; } 1817 if(data.max_price) { this.max_price = data.max_price; } 1818 if(data.sortID_default) { this.sortID_default = data.sortID_default; } 1819 1820 if(data.label_items_count) { this.label_items_count = data.label_items_count; } 1821 if(data.label_items_empty) { this.label_items_empty = data.label_items_empty; } 1822 if(data.label_search_placeholder) { this.label_search_placeholder = data.label_search_placeholder; } 1823 1824 if(data.label_range_price) { this.label_range_price = data.label_range_price; } 1825 if(data.label_range_price_unit) { this.label_range_price_unit = data.label_range_price_unit; } 1826 if(data.label_range_to) { this.label_range_to = data.label_range_to; } 1827 1828 if(data.label_filter_reset) { this.label_filter_reset = data.label_filter_reset; } 1829 if(data.label_filter_show) { this.label_filter_show = data.label_filter_show; } 1830 if(data.label_filter_hide) { this.label_filter_hide = data.label_filter_hide; } 1831 if(data.label_filter_expand) { this.label_filter_expand = data.label_filter_expand; } 1832 if(data.label_filter_collapse) { this.label_filter_collapse = data.label_filter_collapse; } 1833 if(data.label_filter_vendor) { this.label_filter_vendor = data.label_filter_vendor; } 1834 1835 if(data.label_sort_title_asc) { this.label_sort_title_asc = data.label_sort_title_asc; } 1836 if(data.label_sort_title_desc) { this.label_sort_title_desc = data.label_sort_title_desc; } 1837 if(data.label_sort_brand_asc) { this.label_sort_brand_asc = data.label_sort_brand_asc; } 1838 if(data.label_sort_brand_desc) { this.label_sort_brand_desc = data.label_sort_brand_desc; } 1839 if(data.label_sort_price_asc) { this.label_sort_price_asc = data.label_sort_price_asc; } 1840 if(data.label_sort_price_desc) { this.label_sort_price_desc = data.label_sort_price_desc; } 1841 1842 this.lazyLoad = data.lazyLoad; 1843 1844 $(function() { 1845 WESTORE.PRODUCT_LIST.jQueryActions(); 1846 }); 1847 1848 }, 1849 load_file_lists: function(lists, callBackFunction) { 1850 1851 // lists = productListPageSets = { 1852 // 101: {1: {loaded: false}, 2: {loaded: false}}, 1853 // 102: {1: {loaded: false}}, 1854 // 103: {1: {loaded: false}, 2: {loaded: false}, 3: {loaded: false}}, 1855 // } 1856 1857 var url = WESTORE.CONFIG.url_script_product_list; 1858 1859 for (let listID in lists) { 1860 1861 if(productListPageSets.hasOwnProperty(listID)) { 1862 1863 var list = lists[listID]; 1864 1865 // Loop sets in current list 1866 for (let setID in list) { 1867 1868 var fileID = `${listID}-${setID}`; 1869 1870 if(!list[setID].loaded) { 1871 1872 if(productListDataRaw.hasOwnProperty(fileID)) { 1873 productListPageSets[listID][setID].loaded = true; 1874 } 1875 else { 1876 1877 WESTORE.clog_time_start('FILE('+fileID+') LOAD DONE. TIME'); // This time is not accurate to client network time 1878 1879 // Load the JavaScript file 1880 $.ajax({
1881 dataType: "script", 1882 url: url.replace('#fileID#', fileID), 1883 cache: true, 1884 success: function(jsRespons) { 1885 productListPageSets[listID][setID].loaded = true; 1886 WESTORE.clog_time_end('FILE('+listID+'-'+setID+') LOAD DONE. TIME'); // This time is not accurate to client network time 1887 }, 1888 error: function (xhr, ajaxOptions, thrownError) { 1889 WESTORE.clog_time_end('FILE('+listID+'-'+setID+') LOAD DONE. TIME'); 1890 WESTORE.clog('FILE LOAD('+listID+'-'+setID+') STATUS: Failed, time out! (Status: '+xhr.status+') (Error: '+thrownError+')'); 1891 } 1892 }); 1893 1894 } 1895 1896 } 1897 1898 } 1899 1900 } 1901 1902 } 1903 1904 var counter = 0; 1905 var interval = 10; 1906 var timout_seconds = 60; 1907 var timout_number = timout_seconds * 1000 / interval; 1908 var interval = window.setInterval(function() { 1909 1910 let allLoaded = true; 1911 1912 for (let listID in lists) { 1913 1914 let list = lists[listID]; 1915 for (let setID in list) { 1916 if(!list[setID].loaded) { 1917 allLoaded = false; 1918 break; 1919 } 1920 } 1921 1922 } 1923 1924 if(allLoaded) { 1925 window.clearInterval(interval); 1926 WESTORE.PRODUCT_LIST.load_file__process_list(lists, callBackFunction); 1927 } 1928 else if(counter >= timout_number) { 1929 window.clearInterval(interval); 1930 callBackFunction('failed'); // Error 1931 } 1932 1933 counter++; 1934 1935 }, 10); 1936 1937 }, 1938 load_file__process_list: function(lists, callBackFunction) { 1939 1940 for (let listID in lists) { 1941 1942 let list = lists[listID]; 1943 1944 if(!productListData.hasOwnProperty(listID)) { 1945 1946 WESTORE.clog_time_start('RAW DATA PROCESS('+listID+') TIME'); 1947 1948 var listData = { 1949 'page_data': [], 1950 'products': [] 1951 }; 1952 1953 var page_data = {}; 1954 var products = []; 1955 var products_object = {}; 1956 1957 // Loop sets in current list 1958 for (let setID in list) { 1959 1960 let fileID = `${listID}-${setID}`; 1961 if(productListDataRaw.hasOwnProperty(fileID)) { 1962 1963 if(setID == "pagedata") { 1964 page_data = productListDataRaw[fileID]; 1965 } 1966 else { 1967 1968 let productsRaw = productListDataRaw[fileID]; 1969 1970 // Loop all products in the file list 1971 for (let i = 0; i < productsRaw.length; i++) { 1972 let product = this.data__product_array_to_object(productsRaw[i]); 1973 if(listID == 'all') { products_object[ product['id'] ] = product; } 1974 products.push(product); 1975 } 1976 1977 } 1978 1979 } 1980 1981 } 1982 1983 listData['page_data'] = page_data; 1984 listData['products'] = products; 1985 if(listID == 'all') { listData['products_object'] = products_object; } 1986 1987 productListData[listID] = listData; 1988 1989 WESTORE.clog('RAW DATA PROCESS('+listID+') PRODUCTS: '+products.length); 1990 WESTORE.clog_time_end('RAW DATA PROCESS('+listID+') TIME'); 1991 1992 // Start background loading products for remaning pages 1993 if(listID == 'all') { 1994 WESTORE.clog('START LOADING ALL productListData LISTS...'); 1995 WESTORE.clog_time_start('LOADING ALL productListData TIME'); 1996 WESTORE.PRODUCT_LIST.load_file_lists(productListPageSets, function(){ 1997 WESTORE.clog('DONE LOADING ALL productListData LISTS!'); 1998 WESTORE.clog_time_end('LOADING ALL productListData TIME'); 1999 }); 2000 } 2001 2002 } 2003 2004 } 2005 2006 callBackFunction('success'); 2007 2008 }, 2009 wait_for_list: function(listID, callBackFunction) { 2010 2011 var counter = 0; 2012 var interval = 10; 2013 var timout_seconds = 10; 2014 var timout_number = timout_seconds * 1000 / interval; 2015 var interval = window.setInterval(function() { 2016
2017 if(productListData.hasOwnProperty(listID)) { 2018 window.clearInterval(interval); 2019 callBackFunction('success'); 2020 } 2021 else if(counter >= timout_number) { 2022 window.clearInterval(interval); 2023 callBackFunction('failed'); // Error 2024 } 2025 counter++; 2026 2027 }, 10); 2028 2029 }, 2030 data__product_array_to_object: function(product) { 2031 return { 2032 'id': product[0], 2033 'urlID': product[1], 2034 'barcode': product[2], 2035 'title': product[3], 2036 'vendor': product[4], 2037 'price': product[5], 2038 'price_deduction': product[6], 2039 'tax': product[7], 2040 'image': product[8] 2041 }; 2042 }, 2043 render: function(pageData, url_base) { 2044 2045 var render__rowHeader = function (invObj) { 2046 2047 return ``; 2048 2049 } 2050 2051 var render__productItem = function(invObj, product) { 2052 2053 var html = ''; 2054 2055 var productImage = ''; 2056 var product_image_url = product.image; 2057 if(product_image_url) { productImage = `<img src="${WESTORE.CONFIG.url_product_images}picture_thumbs/${product_image_url}">`; } 2058 else { productImage = `<img src="${WESTORE.CONFIG.url_root}assets/images/product_thumbnail_320x320.jpg">`; } 2059 2060 var price = product.price; 2061 var price_deduction_percent = product.price_deduction; 2062 var price_deduction_amount = price * price_deduction_percent / 100; 2063 var price_after_deduction = price - price_deduction_amount; 2064 var price_deduction_percent_html = ""; 2065 2066 display_price = WESTORE.get_display_price(price); 2067 display_price_after_deduction = WESTORE.get_display_price(price_after_deduction); 2068 2069 if(price_deduction_percent) { 2070 display_price = `<span class='display-price-before-deduction'>${display_price}</span> <span class='display-price-after-deduction'>${display_price_after_deduction}</span>`; 2071 price_deduction_percent_html = `<div class="price-deduction-percent">${price_deduction_percent}%</div>`; 2072 } 2073 2074 var dataTags = ""; 2075 dataTags += 'data-productID="'+product.id+'" '; 2076 dataTags += 'data-title="'+product.title+'" '; 2077 dataTags += 'data-price="'+price+'" '; 2078 dataTags += 'data-price-deduction="'+price_deduction_percent+'" '; 2079 dataTags += 'data-tax="'+product.tax+'" '; 2080 dataTags += 'data-urlID="'+product.urlID+'" '; 2081 dataTags += 'data-image="'+product.image+'" '; 2082 2083 if(invObj.page_view == "rows") { 2084 html = ` 2085 <a class="product-item bi-item bi-item-row storeContentLoadLinkProduct" data-productID="${product.id}" data-page="${invObj.page_number}" href="${WESTORE.CONFIG.url_root}${WESTORE.CONFIG.url_page_product}${product.urlID}"> 2086 <div class="bi-item-td bi-row-image"> 2087 <div class="height-wrapper"> 2088 <div class="height-content">${productImage}</div> 2089 </div> 2090 ${price_deduction_percent_html} 2091 </div> 2092 <div class="bi-item-td">${product.title}</div> 2093 <div class="bi-item-td">${product.vendor}</div> 2094 <div class="bi-item-td td-center">${display_price}</div> 2095 <data ${dataTags}></data> 2096 </a> 2097 `; 2098 } 2099 else { 2100 html = ` 2101 <DIV class="product-item bi-item bi-item-grid" data-productID="${product.id}" data-page="${invObj.page_number}"> 2102 <a class="bi-grid-image color-border-primary-hover storeContentLoadLinkProduct" href="${WESTORE.CONFIG.url_root}${WESTORE.CONFIG.url_page_product}${product.urlID}"> 2103 <div class="height-wrapper"> 2104 <div class="height-content">${productImage}</div> 2105 </div> 2106 ${price_deduction_percent_html} 2107 </a> 2108 <div class="product-title">${product.title}</div> 2109 <div class="product-price">${display_price}</div> 2110 <div class="clear"></div> 2111 <data ${dataTags}></data> 2112 </DIV> 2113 `; 2114 } 2115 2116 return html; 2117 2118 } 2119 2120 let products = []; 2121 let pageID = pageData.pageID; 2122 2123 let urlObject = window.location; 2124 let url_params = new URLSearchParams(urlObject.search); 2125 2126 let search_active = false; 2127 let search = ''; 2128 let sortID = ''; 2129 let ranges = ''; 2130 let filters = ''; 2131 let page_view = ''; 2132 let page_size = 0; 2133 let page_number = 0; 2134 2135 // Get values 2136 if(url_params.has('search')) { search = url_params.get('search'); } 2137 if(url_params.has('sortID')) { sortID = url_params.get('sortID'); } 2138 if(url_params.has('ranges')) { ranges = url_params.get('ranges'); } 2139 if(url_params.has('filters')) { filters = url_params.get('filters'); } 2140 if(url_params.has('page_view')) { page_view = url_params.get('page_view'); } 2141 if(url_params.has('page_size')) { page_size = url_params.get('page_size'); } 2142 if(url_params.has('page_number')) { page_number = url_params.get('page_number'); } 2143 2144 // Integers check 2145 if(!Number.isInteger(page_size) || page_size < 0) { page_size = 0; } 2146 if(!Number.isInteger(page_number) || page_number < 0) { page_number = 0; } 2147 2148 2149 let label_items_count = this.label_items_count+' <b>'+pageData.name+'</b> '; 2150 let label_search_placeholder = this.label_search_placeholder; 2151 2152 // Page products 2153 if(pageID && pageData.pagetype == "STORE_PRODUCT_LIST") { 2154 if(productListData.hasOwnProperty(pageID)) { 2155 products = productListData[pageID].products; 2156 navigation = productListData[pageID].page_data.navigation; 2157 } 2158 label_search_placeholder = this.label_search_placeholder+' - '+pageData.name; 2159 } 2160 // Search products 2161 else { 2162 if(productListData.hasOwnProperty('all')) { 2163 products = productListData['all'].products; 2164 navigation = productListData['all'].page_data.navigation; 2165 } 2166 search_active = true; 2167 } 2168 2169 var langCode = 'se'; 2170 if(WESTORE.CONFIG.country == "US") { langCode = 'en'; } 2171 2172 let data = { 2173 id: 'cms_products', 2174 selector: '#productlist', 2175 url_base: url_base, 2176 lazyLoad: this.lazyLoad, 2177 lang: langCode, 2178 label_items_count: label_items_count, 2179 label_items_empty: this.label_items_empty, 2180 label_range_to: this.label_range_to, 2181 label_filter_reset: this.label_filter_reset, 2182 label_filter_show: this.label_filter_show, 2183 label_filter_hide: this.label_filter_hide,
2184 label_filter_expand: 'Show more', 2185 label_filter_collapse: 'Show less', 2186 label_search_placeholder: label_search_placeholder, 2187 items: products, 2188 renderItem: render__productItem, 2189 renderItemRowHeader: render__rowHeader, 2190 search: search, 2191 search_fields: ['title', 'vendor', 'urlID', 'barcode'], 2192 search_active: search_active, 2193 page_number: page_number, 2194 page_size: page_size, 2195 page_view: page_view, 2196 sortID: sortID, 2197 sortID_default: this.sortID_default, 2198 sort_structure: { 2199 1001: {field: 'title', direction: 'ASC', label: this.label_sort_title_asc}, 2200 1002: {field: 'title', direction: 'DESC', label: this.label_sort_title_desc}, 2201 1003: {field: 'price', direction: 'ASC', label: this.label_sort_price_asc}, 2202 1004: {field: 'price', direction: 'DESC', label: this.label_sort_price_desc}, 2203 1005: {field: 'brand', direction: 'ASC', label: this.label_sort_brand_asc}, 2204 1006: {field: 'brand', direction: 'DESC', label: this.label_sort_brand_desc}, 2205 }, 2206 navigation: navigation, 2207 ranges_posted: ranges, 2208 range_structure: { 2209 price: { 2210 min: 0, max: this.max_price, label: this.label_range_price, unit: this.label_range_price_unit 2211 }, 2212 }, 2213 filters_posted: filters, 2214 filter_structure: { 2215 'vendor': {label: this.label_filter_vendor} 2216 }, 2217 }; 2218 2219 // Render page 2220 $(function() { 2221 2222 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 2223 2224 if(pageData.pageID == "STORE_SEARCH") { 2225 let html = ` 2226 <div class='row_content'> 2227 <div id='productlist' class='store-content store-content-search'></div> 2228 </div> 2229 `; 2230 2231 $(WESTORE.CONFIG.targetElement).html(html); 2232 } 2233 else { 2234 let html = `<div id='productlist' class='store-content'></div>`; 2235 $("#store-container-productlist").html(html); 2236 } 2237 2238 } 2239 else { 2240 let html = `<div id='productlist' class='store-content'></div>`; 2241 $(WESTORE.CONFIG.targetElement).html(html); 2242 } 2243 2244 productInventory.init(data); 2245 2246 }); 2247 2248 }, 2249 jQueryActions: function() { 2250 2251 // bjorkInventory Navigation clicked 2252 $("body").on("click", ".bjorkInventory .bi-navigation .item", function(event) { 2253 2254 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 2255 contentLoad.load($(this), event); 2256 } 2257 else { 2258 event.preventDefault(); 2259 WESTORE.ROUTE.loadPageContent($(this).attr('href')); 2260 } 2261 2262 }); 2263 2264 } 2265}; 2266 2267WESTORE.PRODUCT_SINGLE = { 2268 called_files: {}, 2269 product: {}, 2270 stockUsed: true, 2271 label_quantity_end: '', 2272 label_buttonInStock: '', 2273 label_buttonOutStock: '', 2274 init: function(data) { 2275 2276 if(data.label_quantity_end) { this.label_quantity_end = data.label_quantity_end; } 2277 if(data.label_buttonInStock) { this.label_buttonInStock = data.label_buttonInStock; } 2278 if(data.label_buttonOutStock) { this.label_buttonOutStock = data.label_buttonOutStock; } 2279 2280 this.stockUsed = data.stockUsed; 2281 2282 $(function() { 2283 WESTORE.PRODUCT_SINGLE.jQueryActions(); 2284 }); 2285 2286 }, 2287 load_file: function(urlID, callBackFunction) { 2288 2289 // Check list already exists 2290 if(!this.called_files.hasOwnProperty(urlID) && !productSingleData.hasOwnProperty(urlID)) { 2291 2292 // To prevent files from beeing called more then once 2293 this.called_files[urlID] = 'X'; 2294 2295 WESTORE.clog('PRODUCT FILE LOAD('+urlID+') LOADING...'); 2296 WESTORE.clog_time_start('PRODUCT FILE LOAD('+urlID+') LOAD TIME'); // This time is not accurate to client network time 2297 2298 // Load the JavaScript file 2299 $.ajax({
2300 dataType: "script", 2301 url: WESTORE.CONFIG.url_script_product_single.replace('#urlID#', urlID), 2302 cache: true, 2303 success: function(jsRespons) { 2304 2305 WESTORE.PRODUCT_SINGLE.load_file_done(urlID, callBackFunction); 2306 2307 }, 2308 error: function (xhr, ajaxOptions, thrownError) { 2309 WESTORE.clog_time_end('PRODUCT FILE LOAD('+urlID+') LOAD TIME'); 2310 WESTORE.clog('PRODUCT FILE LOAD('+urlID+') STATUS: Failed, time out! (Status: '+xhr.status+') (Error: '+thrownError+')'); 2311 } 2312 }); 2313 2314 } 2315 else { 2316 2317 this.load_file_done(urlID, callBackFunction); 2318 2319 } 2320 2321 }, 2322 load_file_done: function(urlID, callBackFunction) { 2323 2324 if(productSingleData.hasOwnProperty(urlID)) { 2325 2326 WESTORE.clog_time_end('PRODUCT FILE LOAD('+urlID+') LOAD TIME'); // This time is not accurate to client network time 2327 WESTORE.clog('PRODUCT FILE LOAD('+urlID+') STATUS: Success!'); 2328 2329 var product = productSingleData[urlID]; 2330 this.product = product; 2331 2332 // Make sure that product object have fields/property 2333 if(!product.hasOwnProperty('productID')) { this.product.productID = 0; } 2334 if(!product.hasOwnProperty('urlID')) { this.product.urlID = ""; } 2335 if(!product.hasOwnProperty('title')) { this.product.title = ""; } 2336 if(!product.hasOwnProperty('vendor')) { this.product.vendor = ""; } 2337 if(!product.hasOwnProperty('option_value1')) { this.product.option_value1 = ""; } 2338 if(!product.hasOwnProperty('option_value2')) { this.product.option_value2 = ""; } 2339 if(!product.hasOwnProperty('option_value3')) { this.product.option_value3 = ""; } 2340 if(!product.hasOwnProperty('description_short')) { this.product.description_short = ""; } 2341 if(!product.hasOwnProperty('description_long')) { this.product.productID = ""; } 2342 if(!product.hasOwnProperty('price')) { this.product.price = 0; } 2343 if(!product.hasOwnProperty('tax')) { this.product.tax = 0; } 2344 if(!product.hasOwnProperty('pageID')) { this.product.pageID = 0; } 2345 if(!product.hasOwnProperty('options')) { this.product.options = []; } 2346 if(!product.hasOwnProperty('variants')) { this.product.variants = []; } 2347 if(!product.hasOwnProperty('images')) { this.product.images = []; } 2348 if(!product.hasOwnProperty('picID')) { this.product.picID = 0; } 2349 2350 callBackFunction(product); 2351 2352 } 2353 else { 2354 WESTORE.clog_time_end('PRODUCT FILE LOAD('+urlID+') LOAD TIME'); 2355 WESTORE.clog('PRODUCT FILE LOAD('+urlID+') STATUS: Failed, something went wrong with productSingleData urlID'); 2356 } 2357 2358 }, 2359 load_file_quantity: function(productID, callBackFunction) { 2360 2361 var url = WESTORE.CONFIG.url_script_product_single_quantity.replace('#productID#', productID); 2362 2363 // Load the js file 2364 $.ajax({
2365 dataType: "script", 2366 url: url, 2367 cache: true, 2368 success: function(jsRespons) { 2369 2370 var quantity = 0; 2371 2372 if(jsRespons.length) { 2373 quantity = jsRespons.replace('//', ''); 2374 quantity = parseInt(quantity); 2375 } 2376 2377 callBackFunction(quantity); 2378 2379 }, 2380 error: function (xhr, ajaxOptions, thrownError) { 2381 WESTORE.clog('PRODUCT QUATITY FILE LOAD('+productID+') STATUS: Failed, time out! (Status: '+xhr.status+') (Error: '+thrownError+')'); 2382 } 2383 }); 2384 2385 2386 }, 2387 render: function(urlID) { 2388 2389 var product = this.product; 2390 var colors = { 2391 primary: "#14a3e8", 2392 dark: "#3a3a3a", 2393 bright: "#b1b1b1" 2394 }; 2395 2396 var price_deduction_percent_html = ""; 2397 var display_price = WESTORE.get_display_price(product.price); 2398 var display_price_after_deduction = WESTORE.get_display_price(product.price_after_deduction); 2399 2400 if(product.price_deduction) { 2401 display_price = `<div class='display-price-before-deduction'>${display_price}</div><div class='display-price-after-deduction'>${display_price_after_deduction}</div>`; 2402 price_deduction_percent_html = `<div class="price-deduction-percent">${product.price_deduction}%</div>`; 2403 } 2404 2405 var dataTags = ""; 2406 dataTags += 'data-productID="'+product.productID+'" '; 2407 dataTags += 'data-title="'+product.title+'" '; 2408 dataTags += 'data-price="'+product.price+'" '; 2409 dataTags += 'data-price-deduction="'+product.price_deduction+'" '; 2410 dataTags += 'data-price-after-deduction="'+product.price_deduction+'" '; 2411 dataTags += 'data-tax="'+product.tax+'" '; 2412 dataTags += 'data-urlID="'+product.urlID+'" '; 2413 dataTags += 'data-image="'+product.images[product.picID]+'" '; 2414 2415 html = {}; 2416 html.thumbnails = ""; 2417 html.options = ""; 2418 2419 // Images 2420 for (var picID in product.images) { 2421 let image = product.images[picID]; 2422 html.thumbnails += ` 2423 <div class="thumbnail" data-picID="${picID}"> 2424 <img src="${WESTORE.CONFIG.url_product_images}picture_thumbs/${image}"> 2425 </div> 2426 `; 2427 } 2428 2429 // Image - Selected 2430 var product_image_url = product.images[product.picID]; 2431 var productImage = `<img src="${WESTORE.CONFIG.url_root}/assets/images/product_thumbnail_320x320.jpg">`; 2432 if(product_image_url) { productImage = `<img src="${WESTORE.CONFIG.url_product_images}${product_image_url}">`; } 2433 2434 // Options html 2435 if(Object.keys(product.options).length) { 2436 for (var optionID in product.options) { 2437 2438 let option = product.options[optionID]; 2439 let option_values = ``; 2440 2441 for (var i = 0; i < option.values_available.length; i++) { 2442 let value = option.values_available[i]; 2443 let selected = ""; 2444 if(value == product['option_value'+optionID]) { selected = "selected"; } 2445 option_values += `<option value='${value}' ${selected}>${value}</option>`; 2446 } 2447 2448 html.options += ` 2449 <div class="option-item"> 2450 <div class="title">${option.title}</div> 2451 <select id="option${optionID}"> 2452 ${option_values} 2453 </select> 2454 </div> 2455 `; 2456 } 2457 html.options = `<div class="options">${html.options}</div>`; 2458 } 2459 2460 html.all = ` 2461 <style> 2462 #product input:focus, select:focus { border-color: ${colors.primary}; } 2463 #product .addButton { background-color: ${colors.primary}; } 2464 #product .addButton.outOfStock { background-color: ${colors.bright}; } 2465 #product .product-image-main .image-next:hover .icon, 2466 #product .product-image-main .image-prev:hover .icon { color: ${colors.dark}; } 2467 </style> 2468 2469 <div id="product" class="store-content"> 2470 2471 <div class="section-header"> 2472 <div class="breadCrumbs"></div> 2473 </div> 2474 <div class="section-content"> 2475 <div class="section-left"> 2476 2477 <div class="product-image-main"> 2478 <div class="inner"> 2479 <div class="image-prev"> 2480 <div class="icon icon-cust-arrow-left"></div> 2481 </div> 2482 <div class="image-next"> 2483 <div class="icon icon-cust-arrow-right"></div> 2484 </div> 2485 ${productImage} 2486 </div> 2487 ${price_deduction_percent_html} 2488 </div> 2489 2490 <div class="product-image-thumbnails"> 2491 ${html.thumbnails} 2492 </div> 2493 2494 </div> 2495 <div class="section-right"> 2496 2497 <h1> 2498 <div>${product.title}</div> 2499 </h1> 2500 2501 <div class="price">${display_price}</div> 2502 2503 <div class="description-short">${product.description_short}</div> 2504 2505 ${html.options} 2506 <div class='stockSection'></div> 2507 2508 </div> 2509 </div> 2510 2511 <div class="section-footer"> 2512 <div class="description-long">${product.description_long}</div> 2513 </div> 2514 2515 <data ${dataTags}></data> 2516 </div> 2517 `; 2518 2519 // Render page 2520 $(function() { 2521 var thisObj = WESTORE.PRODUCT_SINGLE; 2522 var container = WESTORE.CONFIG.targetElement; 2523 2524 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 2525 html.all = ` 2526 <div class="row_content"> 2527 ${html.all} 2528 </div> 2529 `; 2530 } 2531 2532 $(container).html(html.all); 2533 $(container+" #product .product-image-thumbnails .thumbnail[data-picID='"+product.picID+"']").addClass('selected'); 2534 2535 $('.CSS_TOP_NAV .item.selected').removeClass('selected'); 2536 2537 // Loads quantity file, then render quantity 2538 thisObj.load_file_quantity(product.productID, function(quantity) { 2539 product.quantity = quantity; 2540 thisObj.product.quantity = quantity; 2541 thisObj.render_quantity();
2542 }); 2543 2544 // Breadcrumbs html 2545 if(product.pageID) { 2546 WESTORE.PRODUCT_LIST.wait_for_list(product.pageID, function(status) { 2547 2548 if(status == "success") { 2549 2550 let breadCrumbs = productListData[product.pageID].page_data.breadCrumbs; 2551 if(breadCrumbs.length) { 2552 2553 let breadCrumbs_html = ""; 2554 for (var i = 0; i < breadCrumbs.length; i++) { 2555 let breadCrumbItem = breadCrumbs[i]; 2556 breadCrumbs_html += ` 2557 <a href='${breadCrumbItem.url}' class='storeContentLoadLinkPage'>${breadCrumbItem.label}</a> 2558 <svg xmlns="http://www.w3.org/2000/svg" width="7px" height="10px" style="margin: 0 4px" viewBox="0 0 5 7.89062"> 2559 <g id="Path_526959" data-name="Path 526959" fill="#8E8E8E"> 2560 <polygon points=".70703 7.89062 0 7.18359 3.23877 3.94336 0 .70703 .70703 0 4.65283 3.94336 .70703 7.89062"></polygon> 2561 </g> 2562 </svg> 2563 `; 2564 } 2565 2566 breadCrumbs_html += product.title; 2567 $(container+" #product .breadCrumbs").html(breadCrumbs_html); 2568 2569 } 2570 2571 } 2572 else { 2573 WESTORE.clog(`Breadcrumbs could not be loaded for this product(${product.productID}).`); 2574 } 2575 2576 }); 2577 } 2578 2579 let option_values_string = ""; 2580 if(product.option_value1) { option_values_string = product.option_value1; } 2581 if(product.option_value2) { option_values_string = ", "+product.option_value2; } 2582 if(product.option_value3) { option_values_string = ", "+product.option_value3; } 2583 2584 var tag_manager_item = { 2585 //currency: "USD", 2586 value: product.price, 2587 items: [ 2588 { 2589 item_id: product.productID, 2590 item_name: product.title, 2591 affiliation: "Product single page", 2592 coupon: "", 2593 discount: 0, 2594 index: 0, 2595 item_brand: product.vendor, 2596 item_category: "", 2597 item_category2: "", 2598 item_category3: "", 2599 item_category4: "", 2600 item_category5: "", 2601 item_list_id: product.pageID, 2602 item_list_name: product.pageID, 2603 item_variant: option_values_string, 2604 location_id: "", 2605 price: product.price, 2606 quantity: 1 2607 } 2608 ] 2609 }; 2610 2611 // Google Analytics 4 2612 if (typeof gtag !== 'undefined') { 2613 gtag("event", "view_item", tag_manager_item); 2614 } 2615 // Google Tag Manager 2616 else if (typeof dataLayer !== 'undefined') { 2617 dataLayer.push({'event': 'view_item', 'ecommerce': tag_manager_item}); 2618 } 2619 2620 2621 }); 2622 2623 }, 2624 render_quantity: function() { 2625 2626 var product = this.product; 2627 var buyButtonLabel = this.label_buttonInStock; 2628 var buyButtonStockClass = ''; 2629 var html = ''; 2630 2631 if(this.stockUsed) { 2632 2633 if(!product.quantity) { 2634 buyButtonStockClass = ' outOfStock'; 2635 buyButtonLabel = this.label_buttonOutStock; 2636 } 2637 2638 html = ` 2639 <div class='addButton${buyButtonStockClass}'>${buyButtonLabel}</div> 2640 <div class='quantity'>${product.quantity} ${this.label_quantity_end}</div> 2641 `; 2642 2643 } 2644 else { 2645 html = `<div class='addButton'>${buyButtonLabel}</div>`; 2646 } 2647 2648 $(WESTORE.CONFIG.targetElement+' .stockSection').html(html); 2649 2650 }, 2651 jQueryActions: function () { 2652 2653 function select_image(picID) { 2654 2655 var images = WESTORE.PRODUCT_SINGLE.product.images; 2656 2657 if(images.hasOwnProperty(picID)) { 2658 2659 $("#product .product-image-main img").attr("src", WESTORE.CONFIG.url_product_images+images[picID]); 2660 $("#product .product-image-thumbnails .thumbnail.selected").removeClass("selected"); 2661 $("#product .product-image-thumbnails .thumbnail[data-picID='"+picID+"']").addClass("selected"); 2662 2663 } 2664 2665 } 2666 2667 // Image select 2668 $("body").on("click", "#product .product-image-thumbnails .thumbnail", function(e) { 2669 var picID = $(this).attr('data-picID'); 2670 select_image(picID); 2671 }); 2672 2673 // Image next 2674 $("body").on("click", "#product .image-next", function(e) { 2675 e.stopPropagation(); 2676 var $thumb = $("#product .product-image-thumbnails .thumbnail.selected").next(); 2677 if(!$thumb.length) { $thumb = $("#product .product-image-thumbnails .thumbnail").first(); } 2678 var picID = $thumb.attr('data-picID'); 2679 select_image(picID); 2680 }); 2681 2682 // Image prev 2683 $("body").on("click", "#product .image-prev", function(e) { 2684 2685 e.stopPropagation(); 2686 var $thumb = $("#product .product-image-thumbnails .thumbnail.selected").prev(); 2687 if(!$thumb.length) { $thumb = $("#product .product-image-thumbnails .thumbnail").last(); } 2688 var picID = $thumb.attr('data-picID'); 2689 select_image(picID); 2690 2691 }); 2692 2693 // Product add button clicked 2694 $("body").on("click", "#product .addButton:not(.outOfStock)", function(e) { 2695 2696 e.preventDefault(); 2697 2698 var $product = $(this).parents('#product'); 2699 var $data = $product.find('data'); 2700 var product = { 2701 productID: $data.attr('data-productID'), 2702 urlID: $data.attr('data-urlID') 2703 }; 2704 2705 WESTORE.CART.add(product, 1); 2706 2707 }); 2708 2709 // Variant option change 2710 $("body").on("change", "#product .options select", function(e) { 2711 2712 var options = WESTORE.PRODUCT_SINGLE.product.options; 2713 var variants = WESTORE.PRODUCT_SINGLE.product.variants;
2714 var variant_selected = {}; 2715 2716 var optionValue1 = ''; 2717 var optionValue2 = ''; 2718 var optionValue3 = ''; 2719 if($("#option1").val() !== undefined) { optionValue1 = $("#option1").val(); } 2720 if($("#option2").val() !== undefined) { optionValue2 = $("#option2").val(); } 2721 if($("#option3").val() !== undefined) { optionValue3 = $("#option3").val(); } 2722 2723 for (var productID in variants) { 2724 2725 let variant = variants[productID]; 2726 2727 variantValue1 = ''+variant['option_value1']; 2728 variantValue2 = ''+variant['option_value2']; 2729 variantValue3 = ''+variant['option_value3']; 2730 2731 // Selected varriant 2732 if(optionValue1 == variantValue1 && optionValue2 == variantValue2 && optionValue3 == variantValue3) { 2733 variant_selected = variant; 2734 } 2735 2736 } 2737 2738 // Load variant 2739 if(variant_selected.hasOwnProperty('urlID')) { 2740 var urlBrowser = WESTORE.CONFIG.url_root+WESTORE.CONFIG.url_page_product+variant_selected.urlID; 2741 WESTORE.ROUTE.loadPageContent(urlBrowser); 2742 } 2743 2744 }); 2745 2746 // Product breadcrumb clicked 2747 $("body").on("click", "#product .breadcrumbs a", function(event) { 2748 2749 if(WESTORE.CONFIG.store_load_method == "CMS" || WESTORE.CONFIG.store_load_method == "LIVE_INTERNAL") { 2750 contentLoad.load($(this), event); 2751 } 2752 else { 2753 event.preventDefault(); 2754 WESTORE.ROUTE.loadPageContent($(this).attr('href')); 2755 } 2756 2757 }); 2758 2759 } 2760}; 2761WESTORE.init({"CONFIG":{"store_load_method":"LIVE_INTERNAL","url_root":"\/","url_cms_root":"\/","url_script_product_list":"\/products\/lists\/productlist-#fileID#.js?261001085505","url_script_product_single":"\/products\/singles\/js\/#urlID#.js?261001085505","url_script_product_single_quantity":"\/products\/singles\/quantity\/#productID#.js?261001085505","url_product_images":"\/pictures_products\/","country":"US","targetElement":".store-container","targetElementCart":"#store-container-cart","targetWidgetIcons":"","widget_icons_top":20,"widget_icons_right":20,"widget_icons_expand":true,"url_page_checkout":"checkout","url_page_checkout_confirmation":"checkout\/confirmation","url_page_search":"search\/products","url_page_product":"product\/","url_domain_api":"https:\/\/api2.epage.se","label_search":"Search"},"ROUTE":{"index_status_default":"index","pages":{"produkter":{"pageID":"78","pagetype":"STORE_PRODUCT_LIST","name":"Produkter","title":"Produkter \u2013 Nordic Gym","searchwords":"","description":"Tr\u00e4ningsmaskiner och gymutrustning f\u00f6r styrketr\u00e4ning, fitness och kondition.","index_status":"index"},"produkter\/styrketraning":{"pageID":"82","pagetype":"STORE_PRODUCT_LIST","name":"Styrketr\u00e4ning","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2013 Nordic Gym","searchwords":"","description":"Uppt\u00e4ck h\u00e5llbar styrketr\u00e4ning med svensktillverkade maskiner fr\u00e5n Nordic Gym. Ergonomisk design och kvalitet som h\u00e5ller f\u00f6r l\u00e5ngsiktig tr\u00e4ning.","index_status":"index"},"produkter\/konditionstraning":{"pageID":"83","pagetype":"STORE_PRODUCT_LIST","name":"Konditionstr\u00e4ning","title":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2013 Nordic Gym","searchwords":"","description":"Uppt\u00e4ck svensktillverkad konditionstr\u00e4ningsutrustning fr\u00e5n Nordic Gym. H\u00e5llbar design och ergonomisk kvalitet som passar alla tr\u00e4ningsmilj\u00f6er.","index_
2761status":"index"},"produkter\/ovrig-traning":{"pageID":"84","pagetype":"STORE_PRODUCT_LIST","name":"\u00d6vrig tr\u00e4ning","title":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning","index_status":"index"},"produkter\/tillbehor":{"pageID":"85","pagetype":"STORE_PRODUCT_LIST","name":"Tillbeh\u00f6r","title":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Tillbeh\u00f6r","index_status":"index"},"produkter\/hemmatraning":{"pageID":"86","pagetype":"STORE_PRODUCT_LIST","name":"Hemmatr\u00e4ning","title":"Kvalitetsprodukter inom Hemmatr\u00e4ning \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Hemmatr\u00e4ning","index_status":"index"},"produkter\/reservdelar":{"pageID":"87","pagetype":"STORE_PRODUCT_LIST","name":"Reservdelar","title":"Kvalitetsprodukter inom Reservdelar \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Reservdelar","index_status":"index"},"produkter\/begagnat":{"pageID":"88","pagetype":"STORE_PRODUCT_LIST","name":"Begagnat","title":"Begagnade tr\u00e4ningsmaskiner och gymmaskiner - Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom begagnade tr\u00e4ningsmaskiner och gymmaskiner","index_
2761status":"index"},"produkter\/styrketraning\/basutrustning":{"pageID":"89","pagetype":"STORE_PRODUCT_LIST","name":"Basutrustning","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Basutrustning \u2013 Nordic Gy","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Basutrustning","index_status":"index"},"produkter\/muskelgrupper":{"pageID":"91","pagetype":"STORE_PRODUCT_LIST","name":"Muskelgrupper","title":"Kvalitetsprodukter inom Maskiner med viktmagasin - Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Maskiner med viktmagasin \u2192 Styrketr\u00e4ning f\u00f6r Armar, Axlar, Ben, Br\u00f6st, B\u00e5l, Rygg och \u00d6vriga musker","index_status":"index"},"produkter\/muskelgrupper\/armar":{"pageID":"92","pagetype":"STORE_PRODUCT_LIST","name":"Armar","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r Armar - Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Maskiner med viktmagasin \u2192 Styrketr\u00e4ning f\u00f6r Armar","index_status":"index"},"produkter\/muskelgrupper\/axlar":{"pageID":"93","pagetype":"STORE_PRODUCT_LIST","name":"Axlar","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r Axlar | Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Maskiner med viktmagasin f\u00f6r Axlar","index_status":"index"},"produkter\/muskelgrupper\/ben":{"pageID":"94","pagetype":"STORE_PRODUCT_LIST","name":"Ben","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r ben - Nordic gym","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Maskiner med viktmagasin f\u00f6r Ben","index_status":"index"},"produkter\/muskelgrupper\/brost":{"pageID":"95","pagetype":"STORE_PRODUCT_LIST","name":"Br\u00f6st","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r br\u00f6st - Nordic gym","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Maskiner med viktmagasin f\u00f6r Br\u00f6st","index_status":"index"},"produkter\/muskelgrupper\/bal":{"pageID":"97","pagetype":"STORE_PRODUCT_LIST","name":"B\u00e5l","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r B\u00e5l - Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Maskiner med viktmagasin \u2192 Styrketr\u00e4ning f\u00f6r B\u00e5l","index_status":"index"},"produkter\/muskelgrupper\/rygg":{"pageID":"98","pagetype":"STORE_PRODUCT_LIST","name":"Rygg","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r rygg - Nordic gym","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Maskiner med viktmagasin f\u00f6r Rygg","index_status":"index"},"produkter\/muskelgrupper\/ovrigt":{"pageID":"99","pagetype":"STORE_PRODUCT_LIST","name":"\u00d6vrigt","title":"Kvalitetsprodukter inom Styrketr\u00e4ning f\u00f6r \u00f6vriga kroppen - Nordic g","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Maskiner med viktmagasin f\u00f6r \u00d6vrigt","index_status":"index"},"produkter\/styrketraning\/bankar":{"pageID":"100","pagetype":"STORE_PRODUCT_LIST","name":"B\u00e4nkar","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 B\u00e4nkar \u2013 Nordic Gym","searchwords":"","description":"Svensktillverkade b\u00e4nkar f\u00f6r styrketr\u00e4ning fr\u00e5n Nordic Gym. Ergonomiska och h\u00e5llbara b\u00e4nkar designade f\u00f6r professionella gym och hemmatr\u00e4ning.","index_status":"index"},"produkter\/styrketraning\/maskiner-med-viktmagasin":{"pageID":"101","pagetype":"STORE_PRODUCT_LIST","name":"Maskiner med viktmagasin","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Maskiner med viktmagasin","searchwords":"","description":"Svensktillverkade styrketr\u00e4ningsmaskiner med viktmagasin fr\u00e5n Nordic Gym. H\u00e5llbara, ergonomiska maskiner som passar f\u00f6r b\u00e5de kommersiell och privat tr\u00e4nin","index_status":"index"},"produkter\/styrketraning\/draghissar":{"pageID":"102","pagetype":"STORE_PRODUCT_LIST","name":"Draghissar","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Draghissar \u2013 Nordic Gym","searchwords":"","description":"Uppt\u00e4ck svensktillverkade draghissar f\u00f6r styr
2761ketr\u00e4ning fr\u00e5n Nordic Gym. Ergonomisk design och h\u00e5llbar kvalitet f\u00f6r professionella och privata gymmilj\u00f6er.","index_status":"index"},"produkter\/styrketraning\/plate-loaded":{"pageID":"103","pagetype":"STORE_PRODUCT_LIST","name":"Plate loaded","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Plate loaded \u2013 Nordic Gym","searchwords":"","description":"Uppt\u00e4ck svensktillverkade plate loaded styrketr\u00e4ningsmaskiner fr\u00e5n Nordic Gym. Ergonomisk och h\u00e5llbar design f\u00f6r s\u00e4ker tr\u00e4ning p\u00e5 gym och hemmagym.","index_status":"index"},"produkter\/styrketraning\/stallningar":{"pageID":"104","pagetype":"STORE_PRODUCT_LIST","name":"St\u00e4llningar","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 St\u00e4llningar \u2013 Nordic Gym","searchwords":"","description":"Svensktillverkade styrketr\u00e4ningsst\u00e4llningar fr\u00e5n Nordic Gym. Ergonomiska och h\u00e5llbara st\u00e4llningar f\u00f6r s\u00e4ker tr\u00e4ning p\u00e5 gym och i hemmamilj\u00f6.","index_status":"index"},"produkter\/styrketraning\/skivstanger":{"pageID":"106","pagetype":"STORE_PRODUCT_LIST","name":"Skivst\u00e4nger","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Skivst\u00e4nger \u2013 Nordic Gym","searchwords":"","description":"Utforska skivst\u00e4nger f\u00f6r styrketr\u00e4ning fr\u00e5n Nordic Gym. Svensktillverkade och ergonomiska skivst\u00e4nger som erbjuder h\u00e5llbar kvalitet f\u00f6r effektiv tr\u00e4ning","index_status":"index"},"produkter\/styrketraning\/viktskivor":{"pageID":"107","pagetype":"STORE_PRODUCT_LIST","name":"Viktskivor","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Viktskivor \u2013 Nordic Gym","searchwords":"","description":"Utforska svensktillverkade viktskivor f\u00f6r styrketr\u00e4ning fr\u00e5n Nordic Gym. H\u00e5llbara och ergonomiska viktskivor som passar f\u00f6r b\u00e5de gym och hemmatr\u00e4ning.","index_status":"index"},"produkter\/styrketraning\/viktstall":{"pageID":"108","pagetype":"STORE_PRODUCT_LIST","name":"Viktst\u00e4ll","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Viktst\u00e4ll \u2013 Nordic Gym","searchwords":"","description":"Utforska svensktillverkade viktst\u00e4ll f\u00f6r styrketr\u00e4ning fr\u00e5n Nordic Gym. H\u00e5llbara och ergonomiska viktst\u00e4ll f\u00f6r s\u00e4ker och organiserad f\u00f6rvaring p\u00e5 gym ","index_status":"index"},"produkter\/styrketraning\/hantlar":{"pageID":"109","pagetype":"STORE_PRODUCT_LIST","name":"Hantlar","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Hantlar \u2013 Nordic Gym","searchwords":"","description":"Uppt\u00e4ck svensktillverkade hantlar f\u00f6r styrketr\u00e4ning fr\u00e5n Nordic Gym. Ergonomiska och slitstarka hantlar som passar b\u00e5de f\u00f6r professionellt och hemmabruk.","index_status":"index"},"produkter\/styrketraning\/kettlebells":{"pageID":"110","pagetype":"STORE_PRODUCT_LIST","name":"Kettlebells","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Kettlebells \u2013 Nordic Gym","searchwords":"","description":"Utforska svensktillverkade kettlebells f\u00f6r styrketr\u00e4ning fr\u00e5n Nordic Gym. H\u00e5llbara och ergonomiska kettlebells som passar f\u00f6r professionellt och hemmabruk.","index_status":"index"},"produkter\/styrketraning\/cirkeltraning":{"pageID":"111","pagetype":"STORE_PRODUCT_LIST","name":"Cirkeltr\u00e4ning","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Cirkeltr\u00e4ning \u2013 Nordic G","searchwords":"","description":"Utforska svensktillverkad utrustning f\u00f6r cirkeltr\u00e4ning inom styrketr\u00e4ning fr\u00e5n Nordic Gym. H\u00e5llbar och ergonomisk design f\u00f6r b\u00e5de gym och hemmatr\u00e4ning.","index_status":"index"},"produkter\/konditionstraning\/cyklar":{"pageID":"112","pagetype":"STORE_PRODUCT_LIST","name":"Cyklar","title":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Cyklar \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Cyklar","index_status":"index"},"produkter\/konditionstraning\/lopband":{"pageID":"113","pagetype":"STORE_PRODUCT_LIST","name":"L\u00f6pband","title":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 L\u00f6pband \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 L\u00f6pband","index_
2761status":"index"},"produkter\/konditionstraning\/roddmaskiner":{"pageID":"114","pagetype":"STORE_PRODUCT_LIST","name":"Roddmaskiner","title":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Roddmaskiner \u2013 Nordic","searchwords":"","description":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Roddmaskiner","index_status":"index"},"produkter\/konditionstraning\/crosstrainer":{"pageID":"115","pagetype":"STORE_PRODUCT_LIST","name":"Crosstrainer","title":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Crosstrainer \u2013 Nordic","searchwords":"","description":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Crosstrainer","index_status":"index"},"produkter\/konditionstraning\/trappmaskiner":{"pageID":"116","pagetype":"STORE_PRODUCT_LIST","name":"Trappmaskiner","title":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Trappmaskiner \u2013 Nordi","searchwords":"","description":"Kvalitetsprodukter inom Konditionstr\u00e4ning \u2192 Trappmaskiner","index_status":"index"},"produkter\/ovrig-traning\/uxcross":{"pageID":"117","pagetype":"STORE_PRODUCT_LIST","name":"UXCROSS","title":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 UXCROSS \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 UXCROSS","index_status":"index"},"produkter\/ovrig-traning\/rorlighet":{"pageID":"118","pagetype":"STORE_PRODUCT_LIST","name":"R\u00f6rlighet","title":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 R\u00f6rlighet \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 R\u00f6rlighet","index_status":"index"},"produkter\/ovrig-traning\/kettlebells":{"pageID":"119","pagetype":"STORE_PRODUCT_LIST","name":"Kettlebells","title":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Kettlebells \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Styrketr\u00e4ning \u2192 Kettlebells","index_status":"index"},"produkter\/ovrig-traning\/bollar":{"pageID":"120","pagetype":"STORE_PRODUCT_LIST","name":"Bollar","title":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 Bollar \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 Bollar","index_status":"index"},"produkter\/ovrig-traning\/ovriga-redskap":{"pageID":"121","pagetype":"STORE_PRODUCT_LIST","name":"\u00d6vriga redskap","title":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 \u00d6vriga redskap \u2013 Nordic","searchwords":"","description":"Kvalitetsprodukter inom \u00d6vrig tr\u00e4ning \u2192 \u00d6vriga redskap","index_status":"index"},"produkter\/tillbehor\/handtag":{"pageID":"122","pagetype":"STORE_PRODUCT_LIST","name":"Handtag","title":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2192 Handtag \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2192 Handtag","index_status":"index"},"produkter\/tillbehor\/stall":{"pageID":"131","pagetype":"STORE_PRODUCT_LIST","name":"St\u00e4ll","title":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2192 St\u00e4ll \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2192 St\u00e4ll","index_status":"index"},"produkter\/tillbehor\/mattor":{"pageID":"132","pagetype":"STORE_PRODUCT_LIST","name":"Mattor","title":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2192 Mattor \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom Tillbeh\u00f6r \u2192 Mattor","index_status":"index"},"produkter\/tillbehor\/underhall":{"pageID":"133","pagetype":"STORE_PRODUCT_LIST","name":"Underh\u00e5ll","title":"Kvalitetsprodukter inom Underh\u00e5ll \u2013 Nordic Gym","searchwords":"","description":"Kvalitetsprodukter inom underh\u00e5ll","index_status":"index"},"search\/products":{"pageID":"STORE_SEARCH","pagetype":"STORE_SYSTEM_PAGE","name":"Search","title":"Search","searchwords":"","description":"Search","index_status":"index"},"checkout":{"pageID":"STORE_CHECKOUT","pagetype":"STORE_SYSTEM_PAGE","name":"Checkout","title":"Checkout","searchwords":"","description":"Checkout","index_
2761status":"index"},"checkout\/confirmation":{"pageID":"STORE_CHECKOUT_CONFIRMATION","pagetype":"STORE_SYSTEM_PAGE","name":"Order confirmation","title":"Order confirmation","searchwords":"","description":"Order confirmation","index_status":"index"},"product":{"pageID":"STORE_PRODUCT","pagetype":"STORE_PRODUCT_PAGE","name":"Product","title":" - Product","searchwords":"","description":" - Product","index_status":"index"}}},"CART":{"cart_active":true,"icon_top":20,"icon_right":20,"label_title":"Cart","label_table_price":"Price","label_table_sum":"Total","label_cart_empty":"Your cart is empty","label_total_sum":"Total price","label_button_checkout":"Go to checkout","label_button_close":"Keep shopping"},"CHECKOUT":{"label_title_cart":"Cart","label_title_payment":"Shipping & payment","label_table_price":"Price","label_table_sum":"Total","label_cart_empty":"Your cart is empty","label_total_sum":"Total price","label_total_sum_before":"Price before discount","label_total_discount":"Discount","label_discountcode_input":"Discount code","label_discountcode_button":"Register","label_button_back_cart":"Change in the cart","label_button_payment":"Go to payment","label_productOutStock":"Out of stock"},"PRODUCT_LIST":{"langCode":"en","lazyLoad":false,"max_price":"4000","sortID_default":"1001","label_items_count":"products found in","label_items_empty":"No products here","label_search_placeholder":"Search","label_range_to":"to","label_range_price":"Price","label_range_price_unit":"$","label_filter_reset":"Reset filters","label_filter_show":"Show filters","label_filter_hide":"Show hits","label_filter_expand":"Show more","label_filter_collapse":"Show less","label_filter_vendor":"Brand","label_sort_title_asc":"Name, A-Z","label_sort_title_desc":"Name, Z-A","label_sort_brand_asc":"Brand, A-Z","label_sort_brand_desc":"Brand, Z-A","label_sort_price_asc":"Price, lowest first","label_sort_price_desc":"Price, highest first"},"PRODUCT_SINGLE":{"stockUsed":true,"label_currency":"Price, highest first","label_quantity_end":"in store","label_buttonInStock":"Add to cart","label_buttonOutStock":"Out of stock"}});
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.