1/* 2 * FRED SHOP 3 * (C) Copyright 2016 eGenius oHG (http://www.egenius.de) and others. 4 */ 5 6var app = app || {}; 7var _paq = _paq || []; 8 9/* 10 * Models 11 */ 12 13app.Catalog = Backbone.Model.extend({ 14 initialize: function () { 15 this.productCategories = new app.ProductCategoryList(); 16 this.products = new app.ProductList(); 17 18 // Assign the Deferred issued by fetch() as a property 19 this.deferred = this.fetch(); // calls overridden sync() 20 }, 21 sync: function (method, model, options) { 22 var catalog = this; 23 24 var defer = $.Deferred(); 25 26 if (PebblesConfig.catalogSrc === "URL") { 27 return this.fetchFromURL(catalog, defer); 28 } else if (PebblesConfig.catalogSrc === "PebblesConfig") { 29 return this.fetchFromPebblesConfig(catalog, defer); 30 } else { 31 return this.fetchFromAPI(catalog, defer); 32 } 33 }, 34 fetchFromURL: function(catalog, defer) { 35 console.log("fetching catalog from URL " + PebblesConfig.shopDataUrl); 36 $.getJSON(PebblesConfig.shopDataUrl, function( response ) { 37 PebblesConfig.storage.setItem("Pebbles.apiData", JSON.stringify(response)); 38 catalog.productCategories.add(response.productCategoryListResult.productCategoryList); 39 catalog.products.add(response.productListResult.productList, {parse: true}); 40 41 defer.resolve(); 42 }); 43 44 return defer.promise(); 45 }, 46 fetchFromPebblesConfig: function(catalog, defer) { 47 console.log("using catalog from PebblesConfig"); 48 catalog.productCategories.add(PebblesConfig.shopData.productCategoryListResult.productCategoryList); 49 catalog.products.add(PebblesConfig.shopData.productListResult.productList, {parse: true}); 50 51 defer.resolve(); 52 return defer.promise(); 53 }, 54 fetchFromAPI: function(catalog,defer) { 55 // try to get from cache 56 var apiCache = JSON.parse(PebblesConfig.storage.getItem("Pebbles.shopData")); 57 if (false) { 58 console.log("using catalog from cache"); 59 catalog.productCategories.add(apiCache.productCategoryListResult.productCategoryList); 60 catalog.products.add(apiCache.productListResult.productList, {parse: true}); 61 62 defer.resolve(); 63 return defer.promise(); 64 } 65 66 // not in cache 67 console.log("fetching catalog from api"); 68 $.ajax({
69 url: PebblesConfig.apiEndpoint + "/getShopData", 70 type: "POST", 71 contentType: "application/x-www-form-urlencoded", 72 dataType: "json", 73 data: { 74 eventId: PebblesConfig.eventId 75 , key: PebblesConfig.apiKey 76 , apiRequest: "{ header: {} }" 77 } 78 }) 79 .done(function (response) { 80 PebblesConfig.storage.setItem("Pebbles.shopData", JSON.stringify(response)); 81 catalog.productCategories.add(response.productCategoryListResult.productCategoryList); 82 catalog.products.add(response.productListResult.productList, {parse: true}); 83 84 defer.resolve(); 85 }); 86 87 return defer.promise(); 88 }, 89 fetchProductData: function(productId) { 90 var apiRequest = { 91 header: {}, 92 data: { 93 productIdFilter: productId 94 } 95 }; 96 97 return $.ajax({ 98 url: PebblesConfig.apiEndpoint + "/getProductList", 99 type: "POST", 100 contentType: "application/x-www-form-urlencoded", 101 dataType: "json", 102 data: { 103 eventId: PebblesConfig.eventId 104 , key: PebblesConfig.apiKey 105 , apiRequest: JSON.stringify(apiRequest) 106 } 107 }) 108 } 109}); 110 111app.Product = Backbone.Model.extend({ 112 idAttribute: "productId", 113 initialize: function () { 114 this.updateDerivedAttributes(); 115 this.on("change:priceGross", this.updateDerivedAttributes, this); 116 // because initialize is called after parse 117 _.defaults(this, { 118 productOptionList: new app.ProductOptionList 119 }); 120 }, 121 parse: function (response) { 122 if (_.has(response, "productOptionList")) { 123 var availableOptions = response.productOptionList.filter(function(option) { 124 return ( (!PebblesConfig.checkItemsOnStock) || (option.itemsOnStock > 0)); 125 }); 126 this.productOptionList = new app.ProductOptionList(availableOptions, { 127 parse: true 128 }); 129 delete response.productOptionList; 130 } 131 if ( ( (!response.imageURLs) || (response.imageURLs.length<=0) ) && (PebblesConfig.isTestSystem) ) { 132 response.imageURLs = ["//placehold.it/1024x768/529223/FFFFFF.jpg?text=" + encodeURI(response.productName), 133 "//placehold.it/1024x768/529223/FFFFFF.jpg?text=" + encodeURI(response.productName+" 2"), 134 "//placehold.it/1024x768/529223/FFFFFF.jpg?text=" + encodeURI(response.productName+" 3"), 135 "//placehold.it/1024x768/529223/FFFFFF.jpg?text=" + encodeURI(response.productName+" 4"), 136 ]; 137 } 138 return response; 139 }, 140 toJSON: function () { 141 var json = _.clone(this.attributes); 142 json.productOptionList = this.productOptionList.toJSON(); 143 json.optionObject = JSON.stringify({ 144 optionId: json.productId, 145 optionAttribute: json.productOptionsAttribute, 146 optionValue: json.optionValue 147 }); 148 return json; 149 }, 150 get: function (attr) { 151 switch (attr) { 152 case "descriptionHtml": 153 var descriptionHtml = Backbone.Model.prototype.get.call(this, attr); 154 if ( ( (!descriptionHtml) || (descriptionHtml.length<=0) ) && (PebblesConfig.isTestSystem) ) { 155 descriptionHtml = faker.lorem.paragraph() + "<br/><br/>" + faker.lorem.paragraph() + "<br/><br/>" + faker.lorem.paragraph(); 156 } 157 return descriptionHtml; 158 case "teaserHtml": 159 return (PebblesConfig.isTestSystem && faker) ? faker.lorem.paragraph() : ""; 160 case "recommendedProducts": 161 var products = []; 162 $(this.get("recommendedProductIds")).each(function (index, productId) { 163 var product = app.catalog.products.get(productId); 164 products.push(product); 165 }); 166 return products; 167 default: 168 return Backbone.Model.prototype.get.call(this, attr); 169 } 170 }, 171 updateDerivedAttributes: function () { 172 this.set({ 173 previewImage: (this.get("imageURLs") && (this.get("imageURLs").length > 0)) ? this.get("imageURLs")[0] : null, 174 descriptionHtml: this.get("descriptionHtml"), 175 teaserHtml: this.get("teaserHtml") 176 }, {silent: true}); 177 } 178}); 179 180app.ProductCategory = Backbone.Model.extend({ 181 idAttribute: "productCategoryId", 182 get: function (attr) { 183 switch (attr) { 184 case "products": 185 var products = []; 186 $(this.get("productIds")).each(function (index, productId) { 187 var product = app.catalog.products.get(productId); 188 products.push(product); 189 }); 190 return products; 191 default: 192 return Backbone.Model.prototype.get.call(this, attr); 193 } 194 }, 195}); 196 197/* 198 * Collections 199 */ 200 201app.ProductOptionList = Backbone.Collection.extend({ 202 model: app.Product 203}); 204 205app.ProductList = Backbone.PageableCollection.extend({ 206 model: app.Product 207 , state: { 208 pageSize: 18 209 } 210}); 211 212app.ProductCategoryList = Backbone.Collection.extend({ 213 model: app.ProductCategory 214}); 215 216/* 217 * Initializations 218 */ 219 220// Initiate the app.catalog 221app.catalog = new app.Catalog(); 222 223/*
224 * Views 225 */ 226 227app.ProductListView = Backbone.View.extend({ 228 el: $("#productListing"), 229 gotoPage: 1, 230 navigation: "", 231 template: Handlebars.compile($("#productListTemplate").html()), 232 paginationTemplate: Handlebars.compile($("#productPaginationTemplate").html()), 233 render: function () { 234 var view = this, rendered; 235 var state = this.collection.state; 236 237 var products = view.collection.getPage(view.gotoPage); 238 state.currentPage = view.gotoPage; 239 240 var pages = []; 241 for (var i = state.firstPage; i <= state.lastPage; i++) { 242 var page = { 243 pageNumber: i, 244 active: (i === state.currentPage), 245 navigation: view.navigation 246 }; 247 pages.push(page); 248 } 249 250 // 1: Product Lising 251 rendered = view.template({ 252 products: products.toJSON() 253 }); 254 $(view.el).append(rendered); 255 256 // 2: Pagination 257 $(".product_pagination").remove(); 258 if (state.lastPage > 1) { 259 rendered = view.paginationTemplate({ 260 pages: pages 261 }); 262 $(view.el).after(rendered); 263 } 264 265 return view; 266 } 267}); 268 269app.ProductDetailView = Backbone.View.extend({ 270 el: $("#productListing"), 271 template: Handlebars.compile($("#productDetailTemplate").html()), 272 carouselTemplate: Handlebars.compile($("#recommendationCarouselTemplate").html()||""), 273 render: function () { 274 var view = this, rendered; 275 276 app.catalog.deferred.then(function () { 277 var product = view.collection.get(view.id); 278 279 rendered = view.template(product.toJSON()); 280 $(view.el).html(rendered); 281 282 if (PebblesConfig.disableCart) { 283 console.debug("shopping cart disabled - removing controls"); 284 $(".item_quantity_wrap").addClass("hidden"); 285 $(".product_detail_tocart").find(".btn").addClass("hidden"); 286 } 287 288 var productList = new app.ProductList(product.get('recommendedProducts'), { mode: "client" }); 289 const carousel = view.carouselTemplate({ 290 caption: 'Empfehlungen', 291 products: productList.toJSON() 292 }); 293 $(view.el).append(carousel); 294 295 PebblesConfig.setShopBackground(false); 296 297 app.breadcrumbs.levels.push({ 298 name: product.get("productName") 299 , navigation: "/product/"+product.get("productId") 300 }); 301 app.breadcrumbs.render(); 302 303 $(".product_pagination").remove(); 304 scrollToShopHeader(); 305 306 $( "#item_option" ).selectmenu({ 307 icons: { button: "countersink" } 308 , width: 250 309 }); 310 311 $( "#item_quantity" ).selectmenu({ 312 icons: { button: "countersink" } 313 , width: 100 314 }); 315 316 if (PebblesConfig.trackingImplementation=="piwik") { 317 _paq.push(['setEcommerceView', 318 product.get("productId"), // (required) SKU: Product unique identifier 319 product.get("productName"), // (optional) Product name 320 [], // (optional) Product category, or array of up to 5 categories 321 product.get("priceGross") // (optional) Product Price as displayed on the page 322 ]); 323 _paq.push(["setDocumentTitle", "Shop-Artikel: "+product.get("productId")+" "+product.get("productName")]); 324 _paq.push(['trackPageView']); 325 } 326 }); 327 328 if ($("#cart_area").unstick) $("#cart_area").unstick(); 329 330 return view; 331 } 332}); 333 334app.ProductCategoryNavigationView = Backbone.View.extend({ 335 el: $("#productCategoryNavigation"), 336 template: Handlebars.compile($("#productCategoryNavigationTemplate").html()), 337 render: function () { 338 var view = this; 339 340 $(view.collection.models).each(function (index, category) { 341 var rendered = view.template(category.toJSON()); 342 $(view.el).append(rendered); 343 }); 344 345 $("#shop_nav_categories").fadeIn(); 346 347 return view; 348 } 349}); 350 351/* 352app.ProductCarouselView = Backbone.View.extend({ 353 el: $("#productListing"), 354 template: Handlebars.compile($("#productCarouselTemplate").html()||""), 355 render: function () { 356 var view = this, rendered; 357 358 if ($("#productCarouselTemplate").length == 0) { 359 return view; 360 } 361 362 var rendered = view.template({}); 363 $(view.el).append(rendered); 364 365 return view; 366 } 367}); 368*/ 369 370app.ProductCategoryListView = Backbone.View.extend({ 371 el: $("#productListing"), 372 template: Handlebars.compile($("#productCategoryListTemplate").html()), 373 carouselTemplate: Handlebars.compile($("#highlightCarouselTemplate").html()||""), // 20220321/msc
373h: DEPRECATED 374 highlightCategoryDisplayMode: 'highlight', 375 render: function () { 376 var view = this, rendered; 377 378 $(view.el).html(""); 379 PebblesConfig.setShopBackground(true); 380 $("#cart_area").show(); 381 382 app.catalog.deferred.then(function () { 383 if (PebblesConfig.namespace === 'KAT') { // 20220321/msch: DEPRECATED 384 // 1: render highlight category products in carousel 385 $(view.collection.models).each(function (index, category) { 386 if (category.get('displayMode') !== view.highlightCategoryDisplayMode) return; 387 388 var productList = new app.ProductList(category.get('products'), { mode: "client" }); 389 const carousel = view.carouselTemplate({ 390 products: productList.toJSON() 391 }); 392 $(view.el).append(carousel); 393 }); 394 395 // 2: append categories in overwiew 396 $(view.collection.models).each(function (index, category) { 397 if (category.get('displayMode') === 'highlight') return; 398 399 const rendered = view.template(category.toJSON()); 400 $(view.el).append(rendered); 401 }); 402 } else { 403 const highlightCategory = view.collection.models.find(category => category.get('displayMode') === view.highlightCategoryDisplayMode); 404 const templateParam = { 405 highlightCategory: (highlightCategory ? highlightCategory.toJSON() : null), 406 } 407 408 // load highlight products 409 if (templateParam.highlightCategory) { 410 const productList = new app.ProductList(highlightCategory.get('products'), { mode: "client" }); 411 templateParam.highlightCategory.products = (productList ? productList.toJSON() : null); 412 } 413 414 const categoryList = view.collection.models.filter(category => category.get('displayMode') !== view.highlightCategoryDisplayMode); 415 templateParam.categoryList = (categoryList ? categoryList.flatMap(e => e.toJSON()) : null); 416 417 console.log(templateParam); 418 const rendered = view.template(templateParam); 419 $(view.el).append(rendered); 420 } 421 422 // xmas2021 scrollToShopHeader(); 423 }); 424 425 $(".product_pagination").remove(); 426 if ($("#cart_area").unstick) $("#cart_area").unstick(); 427 428 if (PebblesConfig.trackingImplementation=="piwik") { 429 // track page view 430 _paq.push(["setDocumentTitle", "Shop: Storefront"]); 431 _paq.push(['trackPageView']); 432 } 433 434 return view; 435 } 436}); 437 438app.ProductCategoryDetailView = Backbone.View.extend({ 439 el: $("#productListing"), 440 template: Handlebars.compile($("#categoryHeaderTemplate").html()), 441 page: 1, 442 render: function () { 443 var view = this, rendered; 444 445 $(view.el).html(""); 446 PebblesConfig.setShopBackground(true); 447 $("#cart_area").show(); 448 449 app.catalog.deferred.then(function () { 450 var category = view.collection.get(view.id); 451 452 rendered = view.template(category.toJSON()); 453 $(view.el).append(rendered); 454 455 var products = category.get('products'); 456 var productList = new app.ProductList(products, { mode: "client" }); 457 var productView = new app.ProductListView({collection: productList}); 458 productView.gotoPage = view.page; 459 productView.navigation = "/category/"+category.get("productCategoryId")+"/"+category.get("productCategoryNamePrettyUrl"); 460 productView.render(); 461 462 app.breadcrumbs.levels = [{ 463 name: category.get("productCategoryName") 464 , navigation: productView.navigation 465 }]; 466 app.breadcrumbs.render(); 467 468 // track category view 469 if (view.page===1) { 470 if (PebblesConfig.trackingImplementation=="piwik") { 471 // on a category page, productSKU and productName are not applicable and are set to false 472 _paq.push(['setEcommerceView', 473 false, // productSku No product on Category page 474 false, // productName No product on Category page 475 category.get("productCategoryName") // category 476 ]); 477 _paq.push(["setDocumentTitle", "Shop-Kategorie: "+category.get("productCategoryName")]); 478 _paq.push(['trackPageView']); 479 } 480 } 481 }); 482 483 scrollToShopHeader(); 484 // $("#cart_area").sticky({topSpacing: 0}); 485 486 return view; 487 } 488}); 489 490function scrollToShopHeader() { 491 var offset = $("#fred_shop").offset(); 492 offset.left -= 20;
493 // offset.top -= 20; 494 495 window.scrollTo(offset.left, offset.top); 496} 497 498app.ProductSearch = { 499 searching: false, 500 searchString: "", 501 page: 1, 502 run: function (searchString, page) { 503 var ps = this; 504 if (ps.searching) return; 505 506 ps.searching = true; 507 ps.page = page; 508 ps.seachString = searchString; 509 510 app.catalog.deferred.then(function () { 511 var compareTo = ps.seachString.toUpperCase(); 512 var findings = app.catalog.products.filter(function (model) { 513 return _.any(model.attributes, function (val, attr) { 514 return (("" + val).toUpperCase().indexOf(compareTo) >= 0); 515 }); 516 }); 517 518 app.breadcrumbs.levels = [{ 519 name: "Suche \""+ps.seachString+"\"" 520 , navigation: "/query/"+ps.seachString 521 }]; 522 app.breadcrumbs.render(); 523 524 scrollToShopHeader(); 525 526 $("#productListing").html(""); 527 PebblesConfig.setShopBackground(true); 528 $("#cart_area").show(); 529 530 var productList = new app.ProductList(findings, { mode: "client" }); 531 var productView = new app.ProductListView({collection: productList}); 532 productView.gotoPage = ps.page; 533 productView.navigation = "/query/"+ps.seachString; 534 productView.render(); 535 536 app.router.navigate(productView.navigation); 537 538 ps.searching = false; 539 }); 540 } 541};
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.