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 9Handlebars.registerHelper('t', function (i18n_key) { 10 var result = i18n.t(i18n_key); 11 12 return new Handlebars.SafeString(result); 13}); 14 15Handlebars.registerHelper('eur', function (amount) { 16 var result = accounting.formatMoney(amount); 17 18 return new Handlebars.SafeString(result); 19}); 20 21// Settings object that controls default parameters for library methods: 22accounting.settings = { 23 currency: { 24 symbol: "â¬", // default currency symbol is '$' 25 format: "%v %s", // controls output: %s = symbol, %v = value/number (can be object: see below) 26 decimal: ",", // decimal point separator 27 thousand: ".", // thousands separator 28 precision: 2 // decimal places 29 }, 30 number: { 31 precision: 0, // default precision on numbers is 0 32 thousand: ".", 33 decimal: "," 34 } 35}; 36 37app.Item = Backbone.Model.extend({ 38 product: null, 39 itemId: null, 40 quantity: 0, 41 option: null, 42 itemGross: 0, 43 lineGross: 0, 44 45 initialize: function() { 46 _.bindAll(this, "getItemGross"); 47 _.bindAll(this, "getLineGross"); 48 49 this.on("change:product", function(model, product) { 50 model.cid = product.id; 51 model.itemId = product.id; 52 model.product = product; 53 model.trigger("calculate",model); 54 }); 55 this.on("change:option", function(model, option) { 56 if (option) { 57 model.cid = model.product.id+"|"+option.optionId; 58 model.itemId = option.optionId; 59 model.option = option; 60 } 61 model.trigger("calculate",model); 62 }); 63 this.on("change:quantity", function(model, quantity) { 64 model.quantity = quantity; 65 model.trigger("calculate",model); 66 }); 67 this.on("calculate", function(model) { 68 model.itemGross = model.getItemGross(); 69 model.lineGross = model.getLineGross(); 70 }); 71 }, 72 73 getItemGross: function() { 74 var result = parseFloat(this.product.get("priceGross")); 75 76 var item = this; 77 $.each(this.product.get("productScalePriceList"), function(index,scale) { 78 if (item.quantity >= scale.quantity) { 79 result = scale.priceGross; 80 } 81 }); 82 83 return result; 84 }, 85 86 getLineGross: function() { 87 return this.quantity * this.getItemGross(); 88 } 89}); 90 91app.ShoppingCart = Backbone.Collection.extend({ 92 model: app.Item, 93 storageKeyCart: PebblesConfig.storageKeyCart, 94 storageKeyFredItems: PebblesConfig.storageKeyFredItems, 95 totalGross: 0, 96 shippingCosts: 0, 97 subTotal: 0, 98 trackChanges: false, 99 100 initialize: function() { 101 _.bindAll(this, "addFromNode"); 102 _.bindAll(this, "addItem"); 103 _.bindAll(this, "setItemQuantity"); 104 _.bindAll(this, "getTotalGross"); 105 _.bindAll(this, "fredItems"); 106 _.bindAll(this, "checkout"); 107 108 this.on("reset", function(collection, options) { 109 collection.totalGross = 0; 110 collection.shippingCosts = 0; 111 collection.subTotal = 0; 112 113 collection.sync("delete"); 114 collection.trigger("render"); 115 }); 116 117 this.on("update", function(collection, options) { 118 collection.sync("update"); 119 collection.totalGross = collection.getTotalGross(); 120 collection.shippingCosts = (collection.totalGross >= PebblesConfig.freeShippingFromRevenue) ? 0 : collection.getShippingCosts(); 121 collection.subTotal = collection.totalGross + collection.shippingCosts; 122 123 if (collection.trackChanges) collection.tracking(); 124 collection.trigger("render"); 125 }); 126 127 // Assign the Deferred issued by fetch() as a property 128 this.deferred = this.fetch(); // calls overridden sync() 129 }, 130 131 addFromNode: function(button) { 132 var scope = button.closest(".product");
133 var productId = scope.find(".item_id").html(); 134 135 var form = scope.find("form")[0]; 136 if (!form.reportValidity()) return; 137 138 var quantity = parseInt(scope.find(".item_quantity").val()); 139 var opt = scope.find(".item_option").val(); 140 var option = (typeof(opt)==="undefined") ? null : JSON.parse(opt); 141 142 if (PebblesConfig.checkItemsOnStock) { 143 app.catalog.fetchProductData(productId).done(function (apiResponse) { 144 var product = (apiResponse && apiResponse.productList) ? apiResponse.productList[0] : null; 145 if (option) { 146 product = product.productOptionList.find(function(optionEntry) { 147 return optionEntry.productId === option.optionId; 148 }); 149 } 150 var key = productId + ( (option) ? "|" + option.optionId : "" ); 151 var item = app.cart.get(key); 152 153 var requiredQuantity = quantity + ( (item) ? item.quantity : 0 ); 154 if (requiredQuantity > product.itemsOnStock) { 155 var msgOutOfStock = PebblesConfig.messages.ShopProductOutOfStock.replace("{itemsOnStock}", product.itemsOnStock); 156 scope.find(".product_detail_outofstock").show().find("div").html( msgOutOfStock ); 157 } else { 158 scope.find(".product_detail_outofstock").hide().find("div").html( "" ); 159 app.cart.addItem(productId, quantity, option); 160 } 161 }); 162 } else { 163 this.addItem(productId, quantity, option); 164 } 165 }, 166 167 addItem: function(productId, quantity, option) { 168 if (typeof(option) === "undefined") option = null; 169 var key = productId + ( (option) ? "|" + option.optionId : "" ); 170 var item = this.get(key); 171 if (item) { 172 item.set("quantity", item.quantity+quantity); 173 if (item.quantity <= 0) this.remove(item); 174 } else { 175 item = new app.Item(); 176 var product = app.catalog.products.get( productId ); 177 if (typeof(product) === "undefined") { 178 this.remove(item); 179 } else { 180 item.set("product", app.catalog.products.get( productId )); 181 item.set("option", option); 182 item.set("quantity", quantity); 183 184 this.add(item); 185 } 186 } 187 188 this.trigger("update",this); 189 }, 190 191 setItemQuantity: function(productId, quantity, option) { 192 if (typeof(option) === "undefined") option = null; 193 var key = productId + ( (option) ? "|" + option.optionId : "" ); 194 var item = this.get(key); 195 if (item) { 196 item.set("quantity", quantity); 197 } 198 199 this.trigger("update",this); 200 }, 201 202 getItemQuantitySum: function() { 203 return this.reduce(function(memo, item) { 204 return memo + item.quantity; 205 }, 0); 206 }, 207 208 getTotalGross: function() { 209 return this.reduce(function(memo, item) { 210 return memo + item.lineGross; 211 }, 0); 212 }, 213 214 getShippingCosts: function() { 215 return this.reduce(function(memo, item) { 216 var shippingCosts = parseFloat(item.product.get("shippingCosts")); 217 return (shippingCosts>memo) ? shippingCosts : memo; 218 }, 0); 219 }, 220 221 tracking: function() { 222 if (PebblesConfig.trackingImplementation=="piwik") { 223 this.each(function(item) { 224 _paq.push(['addEcommerceItem', 225 item.product.id, // (required) SKU: Product unique identifier 226 item.product.get("productName") + ( (item.option) ? (" - "+item.option.optionAttribute+": "+item.option.optionValue) :"") , // (optional) Product name 227 [], // (optional) Product category, string or array of up to 5 categories 228 item.quantity, // (recommended) Product price 229 item.get("itemGross") // (optional, default to 1) Product quantity 230 ]); 231 }); 232 233 _paq.push(['trackEcommerceCartUpdate',app.cart.getTotalGross()]); // (required) Cart amount 234 _paq.push(["setDocumentTitle", "Shop: Cart Update"]); 235 _paq.push(['trackPageView']); 236 } 237 }, 238 239 fredItems: function() { 240 return this.map(function(item) { 241 return { 242 isOption: (item.option!==null), 243 productId: item.itemId, 244 quantity: item.quantity 245 }; 246 }); 247 }, 248 249 sync: function(method, model, options) { 250 var shoppingCart = this; 251 var defer = $.Deferred(); 252 253 if ( (method === "create") || (method === "update") ) { 254 255 var cartData = new Array(); 256 this.each(function(model) { 257 var item = { 258 productId : model.product.id, 259 quantity : model.quantity, 260 option : model.option 261 }; 262 cartData.push(item); 263 }); 264 PebblesConfig.storage.setItem(this.storageKeyCart, JSON.stringify(cartData)); 265 PebblesConfig.storage.setItem(this.storageKeyFredItems, JSON.stringify(this.fredItems())); 266 } else if (method === "delete") { 267 PebblesConfig.storage.removeItem(this.storageKeyCart); 268 } else { 269 var storedCart = PebblesConfig.storage.getItem(shoppingCart.storageKeyCart); 270 if (storedCart) { 271 var cartData = JSON.parse(storedCart); 272 $.each(cartData, function(index, value) { 273 shoppingCart.addItem(value.productId, value.quantity, value.option); 274 }); 275 } 276 this.trackChanges = true; 277 } 278 279 defer.resolve(); 280 return defer.promise(); 281 }, 282 283 checkout: function() { 284 var form = $("<form style='display:block'></form>").appendTo("body"); 285 form.attr('action', PebblesConfig.checkoutUrl); 286 form.attr('method', "POST"); 287 288 // add items to data 289 fredItems = JSON.stringify( this.fredItems() ); 290 form.append($("<input>
290").attr("type","hidden").attr("name","items").val(fredItems)); 291 292 if (PebblesConfig.accessToken) { 293 form.append($("<input>").attr("type","hidden").attr("name","accessToken").val(PebblesConfig.accessToken)); 294 form.append($("<input>").attr("type","hidden").attr("name","entryId").val(PebblesConfig.entryId)); 295 } 296 297 form.submit(); 298 299 form.remove(); 300 } 301}); 302 303/******************************************************************* 304 * CART VIEW 305 *******************************************************************/ 306 app.ShoppingCartView = Backbone.View.extend({ 307 btnTemplate: Handlebars.compile($("#btnShoppingCartTemplate").html()), 308 itemsTemplate: Handlebars.compile($("#itemsShoppingCartTemplate").html()), 309 timeout: null, 310 initialize: function(){ 311 this.btnEl = $("#btnShoppingCart"); 312 this.itemsEl = $("#cart_items"); 313 this.collection.on("render", this.render, this); 314 }, 315 render: function () { 316 if (PebblesConfig.disableCart) { 317 console.debug("shopping cart disabled - skipping renderinng"); 318 $(".productBasketStickyWrap").hide(); 319 return; 320 } 321 322 console.debug("shopping cart render"); 323 324 var view = this; 325 326 view.collection.deferred.then(function () { 327 var templateData = { 328 items: view.collection.toArray(), 329 totalGross: view.collection.totalGross, 330 freeShippingFromRevenue: PebblesConfig.freeShippingFromRevenue, 331 noFreeShipping: PebblesConfig.noFreeShipping, 332 shippingCosts: view.collection.shippingCosts, 333 subTotal: view.collection.subTotal, 334 itemQuantitySum: view.collection.getItemQuantitySum() 335 }; 336 337 var btnRendered = view.btnTemplate(templateData); 338 view.btnEl.html(btnRendered); 339 340 var itemsRendered = view.itemsTemplate(templateData); 341 view.itemsEl.html(itemsRendered); 342 343 // view.items = $("#cart_items"); 344 }); 345 346 return view; 347 } 348}); 349
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.