`;$("#main").prepend(html);setTimeout(function(){$("#popin-SB").removeClass("appear");}); // Show popin});// Add popin events: close popin$(document).off("click", ".popin-overlay, .popin-btn-close").on("click", ".popin-overlay, .popin-btn-close", function(){$("#popin-SB").addClass("appear");setTimeout(function(){$("#popin-SB").remove();}, 250);});// Add to bag events$(document).off("click", ".QuantitySelector__popin").on("click", ".QuantitySelector__popin", () => {if (this.dbUpdate !== false) this.dbUpdate();});}canShowBannerOnTechno(bannerLocation){/* Check if a banner could be displayed on current page, based on current techno and banner location. Only used for PLP / PDP */let locationSplit = bannerLocation.split("_"); // gives something live ["plp", "coffee", "original"]if (locationSplit[0] !== "plp") return true; // Don't manage techno visibility for non PLP bannersif (locationSplit[2]) if (locationSplit[2] !== this.techno) return false;return true;}canShowBannerOnUrl(bannerId, banner){/* Check if a banner could be displayed on current page, based on URL and bannerId (example of Id: home_header#1). It's the first check done before adding a banner to banners */let [bannerType] = bannerId.split("_");if (bannerId.includes("checkout_topbanner") && this.pageUrl.pageType === "my-offers") return true; // My offers pageif (bannerType === "menu" && this.pageUrl.pageType !== "checkout") return true; // Megamenu bannersif (bannerType === "home" && this.pageUrl.pageType === "home") return true; // Homepage bannersif (bannerType === "plp" && this.pageUrl.pageType === "plp"){ // PLP: check type: coffee orif (bannerId.includes("plp_badges")) return true;if (this.pageUrl.pageDetail === "capsules") if (bannerId.includes("plp_coffee")) return true;if (this.pageUrl.pageDetail === "machines") if (bannerId.includes("plp_machine")) return true;}if (bannerType === "checkout" && this.pageUrl.pageType === "checkout") return true; // Checkout top & big right bannersif (bannerId.includes("plp_badges") && this.pageUrl.pageType === "pdp") return true; // Badges on PDPsif (bannerId.includes("checkout_bigrightbanner") && this.pageUrl.pageType === "checkout") return true; // Big right banner// Interspacedif (bannerType.includes("interbanner") && this.pageUrl.pageType === "plp") return true;// DBif (bannerType === "db"){if (banner.db_checkout === "yes" && this.pageUrl.pageType === "checkout") return true; // DB// Non DB:if (this.pageUrl.pageType !== "plp") return false;if (this.pageUrl.baseUrl.includes(banner.db_display)) return true;}// Specific LP?let display = false;$.each(banner.tresholds, (tresholdId, treshold) => {if (treshold.location && this.pageUrl.baseUrl.includes(treshold.location)){display = true;return true;}});return display; // Default return for non-home banners}canShowTresholdOnVisitor(treshold) {/* Check if a banner's threshold can be shown on current visitor */if (!treshold.selIDs) return false; // Don't process if no selIDs set on treshold// Split sellIds, clean them up, and check visibilityreturn treshold.selIDs.split(" ").map(sellId => sellId.replace("~", " ").trim()).some(sellId => {if (!sellId) return false; // Skip empty strings// Reverse condition: if visitor_Is[sellId] is true, return falseif (sellId.startsWith("!")) if (visitor_Is[sellId.substring(1)]) return false;// Normal condition: check if sellId is in visitor_Isif (visitor_Is[sellId]) return true;// Check ecomtest conditionif (sellId.toLowerCase().includes("ecomtest")) {const testKey = sellId.toLowerCase().split("ecomtest")[1].split("_")[0];return visitor_Is[`id_${this.testIDs[testKey]}`] === true;}return false; // Default return});}getCSS(){/* Main CSS used for banners */const css = ``;return `
`;}getTechno(){/* Returns the techno of current page by url, or false if no techno associated with the page */if (this.pageUrl.pageTechno) return this.pageUrl.pageTechno;return false;}async pushBanners(campaign, banners, push){/* This function pushes banners called with a set_customSB to current class:Check each banners / threshold to see if it should be displayed or notManage this.debug with debug informationUpdate this.banners.current containg banners to displayUpdate this.banners.[...] with other banners informationPush is true by default to push banners to this.config, or false on page dynamic reloadSets a timeout to display the banners (timeout to eventually wait for other set_customSB calls)*/this.campaigns ++;// Define campaign and prevent empty campaignif (campaign.campaign === "") campaign.campaign = "undefined_"+this.campaigns;campaign = campaign.campaign;// Wait for CHMarketVisitor.Is() to resolve and visitor_Is to be definedawait this.visitorPromise;// Iterate each banner of banners and check visibilitylet interspaceNb = 0;$.each(banners, (bannerId, banner) => {let bannerLocation = bannerId.split("#")[0]; // Get banner location// If interspace, allow multiple banneLocationsif (bannerLocation === "interbanner"){interspaceNb ++;bannerLocation += "_"+interspaceNb;}let debug = []; // Initialize debug array containing messages// Initialize campaigns debugif (!this.debug.campaigns[campaign]) this.debug.campaigns[campaign] = [];if (push) this.debug.campaigns[campaign].push(debug);// Initialize banners debugif (!this.debug.banners[bannerLocation]) this.debug.banners[bannerLocation] = [];if (push) this.debug.banners[bannerLocation].push(debug);if (push) debug.push(`@${campaign } /BANNER: ${bannerId}`);// Check if banner can be displayed on page, based on URLif (push) debug.push(` /URL`);if (!this.canShowBannerOnUrl(bannerId, banner)){if (push) debug.push(` /URL not compatible with banner #END`);delete banners[bannerId];return true;}else if (push) debug.push(` #URL compatible, #continue`);// Remove ineligible tresholds from banner and store latest available tresholdlet shownTresholdId = null;let shownTreshold = null;$.each(banner.tresholds, (tresholdId, treshold) => {if (push) debug.push(` /THRESHOLDS - ${tresholdId}`);if (!this.canShowTresholdOnVisitor(treshold)){if (push) debug.push(` /THRESHOLD not compatible with visitor #END`);delete banner.tresholds[tresholdId];}else{if (push) debug.push(` /THRESHOLD compatible with visitor #continue`);shownTresholdId = tresholdId;shownTreshold = treshold;}});// Remove banner from banners if no tresholds leftif (Object.keys(banner.tresholds).length === 0){if (push) debug.push(`/BANNER No threshold left for current banner #END`);delete banners[bannerId];return true;}// Check if banner can be displayed on current page, based on techno (PLP / PDP)if (push) debug.push(` /THRESHOLD Currently set as active banner for ${bannerLocation}`);// DBif (this.canShowBannerOnTechno(bannerLocation)){// Brand banner: define which treshold to show as shownTresholdif (bannerLocation === "home_brand") shownTreshold = this.getBannerBrandTreshold(banner.tresholds);// DB: show all tresholdsif (bannerLocation === "db_coffee") shownTreshold = banner.tresholds;this.banners.current[bannerLocation] = {campaign: campaign,bannerId: bannerId,banner: banner,tresholdId: shownTresholdId, treshold: shownTreshold};}});// If banner still there, add to listif (Object.keys(banners).length) {if (push) this.banners.eligible.push({campaign, banners }); // Add banners to eligible bannersif (this.timeOut) clearTimeout(this.timeOut); // Clear existing timeout if any// Set a new timeout to display bannersthis.timeOut = setTimeout(() => {this.displayBanners();}, 80);}}displayBanners(){/* This function displays this.banners.current in the page */let bannerCount = 0;// Add base CSS to headerconst css = this.getBannerSharedCss();if (!$("#chb-css").length) $("head").prepend(`
`);this.displayBannersMyOffers(); // Display my offers banners on top (checkout topbanners)$.each(this.banners.current, (bannerLoc, banner) => {bannerCount++;const displayedIndex = `${banner.campaign}_${banner.bannerId}`;// Check if banner already displayed, except for interspacedif (this.banners.displayed[displayedIndex]) return true;this.banners.displayed[displayedIndex] = true;if (this.displayEventsAdd) clearTimeout(this.displayEventsAdd);this.displayEventsAdd = setTimeout(() => {this.displayEventsAddFunction();}, 500);// Megamenu bannersif (bannerLoc.startsWith("menu_")){this.displayBanner_megamenu(bannerLoc, banner, bannerCount);return true;}// Interspace bannersif (bannerLoc.startsWith("interbanner_")){this.displayBanner_Interspace(bannerLoc, banner, bannerCount);return true;}// Other bannersswitch (bannerLoc){// Checkoutcase "checkout_bigrightbanner":this.displayBanner_right_banner(bannerLoc, banner, bannerCount);break;case "checkout_topbanner":if (this.pageUrl.pageType !== "my-offers") // Already added on previous iterations in my offer pagethis.displayBanner_top_banner(bannerLoc, banner, bannerCount);break;// PLP PDPcase "plp_coffee_vertuo": case "plp_coffee_original" : case "plp_coffee": // Top bannerscase "plp_machine_vertuo": case "plp_machine_original" : case "plp_machine": // Top bannersthis.displayBanner_top_banner(bannerLoc, banner, bannerCount);break;case "db_coffee":this.displaybanner_db(bannerLoc, banner, bannerCount);break;case "plp_badges":this.displaybanner_badges(bannerLoc, banner, bannerCount);break;// Home pagecase "home_header":this.displayBanner_home_header(bannerLoc, banner, bannerCount);break;case "home_x3top_1": case "home_x3top_2": case "home_x3top_3":this.displaybanner_x3_top(bannerLoc, banner, bannerCount);break;case "home_x2_1": case "home_x2_2":this.displaybanner_x2(bannerLoc, banner, bannerCount);break;case "home_x3bottom_1": case "home_x3bottom_2": case "home_x3bottom_3":this.displaybanner_x3_bottom(bannerLoc, banner, bannerCount);break;case "home_x1":this.displaybanner_x1(bannerLoc, banner, bannerCount);break;case "home_brand":this.displaybanner_brand(bannerLoc, banner, bannerCount);break;}})// Indicate homepage is loadedif (this.pageUrl.pageType === "home") check_page_loaded_home=1;}displayBannersMyOffers(){let bannerCount = 0;if (this.pageUrl.pageType === "my-offers"){$.each(this.banners.eligible, (nb, bannerObj) => {$.each(bannerObj.banners, (bannerLoc, banner) => {if (!bannerLoc.startsWith("checkout_topbanner")) return true; // Don't process non checkout (my-offers) banners// Iterate all tresholds of the banner and add banner$.each(banner.tresholds, (tresholdId, treshold) => {bannerCount ++;let displayBanner = {campaign: bannerObj.campaign, bannerId: tresholdId, tresholdId: tresholdId, banner: banner, treshold: treshold}this.displayBanner_top_banner(bannerLoc, displayBanner, `offer_${bannerCount}`, true);});});});}}displayEventsAddFunction(subSelector = ""){/* List all banners (.chb) with a factultative subselector and add event to show that banners has been shown */let toAdd = [];$(".chb"+subSelector).each((index, element) => {const $banner = $(element);const campaign = $banner.data('campaign');const threshold = $banner.data('threshold');const location = $banner.data('location');const placement = $banner.data('placement');// Add view event immediatelythis.addEvent(campaign, threshold, location, placement, "view");// Add click event onclick$banner.on("click", () => {this.addEvent(campaign, threshold, location, placement, "click");});// Add mouseevent on enter$banner.on("mouseenter", () => {this.addEvent(campaign, threshold, location, placement, "hover");});});}addEvent(campaign, threshold, location, placement, action){/* Add an event (view, hover, click) and push it to GTM */// Remove "-" from names and lower casecampaign = campaign.replaceAll("-","_").toLowerCase();threshold = (""+threshold).replaceAll("-","_").toLowerCase();// Build variablesconst event_category=`custom - banner - ${location} - ${placement}`;const event_action=action;if (campaign=="") campaign="campaign not defined";const event_label=`${campaign} - ${threshold}`;window.gtmDataObject = window.gtmDataObject || [];gtmDataObject.push({// GA4event: 'local_event', //as is, do not change!!event_raised_by:'ch', //as is, do not change!!local_event_category: 'custom - banner – '+location+' - '+placement, //free to fill field, please use lower caselocal_event_action: event_action, //free to fill field, please use lower caselocal_event_label: event_label //free to fill field, please use lower case});}reloadPlpBanners(forceLoc) {// Update PLP top banners with a forced location as needed, function launched on techno switch on PLPs// Only process if no DB presentif ($(".chb.db").length > 0) return false;// Clear displayed bannersthis.banners.displayed = {};// Update pageUrl based on forceLocconst locationMap = {"PLP-machine-original": { pageDetail: "machines", pageTechno: "original" },"PLP-machine-vertuo": { pageDetail: "machines", pageTechno: "vertuo" },"PLP-coffee-original": { pageDetail: "capsules", pageTechno: "original" },"PLP-coffee-vertuo": { pageDetail: "capsules", pageTechno: "vertuo" }};const locationDetails = locationMap[forceLoc];if (locationDetails) {this.pageUrl.pageDetail = locationDetails.pageDetail;this.pageUrl.pageTechno = locationDetails.pageTechno;}this.techno = this.getTechno(); // Refresh techno of the page based on this.pageUrl.pageTechnothis.visitorPromise = CHMarketVisitor.Is(); // Relaunch promise to wait for visitor_Is to be ready// Empty current PLP top bannersObject.keys(this.banners.current).forEach(location => {if (location.startsWith("plp_")) if (location !== "plp_badges") delete this.banners.current[location]; // Delete plp from current to re-write as needed});// Iterate all bannersthis.banners.eligible.forEach(campaign => {// Re-display banners only if campaign.banners contains a PLP top bannerconst hasPlpBanner = Object.keys(campaign.banners).some(bannerId => bannerId.startsWith("plp_"));if (hasPlpBanner) {set_customSB({ campaign: campaign.campaign }, campaign.banners, false);}});}// DISPLAY BANNERS FUNCTIONS: MEGAMENUdisplayBanner_megamenu(bannerLoc, banner, bannerCount){/* Manage megamenu banners, will be redone after new megamenu ready */// First: add event only once, trigger when mouse over megamenuif (this.menuUpdate === false){this.menuUpdate = true;$(document).on("mouseenter", "#header .HeaderNavigationBarItem__anchor", () => {// Bug image existe déjà avant? On la supprime de suite si image HQ?// Wait for submenu to be visible$(".HeaderNavigationBar__menu").addClass("hidePush");setTimeout(() => {CHMarketToolbox.getDOMElementAsync('#header .HeaderNavigationBar__menu .Banner', ($elem => {// Banner is visible, starts megamenu$elem.css("opacity", "0");$(".HeaderNavigationBar__menu").removeClass("hidePush");setTimeout(() => {this.displayBanner_megamenu_replace(bannerLoc, $elem);}, 50);setTimeout(() => {$elem.css("opacity", "1");}, 500);}),{maxTries:60,interval: 80} );}, 50);});}}displayBanner_megamenu_replace(bannerLoc, $wrapper){/* Replaces a current visible HQ banner by new megamenu banners *///$wrapper.html(""); // Empty HQ content// Get which menu item is openedlet menuItem = $wrapper.parents("li.HeaderNavigationBarItem");let openedIndex = menuItem.index() + 1; // index of opened itemlet title = menuItem.find(".HeaderNavigationBarItem__title").html(); // Use text for reliable comparison// Map index with menu typelet menuIndexes ={"1": "menu_compostable","2": "menu_coffee","3": "menu_machine","4": "menu_accessories","5": "menu_gifting","6": "menu_subscription","7": "menu_experience","8": "menu_recycling"}if (!menuIndexes[openedIndex]){return false; // Don't process non existing index}const bannerObj = this.banners.current[menuIndexes[openedIndex]];if (!bannerObj) return false; // Don't process if nothing to display// Now display menuItem in the $openedconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-menu-${openedIndex}`;// CSSconst css = `/* SHARED - MEGAMENU CSS */#${id}{width:238px;height:228px;}#${id} img{width:100%;height:100%:object-fit:cover;}#${id} .overlay{position:absolute;top:0;left0;padding:10px 24px;width:100%;height:100%;}#${id} .overlay > *{width:100%;left:0;position:absolute;text-align:center;}#${id} .overlay h3{font-weight:bold;}#${id} .overlay .cta span{margin:auto;height:30px;line-height:10px!important;font-weight:bold;}/* SPECIFIC CSS */#${id} .serving_suggestion{font-size:10px;line-height:14px;letter-spacing:0px;}#${id} .overlay h3{margin-top:${banner.title_top}px;}#${id} .overlay p{margin-top:${banner.description_top}px;}#${id} .overlay .cta{margin-top:${banner.cta_top}px;}/* FONT SIZES & COLOR*/#${id} .overlay h3{${this.getBannerElementCss("menubanner", "h3", bannerObj)}}#${id} .overlay p{${this.getBannerElementCss("menubanner", "p", bannerObj)}}#${id} .overlay .cta span{${this.getBannerElementCss("menubanner", "cta", bannerObj)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}`;const html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`}${banner.theme} chb menubanner" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)}>
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=medium&imwidth=480 1x, ${treshold.image}?impolicy=medium&imwidth=720 1.2x, ${treshold.image}?impolicy=medium&imwidth=960 2x"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${this.getBannerPopin(treshold)}
${this.getBannerServing(id, bannerObj)}
${treshold.link ? 'a' : 'div'}>`;$wrapper.html(html);$wrapper.css("opacity", "1");this.displayEventsAddFunction(".menubanner")}// DISPLAY BANNERS FUCTIONS: PLP - PDP - CHECKOUTdisplayBanner_top_banner(bannerLoc, bannerObj, bannerCount, myoffers = false){ // PLP or checkout topbanner/* Function to display a topbanner */// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// Specific my offers variablelet myOffersType = ""; // Store offer type: machine, coffee, secondarylet myOffersHtml = ""; // Specific my offers html tagslet myOfferImgLeft = 0;// As this banner could be reloaded, check that it doesn't exist firstif (myoffers === false){let existingBanner = $(".chb.topbanner")if (existingBanner.length > 0){ // Current topbanner exists// Check if banner is identicalif (existingBanner.attr("data-campaign") === bannerObj.campaign && existingBanner.attr("data-banner") === bannerObj.bannerId) return false;// Banner not identical, remove old of not in my offersif (!myoffers) existingBanner.remove();}}else{ // My offers versionslet shouldReturn = false;$(".chb.topbanner").each(function() {const $this = $(this);const campaignData = $this.data("campaign");const bannerData = $this.data("banner");if (campaignData === bannerObj.campaign && bannerData === bannerObj.bannerId){shouldReturn = true;return false;}});if (shouldReturn) return false; // Don't process as banner already exists// My offers specific: get type of bannermyOffersType = this.displayBanner_myOffer_getType(treshold)banner.theme += ` ${myOffersType}`; // Will add offertype as class of the banner// Add a link if no link is definedif (!treshold.link){if (myOffersType === "coffee") treshold.link = "order/capsules";if (myOffersType === "machine") treshold.link = "order/machines";}// If CTA too long, add to descriptionif (treshold["cta_" + this.lang] && treshold["cta_" + this.lang].length > 30){treshold["cta_" + this.lang] = '';}// Add a CTA if no CTA is definedif (!treshold["cta_" + this.lang]){treshold["cta_" + this.lang] = this.translations.defaultCta[this.lang];}// If main offer, do some adjustementsif (myOffersType !== "secondary"){myOffersHtml += ` data-offertype="${myOffersType}"`;// Calculate image left margin for a 1297 with imgmyOfferImgLeft = 12.97 * (-55 + parseInt(banner.text_left)) ;myOffersHtml += ` data-text_left="${banner.text_left}"`;myOffersHtml += ` data-title="${treshold["title_" + this.lang] || ''}"`;if (myOffersType === "coffee") treshold["title_" + this.lang] = this.translations.offerCoffee[this.lang];else treshold["title_" + this.lang] = this.translations.offerMachine[this.lang];// Remove BR from ptreshold["description_" + this.lang] = treshold["description_" + this.lang].replace(/
/gi, ' ');// Move main offer to secondary if already existsconst $existingDom = $(`#custom_offers_main .chb[data-offertype='${myOffersType}']`);if ($existingDom.length > 0){// A main offer already exists, so push main offer to secondarythis.displayBanner_myOffer_moveDown($existingDom, myOffersType);}}}// CSSlet css = `/* SHARED - TOP BANNERS CSS */${treshold.background ? `#${id}{background:${treshold.background}}`: ''}${(this.pageUrl.pageType === "checkout") ? `#${id}{margin-top:-20px;}` : ''}#${id}{opacity: 0;animation: ${id}fadeIn .5s ease-in-out forwards;}@keyframes ${id}fadeIn {from {opacity: 0;}to {opacity: 1;}}#${id} .wrapper{position:relative;max-width:1128px;}#${id} .wrapper > img{display:block;width:100%;}#${id} .overlay{position:absolute;top:0;left:${banner.text_left || "0"}%;padding:8px 0;width:50%;height:100%;}#${id} .overlay > *{position:absolute;width:100%;text-align:center;}#${id} .overlay .cta span{height:30px;line-height:10px!important;}#${id} .overlay .popin_conditions_SB{right:0;}/* SPECIFIC CSS */#${id} .wrapper{height:130px;transform-origin:top center;}#${id} .wrapper > img{height:100%;object-fit:cover;}#${id} .overlay .h3{margin-top:${banner.title_top}px;}#${id} .overlay p{margin-top:${banner.description_top}px;}#${id} .overlay .cta{margin-top:${banner.cta_top}px;}/* SPECIFIC CSS: RWD */@media screen and (max-width:1128px){#${id}{height:114px;}#${id} .wrapper{transform:scale(calc(114 / 130));}}@media screen and (max-width:996px){#${id}{margin-top:0;}${(this.pageUrl.pageType === "checkout") ? `#${id}{margin-top:-4px;}` : ''}#${id} .wrapper{width:calc(100% *130 / 114);transform-origin:top left;overflow:hidden;}#${id} .wrapper > img{position:relative;left:-${2*banner.text_left || "0"}%;width:200%;}#${id} .overlay{left:0;width:100%;}}@media screen and (max-width:480px){#${id} .wrapper{transform:scale(0.7);width:calc(100% / 0.7);}#${id}{height:91px;}}/* FONT SIZES & COLOR*/#${id} .overlay .h3{${this.getBannerElementCss("topbanner", "h3", bannerObj)}}#${id} .overlay p{${this.getBannerElementCss("topbanner", "p", bannerObj)}}#${id} .overlay .cta span{${this.getBannerElementCss("topbanner", "cta", bannerObj)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}`;// MY OFFERS CSSif (myoffers){css += `#${id}{${treshold.background || "#fff"}}#${id} .wrapper > img{left:${myOfferImgLeft}px;}`;}const html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`}${banner.theme} chb topbanner" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)} ${myOffersHtml}>
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=large&imwidth=1200 1x, ${treshold.image}?impolicy=large&imwidth=1920 1.5x"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] ? `
${treshold["cta_"+this.lang]}
` : ''}${this.getBannerPopin(treshold)}
${this.getBannerServing(id, bannerObj)}
${treshold.link ? 'a' : 'div'}>`;if ($(".chb.db").length > 0) $(".chb.db").remove(); // Clean db// Prepend location depends on banner locationswitch (this.pageUrl.pageType){case "checkout":$("#top").after(html); // Checkout: insert after headerbreak;case "my-offers":if (myOffersType === "secondary") $("#custom_offers_secondary").append(html);else $("#custom_offers_main").append(html);break;default: // Default$("#main").prepend(html); // PLP: prepend on main}}displayBanner_myOffer_moveDown($existingDom, offetType){/* Move down a main my offers as a secondary offer to a new main offer can take its place */// Build a new banner objectlet domTitle = $existingDom.attr("data-title");if (domTitle === "undefined") domTitle = "";$existingDom.find(".h3").html(domTitle);$existingDom.appendTo($("#custom_offers_secondary"));}displayBanner_myOffer_getType(treshold){/* Define is a my offer banner is a coffee or machine banner */if (treshold.offer_type) return treshold.offer_type; // If offer type is defined, returns it// Try to guess type of offerconst testingString =treshold[`conditions_${this.lang}`] +treshold[`description_${this.lang}`] +treshold[`title_${this.lang}`] +treshold.link;// Machinelet testingKeywords = ["machine", "macchi", "maschin"] // Machine detectionfor (const testingKeyword of testingKeywords)if (testingString.includes(testingKeyword)) return "machine";// CoffeetestingKeywords = ["coffee", "capsule", "café", "caffè", "capsule"] // Coffee detectionfor (const testingKeyword of testingKeywords)if (testingString.includes(testingKeyword)) return "coffee";// Default: secondaryreturn "secondary";}displayBanner_Interspace(bannerLoc, bannerObj, bannerCount){/* Display an interspaced banner in a PLP */// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;if (!banner["range-id"]) return; // Don't process banners without id// Define banner orderlet bannerOrder = (banner["position-nb"] -1) * 10;if (banner.position === "first") bannerOrder = -1;const css = `/* SHARED - PLP INTERSPACED CSS */#${id}{position:relative;margin-right:auto;margin-left:auto;border-radius:24px;border:1px solid #d0d0d0;overflow:hidden;}.interbanner img{position:relative;display:block;width:100%;height:100%;}#${id} .overlay{position:absolute;top:0;left:0;width:100%;height:100%;}#${id} .overlay > *{width:100%;color:black;padding:0 16px;text-align:center;position:absolute;}#${id} .overlay h3{font-weight:700;}#${id} .overlay p{font-weight:500;}#${id} .overlay .cta span{font-weight:500;padding:10px 16px;line-height:20px!important;}/* SPECIFIC CSS */#${id}{width:calc((100% / 4) - 32px);height:auto;margin-bottom:32px;margin-left:32px;}#${id} .wrapper{width:100%;height:100%;}#${id} .overlay h3{margin-top:${banner.title_top}px;}#${id} .overlay p{margin-top:${banner.description_top}px;}#${id} .overlay .cta{margin-top:${banner.cta_top}px;left:${banner.cta_left}%!important;right:auto;top:0;bottom:auto;width:100%!important;}/* FONT SIZES & COLOR*/#${id} .overlay h3{${this.getBannerElementCss("interbanner", "h3", bannerObj)}}#${id} .overlay p{${this.getBannerElementCss("interbanner", "p", bannerObj)}}#${id} .overlay .cta span{${this.getBannerElementCss("interbanner", "cta", bannerObj)};border-radius:2em;}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}@media screen and (max-width:1024px){#${id}{width:calc((100% / 3) - 32px);}}@media screen and (max-width:768px){#${id}{${treshold["narrow-background"] ? `background:${treshold["narrow-background"]};` : ''}}#${id}{width:100%;height:auto;margin-bottom:16px;}#${id} .overlay{position:relative;height:auto;padding-bottom:16px;}#${id} img{position:absolute;left:-9999px;right:-9999px;bottom:-9999px;top:-9999px;margin:auto;max-width:105%;width:105%;height:auto;}#${id} img{${treshold["narrow-image-blur"] ? `filter:blur(${treshold["narrow-image-blur"]}px);` : ''}${treshold["narrow-image-opacity"] ? `opacity:${treshold["narrow-image-opacity"]};` : ''}}#${id} .overlay > *{position:relative!important;text-align:center!important;margin-top:16px!important;}#${id} h3{font-size:20px!important;line-height:24px!important;letter-spacing:1px!important;padding:0 32px;}#${id} p{font-size:16px!important;line-height:19px!important;letter-spacing:1px!important;padding:0 16px;}#${id} .cta span{font-size:20px;line-height:24px;letter-spacing:1px;}}`;let html = `
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=small&imwidth=288 1x, ${treshold.image}?impolicy=small&imwidth=450 1.5x, ${treshold.image}?impolicy=small&imwidth=600 2x, "/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] ?`<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}"` : 'div'} class="cta">${treshold["cta_"+this.lang] || ''}${treshold.link ? 'a ' : 'div'}>`: ''}${this.getBannerPopin(treshold)}
`;// Function to display bannerconst appendBanner = ($elem) =>{const parent = $elem.parent(); // Get the parent element// Check that the banner doesn't exist alreadyif (parent.find(`.chb.interbanner[data-campaign="${bannerObj.campaign}"][data-banner="${bannerObj.bannerId}"]`).length > 0) return false;parent.append(html); // Append the new banner HTMLif (banner.position === "first"){$elem.css("display", "none"); // If the banner is in the "first" position, replace the existing one}// Handle popin CTA behaviorconst $interspaced = $(`#${id}`);const $popinConditions = $interspaced.find(".popin_conditions_SB");// If popin conditions exist, remove them and add the class to the CTAif ($popinConditions.length > 0) {$popinConditions.remove();$interspaced.find(".cta").addClass("popin_conditions_SB");}}// Find wrapper and add element to wrapperlet appendBannerCount = 0;const $rangeFound = $(`#${banner["range-id"]}`);if ($rangeFound.length > 0) appendBanner($rangeFound);else{CHMarketToolbox.getDOMElementAsync(`#${banner["range-id"]}`, ($elem => {appendBanner($elem);}), { maxTries: 40, interval: 180 });}}displayBanner_right_banner(bannerLoc, bannerObj, bannerCount){/* Function to display a check big right banner */// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// CSSconst css = `/* SHARED - TOP BANNERS CSS */#${id} .wrapper{position:relative;width:280px;height:464px;}#${id} .wrapper > img{display:block;width:100%;height:100%;object-fit:cover;}#${id} .overlay{position:absolute;top:0;left:0;padding:8px 0;width:100%;height:100%;}#${id} .overlay > *{position:absolute;width:100%;text-align:center;}#${id} .overlay .cta span{height:30px;line-height:30px!important;padding-top:0;padding-bottom:0;}#${id} .overlay .popin_conditions_SB{right:0;}/* SPECIFIC CSS */#${id}{margin-top:-20px;}#${id} .overlay h3{margin-top:${banner.title_top}px;}#${id} .overlay p{margin-top:${banner.description_top}px;}#${id} .overlay .cta{margin-top:${banner.cta_top}px;}@media screen and (max-width:768px){#${id}{margin-left:-20px;}}/* FONT SIZES & COLOR*/#${id} .overlay h3{${this.getBannerElementCss("topbanner", "h3", bannerObj)}}#${id} .overlay p{${this.getBannerElementCss("topbanner", "p", bannerObj)}}#${id} .overlay .cta span{${this.getBannerElementCss("topbanner", "cta", bannerObj)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}`;const html = `
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=medium&imwidth=360 1x, ${treshold.image}?impolicy=medium&imwidth=450 1.2x, ${treshold.image}?impolicy=medium&imwidth=600 2x"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] ?`<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}"` : 'div'} class="cta">${treshold["cta_"+this.lang] || ''}${treshold.link ? 'a ' : 'div'}>`: ''}
${this.getBannerServing(id, bannerObj)}
`;if ($(".chb.rightbanner").length > 0) $(".chb.rightbanner").remove(); // Clean previous$(".responsive-shopping-bag-aside__items").prepend(html);}displaybanner_badges(bannerLoc, bannerObj, bannerCount) {// Display badges on PLPs / PDPs// CSS for both PLP and PDPconst banner = bannerObj.banner;const id = `chb-${bannerCount}`;const css = `/* SHARED - PLP BADGES CSS */.${id} img{position:absolute;left:50%;transform:translateX(-50%);}.${id} .overlay > *{padding:0;position:relative;text-align:center;}.${id} .cta{position:absolute;left:0;top:0;}.${id} .cta span{display:block;width:20px;height:20px;line-height:20px!important;text-align:center;padding:0;border-radius:100%;padding-left:2px;}/* SPECIFIC CSS */.${id}{position:absolute;z-index:1;top:16px;left:8px!important;background:${banner.badge_background};width: ${banner.round_scale === -1 ? 'auto' : `${66 + (banner.round_scale * 2)}px`}!important;height: ${banner.round_scale === -1 ? 'auto' : `${66 + (banner.round_scale * 2)}px`};border-radius: ${banner.round_scale === -1 ? '0' : `100%`};transform: scale(0.9)!important;transform-origin: bottom left!important;}.${id} .overlay{position:absolute;top:0;left:0;width:100%;height:100%;}.${id}.popin_conditions_SB {cursor:help;}.${id} img{width:${banner.img_size}px;margin-top:${banner.img_top}px;}@media screen and (max-width:768px){.${id}{left:auto!important;right:70px;top:4px;transform: scale(0.7)!important;transform-origin: top right!important; }}@media screen and (max-width:480px){.${id}{transform: scale(0.6)!important;transform-origin: top right !important;}}.${id} .overlay .h3{margin-top:${banner.title_top}px;}.${id} .overlay p{margin-top:${banner.description_top}px;}.${id} .overlay .cta{margin-top:${banner.cta_top}px;margin-left:${banner.cta_left}px;}.${id} .popin_conditions_SB{}/* OLD ACCESSORIES PLP SPECIFICS */@media screen and (max-width:768px){.newPLP:not(.row_view) .${id}{right:16px!important;left:auto!important;}}/* PDP SPECIFICS */.ProductDetailsBody .${id}{left:0!important;}.ProductDetailsBody .${id}{position:absolute;right:16px;top:16px;left:auto!important;} /* Coffee PDP */@media screen and (max-width:1024px){#uPDP.accessories .${id}{left:auto!important;} /* Accessories narrow PDP */}/* FONT SIZES & COLOR*/.${id} .overlay .h3{${this.getBannerElementCss("plp_badges", "h3", bannerObj)}}.${id} .overlay p{${this.getBannerElementCss("plp_badges", "p", bannerObj)}}.${id} .overlay .cta span{${this.getBannerElementCss("plp_badges", "cta", bannerObj)}}/* CUSTOM CSS */${this.getBannerCustomCss(`.${id}`, banner.custom_style)}`;$(`#badges-css-${id}`).remove();// If page is PLP, wait for plp ready variableif (this.pageUrl.pageType === "plp"){const isPLPReady = () => { // Helper functionswitch (this.pageUrl.pageDetail) {case "capsules": return check_page_loaded_plp_coffee === 1;case "machines": return check_page_loaded_plp_machines === 1;case "accessories":return plp_started === true;default: return false;}};// Wait for PLPlet checkPLPReadyTick = 0; // Security countconst checkPLPReady = setInterval(() => {checkPLPReadyTick++; // security incrementif (checkPLPReadyTick > 50) { // Gives up after a whileclearInterval(checkPLPReady);return;}if (isPLPReady()) { // Page is ready, display badgesclearInterval(checkPLPReady);$("#main").prepend(`
`); // Add cssthis.displaybanner_badges_plp(bannerLoc, bannerObj, bannerCount);}}, 180);return;}// If page is PDP, wait for pdp readyif (this.pageUrl.pageType === "pdp"){CHMarketToolbox.getDOMElementAsync('.CHPdp', ($elem => {// Depending on PDP type, look for different informationswitch (this.pageUrl.pageDetail){case "machines":let addedCss = false;$(`.enriched_badge`).each((index, badge) => { const badgeSku = $(badge).attr("data-sku");if (bannerObj.treshold.skus.includes(badgeSku)){if (addedCss === false) $("#main").prepend(`
`); // Add cssthis.displaybanner_badges_pdp(bannerLoc, $(badge), bannerObj, bannerCount, "prepend");}});break;case "capsules":var sku = $elem.attr("data-product-code");if (bannerObj.treshold.skus.includes(sku)){var wrapper = $(".ProductDetailsBody .ResponsiveContainer").first();$("#main").prepend(`
`); // Add cssthis.displaybanner_badges_pdp(bannerLoc, wrapper, bannerObj, bannerCount, "prepend");}break;case "accessories":var sku = $elem.attr("data-product-code");if (bannerObj.treshold.skus.includes(sku)){var wrapper = $("h1.ProductDetails__information").first();$("#main").prepend(`
`); // Add cssthis.displaybanner_badges_pdp(bannerLoc ,wrapper, bannerObj, bannerCount, "append");}break;}}),{maxTries:50,interval: 350} );}}displaybanner_badges_pdp(bannerLoc, domtoPrepend, bannerObj, bannerCount, method = "prepend"){/* Displays a badge on a PDP */const treshold = bannerObj.treshold;const id = `chb-${bannerCount}`;// HTMLlet html = `
${treshold.image ? `", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=small&imwidth=72 1x, ${treshold.image}?impolicy=large&imwidth=108 1.5x, ${treshold.image}?impolicy=large&imwidth=144 2x"/>` : ''}
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${treshold[`conditions_${this.lang}`] ? this.getBannerPopin(treshold) : ''}`;// Remove
...
from htmlhtml = html.replace(/
.*?<\/div>/s, '');$(`.plp_badges.${id}`).remove(); // Clear badges$(".CHPdp").addClass("withbadge");switch (method){case "prepend":domtoPrepend.prepend(html); // Add badgebreak;case "append":domtoPrepend.append(html); // Add badgebreak;}}displaybanner_badges_plp(bannerLoc, bannerObj, bannerCount){/* PLP is ready, display badges */const treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// HTMLlet html = `
${treshold.image ? `", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=small&imwidth=72 1x, ${treshold.image}?impolicy=large&imwidth=108 1.5x, ${treshold.image}?impolicy=large&imwidth=144 2x"/>` : ''}
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${treshold[`conditions_${this.lang}`] ? this.getBannerPopin(treshold) : ''}`;// Remove
...
from htmlhtml = html.replace(/
.*?<\/div>/s, '');$(`.plp_badges.${id}`).remove(); // Clear badges// Try to add each badge to an existing articlelet skusArray = treshold.skus.split(",");for (let sku of skusArray){sku = sku.trim();$(`article[data-product-code="${sku}"]`).each(function(){$(this).addClass("plp_badges");$(this).prepend(html);});}}displaybanner_db(bannerLoc, bannerObj, bannerCount){/* Display a dynamic banners, ineligible treshold already hidden */// Assign variable shortcutsconst tresholds = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// Create artificial treshold to work with getBannerPopin functionlet treshold = banner;// Get DB specific variables or default colorsconst tag = banner["tag_"+this.lang];const maxCaps = this.db_getMaxCapsules(tresholds);const bgProgress = banner.slider || "#35939a";let bgBanner = banner.background || "#fff";const txtCol = banner.color || "#000";const specialCol = banner.special || "#8f7247";// Background: imageif (bgBanner.includes('.jpg') || bgBanner.includes('.jpeg') || bgBanner.includes('.png')) {bgBanner = `url('${bgBanner}')`;}// CSSconst css = `/* SPECIFIC CSS */${(this.pageUrl.pageType === "checkout") ? `#${id}:not(.sticky){margin-top:-20px;}` : ''}#${id}.notready{opacity:0;}#${id}{background:${bgBanner};color:${txtCol};opacity:1;border-top:1px solid #d0d0d0;border-bottom:1px solid #d0d0d0;}#${id} .wrapper{position:relative;max-width:1160px;margin:auto;padding:8px 16px;padding-bottom:16px;}#${id} .wrapper div.popin_conditions_SB{right:16px;}#${id} .h3{font-weight:700;letter-spacing:1px;font-size:16px;line-height:19px;text-align:center;}#${id} .tag{position:absolute;background:${specialCol};top:0;border-bottom-left-radius:20px;border-bottom-right-radius:20px;font-size:11px;height:12px;line-height:12px;padding:1px 8px 0;color:#fff;}#${id} .bar{position:relative;height:10px;margin:16px auto 0;width:920px;}#${id} .bar > *{position:absolute;}#${id} .bar-bg{width:100%;height:10px;border-radius:10px;background:white;border:1px solid #d0d0d0;overflow:hidden;}#${id} .bar-fill{width:0;height:100%;background:${bgProgress};transition:width 1s ease;}#${id} .bar-steps{width:100%;height:100%;}#${id} .bar-step{position:absolute;top:0;height:29px;}#${id} .bar-steps .bar-line{width:4px;height:14px;background:#000;margin-top:-2px;margin-left:-2px;border-radius:2px;}#${id} .bar-steps .bar-nb{position:absolute;top:-16px;left:-12px;width:25px;text-align:center;font-weight:400;letter-spacing:1px;font-size:12px;line-height:14px;}#${id} .steps-wrapper{overflow:hidden;padding-bottom:12px;margin-bottom:-12px;}#${id} .steps{display:flex;width:calc(100% + 64px);position:relative;left:-32px;overflow:visible;white-space:nowrap;margin-top:8px;transition:all 1s ease;}#${id} .step{display:flex;align-items:center;width:calc(50% - 128px);height:80px;flex-shrink:0;margin:0 64px;}#${id}.unique .step{margin:0 auto;}#${id} .step .state{width:calc(100% - 80px - 32px);margin:0 16px;white-space:normal;text-align:center;color:${txtCol};font-weight: 400;letter-spacing: 1px;font-size: 14px;line-height: 17px;}#${id} .step .state b{font-weight:bold;color:${specialCol};}#${id} .step .state strong ~ b:first-of-type{background:#fff;color:${specialCol};padding:0 5px;}#${id} .step .state i{font-style:italic;}#${id} .step[data-state='reached'] .state{opacity:0;transform:scale(2.5);}#${id} .step .state.anime{opacity:1;transform:scale(1);transition:opacity 1.25s ease, transform 0.5s ease;}#${id} .step.last[data-state='reached']{margin-left:calc(25% + 28px);}#${id} .step img{width:80px;height:80px;object-fit:cover;position:relative;transition:transform .1s;right:0;cursor:help;}#${id} .step img:hover{transform:scale(1.05);transform-origin:center;}#${id} .step img.shadow{margin-right:-80px;}#${id} .or{width:128px;text-align:center;margin-right:16px;flex-shrink:0;line-height:70px;margin-left:-64px;margin-right:-64px;}.popin-content img.gwp{width:300px;height:300px;margin-top:16px;}/* STICKY */#${id}{overflow:hidden;}#${id} .h3:not(.or){height:20px;}#${id}.sticky .tag, #${id} .h3:not(.or){transition:margin-top .5s ease;}@media screen and (min-width:997px){#${id}.sticky{position:fixed;top:60px;left:0;width:100%;z-index:9;padding-bottom:8px;}#${id}.sticky .wrapper{padding-top:12px;padding-bottom:0;}#${id}.sticky .h3:not(.or){margin-top:-28px;transition:margin-top 0s;}#${id}.sticky .bar{padding-top:4px;}#${id}.sticky .tag{margin-top:-14px;transition:margin-top 0s;}#${id}.sticky .steps-wrapper{margin-top:-32px;width:calc(100% + 32px);margin-left:-16px;padding:40px 16px 8px;}#${id}.sticky .step{height:60px;}#${id}.sticky .imgLeft::before, #${id}.sticky .imgRight::before{content:" ";position:relative;width:40px;height:100%;}#${id}.sticky .imgLeft .state, #${id}.sticky .imgRight .state{width:calc(100% - 32px);}#${id}.sticky .imgLeft:not(.last) img,#${id}.sticky .imgRight img {width: 60px;height: 60px;margin-left: calc(-100% - 72px);margin-top: -84px;animation: stickyFadeIn 0.5s ease-in-out forwards;}@keyframes stickyFadeIn {0% {opacity: 0;}100% {opacity: 1;}}#${id}.sticky .imgLeft:not(.last) img,#${id}.sticky .imgRight img {opacity: 0;}#${id}.sticky .imgLeft:not(.last) img.shadow, #${id}.sticky .imgRight img.shadow{margin-right:-60px;}#${id}.sticky .imgLeft:not(.last) img.shadow + img, #${id}.sticky .imgRight img.shadow + img{margin-left:0;}#${id}.sticky .imgRight img{margin-left:32px;margin-right:-32px;}#${id}.sticky .step.last.imgLeft img{width:60px;height:60px;}#${id}.sticky .step.last.imgLeft img.shadow{margin-right:-60px;}#${id}.sticky .wrapper div.popin_conditions_SB{bottom:4px;}}@media screen and (max-width:996px){${(this.pageUrl.pageType === "plp") ?`#${id} {position:fixed;bottom:0;z-index:999;left:0;width:100%;}` :`#${id}{margin-top:0!important;}` }#${id} .h3{display:none;}#${id} .step.imgLeft + .or{display:block;}#${id} .tag, #${id} .bar{display:none;}#${id} .state{padding:0!important;}#${id} .steps{width:100%;left:0;white-space:normal;flex-wrap:wrap;margin-left:0!important;}#${id} .step:not(.imgLeft):not(.imgRight){display:none;}#${id} .step{width:100%;margin:0;height:auto;min-height:50px;}#${id} .step.last[data-state='reached']{transform:none;height:80px;}#${id} .step.last.imgLeft{margin-left:0;}#${id} .step img{display:none;}#${id} .step.imgLeft img{display:block;position:absolute;left:0!important;top:16px!important;}#${id} .state, #${id} .or{width:100%!important;margin:0 auto!important;padding:0 0 0 92px!important;max-width:600px;}#${id} .state strong:first-of-type{display:block;margin-bottom:-8px;}#${id} .or{height:30px;line-height:30px;}}`;// HTML// Define stepslet steps = [], stepsHtml = '', nb = 0;let keys = Object.keys(tresholds); // Get all keys of the object$.each(tresholds, (tresholdId, treshold) => { // Iterate tresholds to fill steps and steps contentnb++;// Define bars and is lastlet isLast = tresholdId == keys[keys.length - 1]; // Check if this is the last keylet x = Math.ceil(100* treshold.threshold_capsules / maxCaps);let tresholdTitle = treshold[`gwp_${this.lang}`];tresholdTitle = tresholdTitle[0].toUpperCase() + tresholdTitle.slice(1);// Define current stepstepsHtml += `
✕
${tresholdTitle}
${!isLast ? `
${this.translations.or[this.lang]}
` : ''}`;});const html = `
${tag ? `
${tag}
` : ''}
${banner["title_"+this.lang]}
${stepsHtml}
${this.getBannerPopin(treshold)}
`;if ($(".chb.topbanner").length > 0) $(".chb.topbanner").remove(); // Clean topbannerif ($(".chb.db").length > 0) $(".chb.db").remove(); // Clean previous banner// HTML insertion depends on page location// Prepend location depends on banner locationif (this.pageUrl.pageType === "checkout") $("#top").after(html); // Checkout: insert after headerelse $("#main").prepend(html); // PLP: prepend on main// Calculate height and add visibility to dbsetTimeout(() => {this.db_setStepsHeight();$(`#${id}`).removeClass("notready");}, 50);// Enrich SKUs, but only if available in this pageif (typeof enriched_labels !== "undefined"){this.skus.getEnricheds(); // Enriched labels is used to test if enriched skus available}// Add eventsthis.db_setEvent(id, tresholds);this.db_update(id, tresholds);}db_setStepsHeight() {/* Adjust dynamic banner elements height */const $container = $(`.chb.db`); // Select the container by idconst $states = $container.find('.state'); // Get all the .stage elements inside the container// Iterate over each .$state element$states.each(function() {let $state = $(this);let padding = 0;let targetHeight = 65; // Target height in pxlet maxPadding = 60; // Maximum padding limit in pxlet stateHeight = $state.height(); // Get the current height of the state// Start with 0 padding and increment by 20px total padding (10px left + 10px right)while (stateHeight < targetHeight && padding < maxPadding) {padding += 10; // Increment by 10px on each side (total 20px)$state.css({'padding-left': `${padding}px`,'padding-right': `${padding}px`});stateHeight = $state.height(); // Update the stage height after padding change// If height exceeds target, revert to previous paddingif (stateHeight >= targetHeight) {$state.css({'padding-left': `${padding - 10}px`,'padding-right': `${padding - 10}px`});break;}}});}db_getMaxCapsules(tresholds){/* Get maximum capsules of a DB */let max = 0;$.each(tresholds, (tresholdId, treshold) => {if (max < treshold.threshold_capsules) max = treshold.threshold_capsules;});return max;}db_setEvent(id, tresholds){/* Set function launched on add-to-bg qty update - don't use cart listener as sometimes bug */this.dbUpdate = function() {// Get initial selector and capsules countlet $selector = $(".MiniBasketButton__quantity").length? $(".MiniBasketButton__quantity"): $("#ta-mini-basket__open span.notranslate");if (!$selector.length) return; // Exit if no selector foundlet capsBefore = parseInt($selector.html());// Wait for capsules count to changeconst waitForCapsUpdate = setInterval(() => {let capsAfter = parseInt($selector.html());if (!document.body.contains($selector[0])) capsAfter = 0; // $selector removed from dom, set caps to 0if (capsAfter !== capsBefore) {clearInterval(waitForCapsUpdate);this.db_update(id, tresholds);}}, 300);setTimeout(() => clearInterval(waitForCapsUpdate), 7000); // Timeout after a while};}db_update(id, tresholds) {/* Cart quantity update, should update current DB */this.db_getcaps().then((capsNb) => {// Initialize variableslet lastAchieved = 0, nextAchieve = false, upperAchieve = false, promoCode = false;// Iterate over thresholds and get last achieved and next achieveslet i = 0, slides = -1, nb = 0;$.each(tresholds, (tresholdId, treshold) => {nb++;let $treshold = $(`#${id} #step_${treshold.threshold_capsules}`); // Get treshold dom itemvar enabledState = `initial_${this.lang}`; // Will contain which state should be visible for this tresholdif (capsNb > 0) enabledState = `ongoing_${this.lang}`;i++;const tCaps = treshold.threshold_capsules;if (capsNb >= tCaps){ // Treshold achievedlastAchieved = tCaps;i = 0;enabledState = `reached_${this.lang}`;promoCode = treshold.threshold_promo;slides ++;}else if (i === 1) nextAchieve = tCaps;else if (i === 2) upperAchieve = tCaps;// Set steps visibility$treshold.find(".state").html(treshold[enabledState].replace("%capsules%", (tCaps - capsNb)));// Set treshold current step and detect any changelet newState = enabledState.split("_")[0];let oldState = $treshold.attr("data-state");if (oldState !== newState){ // State change$treshold.find(".state").removeClass("anime");if (newState === `reached`){ // Step reached, add an animationsetTimeout(() => {$treshold.find(".state").addClass("anime");}, 50);}}$treshold.attr("data-state", newState);});// Define first step at 0let barsHtml = `
`;// Is there any middle step?let middle = lastAchieved, last = nextAchieve;if (!lastAchieved && nb > 1){middle = nextAchieve;last = upperAchieve;}// Last step achievedif (nextAchieve === false){last = middle;middle = false;}// Define middle barif (middle !== false) {const x = (100 * middle) / last;barsHtml += `
${middle}
`;}// Define last stepbarsHtml += `
${last}
`;// Update the DOM$(`#${id} .bar-steps`).html(barsHtml);// Slide the steps and detect imagesif (slides < 0) slides = 0; // Minimumconst marginLeftPercent = slides * 50;const marginLeftPx = slides * 32;$(`#${id} .steps`).css("margin-left", `calc(-${marginLeftPercent}% - ${marginLeftPx}px)`);// Define sticky images order$(`#${id} .step`).removeClass("imgLeft").removeClass("imgRight");var steps = $(`#${id} .steps-wrapper .step`);steps.eq(slides).addClass("imgLeft"); // 0 is the index of the first stepsteps.eq(slides+1).addClass("imgRight"); // 1 is the index of the second step// Fill bar bglet fillWidth = Math.ceil(100 * capsNb / last);$(`#${id} .bar-fill`).css("width", `${fillWidth}%`);});}db_getcaps(){/* Function to calculate and return caps qty in cart */return napi.cart().read().then((cartLines) => {return cartLines.reduce((capsNb, { productId, quantity }) => { // Accumulate capsule count using reduceconst productSku = productId.split("/").pop(); // Get product SKUconst product = this.skus.enrichedSKUs[this.skus.getID(productSku)]; // Get productif (!product) return capsNb; // Skip if product not found// Add quantity for coffee productsif (product.type === "coffee"){return capsNb + parseInt(quantity);}// Add quantity for coffee packs by iterating over pack contentsif (product.type === "coffee_pack") {const packCaps = product.pack_content.reduce((packTotal, packLine) => {const packProduct = this.skus.enrichedSKUs[packLine.sku];if (packProduct && packProduct.type === "coffee") return packTotal + parseInt(packLine.qty);return packTotal;}, 0);return capsNb + packCaps;}return capsNb; // Default return if no matching product type}, 0); // Start with 0 for the total caps count});};// DISPLAY BANNERS FUCTIONS: HOMEPAGEdisplayBanner_home_header(bannerLoc, bannerObj, bannerCount){/* Function to display a home hero / header banner */this.addHomeWrapper(); // Add homepage wrapper if necessary$("#home-wrapper").addClass("widthheader");// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// Define X3 top background as well?let x3bg = treshold.x3bg ? `#main .home_x3_top{background:${treshold.x3bg}}` : '';const css = `/* SHARED - HEADER / HERO BANNER CSS */#${id} .wrapper{max-width:1920px;height:460px;margin:auto;position:relative;}#${id} .wrapper img{position:absolute;left:-9999px;right:-9999px;margin:auto;height:100%;width:auto;}#${id} .overlay{max-width:1160px;margin:auto;position:relative;height:360px;}#${id} .overlay > *{position:absolute;width:50%;text-align:left;}#${id} .overlay > h2{font-weight:700;}#${id} .overlay .cta{display:inline-block;width:auto;}#${id} .overlay .cta span{margin:0;}#${id} .overlay .popin_conditions_SB{left:0;right:auto;bottom:8px;}@media screen and (max-width:996px){#${id} .wrapper{overflow:hidden;height:auto;background:${treshold.mbackground};display:flex;flex-direction:column;}#${id}.txt_first .wrapper{flex-direction:column-reverse;}#${id} .wrapper img{position:initial;display:block;width:200%;height:auto;margin-left:-100%;}#${id} .overlay{height:auto;padding-bottom:16px;width:calc(375px + 32px);max-width:100%;}#${id} .overlay > *{position:relative;width:100%;margin-top:16px!important;}}/* SPECIFIC CSS */${x3bg}#${id} .overlay h2{margin-top:${banner.title_top}px;}#${id} .overlay p{margin-top:${banner.description_top}px;}#${id} .overlay .cta{margin-top:${banner.cta_top}px;}/* FONT SIZES */#${id} .overlay h2{${this.getBannerElementCss("home-header", "h3", bannerObj)}}#${id} .overlay p{${this.getBannerElementCss("home-header", "p", bannerObj)}}#${id} .overlay .cta span{${this.getBannerElementCss("home-header", "cta", bannerObj)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}`;const html = `
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=large&imwidth=1920 1x, ${treshold.image}?impolicy=large&imwidth=2800 1.5x, ${treshold.image}?impolicy=large&imwidth=3840 2x"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}"` : 'div'} class="cta">${treshold["cta_"+this.lang] || ''}${treshold.link ? 'a ' : 'div'}>${this.getBannerPopin(treshold)}
${this.getBannerServing(id, bannerObj, true, {"addserving_y_narrow": "calc(100vw * %value% / 400)"})}
`;if ($(".chb.home-header").length > 0) $(".chb.home-header").remove(); // Clean previous banner$("#home-wrapper").prepend(html);}displaybanner_x3_top(bannerLoc, bannerObj, bannerCount){/* Function to display a home X3 top banner, left, middle or right (bannerLoc) */this.addHomeWrapper(); // Add homepage wrapper if necessary// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// Add homepage X3 top wrapper if necessaryif (!$("#x3top-container").length > 0) $("#home-wrapper").append(`
`);// Define CSSconst css = `/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}#${id} .overlay h2{${this.getBannerTextColor("h3", banner)}}#${id} .overlay p{${this.getBannerTextColor("p", banner)}}#${id} .overlay .cta span{${this.getBannerTextColor("cta", banner)}}`;// Define HTMLconst html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`}${banner.theme} chb" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)}>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${this.getBannerPopin(treshold)}
${treshold.link ? 'a' : 'div'}>`;// Replace current location html by new html$(`#${bannerLoc}`).html(html);}displaybanner_x2(bannerLoc, bannerObj, bannerCount){/* Function to display a home X2 banner, left or right (bannerLoc) */this.addHomeWrapper(); // Add homepage wrapper if necessary// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// Add homepage X2 top wrapper if necessaryif (!$("#x2-container").length > 0) $("#home-wrapper").append(`
`);// Define CSSconst css = `/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}#${id} .overlay h2{${this.getBannerTextColor("h3", banner)}}#${id} .overlay p{${this.getBannerTextColor("p", banner)}}#${id} .overlay .cta span{${this.getBannerTextColor("cta", banner)}}`;// Define HTMLconst html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`}${banner.theme} chb" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)}>
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=medium&imwidth=600 1x, ${treshold.image}?impolicy=large&imwidth=800 1.5x, ${treshold.image}?impolicy=large&imwidth=960 2x"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${this.getBannerPopin(treshold)}
${this.getBannerServing(id, bannerObj)}${treshold.link ? 'a' : 'div'}>`;// Replace current location html by new html$(`#${bannerLoc}`).html(html);}displaybanner_x3_bottom(bannerLoc, bannerObj, bannerCount){/* Function to display a home X3 bottom banner, left, center or right (bannerLoc) */this.addHomeWrapper(); // Add homepage wrapper if necessary// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// Add homepage X2 top wrapper if necessaryif (!$("#x3bottom-container").length > 0) $("#home-wrapper").append(`
`);// Define CSSconst css = `/* FONT SIZES & COLOR*/#${id} .overlay h2{${this.getBannerTextColor("h3", banner)}}#${id} .overlay p{${this.getBannerTextColor("p", banner)}}#${id} .overlay .cta span{${this.getBannerTextColor("cta", banner)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}`;// Define HTMLconst srcAndSet = this.getBannerX3SrcAndSet(false, treshold.image, treshold["image-narrow"]);const html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`}${banner.theme} ${treshold.bg_narrow_theme} chb" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)}>
", " ") : ""}" wide-src="${treshold.image}" narrow-src=${treshold["image-narrow"]} src="${srcAndSet.src}" srcset="${srcAndSet.srcSet}"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${this.getBannerPopin(treshold)}
${this.getBannerServing(id, bannerObj)}${treshold.link ? 'a' : 'div'}>`;// Replace current location html by new html$(`#${bannerLoc}`).html(html);}displaybanner_x1(bannerLoc, bannerObj, bannerCount){/* Display a X1 homepage banner */this.addHomeWrapper(); // Add homepage wrapper if necessary// Assign variable shortcutsconst treshold = bannerObj.treshold;const banner = bannerObj.banner;const id = `chb-${bannerCount}`;// CSSconst css = `/* SHARED - X1 CSS */#${id} {position:relative;width:calc(100% - 32px);max-width:1160px;height:140px;margin:auto;overflow:hidden;}#${id} .wrapper > img{position:absolute;display:block;width:100%;height:100%;object-fit:cover;}#${id} .overlay{position:absolute!important;top:0;left:${banner.text_left || "0"}%;width:50%;height:100%;padding:8px 0;}#${id} .overlay h2{padding-bottom:4px!important;}#${id} .overlay .cta span{height:30px;line-height:10px!important;}@media screen and (max-width:996px){#${id} {width:calc(100% - 32px);max-width:564px;height:auto;}#${id} .wrapper{display:grid;grid-template-rows:1fr 1fr;}#${id} .wrapper > img{}#${id} .wrapper::before{content:" ";position:relative;display:block;}#${id} .wrapper .overlay{position:relative;width:100%;left:0!important;padding:16px 0;}#${id} .wrapper .overlay h2{display:none;}#${id} .overlay .cta{position:relative;margin-top:16px!important;}}/* SPECIFIC CSS */#${id}{margin-top:32px;}#${id} .overlay > *{position:absolute;width:100%;bottom:auto;}#${id} .overlay h3{margin-top:${banner.title_top}px;}#${id} .overlay p{margin-top:${banner.description_top}px;}#${id} .overlay .cta{margin-top:${banner.cta_top}px;}/* FONT SIZES & COLOR*/#${id} .overlay h2{${this.getBannerTextColor("h3", banner)}}#${id} .overlay p{${this.getBannerTextColor("p", banner)}}#${id} .overlay .cta span{${this.getBannerTextColor("cta", banner)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, banner.custom_style)}`;const srcAndSet = this.getBannerX1SrcAndSet(false, treshold.image, treshold["image-narrow"]);const html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`}${banner.theme} chb home_x1 home-bloc" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)}>
", " ") : ""}" wide-src="${treshold.image}" narrow-src=${treshold["image-narrow"]} src="${srcAndSet.src}" srcset="${srcAndSet.srcSet}"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${this.getBannerServing(id, bannerObj)}
${treshold.link ? 'a' : 'div'}>`;if ($(".chb.home_x1").length > 0) $(".chb.home_x1").remove(); // Clean previous banner$("#home-wrapper").prepend(html);}displaybanner_brand(bannerLoc, bannerObj, bannerCount){/* Show brand banner */this.addHomeWrapper(); // Add homepage wrapper if necessary// Assign variable shortcutsconst treshold = bannerObj.treshold;const id = `chb-${bannerCount}`;// Img left / top / sizelet imgLeft = (treshold.img_left || 0);let imgTop = (treshold.img_top || 0);let imgWidth = treshold.img_width;const css = `/* SHARED - HOME BRAND CSS */#${id}{background:${treshold.left_bg};background-size:contain;background-repeat:repeat;overflow:hidden;}#${id} .wrapper{position:relative;display:flex;align-items:stretch;width:calc(100% - 32px);max-width:1160px;margin:auto;}#${id} .wrapper img{position: relative;order: 2;z-index: 10;height: auto;object-fit: contain;margin: 0 auto 0;padding-right:calc(50% - 580px);box-sizing:content-box;}#${id} .overlay{width:50%;height:auto!important;display:flex;flex-direction:column;justify-content:space-around;position:relative;z-index:10;}#${id} .overlay > *{top:0;margin:0;padding:16px 32px!important;}#${id} .overlay .cta{position:relative;}@media screen and (max-width:996px){#${id}{background-size:cover!important;}#${id} .wrapper{width:100%;}#${id} .wrapper img{display:none;}#${id}.narrow_img_semvisible .wrapper img{display:block;position:absolute;opacity:0.2;object-fit: cover;left:0;top:0;width:100%;height:100%;}#${id} .wrapper .overlay{width:100%;}#${id} .overlay::after{display:none!important;}}/* SPECIFIC - CSS */#${id}{margin-top:32px!important;}#${id} .wrapper img{width:${imgWidth}px;top:${imgTop}px;left:${imgLeft}px;}#${id} .overlay::after{position: absolute;content:"";display: block;left: 100%;top: 0;width:calc(100vw - 50%);height: 100%;background:${treshold.right_bg};background-size: cover !important;background-position: center !important;}/* FONT SIZES & COLOR*/#${id} .overlay h2{${this.getBannerTextColor("h3", treshold)}}#${id} .overlay p{${this.getBannerTextColor("p", treshold)}}#${id} .overlay .cta span{${this.getBannerTextColor("cta", treshold)}}/* CUSTOM CSS */${this.getBannerCustomCss(`#${id}`, treshold.custom_style)}`;const html = `<${treshold.link ? `a href="${this.getBannerLink(treshold.link)}" class="a ` : `div class="`} chb home_brand home-bloc ${treshold.bg_narrow_theme}" id="${id}" ${this.addBannerInfos(bannerLoc, bannerObj)}>
", " ") : ""}" src="${treshold.image}" srcset="${treshold.image}?impolicy=medium&imwidth=360 1x, ${treshold.image}?impolicy=medium&imwidth=540 1.5x, ${treshold.image}?impolicy=medium&imwidth=720 2x"/>
${treshold["title_" + this.lang] || ''}
${treshold["description_" + this.lang] || ''}
${treshold["cta_"+this.lang] || ''}
${this.getBannerServing(id, bannerObj)}
${treshold.link ? 'a' : 'div'}>`;if ($(".chb.home_brand").length > 0) $(".chb.home_brand").remove(); // Clean previous banner$("#home-wrapper").prepend(html);}// DISPLAY BANNERS MISC FUNCTIONSclearOldBanner(location){}addBannerInfos(bannerLoc, bannerObj){/* Add html infos (data-...) on banners */const location = this.getTrackingLocation().replaceAll("-", " ").toLowerCase();const placement = this.getTrackingPlacement(bannerLoc);//const location = ctool.pageLoc.replaceAll("-", " ").toLowerCase();return ` data-campaign="${bannerObj.campaign}" data-banner="${bannerObj.bannerId}" data-threshold="${bannerObj.tresholdId}" data-location="${location}" data-placement="${placement}"`;}addHomeWrapper(){/* Add a #home-wrapper div containg all homepage banners */if (!($("#home-wrapper").length > 0)){$("#main").prepend(`
`);}}// SPECIAL: BRAND BANNERgetBannerBrandTreshold(tresholds, relaunch = false){/* Iterate brand banner treshold and define which one should be displayed, update cookies as well on localstorge */let shownTreshold = false, cookieKey = false, delCookies = [];$.each(tresholds, (tresholdId, treshold) => {cookieKey = `hbrand_${tresholdId}`;delCookies.push(cookieKey);if (localStorage.getItem(cookieKey) === "null"){ // Cookie don't existe, create idlocalStorage.setItem(cookieKey, 1);shownTreshold = treshold;return false;}// Cookie storage exists, see if banner can be incremented / viewlet cookieVal = parseInt(localStorage.getItem(cookieKey)) || 0;if (cookieVal < treshold.treshold_views){ // Show banner and increase cookiecookieVal++;localStorage.setItem(cookieKey, cookieVal);shownTreshold = treshold;return false;}});if (shownTreshold) return shownTreshold; // A treshold to shown has been found// No treshold found, clear all cookies and relaunch functionif (relaunch === false){ // Security to ensure no loop on function relaunchfor (const delCookie of delCookies) localStorage.removeItem(delCookie); // Clear all cookiesreturn this.getBannerBrandTreshold(tresholds, true); // Relaunch function}}// HTML FUNCTIONSgetBannerLink(link){/* Return a link givent by treshold.link */link = link.trim(); // Remove empty spaceslink = link.replace("{lang}", this.lang); // Replace language in linkif (!link.charAt(0) === "{") return link; // Normal link// Process link in the form of {fr|link1}{de|link2}{en|link3}{it|link4}link = link.replace(new RegExp(`.*{${this.lang}\\|([^}]+)}.*`), "$1");return link;}getBannerX1SrcAndSet($banner = false, wideSrc = false, narrowSrc = false){/* return $banner {src:src, srcset:srcset} depending on current screen resolutionEither $banner as parameter getBannerX3SrcSet($banner)Or wide + narrow as parameter getBannerX3SrcSet(false, wideSrc, narrowSrc)*/if ($banner){wideSrc = $banner.attr("wide-src");narrowSrc = $banner.attr("narrow-src");}let srcSet = '', src = '';if (window.matchMedia("(max-width: 996px)").matches) { // NarrowsrcSet = `${narrowSrc}?impolicy=medium&imwidth=600 1x, ${narrowSrc}?impolicy=medium&imwidth=900 1.5x, ${narrowSrc}?impolicy=medium&imwidth=1080 2x`;src = narrowSrc;}else { // WidesrcSet = `${wideSrc}?impolicy=medium&imwidth=1200 1x, ${wideSrc}?impolicy=medium&imwidth=1800 1.5x, ${wideSrc}?impolicy=medium&imwidth=2400 2x`;src = wideSrc;}// If banner exists, update directly src and srcsetif ($banner){$banner.attr("src", src);$banner.attr("srcset", srcSet);}return {src:src, srcSet:srcSet};}getBannerX3SrcAndSet($banner = false, wideSrc = false, narrowSrc = false){/* return $banner {src:src, srcset:srcset} depending on current screen resolutionEither $banner as parameter getBannerX3SrcSet($banner)Or wide + narrow as parameter getBannerX3SrcSet(false, wideSrc, narrowSrc)*/if ($banner){wideSrc = $banner.attr("wide-src");narrowSrc = $banner.attr("narrow-src");}let srcSet = '', src = '';if (window.matchMedia("(max-width: 996px)").matches) { // NarrowsrcSet = `${narrowSrc}?impolicy=medium&imwidth=800 1x, ${narrowSrc}?impolicy=large&imwidth=400 1.5x, ${narrowSrc}?impolicy=large&imwidth=560 2x`;src = narrowSrc;}else { // WidesrcSet = `${wideSrc}?impolicy=medium&imwidth=600 1x, ${wideSrc}?impolicy=large&imwidth=800 1.5x, ${wideSrc}?impolicy=large&imwidth=960 2x`;src = wideSrc;}// If banner exists, update directly src and srcsetif ($banner){$banner.attr("src", src);$banner.attr("srcset", srcSet);}return {src:src, srcSet:srcSet};}getBannerPopin(treshold){/* Get a popin HTML code for a banner, or return empty string if not necessary */const lang = this.lang;if (!treshold[`conditions_${lang}`]) return ''; // Don't process further if no conditions definedlet popinContent = treshold[`conditions_${lang}`].trim();if (!popinContent) return ''; // Don't process further if conditions are empty// Add br to conditionspopinContent = popinContent.replaceAll("\n", "
");// Process popinlet html = `
${this.translations.conditionsLink[lang]}
✕
${this.translations.conditionsTitle[lang]}
${popinContent}
`;return html;}getBannerServing(id, bannerObj, marginLeft = false, matrix = false){/* Add serving suggestion and coordinates of serving suggestions on banner. Matrix is a transformation matrix used in some rare cases (header banner) */const treshold = bannerObj.treshold;const banner = bannerObj.banner;if (treshold.addserving !== "yes") return ''; // Don't process if no addservinglet x = isNaN(parseFloat(treshold.addserving_x)) ? "0px" : parseFloat(treshold.addserving_x).toFixed(0) + "px";let y = isNaN(parseFloat(treshold.addserving_y)) ? "0px" : parseFloat(treshold.addserving_y).toFixed(0) + "px";let narrow_x = isNaN(parseFloat(treshold.addserving_x_narrow)) ? "0px" : parseFloat(treshold.addserving_x_narrow).toFixed(0) + "px";let narrow_y = isNaN(parseFloat(treshold.addserving_y_narrow)) ? "0px" : parseFloat(treshold.addserving_y_narrow).toFixed(0) + "px";let servingColor = banner.serving_color ? `color:${banner.serving_color}` : '';if (treshold.addserving !== "yes") return ''; // Don't process if no addserving// Eventual matrix transformationif (matrix !== false){if (matrix.addserving_x_narrow) narrow_y = matrix.addserving_x_narrow.replace("%value%", treshold.addserving_x_narrow);if (matrix.addserving_y_narrow) narrow_y = matrix.addserving_y_narrow.replace("%value%", treshold.addserving_y_narrow);if (matrix.addserving_x) narrow_y = matrix.addserving_x.replace("%value%", treshold.addserving_x);if (matrix.addserving_y) narrow_y = matrix.addserving_y.replace("%value%", treshold.addserving_y);}let htmlcss = `
${this.translations.servingSuggestions[this.lang]}
`;return htmlcss;}// CSS FUNCTIONSgetBannerCustomCss(id, customStyle) {/* Generate custom style CSS for banners */if (!customStyle) return ''; // Don't process if no custom style defined, return empty csslet css = '';for (let line of customStyle.split(/\r?\n|\r/)) {line = line.trim(); // Trim contentif (!line) continue;// Check min/max view in linesconst match = line.match(/@(min|max)(\d+)/);if (match) {const type = match[1];const number = match[2];line = line.replace(`@${type}${number}`, ""); // Remove info// Adds importantif (!line.includes("!important")) line = line.replaceAll(";", "!important;");css += `\n@media screen and (${type}-width: ${number}px){${id} ${line}}`;}// No min/max view, simply add the line to custom csselse css += `\n${id} ${line}`; // Adds a line to custom CSS}return css;}getBannerElementCss(bannerType, element, bannerObj){/* Generates specific CSS for elements of a banner (h3, p, .cta) */let css = '';css += this.getBannerFontCss(bannerType, element, bannerObj.banner);css += this.getBannerPositionCss(element, bannerObj);css += this.getBannerTextColor(element, bannerObj.banner);return css;}getBannerFontCss(bannerType, element, banner){/* Generate font (size, line-height, letter-spacing) CSS for elements of a banner */const elementScale = banner[`${element}_scale`];// Default font propertieslet fontProperties = {"h3":{fontSize: 16, lineHeight: 24, letterSpacing: 2},"p":{fontSize: 14, lineHeight: 21, letterSpacing: 1},"cta":{fontSize: 14, lineHeight: 21, letterSpacing: 1},}// Specific bannerType font propertiesswitch(bannerType){case "home-header":fontProperties = {"h3":{fontSize: 28, lineHeight: 38, letterSpacing: 4},"p":{fontSize: 16, lineHeight: 24, letterSpacing: 1},"cta":{fontSize: 14, lineHeight: 40, letterSpacing: 1},}break;case "plp_badges":fontProperties = {"h3":{fontSize: 16, lineHeight: 24, letterSpacing: 2},"p":{fontSize: 10, lineHeight: 12, letterSpacing: .5},"cta":{fontSize: 16, lineHeight: 24, letterSpacing: 2},}break;case "interbanner":fontProperties = {"h3":{fontSize: 20, lineHeight: 24, letterSpacing: 1},"p":{fontSize: 16, lineHeight: 19, letterSpacing: 1},"cta":{fontSize: 20, lineHeight: 24, letterSpacing: 1},}break;default:break;}// Get current element font properties and adapt relatively to scalelet cssProps = fontProperties[element];cssProps.fontSize = (cssProps.fontSize * banner.text_scale * elementScale).toFixed(1);cssProps.lineHeight = (cssProps.lineHeight * banner.height_scale * banner.text_scale * elementScale).toFixed(1);cssProps.letterSpacing = (cssProps.letterSpacing * banner.spacing_scale).toFixed(1);const css = `font-size:${cssProps.fontSize}px;letter-spacing:${cssProps.letterSpacing}px;line-height:${cssProps.lineHeight}px;`;return css;}getBannerTextColor(element, banner) {// Generate CSS for elements colorslet selector = element === "cta" ? "cta_color" : "text_color";let css = banner[selector] ? `color:${banner[selector]}!important;` : '';if (element === "cta") {if (banner.cta_background) css += `background:${banner.cta_background}`;}return css;}getBannerPositionCss(element, bannerObj){/* Generate specific positionning CSS for elements of a banner, if needed */let attributeX = `${element}_${this.lang}_x`;let attributeY = `${element}_${this.lang}_y`;// Return empty string if no positionning defined for this banner's tresholdif (!((bannerObj.treshold[attributeX]) || (bannerObj.treshold[attributeY]))) return '';const x = bannerObj.treshold[attributeX]; // get position X valueconst y = bannerObj.treshold[attributeY]; // get position Y value// Fill CSS string to be returnedlet css = '';if (x < 0) css += `left:${x}px;padding-left:${-x}px;`;else css += `left:${x}px;padding-right:${x}px;`;css += `top:${y}px;`;return css;}getBannerSharedCss(){/* Returns the base CSS that is shared by all banners (themes, ...) */const css = `/* SHARED - BASE */#main{background:#fff;}.chb{display:block;font-family:NespressoLucas;}.chb .overlay > *{padding:0 16px;}.chb .overlay > a{display:block;}.chb .serving_suggestion{position:absolute;white-space:nowrap;color:#fff;font-size:12px;line-height:16px;letter-spacing:0px;}/* SPECIFIC - BASE */#main{display:flex;flex-direction:column;}.chb:not(.black):not(.white) .overlay > *{color:black;}@media screen and (max-width:996px){.chb + *{margin-top:0!important;}}.HeaderNavigationBar__menu.hidePush .Banner{opacity:0;}/* THEME COLORS */.chb.black{color:black;}.chb.black .cta span{color:black;}.chb.white{color:white;}.chb.white .cta span{color:white;}.chb.shadow{text-shadow:0 0 25px black;};.chb.black.shadow{text-shadow:0 0 25px white;}.chb.gradient .overlay{background: linear-gradient(#00000066 0%, #00000033 30%, #00000000 50%, #00000000 70%, #00000033 80%, #00000066 100%);}.chb.gradient.black .overlay{background: linear-gradient(#ffffff66 0%, #ffffff33 30%, #ffffff00 50%, #ffffff00 70%, #ffffff33 80%, #ffffff66 100%);}/* CTA - LINKS OR DIV AND THEMES */.chb .cta span{display:inline-block;}.a.chb .cta span{border-radius:2em;white-space:nowrap;height:40px;line-height:20px!important;padding:10px 16px;box-sizing:border-box;}.a.chb.white .cta span{border:1px solid white;}.a.chb.black .cta span{border:1px solid black;}.a.chb.cta_black .cta span{background:#000;color:#fff;border:1px solid #000!important;}.a.chb.cta_white .cta span{background:#fff;color:#000;border:1px solid #fff!important;}.a.chb.cta_gold .cta span{background:#8F7247;color:white;border:1px solid #8F7247!important;}.a.chb.cta_link .cta span{background:none;color:#8F7247;border:none!important;transform:scale(0.6);transform-origin:center;}/* CTA HOVER */.a.chb.cta_black .cta span:hover{background:#fff;color:#000;border:1px solid #000!important;}.a.chb.cta_white .cta span:hover{background:#000;color:#fff;border:1px solid #fff!important;}.a.chb.cta_gold .cta span:hover{background:#655032;border:1px solid #655032!important;}/* SHARED - HOMEPAGE */.home-bloc .overlay{position:relative;height:100%;}.home-bloc h3, .home-bloc h2{font-size:30px;line-height:36px;letter-spacing:1px;font-weight:500;}.home-bloc p{font-size:16px;line-height:19px;letter-spacing:1px;font-weight:400;}.home-bloc .cta{font-size:16px;line-height:19px;letter-spacing:1px;font-weight:700;}.home-bloc h3, .home-bloc h2, .home-bloc p, .home-bloc .cta{padding-bottom:16px!important;text-align:center;}.home-bloc h3, .home-bloc h2{padding-top:16px!important;}.home-bloc .cta{position:absolute;bottom:16px;width:100%;padding-bottom:0!important;}.home_x3_top, .home_x2, .home_x3_bottom, .home_x1{position:relative;border:1px solid #999;box-sizing:border-box;border-radius:36px;}.home_x3_top{width:365px;height:200px;background:#f4e5d2ee;overflow:hidden;}.home_x2{width:564px;height:400px;overflow:hidden;}.home_x2 img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;}.home_x2 h2{text-transform:uppercase;padding-left:32px!important;padding-right:32px!important;}.home_x3_bottom{width:365px;height:400px;overflow:hidden;}.home_x3_bottom img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;}.home_x3_bottom h2{text-transform:uppercase;}@media screen and (max-width:996px){.home_x3_bottom{width:400px;height:180px;margin-bottom:32px;}.home_x3_bottom .overlay{width:50%;}.home_x3_bottom .align_right .overlay{margin-left:50%;}.home_x3_bottom h2{text-align:left;height:120px;display:flex;align-items:center;margin-left:12px;padding-left:16px!important;padding-right:16px!important;}.home_x3_bottom .align_right h2{justify-content:flex-end;}.home_x3_bottom p{display:none;}.home_x3_bottom .cta{text-align:left;}.home_x3_bottom .align_right .cta{text-align:right;}}/* SPECIFIC - HOMEPAGE - GLOBAL */#home-wrapper{display:flex;flex-direction:column;}.home-header{order:5;}#x3top-container{order:10;}.home_x1{order:15;}.home_brand{order:20;}#x2-container{order:25;}#x3bottom-container{order:30;}/* SPECIFIC - HOMEPAGE - X3 TOP */#x3top-container{display:flex;justify-content:space-between;width:100%;max-width:1160px;position:relative;margin:12px auto 0;}.widthheader #x3top-container{margin:-100px auto 0;}.home_x3_top{max-width:calc((100% - 64px) / 3);}.home_x3_top > *{width:100%;height:100%;}@media screen and (max-width:996px){#x3top-container{flex-direction:column;align-items:center;margin-top:32px!important;}.home_x3_top{margin-bottom:32px;max-width:initial;width:calc(100% - 32px);max-width:400px;}.home_x3_top:last-of-type{margin-bottom:0;}}/* SPECIFIC - HOMEPAGE - X2 */#x2-container{position:relative;display:flex;justify-content:space-between;width:100%;max-width:1160px;margin:32px auto 0;}.home_x2{max-width:calc((100% - 32px) / 2);}.home_x2 > *{width:100%;height:100%;}@media screen and (max-width:996px){#x2-container{flex-direction:column;align-items:center;margin-top:32px;}.home_x2{width:calc(100vw - 32px);max-width:564px;}#home_x2_1{margin-bottom:32px;}}@media screen and (max-width:540px){ /* Box starts to shrink, remove p */.home_x2 p{display:none;}}/* SPECIFIC - HOMEPAGE - X3 BOTTOM */#x3bottom-container{position:relative;display:flex;justify-content:space-between;width:100%;max-width:1160px;margin:32px auto 32px;}.home_x3_bottom{max-width:calc((100% - 64px) / 3);}.home_x3_bottom > *{width:100%;height:100%;}@media screen and (max-width:996px){#x3bottom-container{flex-direction:column;align-items:center;}.home_x3_bottom{max-width:calc(100% - 32px);}}/* SHARED - POPIN */.popin_conditions_SB{position: absolute; bottom: 0;right:50%; width: auto!important;left: auto!important; line-height: 20px;cursor: pointer;font-size:10px;pointer-events: initial;}@media screen and (max-width: 767px){.popin_conditions_SB{right:25%;}}#popin-SB, .popin-SB{position:fixed;display:none;z-index:5000;left:0;top:0;height:100%;width:100%;background: rgba(100, 100, 100, 0.75);}#popin-SB .popin-overlay{position:fixed;width:100%;height:100%;}#popin-SB .popin-content{position:fixed;overflow: auto;padding:32px;width: 90%;max-height:90%;max-width:760px;left:50%;top:50%;transform: translate(-50%, -50%);background-color:white;color:black;font-family: NespressoLucas;text-align:center;border-radius:4px;box-shadow:0px 0px 15px #000000a0;}@media (max-width: 767px) {#popin-SB .popin-content {padding:32px 16px;width:85%;}}#popin-SB .popin-content .popin-title {margin-bottom:20px;font-size:20px;line-height: 24px;letter-spacing: 1px;font-weight:700;}#popin-SB .popin-content p{margin-top: 10px;font-size:14px;line-height:17px;letter-spacing:1px;font-weight: normal;}#popin-SB .popin-content a{color:#8F7247;text-decoration:underline;}#popin-SB .popin-btn-close{position:absolute;font-size:20px;font-weight:500;width:20px;height:20px;right:10px;top:10px;cursor: pointer;}#popin-SB .popin-mobile{position:fixed;width: 100%;top:0;left:0;}/* SPECIFIC - POPIN */#popin-SB{display:block;opacity:1;transition: opacity .25s;}#popin-SB.appear{opacity:0;}/* SPECIFIC - MY OFFERS: MAIN */#custom_offers_main .topbanner{width:calc(50% - 25px);border:1px solid #666;margin-top:50px;}#custom_offers_main .topbanner .wrapper{position:relative;overflow:hidden;height:330px;}#custom_offers_main .topbanner .wrapper > img{width:auto;width:1297px;height:150px;position:relative;}#custom_offers_main .topbanner .overlay{position:relative;width:100%;left:0;height:180px;padding:16px 0;display:flex;flex-direction:column;justify-content:space-between;}#custom_offers_main .topbanner .overlay > *{position:relative;margin:0!important;font-size:16px;line-height:19px;letter-spacing:1px;}#custom_offers_main .topbanner .overlay > .h3{font-size:20px;line-height:24px;letter-spacing:1px;font-weight:bold;}#custom_offers_main .topbanner .overlay .cta span{font-weight:bold;}#custom_offers_main .topbanner .popin_conditions_SB{display:none!important;}@media screen and (max-width:1128px){#custom_offers_main .topbanner{width:564px;max-width:calc(100% - 32px);margin:50px auto 0!important;height:auto;}#custom_offers_main .topbanner .wrapper{transform:none;width:100%!important;height:auto;}#custom_offers_main .topbanner .overlay{height:auto;position:relative;}#custom_offers_main .topbanner .overlay > *:not(h3){margin-top:16px!important;}}@media screen and (max-width:768px){#custom_offers_main .topbanner .wrapper > img{position:absolute;height:100%;opacity:0.2;}}/* SPECIFIC MY OFFERS: SECONDARY */#custom_offers_secondary .topbanner{margin-bottom:32px;border:1px solid #666;}#custom_offers_secondary .topbanner:first-child{margin-top:16px;}#custom_offers_secondary .topbanner .overlay{display:flex;flex-direction:column;justify-content:space-around;}#custom_offers_secondary .topbanner .overlay > *{position:relative;margin:0!important;font-size:16px;line-height:19px;letter-spacing:1px;}#custom_offers_secondary .topbanner .overlay > *:not(p){font-weight:bold;}#custom_offers_secondary .topbanner .popin_conditions_SB{display:none!important;}@media screen and (max-width:1128px){#custom_offers_secondary .topbanner{height:auto;}#custom_offers_secondary .topbanner .wrapper{transform:none;}}@media screen and (max-width:996px){#custom_offers_secondary .topbanner{width:564px;max-width:calc(100% - 32px);margin:16px auto!important;height:auto;}#custom_offers_secondary .topbanner .wrapper{transform:none;width:100%!important;height:auto;overflow:hidden;}#custom_offers_secondary .topbanner .overlay{height:auto;min-height:150px;position:relative;justify-content:center;}#custom_offers_secondary .topbanner .overlay > *:not(h3){margin-top:16px!important;}#custom_offers_secondary .topbanner .wrapper > img{position:absolute;width:1297px;height:150px;opacity:0.2;}}`;return css;}}var ctool = new CHMarketBanners(); // Main banner classfunction set_customSB(campaign, banners, push = true){/* Main function called to set banners */// Push banners into classctool.pushBanners(campaign, banners, push);}// Manages resize eventvar CHBannersResizeTimeout;var CHBannerLastResizeCallTime = 0;function CHBannersResize() {// Change image on home X3 bottom$(".home_x3_bottom").each(function(){ctool.getBannerX3SrcAndSet($(this).find("img"));});// Change image on home X1$(".home_x1").each(function(){ctool.getBannerX1SrcAndSet($(this).find("img"));});}function CHBannersResizeHandle() {const now = Date.now(); /* Function to handle the resize event */if (now - CHBannerLastResizeCallTime > 750) { // Call the resize function immediatelyCHBannersResize();CHBannerLastResizeCallTime = now;}clearTimeout(CHBannersResizeTimeout); // Clear the previous timeoutCHBannersResizeTimeout = setTimeout(function() { // Set a new timeout to trigger 500ms after resizing stopsCHBannersResize(); // Call the function one last time after resizing has stopped}, 750);}if (ctool.pageUrl.pageType === "home"){ // Add resize events only on homepagewindow.addEventListener('resize', CHBannersResizeHandle);}// Manage Scrolling eventsvar CHBannersScrollTimeout, CHBannersScrollInterval, CHBannersIsScrolling = false;var CHBannersHeaderHeigh = false, CHBannersDBHeight = 0, CHBannersDBHeightSticky = 0;// Function to be executed during scrollfunction handleScroll() {if (CHBannersIsScrolling === false) clearInterval(CHBannersScrollInterval); // Clear intervalconst $element = $('.chb.db');if (!$element.length > 0) return false;if (!CHBannersDBHeight) CHBannersDBHeight = $element.outerHeight();// Handle scroll: add sticky to DBif (!CHBannersHeaderHeigh) CHBannersHeaderHeigh = $(".Header__top-wrapper").height(); // Recalc header height// Set security heightlet securityHeight = CHBannersHeaderHeigh;if (securityHeight < 60) securityHeight = 60; // 60 px minimumif (ctool.pageUrl.pageType === "checkout"){securityHeight = 34; // Depending on h3 height + paddingCHBannersHeaderHeigh = 0;}const scrollTop = $(window).scrollTop();if ($element.hasClass("sticky")){if (scrollTop <= securityHeight){$element.removeClass("sticky");ctool.db_setStepsHeight();$element.next().css("margin-top", "0");}}else if (scrollTop > securityHeight + CHBannersHeaderHeigh){$element.addClass("sticky");if (!CHBannersDBHeightSticky) CHBannersDBHeightSticky = $element.height();ctool.db_setStepsHeight();// Margin top to apply depends on page locationlet marginTop = CHBannersDBHeight;if (ctool.pageUrl.pageType === "checkout") marginTop += 10;$element.next().css("margin-top", `${marginTop}px`);}}// Event listener for scrollif (ctool.pageUrl.pageType === "plp" || ctool.pageUrl.pageType === "checkout"){ // Add scroll events events only on PLP and checkoutwindow.addEventListener('scroll', function () {if (!CHBannersIsScrolling) { // Only run the scrolling function every 150msCHBannersIsScrolling = true;CHBannersScrollInterval = setInterval(handleScroll, 150); // Run every 150ms while scrolling}clearTimeout(CHBannersScrollTimeout); // Set a timeout to detect when scrolling endsCHBannersScrollTimeout = setTimeout(function () {CHBannersIsScrolling = false;handleScroll(); // Run the function one last time after scroll}, 100);});}
DISCOVER OUR VARIOUS
POINTS OF SALE
BOUTIQUES
Experience Nespresso with all of your senses in our signature Nespresso boutiques, your one-stop-shop for coffee excellence. Here, you can discover our full range of coffees, machines and accessories, learn more about coffee science and Nespresso sustainability initiatives with our coffee experts, and even take part in exclusive coffee Masterclasses and events.
NESPRESSO POINT
ORDERING TERMINAL
Ordering terminal are interactive coffee terminals where you can conveniently purchase your favourite Nespresso coffees*. Simply place your order at the ordering terminal and your coffee will be waiting for you at the nearest checkout counter.
*Selected range of coffee references (no Vertuo)
');}$(".topbannermyaccountTrackTrace").click(function() {gtmDataObject.push({event: 'customEvent',eventRaisedBy: 'FreeHTML',eventCategory: 'Custom - Track & Trace',eventAction: 'Click banner Track & Trace',eventLabel: '',nonInteraction: 0});});$(".topbannermyaccountTrackTraceMobile").click(function() {gtmDataObject.push({event: 'customEvent',eventRaisedBy: 'FreeHTML',eventCategory: 'Custom - Track & Trace',eventAction: 'Click banner Track & Trace',eventLabel: '',nonInteraction: 0});});napi.checkout().getMyLastOrder().then(function(lastorder){lastorder.delivery.deliveryAddressnapi.customer().getAddresses().then(function(addresses){$(addresses).each(function(i,v){if(v.id == lastorder.delivery.deliveryAddress){console.log(v.zipCode)$('.topbannermyaccountTrackTraceLink').attr('href',$('.topbannermyaccountTrackTraceLink').attr('href')+"?pp_order_id="+lastorder.orderId+"&pp_postal_code="+v.zipCode)}})})},function(){})clearInterval(addTopBannerTrackTrace);}}, 100);}