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