import Common; import js.JQuery; /** * JS Shopping Cart * * @author fbarbut */ class ShopCart { public var products : Map; //product db public var productsArray : Array; //to keep order of products public var categories : Array<{name:String,pinned:Bool,categs:Array}>; //categ db public var pinnedCategories : Array<{name:String,pinned:Bool,categs:Array}>; //categ db public var order : OrderInSession; var loader : JQuery; //ajax loader gif //for scroll mgmt var cartTop : Int; var cartLeft : Int; var cartWidth : Int; var jWindow : JQuery; var cartContainer : JQuery; var date : String; var place : Int; public function new() { products = new Map(); productsArray = []; order = cast { products:[] }; categories = []; pinnedCategories = []; } public function add(pid:Int) { loader.show(); var q = App.j('#productQt' + pid).val(); var qt = 0.0; var p = this.products.get(pid); if (p.hasFloatQt) { q = StringTools.replace(q, ",", "."); qt = Std.parseFloat(q); }else { qt = Std.parseInt(q); } if (qt == null) { qt = 1; } //trace("qté : "+qt); //add server side var r = new haxe.Http('/shop/add/$pid/$qt'); r.onData = function(data:String) { loader.hide(); var d = haxe.Json.parse(data); if (!d.success) js.Browser.alert("Erreur : "+d); //add locally subAdd(pid, qt); render(); } r.request(); } function subAdd(pid, qt:Float ) { for ( p in order.products) { if (p.productId == pid) { p.quantity += qt; render(); return; } } order.products.push( { productId:pid, quantity:qt } ); } /** * Render the shopping cart and total */ function render() { var c = App.j("#cart"); c.empty(); //render items in shopping cart c.append( Lambda.map(order.products, function( x ) { var p = this.products.get(x.productId); if (p == null) { //the product may have been disabled by an admin return ""; } var btn = " "; return "
" + x.quantity + " x " + p.name+"
"+btn+"
"; }).join("\n") ); //compute total price var total = 0.0; for (p in order.products) { var pinfo = products.get(p.productId); if (pinfo == null) continue; total += p.quantity * pinfo.price; } var ffilter = new sugoi.form.filters.FloatFilter(); var total = ffilter.filterString(Std.string(App.roundTo(total,2))); c.append("
TOTAL : " + total + "
"); if (order.products.length > 0){ App.instance.setWarningOnUnload(true,"Vous avez une commande en cours. Si vous quittez cette page sans confirmer, votre commande sera perdue."); }else{ App.instance.setWarningOnUnload(false); } } function findCategoryName(cid:Int):String{ for ( cg in this.categories ){ for (c in cg.categs){ if (cid == c.id) { return c.name; } } } for ( cg in this.pinnedCategories ){ for (c in cg.categs){ if (cid == c.id) { return c.name; } } } return null; } /** * Dynamically sort products by categories */ public function sortProductsBy(){ //store products by groups var groups = new Map}>(); var pinned = new Map}>(); trace(this.categories); var firstCategGroup = null; if (this.categories.length > 0){ firstCategGroup = this.categories[0].categs; } //trace(firstCategGroup); //trace(pinnedCategories); var pList = this.productsArray.copy(); //for ( p in pList) trace(p.name+" : " + p.categories); //trace("----------------"); //sort by categs for ( p in pList.copy() ){ //trace(p.name+" : " + p.categories); untyped p.element.remove(); for ( categ in p.categories){ if (firstCategGroup != null && Lambda.find(firstCategGroup, function(c) return c.id == categ) != null){ //is in this category group var g = groups.get(categ); if ( g == null){ var name = findCategoryName(categ); g = {name:name,products:[]}; } g.products.push(p); //trace("remove " + p.name); pList.remove(p); groups.set(categ, g); } else{ // is in pinned group ? var isInPinnedCateg = false; for ( cg in pinnedCategories){ if (Lambda.find(cg.categs, function(c) return c.id == categ) != null){ isInPinnedCateg = true; break; } } if (isInPinnedCateg){ var c = pinned.get(categ); if ( c == null){ var name = findCategoryName(categ); c = {name:name,products:[]}; } c.products.push(p); //trace( "add " + p.name+" in PINNED"); pList.remove(p); pinned.set(categ, c); }else{ //not in the selected categ nor in pinned groups continue; } } } } //if some untagged products remain if (pList.length > 0){ groups.set(0,{name:"Autres",products:pList}); } //trace("----------------"); //render var container = App.j(".shop .body"); //render firts "pinned" groups , then "groups" for ( source in [pinned, groups]){ for (o in source){ if (o.products.length == 0) continue; container.append("
" + o.name + "
"); for ( p in o.products){ //trace("GROUP "+o.name+" : "+p.name); //if the element has already been inserted, we need to clone it if (untyped p.element.parent().length == 0){ container.append( untyped p.element ); }else{ var clone = untyped p.element.clone(); container.append( clone ); } } } } App.j(".product").show(); } /** * is shopping cart empty ? */ public function isEmpty(){ return order.products.length == 0; } /** * submit cart */ public function submit() { var req = new haxe.Http("/shop/submit"); req.onData = function(d) { App.instance.setWarningOnUnload(false); js.Browser.location.href = "/shop/validate/"+place+"/"+date; } req.addParameter("data", haxe.Json.stringify(order)); req.request(true); } /** * filter products by category */ public function filter(cat:Int) { //icone sur bouton App.j(".tag").removeClass("active").children().remove("span");//clean var bt = App.j("#tag" + cat); bt.addClass("active").prepend(" "); //affiche/masque produits for (p in products) { if (cat==0 || Lambda.has(p.categories, cat)) { App.j(".shop .product" + p.id).fadeIn(300); }else { App.j(".shop .product" + p.id).fadeOut(300); } } } /** * remove a product from cart * @param pid */ public function remove(pid:Int ) { loader.show(); //add server side var r = new haxe.Http('/shop/remove/$pid'); r.onData = function(data:String) { loader.hide(); var d = haxe.Json.parse(data); if (!d.success) js.Browser.alert("Erreur : "+d); //remove locally for ( p in order.products.copy()) { if (p.productId == pid) { order.products.remove(p); render(); return; } } render(); } r.request(); } /** * loads products DB and existing cart in ajax */ public function init(place:Int,date:String) { this.place = place; this.date = date; loader = App.j("#cartContainer #loader"); var req = new haxe.Http("/shop/init/"+place+"/"+date); req.onData = function(data) { loader.hide(); var data : { products:Array, categories:Array<{name:String,pinned:Bool,categs:Array}>, order:OrderInSession } = haxe.Unserializer.run(data); //populate local categories lists for ( cg in data.categories){ if (cg.pinned){ pinnedCategories.push(cg); }else{ categories.push(cg); } } //product DB for (p in data.products) { //catch dom element for further usage untyped p.element = App.j(".product"+p.id); var id : Int = p.id; //var id : Int = p.id; //id = id + 1; this.products.set(id, p); this.productsArray.push(p); //trace(p.name+" : " + p.categories); } //existing order for ( p in data.order.products) { subAdd(p.productId,p.quantity ); } render(); sortProductsBy(); } req.request(); //DISABLED : pb quand le panier est plus haut que l'ecran //scroll mgmt, only for large screens. Otherwise let the cart on page bottom. /*if (js.Browser.window.matchMedia("(min-width: 1024px)").matches) { jWindow = App.j(js.Browser.window); cartContainer = App.j("#cartContainer"); cartTop = cartContainer.position().top; cartLeft = cartContainer.position().left; cartWidth = cartContainer.width(); jWindow.scroll(onScroll); }*/ } /** * keep the cart on top when scrolling * @param e */ public function onScroll(e:Dynamic) { //cart container top position if (jWindow.scrollTop() > cartTop) { //trace("absolute !"); cartContainer.addClass("scrolled"); cartContainer.css('left', Std.string(cartLeft) + "px"); cartContainer.css('top', Std.string(/*cartTop*/10) + "px"); cartContainer.css('width', Std.string(cartWidth) + "px"); }else { cartContainer.removeClass("scrolled"); cartContainer.css('left',""); cartContainer.css('top', ""); cartContainer.css('width', ""); } } }